From ebc80644ea075679639954be20ce90f81b38c45e Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:44:17 +0000 Subject: [PATCH 01/26] chore: refresh skill coverage and alerts --- ref_repo_change_alerts.json | 2 +- repo_state.json | 8 +- skill_coverage.json | 484 +++++++++++++++++++----------------- 3 files changed, 267 insertions(+), 227 deletions(-) diff --git a/ref_repo_change_alerts.json b/ref_repo_change_alerts.json index b9499df18..5f972c307 100644 --- a/ref_repo_change_alerts.json +++ b/ref_repo_change_alerts.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated": "2026-04-21T14:42:33Z", + "generated": "2026-05-15T14:44:16Z", "summary": { "reference_changes": 0, "skills_affected": 0 diff --git a/repo_state.json b/repo_state.json index 6b74d5cd7..e29345370 100644 --- a/repo_state.json +++ b/repo_state.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "last_checked": "2026-04-21T14:42:33Z", + "last_checked": "2026-05-15T14:44:16Z", "docs_repo": { "branch": "master", "last_checked_commit": "cc6c24ebf10e1e4e620ad152ae9af9e3d19aa6ae" @@ -12,7 +12,11 @@ }, "polkadot-cookbook": { "branch": "master", - "last_checked_commit": "fcde576647f457574fc4168f316b0941ed8e34ea" + "last_checked_commit": "8a5f73878efe1b1f67fb18b0dd715f3ea04f231e" + }, + "polkavm-hardhat-examples": { + "branch": "master", + "last_checked_commit": "c84f8a3c0cbb871dad6b81a69a77bccf148c78ab" } } } diff --git a/skill_coverage.json b/skill_coverage.json index e22b286f2..d8185f78b 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,941 +1,977 @@ { "schema_version": "1", - "generated": "2026-04-21T23:59:00Z", + "generated": "2026-05-15T14:44:16Z", "summary": { - "total_candidates": 136, - "up_to_date": 80, - "stale": 0, - "uncovered": 56, - "reviewed_no_skill": 0 + "total_candidates": 142, + "up_to_date": 0, + "stale": 56, + "uncovered": 63, + "reviewed_no_skill": 23 }, "pages": { "chain-interactions/accounts/create-account.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "create-polkadot-account" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/accounts/query-accounts.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "query-account-info-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/query-data/query-rest.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "query-chain-data-sidecar-rest" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/query-data/query-sdks.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "query-chain-state-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/query-data/runtime-api-calls.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "call-runtime-apis-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "calculate-transaction-fees-papi" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "replay-dry-run-xcm-chopsticks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "estimate-xcm-fees-teleport" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "transfer-assets-parachains-paraspell" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "pay-fees-alternative-token" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/with-sdks.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "send-transactions-sdks" ], - "status": "up_to_date" + "status": "stale" + }, + "chain-interactions/store-data/bulletin-chain.md": { + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, + "skills": [], + "status": "uncovered" }, "chain-interactions/token-operations/convert-assets.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "chain-interactions/token-operations/register-foreign-asset.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "chain-interactions/token-operations/register-local-asset.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "get-support.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-collator.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-node/parachain-rpc.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "run-parachain-rpc-node" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "run-polkadot-hub-rpc-node" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-node/relay-chain/bootnode.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-node/relay-chain/full-node.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-node/relay-chain/secure-wss.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "set-up-polkadot-validator-node" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/operational-tasks/general-management.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/operational-tasks/upgrade-your-node.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/requirements.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [ "onboard-polkadot-validator" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/staking-mechanics/rewards.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/customize-runtime/add-existing-pallets.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "add-existing-pallet-to-runtime" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/add-pallet-instances.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "configure-multiple-pallet-instances" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/add-smart-contract-functionality.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "benchmark-frame-pallet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "create-frame-pallet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/pallet-development/mock-runtime.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "set-up-pallet-mock-runtime" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "unit-test-frame-pallet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/get-started.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/install-polkadot-sdk.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "install-polkadot-sdk" ], - "status": "up_to_date" + "status": "stale" }, "parachains/integrations/indexers.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/integrations/oracles.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/integrations/wallets.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/interoperability/channels-between-parachains.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/interoperability/channels-with-system-parachains.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/interoperability/get-started.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [ "deploy-parachain-to-polkadot-testnet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/launch-a-parachain/obtain-coretime.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "set-up-parachain-template" ], - "status": "up_to_date" + "status": "stale" }, "parachains/runtime-maintenance/coretime-renewal.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/runtime-maintenance/runtime-upgrades.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/runtime-maintenance/storage-migrations.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/runtime-maintenance/unlock-parachains.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/testing/fork-a-parachain.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "parachains/testing/run-a-parachain-network.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "policies/ai-chatbot-policy.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "policies/cookie-policy.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "policies/privacy-policy.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "policies/terms-of-use.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/glossary.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/governance/origins-tracks.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/accounts.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/blocks-transactions-fees/blocks.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/blocks-transactions-fees/fees.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/blocks-transactions-fees/transactions.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/chain-data.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "retrieve-runtime-metadata" ], - "status": "up_to_date" + "status": "stale" }, "reference/parachains/consensus/async-backing.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/consensus/elastic-scaling.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/consensus/inclusion-pipeline.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/cryptography.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/data-encoding.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/interoperability.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/networks.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/node-and-runtime.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/randomness.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/assets.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/bridging.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/collectives-and-daos.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/consensus-and-security/agile-coretime.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/consensus-and-security/pos-consensus.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/consensus-and-security/relay-chain.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, + "skills": [], + "status": "uncovered" + }, + "reference/polkadot-hub/data-storage.md": { + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/people-and-identity.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/smart-contracts.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/chopsticks.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [ "set-up-chopsticks-fork" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/dedot.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "interact-with-chain-dedot" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/light-clients.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/moonwall.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [ "set-up-e2e-testing-moonwall" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/omninode.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "run-parachain-node-omni-node" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/papi.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/paraspell.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/polkadart.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "reference/tools/polkadot-js-api.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "use-polkadot-js-api" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/pop-cli.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "reference/tools/py-substrate-interface.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "interact-polkadot-node-py-substrate" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/sidecar.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/subxt.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "interact-polkadot-node-subxt" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/xcm-tools.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/zombienet.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "spawn-test-network-zombienet" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/connect.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [ "connect-polkadot-hub-testnet" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "build-dapp-viem-nextjs" ], - "status": "up_to_date" + "status": "stale" }, - "smart-contracts/cookbook/eth-dapps/uniswap-v2.md": { - "last_edited": "2026-04-21T13:05:20+00:00", - "last_scanned": "2026-04-21T20:00:00Z", + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, + "skills": [], + "status": "uncovered" + }, + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, "skills": [], - "status": "up_to_date" + "status": "uncovered" + }, + "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, + "skills": [], + "status": "uncovered" + }, + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, + "skills": [], + "status": "uncovered" + }, + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { + "last_edited": "2026-05-15T14:44:08+00:00", + "last_scanned": null, + "skills": [], + "status": "uncovered" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "deploy-basic-contract-hardhat" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "deploy-basic-contract-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "deploy-erc20-token-hardhat" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "deploy-erc20-token-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "deploy-erc721-nft-hardhat" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "deploy-erc721-nft-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/foundry.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "set-up-foundry-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/hardhat-polkadot.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "set-up-hardhat-pvm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/hardhat.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "set-up-hardhat-evm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/local-dev-node.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "set-up-local-dev-node" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/remix.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "connect-remix-polkadot" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/explorers.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/faucet.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "up_to_date" + "status": "reviewed_no_skill" }, "smart-contracts/for-eth-devs/accounts.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/blocks-transactions-fees.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/contract-deployment.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/dual-vm-stack.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/evm-vs-pvm.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/gas-model.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/json-rpc-apis.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/get-started.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/integrations/wallets.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "connect-wallet-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/ethers-js.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "deploy-contracts-ethers-js" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/viem.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "deploy-contracts-viem" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/wagmi.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "use-wagmi-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/web3-js.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "deploy-interact-contracts-web3js" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/web3-py.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "deploy-interact-contracts-web3py" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/overview.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/precompiles/erc20.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "interact-erc20-precompile-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/precompiles/eth-native.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/precompiles/storage.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "interact-storage-precompile-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/precompiles/system.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "interact-system-precompile-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/precompiles/xcm.md": { - "last_edited": "2026-04-21T13:05:20+00:00", + "last_edited": "2026-05-15T14:44:08+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [ "interact-xcm-precompile-remix" ], - "status": "up_to_date" + "status": "stale" } } } From 5ff9e52b9c6e347a84b3ca6d97f683fb4951ab12 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:44:38 +0000 Subject: [PATCH 02/26] chore: refresh skill coverage and alerts --- ref_repo_change_alerts.json | 2 +- repo_state.json | 2 +- skill_coverage.json | 286 ++++++++++++++++++------------------ 3 files changed, 145 insertions(+), 145 deletions(-) diff --git a/ref_repo_change_alerts.json b/ref_repo_change_alerts.json index 5f972c307..06c7d7b2b 100644 --- a/ref_repo_change_alerts.json +++ b/ref_repo_change_alerts.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated": "2026-05-15T14:44:16Z", + "generated": "2026-05-15T14:44:38Z", "summary": { "reference_changes": 0, "skills_affected": 0 diff --git a/repo_state.json b/repo_state.json index e29345370..90e710a7a 100644 --- a/repo_state.json +++ b/repo_state.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "last_checked": "2026-05-15T14:44:16Z", + "last_checked": "2026-05-15T14:44:38Z", "docs_repo": { "branch": "master", "last_checked_commit": "cc6c24ebf10e1e4e620ad152ae9af9e3d19aa6ae" diff --git a/skill_coverage.json b/skill_coverage.json index d8185f78b..bb060ec00 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated": "2026-05-15T14:44:16Z", + "generated": "2026-05-15T14:44:38Z", "summary": { "total_candidates": 142, "up_to_date": 0, @@ -10,7 +10,7 @@ }, "pages": { "chain-interactions/accounts/create-account.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "create-polkadot-account" @@ -18,7 +18,7 @@ "status": "stale" }, "chain-interactions/accounts/query-accounts.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "query-account-info-sdks" @@ -26,7 +26,7 @@ "status": "stale" }, "chain-interactions/query-data/query-rest.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "query-chain-data-sidecar-rest" @@ -34,7 +34,7 @@ "status": "stale" }, "chain-interactions/query-data/query-sdks.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "query-chain-state-sdks" @@ -42,7 +42,7 @@ "status": "stale" }, "chain-interactions/query-data/runtime-api-calls.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "call-runtime-apis-sdks" @@ -50,7 +50,7 @@ "status": "stale" }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "calculate-transaction-fees-papi" @@ -58,7 +58,7 @@ "status": "stale" }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "replay-dry-run-xcm-chopsticks" @@ -66,7 +66,7 @@ "status": "stale" }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "estimate-xcm-fees-teleport" @@ -74,13 +74,13 @@ "status": "stale" }, "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "transfer-assets-parachains-paraspell" @@ -88,7 +88,7 @@ "status": "stale" }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "pay-fees-alternative-token" @@ -96,7 +96,7 @@ "status": "stale" }, "chain-interactions/send-transactions/with-sdks.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T17:00:00Z", "skills": [ "send-transactions-sdks" @@ -104,43 +104,43 @@ "status": "stale" }, "chain-interactions/store-data/bulletin-chain.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "chain-interactions/token-operations/convert-assets.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "chain-interactions/token-operations/register-foreign-asset.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "chain-interactions/token-operations/register-local-asset.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "get-support.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-collator.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-node/parachain-rpc.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:30:00Z", "skills": [ "run-parachain-rpc-node" @@ -148,7 +148,7 @@ "status": "stale" }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "run-polkadot-hub-rpc-node" @@ -156,31 +156,31 @@ "status": "stale" }, "node-infrastructure/run-a-node/relay-chain/bootnode.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-node/relay-chain/full-node.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-node/relay-chain/secure-wss.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "set-up-polkadot-validator-node" @@ -188,43 +188,43 @@ "status": "stale" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/operational-tasks/general-management.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/operational-tasks/upgrade-your-node.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "node-infrastructure/run-a-validator/requirements.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [ "onboard-polkadot-validator" @@ -232,19 +232,19 @@ "status": "stale" }, "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "node-infrastructure/run-a-validator/staking-mechanics/rewards.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/customize-runtime/add-existing-pallets.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "add-existing-pallet-to-runtime" @@ -252,7 +252,7 @@ "status": "stale" }, "parachains/customize-runtime/add-pallet-instances.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "configure-multiple-pallet-instances" @@ -260,13 +260,13 @@ "status": "stale" }, "parachains/customize-runtime/add-smart-contract-functionality.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "benchmark-frame-pallet" @@ -274,7 +274,7 @@ "status": "stale" }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "create-frame-pallet" @@ -282,7 +282,7 @@ "status": "stale" }, "parachains/customize-runtime/pallet-development/mock-runtime.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "set-up-pallet-mock-runtime" @@ -290,7 +290,7 @@ "status": "stale" }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "unit-test-frame-pallet" @@ -298,13 +298,13 @@ "status": "stale" }, "parachains/get-started.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/install-polkadot-sdk.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "install-polkadot-sdk" @@ -312,43 +312,43 @@ "status": "stale" }, "parachains/integrations/indexers.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/integrations/oracles.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/integrations/wallets.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/interoperability/channels-between-parachains.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/interoperability/channels-with-system-parachains.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/interoperability/get-started.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [ "deploy-parachain-to-polkadot-testnet" @@ -356,13 +356,13 @@ "status": "stale" }, "parachains/launch-a-parachain/obtain-coretime.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [ "set-up-parachain-template" @@ -370,103 +370,103 @@ "status": "stale" }, "parachains/runtime-maintenance/coretime-renewal.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/runtime-maintenance/runtime-upgrades.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/runtime-maintenance/storage-migrations.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "parachains/runtime-maintenance/unlock-parachains.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/testing/fork-a-parachain.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "parachains/testing/run-a-parachain-network.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "policies/ai-chatbot-policy.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "policies/cookie-policy.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "policies/privacy-policy.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "policies/terms-of-use.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/glossary.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/governance/origins-tracks.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/accounts.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/blocks-transactions-fees/blocks.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/blocks-transactions-fees/fees.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/blocks-transactions-fees/transactions.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/chain-data.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "retrieve-runtime-metadata" @@ -474,115 +474,115 @@ "status": "stale" }, "reference/parachains/consensus/async-backing.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/consensus/elastic-scaling.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/consensus/inclusion-pipeline.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/cryptography.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/data-encoding.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/interoperability.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/networks.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/node-and-runtime.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/parachains/randomness.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/assets.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/bridging.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/collectives-and-daos.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/consensus-and-security/agile-coretime.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/consensus-and-security/pos-consensus.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/consensus-and-security/relay-chain.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/data-storage.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/people-and-identity.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/polkadot-hub/smart-contracts.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/chopsticks.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [ "set-up-chopsticks-fork" @@ -590,7 +590,7 @@ "status": "stale" }, "reference/tools/dedot.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "interact-with-chain-dedot" @@ -598,13 +598,13 @@ "status": "stale" }, "reference/tools/light-clients.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/moonwall.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [ "set-up-e2e-testing-moonwall" @@ -612,7 +612,7 @@ "status": "stale" }, "reference/tools/omninode.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "run-parachain-node-omni-node" @@ -620,25 +620,25 @@ "status": "stale" }, "reference/tools/papi.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/paraspell.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/polkadart.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [], "status": "reviewed_no_skill" }, "reference/tools/polkadot-js-api.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "use-polkadot-js-api" @@ -646,13 +646,13 @@ "status": "stale" }, "reference/tools/pop-cli.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "reviewed_no_skill" }, "reference/tools/py-substrate-interface.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "interact-polkadot-node-py-substrate" @@ -660,13 +660,13 @@ "status": "stale" }, "reference/tools/sidecar.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/subxt.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [ "interact-polkadot-node-subxt" @@ -674,13 +674,13 @@ "status": "stale" }, "reference/tools/xcm-tools.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "reference/tools/zombienet.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "spawn-test-network-zombienet" @@ -688,7 +688,7 @@ "status": "stale" }, "smart-contracts/connect.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [ "connect-polkadot-hub-testnet" @@ -696,7 +696,7 @@ "status": "stale" }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "build-dapp-viem-nextjs" @@ -704,37 +704,37 @@ "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "deploy-basic-contract-hardhat" @@ -742,7 +742,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "deploy-basic-contract-remix" @@ -750,7 +750,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "deploy-erc20-token-hardhat" @@ -758,7 +758,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "deploy-erc20-token-remix" @@ -766,7 +766,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "deploy-erc721-nft-hardhat" @@ -774,7 +774,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "deploy-erc721-nft-remix" @@ -782,7 +782,7 @@ "status": "stale" }, "smart-contracts/dev-environments/foundry.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "set-up-foundry-polkadot-hub" @@ -790,7 +790,7 @@ "status": "stale" }, "smart-contracts/dev-environments/hardhat-polkadot.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "set-up-hardhat-pvm" @@ -798,7 +798,7 @@ "status": "stale" }, "smart-contracts/dev-environments/hardhat.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T18:00:00Z", "skills": [ "set-up-hardhat-evm" @@ -806,7 +806,7 @@ "status": "stale" }, "smart-contracts/dev-environments/local-dev-node.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [ "set-up-local-dev-node" @@ -814,7 +814,7 @@ "status": "stale" }, "smart-contracts/dev-environments/remix.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "connect-remix-polkadot" @@ -822,67 +822,67 @@ "status": "stale" }, "smart-contracts/explorers.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/faucet.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "reviewed_no_skill" }, "smart-contracts/for-eth-devs/accounts.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/blocks-transactions-fees.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/contract-deployment.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/dual-vm-stack.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/evm-vs-pvm.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/gas-model.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/for-eth-devs/json-rpc-apis.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/get-started.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/integrations/wallets.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [ "connect-wallet-polkadot-hub" @@ -890,7 +890,7 @@ "status": "stale" }, "smart-contracts/libraries/ethers-js.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "deploy-contracts-ethers-js" @@ -898,7 +898,7 @@ "status": "stale" }, "smart-contracts/libraries/viem.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:00:00Z", "skills": [ "deploy-contracts-viem" @@ -906,7 +906,7 @@ "status": "stale" }, "smart-contracts/libraries/wagmi.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "use-wagmi-polkadot-hub" @@ -914,7 +914,7 @@ "status": "stale" }, "smart-contracts/libraries/web3-js.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:30:00Z", "skills": [ "deploy-interact-contracts-web3js" @@ -922,7 +922,7 @@ "status": "stale" }, "smart-contracts/libraries/web3-py.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "deploy-interact-contracts-web3py" @@ -930,13 +930,13 @@ "status": "stale" }, "smart-contracts/overview.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/precompiles/erc20.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "interact-erc20-precompile-polkadot-hub" @@ -944,13 +944,13 @@ "status": "stale" }, "smart-contracts/precompiles/eth-native.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": null, "skills": [], "status": "uncovered" }, "smart-contracts/precompiles/storage.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "interact-storage-precompile-remix" @@ -958,7 +958,7 @@ "status": "stale" }, "smart-contracts/precompiles/system.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:00:00Z", "skills": [ "interact-system-precompile-remix" @@ -966,7 +966,7 @@ "status": "stale" }, "smart-contracts/precompiles/xcm.md": { - "last_edited": "2026-05-15T14:44:08+00:00", + "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [ "interact-xcm-precompile-remix" From 1ae8cdd21dc42a46f4638ab66bdf8aa694326416 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:48:53 +0000 Subject: [PATCH 03/26] chore: auto-triage skill candidates (batch) --- skill_candidates.json | 241 ++++++++++++++++++++++++++++++++---------- 1 file changed, 184 insertions(+), 57 deletions(-) diff --git a/skill_candidates.json b/skill_candidates.json index 63ebe5a66..50d329111 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,6 +1,6 @@ { "schema_version": "2", - "generated": "2026-04-21T23:30:00Z", + "generated": "2026-05-15T14:48:53Z", "project_id": "polkadot-docs", "scoring_rubric_version": "1.0", "candidates": [ @@ -281,7 +281,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent \u2014 requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent \u2014 bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", + "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent — requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent — bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", "scoring": { "signals": [ "P1", @@ -345,7 +345,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:30:00Z", - "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account \u2014 no real tokens needed). Self-contained. No cookbook badge.", + "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account — no real tokens needed). Self-contained. No cookbook badge.", "scoring": { "signals": [ "P1", @@ -490,7 +490,7 @@ }, "priority_score": 11 }, - "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) \u2014 prerequisite provisioning is outside current skill capability." + "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) — prerequisite provisioning is outside current skill capability." }, { "skill_id": "install-polkadot-sdk", @@ -502,7 +502,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch \u2014 covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", + "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch — covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", "scoring": { "signals": [ "P1", @@ -535,7 +535,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T19:00:00Z", - "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent \u2014 directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 \u2014 requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime).", + "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent — directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 — requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime).", "scoring": { "signals": [ "P1", @@ -666,7 +666,7 @@ "category": "guide", "priority": "high", "status": "blocked", - "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 \u2192 guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", + "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 → guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", "scoring": { "signals": [ "P2", @@ -699,7 +699,7 @@ "category": "guide", "priority": "medium", "status": "blocked", - "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 \u2192 Rule 6 guide. K1=0 \u2014 prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", + "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 → Rule 6 guide. K1=0 — prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", "scoring": { "signals": [ "P2", @@ -730,7 +730,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present \u2014 numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", + "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present — numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", "scoring": { "signals": [ "P1", @@ -761,7 +761,7 @@ "category": "tutorial", "priority": "low", "status": "blocked", - "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 \u2014 intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", + "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 — intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -789,7 +789,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 \u2014 the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", + "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 — the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -821,7 +821,7 @@ "category": "tutorial", "priority": "low", "status": "blocked", - "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 \u2014 intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", + "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 — intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -854,7 +854,7 @@ "composite": true, "priority": "medium", "status": "blocked", - "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 \u2192 medium.", + "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 → medium.", "scoring": { "signals": [ "P1", @@ -920,7 +920,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself \u2014 delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", + "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself — delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", "scoring": { "signals": [ "P1", @@ -956,7 +956,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 \u2014 requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 \u2192 medium.", + "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 — requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 → medium.", "scoring": { "signals": [ "P1", @@ -988,7 +988,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 \u2014 requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 \u2192 medium.", + "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 — requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 → medium.", "scoring": { "signals": [ "P1", @@ -1020,7 +1020,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 \u2014 requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", + "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 — requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", "scoring": { "signals": [ "P1", @@ -1053,7 +1053,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 \u2014 requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", + "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 — requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", "scoring": { "signals": [ "P1", @@ -1087,7 +1087,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 \u2014 requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", + "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 — requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", "scoring": { "signals": [ "P1", @@ -1121,7 +1121,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 \u2014 requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", + "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 — requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", "scoring": { "signals": [ "P1", @@ -1231,7 +1231,7 @@ "category": "tutorial", "priority": "low", "status": "built", - "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 \u2014 intro explicitly references set-up-the-parachain-template as required prior step. K2=0 \u2014 INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", + "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 — intro explicitly references set-up-the-parachain-template as required prior step. K2=0 — INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", "scoring": { "signals": [ "P1", @@ -1263,7 +1263,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 \u2014 explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", + "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 — explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", "scoring": { "signals": [ "P1", @@ -1288,7 +1288,7 @@ ], "priority_score": 9 }, - "blocked_reason": "Requires acquiring on-chain coretime on a live network \u2014 financial and governance prerequisites make it impractical as a generated skill." + "blocked_reason": "Requires acquiring on-chain coretime on a live network — financial and governance prerequisites make it impractical as a generated skill." }, { "skill_id": "renew-parachain-coretime", @@ -1299,7 +1299,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 \u2014 requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", + "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 — requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", "scoring": { "signals": [ "P1", @@ -1335,7 +1335,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 \u2014 requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 \u2014 code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", + "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 — requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 — code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", "scoring": { "signals": [ "P1", @@ -1368,7 +1368,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 \u2192 Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 \u2014 requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", + "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 → Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 — requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", "scoring": { "signals": [ "P1", @@ -1404,7 +1404,7 @@ "category": "tutorial", "priority": "high", "status": "blocked", - "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 \u2014 tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) \u2192 category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", + "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 — tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) → category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", "scoring": { "signals": [ "P1", @@ -1445,7 +1445,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present \u2014 score 20, no ambiguity. K1=0 \u2014 Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 \u2014 INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 \u2192 medium.", + "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present — score 20, no ambiguity. K1=0 — Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 — INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 → medium.", "scoring": { "signals": [ "P1", @@ -1477,7 +1477,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic \u2014 same endpoint, same output. K3/K4/S3 absent.", + "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic — same endpoint, same output. K3/K4/S3 absent.", "scoring": { "signals": [ "P1", @@ -1650,7 +1650,7 @@ "category": "guide", "priority": "high", "status": "blocked", - "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) \u2014 Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", + "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) — Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", "scoring": { "signals": [ "P2", @@ -1757,7 +1757,7 @@ "category": "guide", "priority": "high", "status": "built", - "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer \u2014 all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", + "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer — all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", "scoring": { "signals": [ "P2", @@ -1858,7 +1858,7 @@ "category": "guide", "priority": "low", "status": "built", - "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher \u2014 flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate \u2192 paste address \u2192 click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", + "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher — flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate → paste address → click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", "scoring": { "signals": [ "P1", @@ -1994,7 +1994,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) \u2014 uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", + "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) — uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", "scoring": { "signals": [ "P1", @@ -2024,7 +2024,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired \u2014 tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", + "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired — tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", "scoring": { "signals": [ "P1", @@ -2056,7 +2056,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired \u2014 workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", + "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired — workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", "scoring": { "signals": [ "P1", @@ -2088,7 +2088,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired \u2014 deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired \u2014 code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", + "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired — deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired — code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", "scoring": { "signals": [ "P1", @@ -2120,7 +2120,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired \u2014 entirely GUI-driven (Remix IDE + MetaMask). K4 not fired \u2014 contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", + "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired — entirely GUI-driven (Remix IDE + MetaMask). K4 not fired — contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", "scoring": { "signals": [ "P1", @@ -2151,7 +2151,7 @@ "category": "guide", "priority": "high", "status": "built", - "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired \u2014 multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired \u2014 no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 \u2014 not ambiguous.", + "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired — multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired — no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 — not ambiguous.", "scoring": { "signals": [ "P2", @@ -2185,7 +2185,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot \u2014 mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", + "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot — mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", "scoring": { "signals": [ "P1", @@ -2218,7 +2218,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown \u2014 P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", + "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown — P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", "scoring": { "signals": [ "P1", @@ -2441,7 +2441,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) \u2014 hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", + "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) — hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", "scoring": { "signals": [ "P2", @@ -2469,7 +2469,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction \u2014 all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", + "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction — all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", "scoring": { "signals": [ "P1", @@ -2501,7 +2501,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction \u2014 all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", + "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction — all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", "scoring": { "signals": [ "P1", @@ -2532,7 +2532,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural \u2265 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", + "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural ≥ 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", "scoring": { "signals": [ "P1", @@ -2603,7 +2603,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 \u2192 medium.", + "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 → medium.", "scoring": { "signals": [ "P1", @@ -2663,6 +2663,104 @@ ] }, "built_at": "2026-04-21T23:30:00Z" + }, + { + "skill_id": "store-retrieve-data-bulletin-chain", + "title": "Store and Retrieve Data on the Bulletin Chain", + "source_pages": [ + "chain-interactions/store-data/bulletin-chain.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "Covers the full store/retrieve/renew cycle via two paths (Console UI and PAPI). Authorization requires Root origin, but the tutorial directs users to the testnet faucet — this is the intended developer onboarding flow, not a privileged on-chain role disqualifier. Code is embedded via --8<-- includes in the docs repo (no separate reference repo).", + "scoring": { + "signals": [ + "P1", + "P2", + "P3", + "P4", + "K1", + "K2", + "K3", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 20, + "reference": 0, + "conceptual": 0, + "not_applicable": 0 + }, + "priority_score": 22 + } + }, + { + "skill_id": "deploy-uniswap-v2-core-pvm", + "title": "Deploy Uniswap V2 Core (PVM) on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "PVM variant using polkavm-hardhat-examples repo. K1=0 because the testing step defers to the Local Development Node guide; TestNet deployment is self-contained. The .env file contains INSERT_* private key placeholders (expected user-specific inputs, not missing code). C1 fires for the Uniswap V2 architecture section but does not compete with the dominant procedural score.", + "scoring": { + "signals": [ + "P1", + "P2", + "P3", + "P4", + "C1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 20, + "reference": 0, + "conceptual": 5, + "not_applicable": 0 + }, + "priority_score": 20 + } + }, + { + "skill_id": "deploy-uniswap-v2-core-evm", + "title": "Deploy Uniswap V2 Core (EVM) on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "EVM (REVM) variant using revm-hardhat-examples repo with standard Hardhat + TypeScript. Canonical toggle variant (toggle.canonical: true). K1=0 because the testing step defers to the Local Development Node guide; TestNet deployment is self-contained. Uses Hardhat configuration variables for secure private key management.", + "scoring": { + "signals": [ + "P1", + "P2", + "P3", + "P4", + "C1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 20, + "reference": 0, + "conceptual": 5, + "not_applicable": 0 + }, + "priority_score": 20 + } } ], "supplementary_only": [ @@ -2929,7 +3027,7 @@ "vote-on-referendum", "create-opengov-referendum" ], - "notes": "Conceptual overview of OpenGov origins and tracks \u2014 explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", + "notes": "Conceptual overview of OpenGov origins and tracks — explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", "scoring": { "signals": [ "R3", @@ -2953,7 +3051,7 @@ "configure-runtime-pallet", "set-up-parachain-template" ], - "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points \u2014 but the page's primary purpose is deep reference exposition, not a task workflow.", + "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points — but the page's primary purpose is deep reference exposition, not a task workflow.", "scoring": { "signals": [ "P2", @@ -3159,7 +3257,7 @@ "build-parachain-runtime", "set-up-parachain-template" ], - "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) \u2014 rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", + "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) — rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", "scoring": { "signals": [ "R1", @@ -3212,7 +3310,7 @@ "set-up-parachain-template", "test-parachain-locally" ], - "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local \u2192 Paseo \u2192 production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", + "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local → Paseo → production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", "scoring": { "signals": [ "P1", @@ -3357,7 +3455,7 @@ "purchase-bulk-coretime", "set-up-parachain-template" ], - "notes": "Explains coretime scheduling concepts \u2014 bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", + "notes": "Explains coretime scheduling concepts — bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", "scoring": { "signals": [ "C1", @@ -3379,7 +3477,7 @@ "set-up-validator-node", "stake-dot-tokens" ], - "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps \u2014 entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", + "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps — entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", "scoring": { "signals": [ "C1", @@ -3401,7 +3499,7 @@ "set-up-parachain-template", "purchase-bulk-coretime" ], - "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram \u2014 no procedural content.", + "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram — no procedural content.", "scoring": { "signals": [ "C1", @@ -3422,7 +3520,7 @@ "relevant_to": [ "establish-on-chain-identity" ], - "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) \u2014 not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", + "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) — not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", "scoring": { "signals": [ "P1", @@ -3450,7 +3548,7 @@ "deploy-basic-contract-remix", "deploy-erc20-token-remix" ], - "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps \u2014 serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", + "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps — serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", "scoring": { "signals": [ "C1", @@ -3493,7 +3591,7 @@ "interact-with-chain-polkadart", "use-polkadot-js-api" ], - "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) \u2014 reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", + "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) — reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", "scoring": { "signals": [ "P2", @@ -3520,7 +3618,7 @@ "relevant_to": [ "transfer-assets-parachains" ], - "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual \u2014 explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", + "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual — explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", "scoring": { "signals": [ "P2", @@ -3802,6 +3900,35 @@ "reference" ] } + }, + { + "page": "reference/polkadot-hub/data-storage.md", + "category": "reference", + "relevant_to": [ + "store-retrieve-data-bulletin-chain" + ], + "notes": "Technical reference for the Bulletin Chain: full extrinsic table with origins, storage items, events, retrieval methods comparison, size limits, and authorization model. Useful supplementary context for the store/retrieve tutorial skill.", + "scoring": { + "signals": [ + "R1", + "R2", + "R3", + "C1", + "C2", + "C3" + ], + "category_scores": { + "procedural": 0, + "reference": 12, + "conceptual": 10, + "not_applicable": 0 + }, + "category_ambiguous": true, + "contending_categories": [ + "reference", + "conceptual" + ] + } } ], "not_applicable": [ @@ -3964,7 +4091,7 @@ { "page": "policies/terms-of-use.md", "category": "not_applicable", - "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet \u2014 pure legal boilerplate with no procedural, reference, or conceptual developer content.", + "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet — pure legal boilerplate with no procedural, reference, or conceptual developer content.", "scoring": { "signals": [ "N2" From e3c228f19fbc0aa5ba7c9d78590cf4fe867233c2 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:49:29 +0000 Subject: [PATCH 04/26] chore: auto-triage skill candidates (batch) --- skill_candidates.json | 69 ++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/skill_candidates.json b/skill_candidates.json index 50d329111..fb4bf6484 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,6 +1,6 @@ { "schema_version": "2", - "generated": "2026-05-15T14:48:53Z", + "generated": "2026-05-15T14:49:29Z", "project_id": "polkadot-docs", "scoring_rubric_version": "1.0", "candidates": [ @@ -2673,7 +2673,7 @@ "category": "tutorial", "priority": "high", "status": "pending", - "notes": "Covers the full store/retrieve/renew cycle via two paths (Console UI and PAPI). Authorization requires Root origin, but the tutorial directs users to the testnet faucet — this is the intended developer onboarding flow, not a privileged on-chain role disqualifier. Code is embedded via --8<-- includes in the docs repo (no separate reference repo).", + "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded — the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", "scoring": { "signals": [ "P1", @@ -2683,7 +2683,6 @@ "K1", "K2", "K3", - "S1", "S2", "S3" ], @@ -2693,19 +2692,19 @@ "conceptual": 0, "not_applicable": 0 }, - "priority_score": 22 + "priority_score": 18 } }, { "skill_id": "deploy-uniswap-v2-core-pvm", - "title": "Deploy Uniswap V2 Core (PVM) on Polkadot Hub", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", "source_pages": [ "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" ], "category": "tutorial", "priority": "high", "status": "pending", - "notes": "PVM variant using polkavm-hardhat-examples repo. K1=0 because the testing step defers to the Local Development Node guide; TestNet deployment is self-contained. The .env file contains INSERT_* private key placeholders (expected user-specific inputs, not missing code). C1 fires for the Uniswap V2 architecture section but does not compete with the dominant procedural score.", + "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded — polkavm-hardhat-examples is listed in context.md.", "scoring": { "signals": [ "P1", @@ -2713,6 +2712,7 @@ "P3", "P4", "C1", + "K1", "K2", "K3", "K4", @@ -2726,26 +2726,26 @@ "conceptual": 5, "not_applicable": 0 }, - "priority_score": 20 + "priority_score": 25 } }, { "skill_id": "deploy-uniswap-v2-core-evm", - "title": "Deploy Uniswap V2 Core (EVM) on Polkadot Hub", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", "source_pages": [ "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" ], "category": "tutorial", "priority": "high", "status": "pending", - "notes": "EVM (REVM) variant using revm-hardhat-examples repo with standard Hardhat + TypeScript. Canonical toggle variant (toggle.canonical: true). K1=0 because the testing step defers to the Local Development Node guide; TestNet deployment is self-contained. Uses Hardhat configuration variables for secure private key management.", + "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded — key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded — revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", "scoring": { "signals": [ "P1", "P2", - "P3", "P4", "C1", + "K1", "K2", "K3", "K4", @@ -2754,12 +2754,45 @@ "S3" ], "category_scores": { - "procedural": 20, + "procedural": 15, "reference": 0, "conceptual": 5, "not_applicable": 0 }, - "priority_score": 20 + "priority_score": 25 + } + }, + { + "skill_id": "deploy-uniswap-v2-periphery-evm", + "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo — no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded — revm-hardhat-examples is listed in context.md.", + "scoring": { + "signals": [ + "P1", + "P2", + "P4", + "C1", + "K1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 15, + "reference": 0, + "conceptual": 5, + "not_applicable": 0 + }, + "priority_score": 25 } } ], @@ -3907,27 +3940,21 @@ "relevant_to": [ "store-retrieve-data-bulletin-chain" ], - "notes": "Technical reference for the Bulletin Chain: full extrinsic table with origins, storage items, events, retrieval methods comparison, size limits, and authorization model. Useful supplementary context for the store/retrieve tutorial skill.", + "notes": "Full technical reference for the Bulletin Chain: all extrinsics (with origins), storage items, events, size limits, retrieval methods, and network endpoints. Useful context for the storage tutorial skill.", "scoring": { "signals": [ "R1", "R2", "R3", "C1", - "C2", "C3" ], "category_scores": { "procedural": 0, "reference": 12, - "conceptual": 10, + "conceptual": 7, "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "reference", - "conceptual" - ] + } } } ], From 22f159e6f6310071abe0d7091257c72f53a3eb5d Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:51:28 +0000 Subject: [PATCH 05/26] chore: auto-generate/update skills --- agent_skills_config.json | 61 +++++----- skill_coverage.json | 232 +++++++++++++++++++-------------------- 2 files changed, 152 insertions(+), 141 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 71f04fa88..34a773d71 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,13 +1,13 @@ { "schema_version": "0.1", - "generated": "2026-05-05T12:44:56Z", - "content_hash": "sha256:c8996d340d4f325a26903aea637c62d540ad63f6752d73d84dcc776bafc4ba01", + "generated": "2026-05-15T15:00:00Z", + "content_hash": "sha256:45a722aa580ffc79a0c0f1cab2e23fbc65779ea366852e285e996cf342003a99", "skills": [ { "id": "create-polkadot-account", "title": "Create a Polkadot Account Programmatically", "description": "Generates a new Polkadot SR25519 account (mnemonic, public key, SS58 address) using @polkadot/util-crypto and @polkadot/keyring. Use when you need to create a wallet, automate key generation, or produce a signing account for testing. Supports TypeScript (primary), Python (substrate-interface), and Rust (sp-core). Trigger phrases: 'create a Polkadot account', 'generate wallet keypair', 'new account', 'key generation'. Output: console-printed 12-word BIP39 mnemonic and SS58 address. No network connection required.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -130,13 +130,14 @@ ], "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console" } - ] + ], + "project_structure": "account-creator/\n├── create-account.ts\n└── package.json" }, { "id": "query-account-info-sdks", "title": "Query Account Information with SDKs", "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -271,13 +272,14 @@ ], "result": "After funding, account data is returned correctly" } - ] + ], + "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json" }, { "id": "query-chain-state-sdks", "title": "Query On-Chain State with SDKs", "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -424,13 +426,14 @@ ], "result": "USDC metadata and address balance returned" } - ] + ], + "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json" }, { "id": "call-runtime-apis-sdks", "title": "Call Runtime APIs with SDKs", "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -565,13 +568,14 @@ ], "result": "User understands nonce 0 is correct for a new or never-transacted account" } - ] + ], + "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json" }, { "id": "send-transactions-sdks", "title": "Send Transactions with SDKs", "description": "Constructs, signs, and submits a Balances.transfer_keep_alive extrinsic to Polkadot Hub using the PAPI TypeScript library. Use when you need to send tokens programmatically from a funded account. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Requires a funded account mnemonic and testnet PAS tokens. Trigger phrases: 'send a transaction', 'transfer tokens programmatically', 'sign and submit extrinsic', 'send PAS balance'. Uses dotenv to protect the sender mnemonic.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -756,7 +760,8 @@ ], "result": "Account funded; transaction succeeds on retry" } - ] + ], + "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json" }, { "id": "install-polkadot-sdk", @@ -918,7 +923,7 @@ "id": "deploy-basic-contract-hardhat", "title": "Deploy a Basic Smart Contract with Hardhat", "description": "Scaffolds a Hardhat v2 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -1115,7 +1120,8 @@ ], "result": "Fresh deployment succeeds with correct gas configuration" } - ] + ], + "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json" }, { "id": "deploy-erc20-token-hardhat", @@ -3788,7 +3794,7 @@ "id": "deploy-contracts-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", "description": "Sets up a Node.js project, compiles a Solidity contract with solc, deploys to Polkadot Hub TestNet using Ethers.js v6, and interacts with the deployed contract via read and write script calls. Use when integrating Ethers.js into a new or existing Node.js project targeting Polkadot Hub. Trigger phrases: 'deploy with ethers.js polkadot', 'ethers.js contract deployment', 'use ethers polkadot hub', 'ethersjs testnet'. Covers dotenv credential setup, custom Polkadot Hub provider configuration, and the compile-deploy-interact workflow. Produces a deployed contract with verified read/write interaction.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -4007,13 +4013,14 @@ ], "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet" } - ] + ], + "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json" }, { "id": "deploy-contracts-viem", "title": "Deploy Contracts to Polkadot Hub with Viem", "description": "Sets up a TypeScript project, compiles a Solidity contract, deploys to Polkadot Hub TestNet using Viem v2 walletClient.deployContract, and interacts with the deployed contract via publicClient and walletClient. Use when building a TypeScript project that needs direct Viem integration with Polkadot Hub without a Hardhat setup. Trigger phrases: 'deploy with viem polkadot', 'viem contract deployment', 'use viem polkadot hub', 'deploy viem typescript'. Covers custom Polkadot Hub chain definition, dotenv credential setup, and the compile-deploy-interact pattern. Companion to deploy-contracts-ethers-js.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -4250,13 +4257,14 @@ ], "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee" } - ] + ], + "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json" }, { "id": "set-up-pallet-mock-runtime", "title": "Set Up a Mock Runtime for Pallet Unit Testing", "description": "Creates a mock runtime module for FRAME pallet unit testing using construct_runtime! and derive_impl. Use when you need a self-contained test environment for a custom FRAME pallet before writing unit tests. Configures frame_system with test defaults, satisfies pallet Config traits, and provides genesis storage helpers for different initial states. Trigger phrases: 'mock runtime pallet', 'set up pallet testing', 'construct_runtime test', 'pallet test environment', 'derive_impl TestDefaultConfig'. Requires a completed FRAME pallet in pallets/pallet-custom. Do NOT use for full parachain runtime integration testing.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -4391,7 +4399,8 @@ ], "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments." } - ] + ], + "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs" }, { "id": "unit-test-frame-pallet", @@ -4903,7 +4912,7 @@ "id": "interact-with-chain-dedot", "title": "Interact with Polkadot Chains Using Dedot", "description": "Sets up a lightweight Dedot TypeScript client to read chain state and send transactions on any Polkadot SDK blockchain. Use when you need a modern, tree-shakable alternative to Polkadot.js for querying storage, subscribing to events, calling runtime APIs, or submitting signed extrinsics. Trigger phrases: 'use Dedot', 'query chain with Dedot', 'Dedot client', 'interact with chain TypeScript'. Output: account balance, runtime constants, and submitted transaction hash. No testnet tokens required for read-only steps.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -5051,7 +5060,8 @@ ], "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs." } - ] + ], + "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json" }, { "id": "run-parachain-node-omni-node", @@ -6074,7 +6084,7 @@ "id": "deploy-interact-contracts-web3js", "title": "Deploy and Interact with Smart Contracts Using Web3.js", "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", - "version": "1.0.0", + "version": "1.0.1", "chain_role": "isolated", "invocation": "user", "workflow_pattern": "sequential", @@ -6259,7 +6269,8 @@ ], "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction" } - ] + ], + "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json" }, { "id": "add-existing-pallet-to-runtime", @@ -8704,4 +8715,4 @@ "public_root": "/ai/", "skills_dir": "skills" } -} +} \ No newline at end of file diff --git a/skill_coverage.json b/skill_coverage.json index bb060ec00..c37ee3323 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,77 +1,77 @@ { "schema_version": "1", - "generated": "2026-05-15T14:44:38Z", + "generated": "2026-05-15T15:00:00Z", "summary": { "total_candidates": 142, - "up_to_date": 0, - "stale": 56, + "up_to_date": 56, + "stale": 0, "uncovered": 63, "reviewed_no_skill": 23 }, "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T17:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "create-polkadot-account" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/accounts/query-accounts.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T17:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "query-account-info-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/query-data/query-rest.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "query-chain-data-sidecar-rest" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/query-data/query-sdks.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T17:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "query-chain-state-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/query-data/runtime-api-calls.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T17:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "call-runtime-apis-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "calculate-transaction-fees-papi" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T19:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "replay-dry-run-xcm-chopsticks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T19:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "estimate-xcm-fees-teleport" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -81,27 +81,27 @@ }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "transfer-assets-parachains-paraspell" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "pay-fees-alternative-token" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/with-sdks.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T17:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "send-transactions-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/store-data/bulletin-chain.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -141,19 +141,19 @@ }, "node-infrastructure/run-a-node/parachain-rpc.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "run-parachain-rpc-node" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T19:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "run-polkadot-hub-rpc-node" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-node/relay-chain/bootnode.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -181,11 +181,11 @@ }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-polkadot-validator-node" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -225,11 +225,11 @@ }, "node-infrastructure/run-a-validator/requirements.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:59:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "onboard-polkadot-validator" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -245,19 +245,19 @@ }, "parachains/customize-runtime/add-existing-pallets.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "add-existing-pallet-to-runtime" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/add-pallet-instances.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "configure-multiple-pallet-instances" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/add-smart-contract-functionality.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -267,35 +267,35 @@ }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "benchmark-frame-pallet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "create-frame-pallet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/pallet-development/mock-runtime.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-pallet-mock-runtime" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "unit-test-frame-pallet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/get-started.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -305,11 +305,11 @@ }, "parachains/install-polkadot-sdk.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "install-polkadot-sdk" ], - "status": "stale" + "status": "up_to_date" }, "parachains/integrations/indexers.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -349,11 +349,11 @@ }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-parachain-to-polkadot-testnet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/launch-a-parachain/obtain-coretime.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -363,11 +363,11 @@ }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T19:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-parachain-template" ], - "status": "stale" + "status": "up_to_date" }, "parachains/runtime-maintenance/coretime-renewal.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -467,11 +467,11 @@ }, "reference/parachains/chain-data.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "retrieve-runtime-metadata" ], - "status": "stale" + "status": "up_to_date" }, "reference/parachains/consensus/async-backing.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -583,19 +583,19 @@ }, "reference/tools/chopsticks.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T19:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-chopsticks-fork" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/dedot.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-with-chain-dedot" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/light-clients.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -605,19 +605,19 @@ }, "reference/tools/moonwall.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T19:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-e2e-testing-moonwall" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/omninode.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "run-parachain-node-omni-node" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/papi.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -639,11 +639,11 @@ }, "reference/tools/polkadot-js-api.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "use-polkadot-js-api" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/pop-cli.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -653,11 +653,11 @@ }, "reference/tools/py-substrate-interface.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-polkadot-node-py-substrate" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/sidecar.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -667,11 +667,11 @@ }, "reference/tools/subxt.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-polkadot-node-subxt" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/xcm-tools.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -681,27 +681,27 @@ }, "reference/tools/zombienet.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "spawn-test-network-zombienet" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/connect.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:59:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "connect-polkadot-hub-testnet" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "build-dapp-viem-nextjs" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -735,91 +735,91 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-basic-contract-hardhat" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-basic-contract-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-erc20-token-hardhat" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-erc20-token-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-erc721-nft-hardhat" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-erc721-nft-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/foundry.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-foundry-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/hardhat-polkadot.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-hardhat-pvm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T18:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-hardhat-evm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/local-dev-node.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-local-dev-node" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "connect-remix-polkadot" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/explorers.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -883,51 +883,51 @@ }, "smart-contracts/integrations/wallets.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T22:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "connect-wallet-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/ethers-js.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-contracts-ethers-js" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/viem.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T20:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-contracts-viem" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/wagmi.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "use-wagmi-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/web3-js.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T21:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-interact-contracts-web3js" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/web3-py.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-interact-contracts-web3py" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/overview.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -937,11 +937,11 @@ }, "smart-contracts/precompiles/erc20.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-erc20-precompile-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/precompiles/eth-native.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -951,27 +951,27 @@ }, "smart-contracts/precompiles/storage.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-storage-precompile-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/precompiles/system.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:00:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-system-precompile-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/precompiles/xcm.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-04-21T23:30:00Z", + "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-xcm-precompile-remix" ], - "status": "stale" + "status": "up_to_date" } } -} +} \ No newline at end of file From c37509ffa67881605910c63cc3c31254f365bda4 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:53:24 +0000 Subject: [PATCH 06/26] chore: auto-triage skill candidates (batch) --- skill_candidates.json | 78 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/skill_candidates.json b/skill_candidates.json index fb4bf6484..e13eb5ff8 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,6 +1,6 @@ { "schema_version": "2", - "generated": "2026-05-15T14:49:29Z", + "generated": "2026-05-15T14:53:24Z", "project_id": "polkadot-docs", "scoring_rubric_version": "1.0", "candidates": [ @@ -2794,6 +2794,82 @@ }, "priority_score": 25 } + }, + { + "skill_id": "deploy-uniswap-v3-core-evm", + "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "Standalone tutorial: clone pinned revm-hardhat-examples commit, compile, test against local dev node, deploy UniswapV3Factory to TestNet via Hardhat Ignition. All workflow steps are CLI-only. Local dev node required only for the test step; TestNet deployment is self-contained. Reference repo exists (revm-hardhat-examples).", + "scoring": { + "signals": [ + "P1", + "P2", + "P4", + "R2", + "C1", + "C2", + "K1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 15, + "reference": 4, + "conceptual": 8, + "not_applicable": 0 + }, + "priority_score": 25 + } + }, + { + "skill_id": "deploy-uniswap-v3-periphery-evm", + "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "Standalone tutorial: clone same monorepo at pinned commit, npm install auto-resolves the @uniswap/v3-core local file reference from the sibling directory — no need to have run the core tutorial beforehand. Deploys SwapRouter and NonfungiblePositionManager to TestNet. High reference score (12) from detailed SwapRouter/NFPM method and parameter documentation in the architecture section; Rule 2 takes precedence (procedural=15, P4 present).", + "scoring": { + "signals": [ + "P1", + "P2", + "P4", + "R1", + "R2", + "R3", + "C1", + "K1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 15, + "reference": 12, + "conceptual": 5, + "not_applicable": 0 + }, + "priority_score": 25, + "category_ambiguous": true, + "contending_categories": [ + "tutorial", + "reference" + ] + } } ], "supplementary_only": [ From 45bd6da0610788d24eb9d0f2ce18b306d1e0bfd4 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:54:43 +0000 Subject: [PATCH 07/26] chore: auto-triage skill candidates (batch) --- skill_candidates.json | 48 +++---------------------------------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/skill_candidates.json b/skill_candidates.json index e13eb5ff8..68b395fb3 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,6 +1,6 @@ { "schema_version": "2", - "generated": "2026-05-15T14:53:24Z", + "generated": "2026-05-15T14:54:43Z", "project_id": "polkadot-docs", "scoring_rubric_version": "1.0", "candidates": [ @@ -2804,7 +2804,7 @@ "category": "tutorial", "priority": "high", "status": "pending", - "notes": "Standalone tutorial: clone pinned revm-hardhat-examples commit, compile, test against local dev node, deploy UniswapV3Factory to TestNet via Hardhat Ignition. All workflow steps are CLI-only. Local dev node required only for the test step; TestNet deployment is self-contained. Reference repo exists (revm-hardhat-examples).", + "notes": "K1=0: test step references external Local Development Node guide; deployment workflow itself is self-contained. K4=3: revm-hardhat-examples repo with pinned commit (3ff28ae). No P3: reader clones the repo, not creates files.", "scoring": { "signals": [ "P1", @@ -2813,7 +2813,6 @@ "R2", "C1", "C2", - "K1", "K2", "K3", "K4", @@ -2827,48 +2826,7 @@ "conceptual": 8, "not_applicable": 0 }, - "priority_score": 25 - } - }, - { - "skill_id": "deploy-uniswap-v3-periphery-evm", - "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" - ], - "category": "tutorial", - "priority": "high", - "status": "pending", - "notes": "Standalone tutorial: clone same monorepo at pinned commit, npm install auto-resolves the @uniswap/v3-core local file reference from the sibling directory — no need to have run the core tutorial beforehand. Deploys SwapRouter and NonfungiblePositionManager to TestNet. High reference score (12) from detailed SwapRouter/NFPM method and parameter documentation in the architecture section; Rule 2 takes precedence (procedural=15, P4 present).", - "scoring": { - "signals": [ - "P1", - "P2", - "P4", - "R1", - "R2", - "R3", - "C1", - "K1", - "K2", - "K3", - "K4", - "S1", - "S2", - "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 12, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 25, - "category_ambiguous": true, - "contending_categories": [ - "tutorial", - "reference" - ] + "priority_score": 20 } } ], From 627cd3bb26da719b820e81bcc27e4cbf5fee9953 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:58:47 +0000 Subject: [PATCH 08/26] chore: auto-triage skill candidates (batch) --- skill_candidates.json | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/skill_candidates.json b/skill_candidates.json index 68b395fb3..e989e4834 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,6 +1,6 @@ { "schema_version": "2", - "generated": "2026-05-15T14:54:43Z", + "generated": "2026-05-15T14:58:47Z", "project_id": "polkadot-docs", "scoring_rubric_version": "1.0", "candidates": [ @@ -2828,6 +2828,42 @@ }, "priority_score": 20 } + }, + { + "skill_id": "deploy-uniswap-v3-periphery-evm", + "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "Full deploy+test workflow for SwapRouter and NonfungiblePositionManager. The 'complete V3 Core tutorial' prereq is a code dependency resolved automatically via local npm file reference in the cloned monorepo; the Ignition module deploys Factory+WETH9+SwapRouter+NFPM in one shot, making this self-contained. Pinned to commit 96696ad15c3cf01b9168a71ad5114f27c34a8726 with docs.test.ts confirming CI-tested code.", + "scoring": { + "signals": [ + "P1", + "P2", + "P3", + "P4", + "R1", + "R2", + "C1", + "K1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 20, + "reference": 9, + "conceptual": 5, + "not_applicable": 0 + }, + "priority_score": 25 + } } ], "supplementary_only": [ From 40e71c3bc2e90d6ebf4af3fd3b0f6a360a879d9c Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 15:09:03 +0000 Subject: [PATCH 09/26] chore: auto-generate/update skills --- agent_skills_config.json | 1472 ++++++++++++++++++++++++++++++-------- skill_candidates.json | 181 ++--- skill_coverage.json | 42 +- 3 files changed, 1281 insertions(+), 414 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 34a773d71..8f4d242d2 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "generated": "2026-05-15T15:00:00Z", - "content_hash": "sha256:45a722aa580ffc79a0c0f1cab2e23fbc65779ea366852e285e996cf342003a99", + "generated": "2026-05-15T16:00:00Z", + "content_hash": "sha256:102fc2efb7582fc21330af06e017972ae25b51ddb1f66ea131fe08a07a4a8907", "skills": [ { "id": "create-polkadot-account", @@ -32,7 +32,7 @@ "mkdir account-creator && cd account-creator", "npm init -y && npm pkg set type=module" ], - "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required — the @polkadot packages use ESM imports." + "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required \u2014 the @polkadot packages use ESM imports." }, { "order": 2, @@ -48,7 +48,7 @@ "order": 3, "action": "Fetch the account creation script", "working_directory": "account-creator", - "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed — the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", + "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed \u2014 the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", "reference_file": "create-account.ts" }, { @@ -59,7 +59,7 @@ "npx tsx create-account.ts" ], "expected_output": "Address: \nMnemonic: <12-word BIP39 phrase>", - "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both — the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." + "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both \u2014 the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." } ], "reference_code": { @@ -131,12 +131,12 @@ "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console" } ], - "project_structure": "account-creator/\n├── create-account.ts\n└── package.json" + "project_structure": "account-creator/\n\u251c\u2500\u2500 create-account.ts\n\u2514\u2500\u2500 package.json" }, { "id": "query-account-info-sdks", "title": "Query Account Information with SDKs", - "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", + "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required \u2014 read-only operation against any funded or unfunded address.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -184,7 +184,7 @@ "commands": [ "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly — note the name you choose, as it must match the import in query-account.ts." + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly \u2014 note the name you choose, as it must match the import in query-account.ts." }, { "order": 4, @@ -273,12 +273,12 @@ "result": "After funding, account data is returned correctly" } ], - "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json" + "project_structure": "papi-query-account-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 query-account.ts\n\u2514\u2500\u2500 package.json" }, { "id": "query-chain-state-sdks", "title": "Query On-Chain State with SDKs", - "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", + "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info \u2014 including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required \u2014 read-only.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -427,12 +427,12 @@ "result": "USDC metadata and address balance returned" } ], - "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json" + "project_structure": "papi-query-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 query-asset.ts\n\u251c\u2500\u2500 query-balance.ts\n\u2514\u2500\u2500 package.json" }, { "id": "call-runtime-apis-sdks", "title": "Call Runtime APIs with SDKs", - "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", + "description": "Executes Polkadot SDK runtime APIs \u2014 AccountNonceApi.account_nonce and Metadata.metadata_versions \u2014 using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required \u2014 read-only operation.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -535,7 +535,7 @@ "slug": "chain-interactions-query-data-query-sdks", "title": "Query On-Chain State with SDKs", "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "Storage-based queries using the same PAPI setup — compare with runtime API calls." + "relevance": "Storage-based queries using the same PAPI setup \u2014 compare with runtime API calls." }, { "slug": "reference-tools-papi", @@ -560,16 +560,16 @@ }, { "scenario": "Edge case: nonce shows 0 for a new address", - "user_says": "The nonce is 0 — is that correct?", + "user_says": "The nonce is 0 \u2014 is that correct?", "actions": [ "Explain that nonce 0 means the account has not yet sent any transactions", "Note that AccountNonceApi returns 0 for any address that has never submitted a transaction, including unfunded accounts", - "No action required — the nonce increments with each submitted transaction" + "No action required \u2014 the nonce increments with each submitted transaction" ], "result": "User understands nonce 0 is correct for a new or never-transacted account" } ], - "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json" + "project_structure": "papi-runtime-api-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 runtime-apis.ts\n\u2514\u2500\u2500 package.json" }, { "id": "send-transactions-sdks", @@ -593,7 +593,7 @@ "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://asset-hub-paseo.dotters.network for testnet)" ], "tokens": [ - "Testnet PAS tokens in the sender account — get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" + "Testnet PAS tokens in the sender account \u2014 get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" ], "wallet": [ "An SR25519 account mnemonic phrase for the sender account, funded with testnet PAS" @@ -654,7 +654,7 @@ "echo 'SENDER_MNEMONIC=\\nWS_ENDPOINT=wss://asset-hub-paseo.dotters.network\\nDEST_ADDRESS=' > .env", "echo '.env' > .gitignore" ], - "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly — fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." + "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly \u2014 fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." }, { "order": 5, @@ -677,7 +677,7 @@ "npx tsx send-transfer.ts" ], "expected_output": "Connected to Polkadot Testnet\nSigning and submitting transaction...\nTransaction submitted with hash: 0x", - "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash — needed in step 8 to verify." + "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash \u2014 needed in step 8 to verify." } ], "reference_code": { @@ -710,7 +710,7 @@ { "pattern": "Transaction submitted but not confirmed / stuck pending", "cause": "Polkadot Hub TestNet can be unstable and drop transactions under load.", - "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction — if you see 'nonce too low', the previous transaction was included despite appearing to fail." + "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction \u2014 if you see 'nonce too low', the previous transaction was included despite appearing to fail." } ], "supplementary_context": { @@ -761,7 +761,7 @@ "result": "Account funded; transaction succeeds on retry" } ], - "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json" + "project_structure": "papi-send-tx-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 send-transfer.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 package.json" }, { "id": "install-polkadot-sdk", @@ -842,7 +842,7 @@ "./target/release/polkadot --version" ], "expected_output": "polkadot 1.x.x-xxxxxxx", - "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string — needed when matching binary versions in parachain template setup." + "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string \u2014 needed when matching binary versions in parachain template setup." } ], "error_patterns": [ @@ -901,7 +901,7 @@ "Run cargo build --release --locked", "Verify with ./target/release/polkadot --version" ], - "result": "Polkadot binary prints version string — SDK toolchain ready for parachain development" + "result": "Polkadot binary prints version string \u2014 SDK toolchain ready for parachain development" }, { "scenario": "Edge case: build runs out of memory on a machine with limited RAM", @@ -941,7 +941,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" + "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -963,7 +963,7 @@ "mkdir hardhat-deployment && cd hardhat-deployment", "npm init -y" ], - "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage — interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." + "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage \u2014 interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." }, { "order": 2, @@ -973,7 +973,7 @@ "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", "npm install dotenv" ], - "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." + "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required \u2014 the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." }, { "order": 3, @@ -992,21 +992,21 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." }, { "order": 5, "action": "Fetch and configure hardhat.config.ts", "working_directory": "hardhat-deployment", "reference_file": "hardhat.config.ts", - "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors)." + "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei \u2014 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes \u2014 on TestNet it causes IGN401 dropped-transaction errors)." }, { "order": 6, "action": "Fetch Storage.sol contract", "working_directory": "hardhat-deployment", "reference_file": "Storage.sol", - "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed — this is a generic Storage contract with get/set functions." + "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed \u2014 this is a generic Storage contract with get/set functions." }, { "order": 7, @@ -1023,7 +1023,7 @@ "action": "Fetch the Ignition deployment module", "working_directory": "hardhat-deployment", "reference_file": "storage.ts", - "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed — the module deploys the Storage contract by name." + "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed \u2014 the module deploys the Storage contract by name." }, { "order": 9, @@ -1032,9 +1032,9 @@ "commands": [ "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" ], - "expected_output": "Hardhat Ignition 🚀 ... Storage deployed to: 0x...", + "expected_output": "Hardhat Ignition \ud83d\ude80 ... Storage deployed to: 0x...", "interactive": true, - "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end — needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." + "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID \u2014 delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end \u2014 needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." } ], "reference_code": { @@ -1121,12 +1121,12 @@ "result": "Fresh deployment succeeds with correct gas configuration" } ], - "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json" + "project_structure": "hardhat-deployment/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 ignition/\n\u2502 \u2514\u2500\u2500 modules/\n\u2502 \u2514\u2500\u2500 Storage.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 hardhat.config.ts\n\u2514\u2500\u2500 package.json" }, { "id": "deploy-erc20-token-hardhat", "title": "Deploy an ERC-20 Token Using Hardhat", - "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", + "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ \u2014 requires evmVersion cancun.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -1146,7 +1146,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas fees and test transactions — get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" + "Testnet PAS tokens for gas fees and test transactions \u2014 get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -1188,13 +1188,13 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." }, { "order": 4, "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", "working_directory": "revm-hardhat-examples/erc20-hardhat", - "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." + "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei \u2014 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block \u2014 required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." }, { "order": 5, @@ -1214,7 +1214,7 @@ "npx hardhat test --network polkadotTestnet" ], "expected_output": "7 passing", - "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output — it confirms the contract logic is correct before deployment." + "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output \u2014 it confirms the contract logic is correct before deployment." }, { "order": 7, @@ -1225,7 +1225,7 @@ ], "expected_output": "MyToken deployed to: 0x...", "interactive": true, - "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success — needed for token interaction and verification." + "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID \u2014 delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success \u2014 needed for token interaction and verification." } ], "error_patterns": [ @@ -1321,7 +1321,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -1354,7 +1354,7 @@ "npm install --save-dev @parity/resolc", "npm install dotenv" ], - "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them — use latest." + "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them \u2014 use latest." }, { "order": 3, @@ -1364,7 +1364,7 @@ "npx hardhat-polkadot init" ], "interactive": true, - "description": "Run the project creation wizard. This is an interactive command — delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." + "description": "Run the project creation wizard. This is an interactive command \u2014 delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." }, { "order": 4, @@ -1384,13 +1384,13 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." }, { "order": 6, "action": "Fetch and configure hardhat.config.ts for TestNet", "working_directory": "hardhat-pvm-example", - "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." + "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei \u2014 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." }, { "order": 7, @@ -1495,11 +1495,11 @@ "primary_page": "smart-contracts/dev-environments/hardhat.md", "prerequisites": { "runtime": [ - "Node.js LTS (18.x, 20.x, or 22.x — even major version numbers only)", + "Node.js LTS (18.x, 20.x, or 22.x \u2014 even major version numbers only)", "npm, pnpm, or yarn" ], "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account — needed before deploying contracts" + "An EVM private key (0x-prefixed) for a funded testnet account \u2014 needed before deploying contracts" ] }, "env_vars": [ @@ -1518,7 +1518,7 @@ "mkdir hardhat-example && cd hardhat-example", "npm init -y" ], - "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage — the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." + "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage \u2014 the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." }, { "order": 2, @@ -1550,13 +1550,13 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable — the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable \u2014 the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." }, { "order": 5, "action": "Fetch and configure hardhat.config.ts", "working_directory": "hardhat-example", - "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." + "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei \u2014 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." } ], "reference_code": { @@ -1634,7 +1634,7 @@ { "id": "query-chain-data-sidecar-rest", "title": "Query On-Chain Data with Sidecar REST API", - "description": "Runs the Substrate API Sidecar REST service connected to Polkadot Hub TestNet and queries on-chain data via curl. Use when you need account balances, asset info, or block data without writing SDK code. Covers Sidecar installation, startup with a TestNet endpoint, and the primary query endpoints: /accounts/{addr}/balance-info, /assets/{id}/asset-info, and /blocks/{id}. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required — read-only operations only.", + "description": "Runs the Substrate API Sidecar REST service connected to Polkadot Hub TestNet and queries on-chain data via curl. Use when you need account balances, asset info, or block data without writing SDK code. Covers Sidecar installation, startup with a TestNet endpoint, and the primary query endpoints: /accounts/{addr}/balance-info, /assets/{id}/asset-info, and /blocks/{id}. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required \u2014 read-only operations only.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -1648,7 +1648,7 @@ "runtime": [ "Node.js v18+ and npm (to install and run @substrate/api-sidecar)", "curl (pre-installed on macOS and Linux; use WSL on Windows)", - "jq (optional, for formatted JSON output — install with: apt install jq or brew install jq)" + "jq (optional, for formatted JSON output \u2014 install with: apt install jq or brew install jq)" ], "network": [ "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network" @@ -1673,7 +1673,7 @@ "SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network substrate-api-sidecar" ], "expected_output": "SAS listening on http://127.0.0.1:8080/", - "description": "Start Sidecar with SAS_SUBSTRATE_URL pointing to the Polkadot Hub TestNet WebSocket endpoint. The service listens on http://127.0.0.1:8080/ by default. Run this in a dedicated terminal and keep it running while executing the following steps. If using npx: SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network npx @substrate/api-sidecar. Polkadot Hub TestNet can be unstable — if the WebSocket connection drops, restart Sidecar." + "description": "Start Sidecar with SAS_SUBSTRATE_URL pointing to the Polkadot Hub TestNet WebSocket endpoint. The service listens on http://127.0.0.1:8080/ by default. Run this in a dedicated terminal and keep it running while executing the following steps. If using npx: SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network npx @substrate/api-sidecar. Polkadot Hub TestNet can be unstable \u2014 if the WebSocket connection drops, restart Sidecar." }, { "order": 3, @@ -1683,7 +1683,7 @@ "curl -s \"http://127.0.0.1:8080/accounts/INSERT_SS58_ADDRESS/balance-info\" | jq ." ], "expected_output": "{\"at\":{\"hash\":\"0x...\",\"height\":\"12345\"},\"nonce\":\"0\",\"tokenSymbol\":\"PAS\",\"free\":\"...\",\"reserved\":\"...\"}", - "description": "Query the balance-info endpoint. Replace 'INSERT_SS58_ADDRESS' with the target SS58 address. Response fields: free — spendable planck; reserved — locked/staked; miscFrozen / feeFrozen — frozen amounts; nonce — transaction count. Divide raw planck values by 10^10 to convert to PAS. Omit '| jq .' if jq is not installed." + "description": "Query the balance-info endpoint. Replace 'INSERT_SS58_ADDRESS' with the target SS58 address. Response fields: free \u2014 spendable planck; reserved \u2014 locked/staked; miscFrozen / feeFrozen \u2014 frozen amounts; nonce \u2014 transaction count. Divide raw planck values by 10^10 to convert to PAS. Omit '| jq .' if jq is not installed." }, { "order": 4, @@ -1704,7 +1704,7 @@ "curl -s \"http://127.0.0.1:8080/assets/1984/asset-info\" | jq ." ], "expected_output": "{\"at\":{...},\"items\":[{\"id\":\"1984\",\"name\":\"USDT\",\"symbol\":\"USDT\",\"decimals\":\"6\"}]}", - "description": "Query metadata for asset ID 1984 (USDT on Polkadot Hub). Replace '1984' with any registered asset ID. If the asset does not exist on TestNet, items will be empty — this is expected since not all mainnet assets are bridged to TestNet." + "description": "Query metadata for asset ID 1984 (USDT on Polkadot Hub). Replace '1984' with any registered asset ID. If the asset does not exist on TestNet, items will be empty \u2014 this is expected since not all mainnet assets are bridged to TestNet." } ], "reference_code": { @@ -1725,7 +1725,7 @@ }, { "pattern": "balance-info returns all-zero values for free/reserved/nonce", - "cause": "The queried account has no on-chain entry — it has never received any tokens.", + "cause": "The queried account has no on-chain entry \u2014 it has never received any tokens.", "resolution": "Fund the account at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation. Re-query the endpoint." }, { @@ -1741,7 +1741,7 @@ "slug": "chain-interactions-query-data-query-sdks", "title": "Query On-Chain State with SDKs", "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "SDK-based alternative to Sidecar — query the same on-chain data using PAPI or Polkadot.js TypeScript code." + "relevance": "SDK-based alternative to Sidecar \u2014 query the same on-chain data using PAPI or Polkadot.js TypeScript code." }, { "slug": "reference-tools-sidecar", @@ -1784,7 +1784,7 @@ { "id": "calculate-transaction-fees-papi", "title": "Estimate Transaction Fees with PAPI", - "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getPaymentInfo method. Use when you need to preview gas costs before submitting — for fee UIs, budget checks, or preflight validation. Returns partial_fee in planck, human-readable PAS, and weight breakdown. Also covers the Polkadot.js paymentInfo alternative described in the source page. Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getPaymentInfo PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required — fee estimation is read-only.", + "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getPaymentInfo method. Use when you need to preview gas costs before submitting \u2014 for fee UIs, budget checks, or preflight validation. Returns partial_fee in planck, human-readable PAS, and weight breakdown. Also covers the Polkadot.js paymentInfo alternative described in the source page. Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getPaymentInfo PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required \u2014 fee estimation is read-only.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -1812,7 +1812,7 @@ "mkdir papi-fee-estimate && cd papi-fee-estimate", "npm init -y && npm pkg set type=module" ], - "description": "Create a new directory 'papi-fee-estimate' and initialize it as an ESM Node.js project. The type=module flag is required — polkadot-api uses ESM imports." + "description": "Create a new directory 'papi-fee-estimate' and initialize it as an ESM Node.js project. The type=module flag is required \u2014 polkadot-api uses ESM imports." }, { "order": 2, @@ -1831,13 +1831,13 @@ "commands": [ "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. Polkadot Hub TestNet can be unstable — retry after a few minutes if the connection fails." + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. Polkadot Hub TestNet can be unstable \u2014 retry after a few minutes if the connection fails." }, { "order": 4, "action": "Create the fee estimation script", "working_directory": "papi-fee-estimate", - "description": "Create a file named 'estimate-fees.ts' in papi-fee-estimate with this content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { polkadotTestNet } from \"@polkadot-api/descriptors\";\n\nconst WS_ENDPOINT = \"INSERT_WS_ENDPOINT\";\nconst SENDER_ADDRESS = \"INSERT_SENDER_ADDRESS\";\nconst DEST_ADDRESS = \"INSERT_DEST_ADDRESS\";\n\nasync function main() {\n const client = createClient(getWsProvider(WS_ENDPOINT));\n const api = client.getTypedApi(polkadotTestNet);\n const tx = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: DEST_ADDRESS },\n value: 10_000_000_000n,\n });\n const info = await tx.getPaymentInfo(SENDER_ADDRESS);\n console.log(\"Fee (planck):\", info.partial_fee.toString());\n console.log(\"Fee (PAS):\", (Number(info.partial_fee) / 1e10).toFixed(6));\n console.log(\"Weight ref_time:\", info.weight.ref_time.toString());\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nAfter creating the file, apply these substitutions: (1) Replace INSERT_WS_ENDPOINT with your WS endpoint (e.g. 'wss://asset-hub-paseo.dotters.network'). (2) Replace INSERT_SENDER_ADDRESS with any valid funded SS58 address — sender address affects fee because weight depends on account state. (3) Replace INSERT_DEST_ADDRESS with any valid SS58 recipient address. Save the file." + "description": "Create a file named 'estimate-fees.ts' in papi-fee-estimate with this content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { polkadotTestNet } from \"@polkadot-api/descriptors\";\n\nconst WS_ENDPOINT = \"INSERT_WS_ENDPOINT\";\nconst SENDER_ADDRESS = \"INSERT_SENDER_ADDRESS\";\nconst DEST_ADDRESS = \"INSERT_DEST_ADDRESS\";\n\nasync function main() {\n const client = createClient(getWsProvider(WS_ENDPOINT));\n const api = client.getTypedApi(polkadotTestNet);\n const tx = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: DEST_ADDRESS },\n value: 10_000_000_000n,\n });\n const info = await tx.getPaymentInfo(SENDER_ADDRESS);\n console.log(\"Fee (planck):\", info.partial_fee.toString());\n console.log(\"Fee (PAS):\", (Number(info.partial_fee) / 1e10).toFixed(6));\n console.log(\"Weight ref_time:\", info.weight.ref_time.toString());\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nAfter creating the file, apply these substitutions: (1) Replace INSERT_WS_ENDPOINT with your WS endpoint (e.g. 'wss://asset-hub-paseo.dotters.network'). (2) Replace INSERT_SENDER_ADDRESS with any valid funded SS58 address \u2014 sender address affects fee because weight depends on account state. (3) Replace INSERT_DEST_ADDRESS with any valid SS58 recipient address. Save the file." }, { "order": 5, @@ -1869,7 +1869,7 @@ { "pattern": "TypeError: tx.getPaymentInfo is not a function", "cause": "Descriptor export name does not match the import, or polkadot-api version is below 1.0.0.", - "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api — version 1.0.0+ required." + "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api \u2014 version 1.0.0+ required." }, { "pattern": "SyntaxError: Cannot use import statement in a module", @@ -1884,7 +1884,7 @@ "slug": "chain-interactions-send-transactions-with-sdks", "title": "Send Transactions with SDKs", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit the same transaction whose fee was estimated — the natural next step." + "relevance": "How to sign and submit the same transaction whose fee was estimated \u2014 the natural next step." }, { "slug": "reference-tools-papi", @@ -1909,9 +1909,9 @@ }, { "scenario": "Edge case: fee appears unusually high compared to Ethereum", - "user_says": "The fee estimate shows 0.001 PAS — that seems expensive", + "user_says": "The fee estimate shows 0.001 PAS \u2014 that seems expensive", "actions": [ - "Explain that Polkadot Hub TestNet has a base fee of 1000 gwei — significantly higher than Ethereum mainnet", + "Explain that Polkadot Hub TestNet has a base fee of 1000 gwei \u2014 significantly higher than Ethereum mainnet", "Note that PAS testnet fees do not reflect real economic cost", "Get testnet tokens from https://faucet.polkadot.io/ if the sender balance is insufficient" ], @@ -1941,7 +1941,7 @@ "Destination parachain WebSocket RPC (e.g., wss://sys.ibp.network/people-paseo for People Chain Paseo)" ], "tokens": [ - "Paseo testnet PAS in the sender account — get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." + "Paseo testnet PAS in the sender account \u2014 get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." ], "wallet": [ "SR25519 account mnemonic phrase for the sender account, funded with Paseo testnet PAS" @@ -1999,7 +1999,7 @@ "order": 4, "action": "Create the XCM transfer script", "working_directory": "paraspell-xcm-transfer", - "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains — chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." + "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains \u2014 chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." }, { "order": 5, @@ -2009,7 +2009,7 @@ "npx tsx xcm-transfer.ts" ], "expected_output": "Sender: ...\nDry-run: OK\nEstimated fee: ...\nStatus: InBlock\nFinalized: 0x...", - "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first — if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." + "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first \u2014 if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." } ], "reference_code": { @@ -2035,8 +2035,8 @@ }, { "pattern": "Transaction submitted but never finalizes", - "cause": "Paseo TestNet instability — the TestNet can drop transactions under load.", - "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script — nonce: -1 fetches the current nonce automatically." + "cause": "Paseo TestNet instability \u2014 the TestNet can drop transactions under load.", + "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script \u2014 nonce: -1 fetches the current nonce automatically." } ], "supplementary_context": { @@ -2091,7 +2091,7 @@ { "id": "pay-fees-alternative-token", "title": "Pay Transaction Fees with an Alternative Token", - "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode — no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", + "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode \u2014 no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -2104,10 +2104,10 @@ "prerequisites": { "runtime": [ "Node.js v18+ and npm", - "npx (bundled with npm v5.2+ — no separate install needed)" + "npx (bundled with npm v5.2+ \u2014 no separate install needed)" ], "network": [ - "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network — Chopsticks downloads state on first run" + "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network \u2014 Chopsticks downloads state on first run" ] }, "env_vars": [], @@ -2128,7 +2128,7 @@ "order": 2, "action": "Create Chopsticks configuration", "working_directory": "fee-proxy-demo", - "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key — this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." + "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key \u2014 this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." }, { "order": 3, @@ -2138,7 +2138,7 @@ "npx @acala-network/chopsticks --config=chopsticks.yml" ], "expected_output": "Listening on port 8000", - "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." + "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state \u2014 subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." }, { "order": 4, @@ -2153,7 +2153,7 @@ "order": 5, "action": "Create the fee-proxy script", "working_directory": "fee-proxy-demo", - "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy — pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." + "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy \u2014 pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." }, { "order": 6, @@ -2206,7 +2206,7 @@ "slug": "chain-interactions-send-transactions-calculate-transaction-fees", "title": "Calculate Transaction Fees", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", - "relevance": "How to estimate fees for a standard transaction — compare with the fee proxy approach to understand the difference." + "relevance": "How to estimate fees for a standard transaction \u2014 compare with the fee proxy approach to understand the difference." } ] }, @@ -2267,7 +2267,7 @@ "order": 1, "action": "Choose deployment method and sync type", "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table — replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." + "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table \u2014 replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." }, { "order": 2, @@ -2295,7 +2295,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" ], "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." + "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync \u2014 initial sync can take hours to days depending on hardware." }, { "order": 5, @@ -2343,13 +2343,13 @@ "slug": "node-infrastructure-run-a-node-polkadot-hub-rpc", "title": "Run an RPC Node for Polkadot Hub", "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/polkadot-hub-rpc.md", - "relevance": "How to run an RPC node for Polkadot Hub specifically — different chain spec and optional Ethereum RPC adapter." + "relevance": "How to run an RPC node for Polkadot Hub specifically \u2014 different chain spec and optional Ethereum RPC adapter." }, { "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", "title": "Set Up Secure WebSocket", "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx \u2014 required for production endpoints." }, { "slug": "node-infrastructure-run-a-node-relay-chain-full-node", @@ -2436,14 +2436,14 @@ "npx @acala-network/chopsticks --config=chopsticks.yml" ], "expected_output": "Listening on port 8000", - "description": "Start Chopsticks in a dedicated terminal. It downloads chain state from the configured endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite cache. Keep this terminal open during all following steps." + "description": "Start Chopsticks in a dedicated terminal. It downloads chain state from the configured endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state \u2014 subsequent runs use db.sqlite cache. Keep this terminal open during all following steps." }, { "order": 4, "action": "Locate the XCM call data on Subscan", "working_directory": ".", "interactive": true, - "description": "This step is manual. Open Subscan for the source chain (e.g., https://polkadot.subscan.io or https://assethub-polkadot.subscan.io). Navigate to the extrinsic or event containing the XCM you want to replay. Find the encoded call data hex string — it typically appears in the 'Call Data' or 'Params' section of the extrinsic detail page. Copy the full hex string (starts with '0x'). You will use this in step 5. Paste it into a local file named 'call-data.txt' for reference." + "description": "This step is manual. Open Subscan for the source chain (e.g., https://polkadot.subscan.io or https://assethub-polkadot.subscan.io). Navigate to the extrinsic or event containing the XCM you want to replay. Find the encoded call data hex string \u2014 it typically appears in the 'Call Data' or 'Params' section of the extrinsic detail page. Copy the full hex string (starts with '0x'). You will use this in step 5. Paste it into a local file named 'call-data.txt' for reference." }, { "order": 5, @@ -2453,7 +2453,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"xcmPallet_dryRunCall\",\"params\":[\"INSERT_SENDER_ADDRESS\", \"INSERT_CALL_DATA_HEX\"]}' http://localhost:8000" ], "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"executionResult\":{\"Ok\":{}},\"emittedEvents\":[],...},\"id\":1}", - "description": "Replace INSERT_SENDER_ADDRESS with the SS58 address that submitted the original XCM and INSERT_CALL_DATA_HEX with the hex string from Subscan (step 4). The response's executionResult indicates success or failure along with the failure reason if any. If the RPC method is not available on the forked chain, use 'xcmPallet_dryRunXcm' as an alternative — check the source page for the correct method name and parameter signature for your chain version. Chopsticks must be running (step 3) for this to work." + "description": "Replace INSERT_SENDER_ADDRESS with the SS58 address that submitted the original XCM and INSERT_CALL_DATA_HEX with the hex string from Subscan (step 4). The response's executionResult indicates success or failure along with the failure reason if any. If the RPC method is not available on the forked chain, use 'xcmPallet_dryRunXcm' as an alternative \u2014 check the source page for the correct method name and parameter signature for your chain version. Chopsticks must be running (step 3) for this to work." } ], "reference_code": { @@ -2490,7 +2490,7 @@ "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", "title": "XCM Fee Estimation", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "How to estimate XCM fees before submission — helps diagnose BuyExecution failures in a dry-run." + "relevance": "How to estimate XCM fees before submission \u2014 helps diagnose BuyExecution failures in a dry-run." }, { "slug": "reference-tools-chopsticks", @@ -2502,7 +2502,7 @@ "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", "title": "Transfer Assets Between Parachains", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "relevance": "How to submit a real XCM transfer between parachains — use after debugging with Chopsticks." + "relevance": "How to submit a real XCM transfer between parachains \u2014 use after debugging with Chopsticks." } ] }, @@ -2535,7 +2535,7 @@ { "id": "estimate-xcm-fees-teleport", "title": "Estimate XCM Fees for Asset Teleport", - "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required — Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", + "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required \u2014 Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -2573,7 +2573,7 @@ "order": 2, "action": "Create Chopsticks configs for source and destination chains", "working_directory": "xcm-fee-estimate", - "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 — 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 — 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." + "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 \u2014 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 \u2014 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." }, { "order": 3, @@ -2600,7 +2600,7 @@ "order": 5, "action": "Create the fee estimation script", "working_directory": "xcm-fee-estimate", - "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples — consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." + "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples \u2014 consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." }, { "order": 6, @@ -2704,7 +2704,7 @@ "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", "prerequisites": { "runtime": [ - "Rust toolchain with wasm32-unknown-unknown target — complete the install-polkadot-sdk skill first", + "Rust toolchain with wasm32-unknown-unknown target \u2014 complete the install-polkadot-sdk skill first", "Git", "Disk: at least 5 GB free for the build artifacts", "RAM: 8 GB minimum; 16 GB recommended for parallel compilation" @@ -2726,7 +2726,7 @@ "order": 2, "action": "Review the template directory structure", "working_directory": "polkadot-sdk-parachain-template", - "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup — the default configuration targets a local development network." + "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup \u2014 the default configuration targets a local development network." }, { "order": 3, @@ -2746,7 +2746,7 @@ "./target/release/parachain-template-node --version" ], "expected_output": "parachain-template-node 0.1.0-...", - "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully — review the cargo build output for compilation errors." + "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully \u2014 review the cargo build output for compilation errors." }, { "order": 5, @@ -2756,7 +2756,7 @@ "./target/release/parachain-template-node --dev" ], "expected_output": "Running in --dev mode\n...\nIdle (0 peers)", - "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain — the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." + "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain \u2014 the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." } ], "reference_code": { @@ -2778,7 +2778,7 @@ }, { "pattern": "Killed / process killed during compilation / OOM", - "cause": "Insufficient RAM for parallel Rust compilation — cargo uses one thread per CPU core by default.", + "cause": "Insufficient RAM for parallel Rust compilation \u2014 cargo uses one thread per CPU core by default.", "resolution": "Reduce parallel compilation jobs: CARGO_BUILD_JOBS=2 cargo build --release. Consider adding swap space on Linux: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile." }, { @@ -2866,7 +2866,7 @@ "order": 1, "action": "Choose deployment method and sync type", "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync — check https://snapshots.polkadot.io or community providers for current snapshots." + "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync \u2014 check https://snapshots.polkadot.io or community providers for current snapshots." }, { "order": 2, @@ -2904,7 +2904,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" ], "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." + "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync \u2014 initial sync can take hours to days depending on hardware." }, { "order": 6, @@ -2915,7 +2915,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" ], "expected_output": "{\"result\":\"Polkadot Asset Hub\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", - "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 — should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." + "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 \u2014 should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." } ], "reference_code": { @@ -2958,7 +2958,7 @@ "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", "title": "Set Up Secure WebSocket", "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx \u2014 required for production endpoints." }, { "slug": "node-infrastructure-run-a-node-relay-chain-full-node", @@ -2982,11 +2982,11 @@ "result": "Polkadot Hub RPC node running, synced, serving requests on ws://localhost:9944" }, { - "scenario": "Edge case: Ethereum tooling cannot connect — eth_chainId returns wrong value", + "scenario": "Edge case: Ethereum tooling cannot connect \u2014 eth_chainId returns wrong value", "user_says": "MetaMask connects to port 8545 but shows the wrong chain ID", "actions": [ "Verify the eth-rpc adapter is configured with the correct --chain flag matching the running node", - "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) — confirm eth_chainId returns this value", + "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) \u2014 confirm eth_chainId returns this value", "If chain ID is wrong, restart the eth-rpc adapter with --chain=asset-hub-paseo for TestNet or --chain=asset-hub-polkadot for mainnet", "Update MetaMask network settings with the correct chain ID and RPC URL http://localhost:8545" ], @@ -3053,7 +3053,7 @@ "chmod +x polkadot polkadot-prepare-worker polkadot-execute-worker", "sudo mv polkadot polkadot-prepare-worker polkadot-execute-worker /usr/local/bin/" ], - "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 — check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE — APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE — Docker:\n docker pull parity/polkadot:stable2512-2" + "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 \u2014 check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE \u2014 APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE \u2014 Docker:\n docker pull parity/polkadot:stable2512-2" }, { "order": 4, @@ -3102,7 +3102,7 @@ "slug": "node-infrastructure-run-a-validator-requirements", "title": "Validator Requirements", "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/requirements.md", - "relevance": "Minimum hardware and skill requirements before starting a validator — read before this skill." + "relevance": "Minimum hardware and skill requirements before starting a validator \u2014 read before this skill." }, { "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-key-management", @@ -3153,7 +3153,7 @@ { "id": "set-up-chopsticks-fork", "title": "Set Up and Use Chopsticks for Chain Forking", - "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", + "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC \u2014 MetaMask cannot connect to a Chopsticks fork.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -3188,7 +3188,7 @@ "order": 2, "action": "Create a fork configuration file", "working_directory": ".", - "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs — download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts — useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." + "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs \u2014 download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts \u2014 useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." }, { "order": 3, @@ -3259,13 +3259,13 @@ "slug": "parachains-testing-fork-a-parachain", "title": "Fork a Parachain Using Chopsticks", "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", - "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks — same tool, focused on the parachain testing workflow with a CI-backed example." + "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks \u2014 same tool, focused on the parachain testing workflow with a CI-backed example." }, { "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", "title": "Replay and Dry Run XCMs", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "Using Chopsticks to replay and dry-run XCM messages — advanced XCM debugging workflow." + "relevance": "Using Chopsticks to replay and dry-run XCM messages \u2014 advanced XCM debugging workflow." }, { "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", @@ -3302,7 +3302,7 @@ { "id": "set-up-e2e-testing-moonwall", "title": "Set Up End-to-End Testing with Moonwall", - "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain — local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", + "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain \u2014 local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -3341,7 +3341,7 @@ "moonwall init" ], "interactive": true, - "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user — do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." + "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user \u2014 do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." }, { "order": 3, @@ -3353,7 +3353,7 @@ "order": 4, "action": "Write a test suite", "working_directory": ".", - "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run — do not create API connections manually." + "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run \u2014 do not create API connections manually." }, { "order": 5, @@ -3427,7 +3427,7 @@ "scenario": "Edge case: test environment fails to start", "user_says": "Moonwall says it cannot connect to the node endpoint", "actions": [ - "Check the foundation type in moonwall.config — for 'dev', verify binPath is correct and the binary has execute permission", + "Check the foundation type in moonwall.config \u2014 for 'dev', verify binPath is correct and the binary has execute permission", "For 'read_only' or 'chopsticks', start the node or Chopsticks fork manually and confirm it is listening on the configured endpoint", "Increase the connection timeout in moonwall.config if the node starts slowly" ], @@ -3458,7 +3458,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for contract deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required for contract deployment gas" ], "wallet": [ "MetaMask configured for Polkadot Hub TestNet (chainId 420420417)", @@ -3468,7 +3468,7 @@ "env_vars": [ { "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly \u2014 never paste in chat.", "required": true } ], @@ -3506,7 +3506,7 @@ "order": 4, "action": "Update hardhat.config.ts for TestNet and security", "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file." + "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string \u2014 Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings \u2014 required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 \u2014 zero causes IGN401 errors on TestNet).\nSave the file." }, { "order": 5, @@ -3526,7 +3526,7 @@ "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" ], "expected_output": "Storage deployed to: 0x...", - "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." + "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address \u2014 needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json \u2014 if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." }, { "order": 7, @@ -3578,7 +3578,7 @@ { "pattern": "IGN401 / Transaction dropped", "cause": "Ignition lost track of the transaction. The transaction may have succeeded or may be stuck in the mempool.", - "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded — use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." + "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded \u2014 use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." }, { "pattern": "invalid opcode: MCOPY", @@ -3639,7 +3639,7 @@ "user_says": "Ignition says IGN401 but I don't know if the contract deployed", "actions": [ "Check ignition/deployments//deployed_addresses.json for a Storage address", - "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", + "If address is present: deployment succeeded \u2014 use that address in contract.ts and proceed to step 7", "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" ], "result": "Contract address recovered from deployment state or clean redeployment completed" @@ -3664,10 +3664,10 @@ "A modern web browser (Chrome or Firefox recommended)" ], "network": [ - "Polkadot Hub TestNet (chainId 420420417) — configured in MetaMask" + "Polkadot Hub TestNet (chainId 420420417) \u2014 configured in MetaMask" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required to pay deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required to pay deployment gas" ], "wallet": [ "MetaMask browser extension installed and unlocked", @@ -3692,7 +3692,7 @@ "order": 3, "action": "Configure the Solidity compiler in Remix", "working_directory": ".", - "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown — required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." + "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown \u2014 required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." }, { "order": 4, @@ -3704,13 +3704,13 @@ "order": 5, "action": "Connect MetaMask to Remix and deploy", "working_directory": ".", - "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect — click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation — review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10–30 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." + "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect \u2014 click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation \u2014 review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10\u201330 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." }, { "order": 6, "action": "Verify the deployment and mint tokens", "working_directory": ".", - "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' — should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." + "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' \u2014 should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." } ], "reference_code": { @@ -3748,7 +3748,7 @@ "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", "title": "Deploy an ERC-20 Using Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "CLI-based ERC-20 deployment using Hardhat — use when the user prefers a terminal workflow over Remix." + "relevance": "CLI-based ERC-20 deployment using Hardhat \u2014 use when the user prefers a terminal workflow over Remix." }, { "slug": "smart-contracts-connect", @@ -3780,7 +3780,7 @@ }, { "scenario": "Edge case: user has no testnet PAS tokens", - "user_says": "Deployment failed — gas fee transaction rejected", + "user_says": "Deployment failed \u2014 gas fee transaction rejected", "actions": [ "Direct user to https://faucet.polkadot.io/ and ask them to enter their MetaMask address", "Wait for confirmation that PAS tokens have arrived (check MetaMask balance)", @@ -3812,7 +3812,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required for deployment gas" ], "wallet": [ "0x-prefixed EVM private key for a funded testnet account" @@ -3821,7 +3821,7 @@ "env_vars": [ { "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly \u2014 never paste in chat.", "required": true }, { @@ -3874,7 +3874,7 @@ "action": "Fetch the Storage.sol contract", "working_directory": "ethers-deploy", "reference_file": "Storage.sol", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target \u2014 a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." }, { "order": 6, @@ -3906,7 +3906,7 @@ "commands": [ "node deploy.js" ], - "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." + "description": "Run the deployment script. Save the deployed contract address from the output \u2014 needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." }, { "order": 10, @@ -3984,7 +3984,7 @@ "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", "title": "Deploy a Basic Contract with Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Hardhat-based deployment workflow — use when the project needs a full Hardhat setup rather than lightweight scripts." + "relevance": "Hardhat-based deployment workflow \u2014 use when the project needs a full Hardhat setup rather than lightweight scripts." } ] }, @@ -4008,13 +4008,13 @@ "user_says": "Deploy failed with transaction underpriced", "actions": [ "Open deploy.ts and add gasPrice override to the deployment transaction options", - "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", + "Set gasPrice: ethers.parseUnits('5000', 'gwei') \u2014 5x the 1000 gwei TestNet base fee", "Retry with npx tsx deploy.ts" ], "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet" } ], - "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json" + "project_structure": "ethers-deploy/\n\u251c\u2500\u2500 artifacts/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 checkStorage.js\n\u251c\u2500\u2500 compile.js\n\u251c\u2500\u2500 connectToProvider.js\n\u251c\u2500\u2500 deploy.js\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 package.json" }, { "id": "deploy-contracts-viem", @@ -4038,7 +4038,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required for deployment gas" ], "wallet": [ "0x-prefixed EVM private key for a funded testnet account" @@ -4047,7 +4047,7 @@ "env_vars": [ { "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly \u2014 never paste in chat.", "required": true } ], @@ -4061,7 +4061,7 @@ "npm init -y && npm pkg set type=module", "mkdir -p src contracts abis artifacts" ], - "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) — the reference scripts assume this layout for relative paths." + "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) \u2014 the reference scripts assume this layout for relative paths." }, { "order": 2, @@ -4088,7 +4088,7 @@ "action": "Fetch the chain configuration", "working_directory": "viem-deploy", "reference_file": "chainConfig.ts", - "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) — apply the same substitutions consistently in all three files." + "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) \u2014 apply the same substitutions consistently in all three files." }, { "order": 5, @@ -4109,7 +4109,7 @@ "action": "Fetch the wallet client setup", "working_directory": "viem-deploy", "reference_file": "createWallet.ts", - "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders — including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." + "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders \u2014 including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." }, { "order": 8, @@ -4221,13 +4221,13 @@ "slug": "smart-contracts-libraries-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js equivalent workflow — use when the user prefers Ethers.js v6 over Viem." + "relevance": "Ethers.js equivalent workflow \u2014 use when the user prefers Ethers.js v6 over Viem." }, { "slug": "smart-contracts-cookbook-dapps-zero-to-hero", "title": "Zero to Hero Smart Contract DApp", "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend — the natural next step after deploying a contract with Viem." + "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend \u2014 the natural next step after deploying a contract with Viem." } ] }, @@ -4258,7 +4258,7 @@ "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee" } ], - "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json" + "project_structure": "viem-deploy/\n\u251c\u2500\u2500 abis/\n\u251c\u2500\u2500 artifacts/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 chainConfig.ts\n\u2502 \u251c\u2500\u2500 compile.ts\n\u2502 \u251c\u2500\u2500 createClient.ts\n\u2502 \u251c\u2500\u2500 createWallet.ts\n\u2502 \u251c\u2500\u2500 deploy.ts\n\u2502 \u2514\u2500\u2500 interact.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 package.json" }, { "id": "set-up-pallet-mock-runtime", @@ -4279,7 +4279,7 @@ "Disk: at least 5 GB free for build artifacts", "Rust toolchain (stable, via rustup)", "Cargo", - "Completed the Make a Custom Pallet guide — the counter pallet must exist in pallets/pallet-custom" + "Completed the Make a Custom Pallet guide \u2014 the counter pallet must exist in pallets/pallet-custom" ], "network": [], "tokens": [], @@ -4347,7 +4347,7 @@ { "pattern": "error[E0277]: the trait bound is not satisfied / missing associated type in Config impl", "cause": "The mock.rs Config implementation is missing an associated type that the pallet's Config trait declares (e.g., WeightInfo).", - "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder — for WeightInfo: type WeightInfo = ();" + "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder \u2014 for WeightInfo: type WeightInfo = ();" }, { "pattern": "error[E0412]: cannot find type in scope / unresolved import", @@ -4400,7 +4400,7 @@ "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments." } ], - "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs" + "project_structure": "polkadot-sdk-parachain-template/\n\u2514\u2500\u2500 pallets/\n \u2514\u2500\u2500 pallet-custom/\n \u2514\u2500\u2500 src/\n \u2514\u2500\u2500 mock.rs" }, { "id": "unit-test-frame-pallet", @@ -4421,8 +4421,8 @@ "Disk: at least 5 GB free for build artifacts", "Rust toolchain (stable, via rustup)", "Cargo", - "Completed the Make a Custom Pallet guide — counter pallet in pallets/pallet-custom", - "Completed the Mock Your Runtime guide — mock.rs must exist in pallets/pallet-custom/src" + "Completed the Make a Custom Pallet guide \u2014 counter pallet in pallets/pallet-custom", + "Completed the Mock Your Runtime guide \u2014 mock.rs must exist in pallets/pallet-custom/src" ], "network": [], "tokens": [], @@ -4467,19 +4467,19 @@ "order": 5, "action": "Write basic operation and event emission tests", "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion — events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() — uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() — uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() — uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" + "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion \u2014 events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() \u2014 uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() \u2014 uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() \u2014 uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" }, { "order": 6, "action": "Write error condition and access control tests", "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() — uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() — uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() — uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() — uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" + "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() \u2014 uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() \u2014 uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() \u2014 uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() \u2014 uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" }, { "order": 7, "action": "Write genesis configuration and interaction tracking tests", "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() — uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() — calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() — account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" + "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() \u2014 uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() \u2014 calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() \u2014 account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" }, { "order": 8, @@ -4505,7 +4505,7 @@ "resolution": "Add System::set_block_number(1); as the first line inside the execute_with closure for any test that checks events." }, { - "pattern": "error: assert_noop! failed — storage was modified", + "pattern": "error: assert_noop! failed \u2014 storage was modified", "cause": "The dispatchable modified storage before returning an error, violating the no-op contract.", "resolution": "This is a pallet bug: all preconditions must be checked before any storage mutations. Fix the pallet extrinsic, not the test." }, @@ -4543,7 +4543,7 @@ "Write increment_works, set_counter_value_works, decrement_works with System::set_block_number(1) for event checks", "Write error tests: overflow, underflow, root-only access control", "Write genesis_config_works and interaction tracking tests", - "Run cargo test --package pallet-custom — expect 15 passing tests" + "Run cargo test --package pallet-custom \u2014 expect 15 passing tests" ], "result": "All 15 tests pass including 2 auto-generated mock tests." }, @@ -4658,7 +4658,7 @@ "cargo build --release --features runtime-benchmarks" ], "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking — do not use for production deployment." + "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking \u2014 do not use for production deployment." }, { "order": 10, @@ -4674,7 +4674,7 @@ "order": 11, "action": "Create the weight Handlebars template file", "working_directory": "polkadot-sdk-parachain-template", - "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content — needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions — you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." + "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content \u2014 needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions \u2014 you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." }, { "order": 12, @@ -4708,7 +4708,7 @@ { "pattern": "Error: No benchmarks found for pallet_custom", "cause": "The pallet was not registered in define_benchmarks! in runtime/src/benchmarks.rs, or the pallet name is misspelled.", - "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! — use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." + "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! \u2014 use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." }, { "pattern": "could not compile parachain-template-runtime / runtime-benchmarks feature error", @@ -4767,7 +4767,7 @@ { "id": "set-up-local-dev-node", "title": "Set Up a Local Development Node", - "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", + "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation \u2014 the local node exposes a subset of the Ethereum JSON-RPC API.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -4780,7 +4780,7 @@ "prerequisites": { "runtime": [ "Rust toolchain (stable + nightly via rustup) with wasm32-unknown-unknown target", - "Polkadot SDK build dependencies — complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", + "Polkadot SDK build dependencies \u2014 complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", "Git", "At least 20 GB free disk space for the release build" ], @@ -4828,7 +4828,7 @@ "./target/release/revive-dev-node --dev" ], "interactive": true, - "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running — open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" + "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running \u2014 open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" }, { "order": 5, @@ -4900,8 +4900,8 @@ "user_says": "The build has been running for over an hour and my machine is unresponsive", "actions": [ "Cancel the build with Ctrl+C", - "Check free disk space (df -h) — at least 20 GB required", - "Check free RAM — at least 8 GB recommended", + "Check free disk space (df -h) \u2014 at least 20 GB required", + "Check free RAM \u2014 at least 8 GB recommended", "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" ], "result": "Build completes more slowly with reduced CPU and memory usage." @@ -4973,7 +4973,7 @@ "npx tsx main.ts" ], "expected_output": "Free balance: ", - "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts — it will not throw an error." + "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts \u2014 it will not throw an error." }, { "order": 6, @@ -5061,7 +5061,7 @@ "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs." } ], - "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json" + "project_structure": "dedot-example/\n\u251c\u2500\u2500 main.ts\n\u251c\u2500\u2500 send-tx.ts\n\u2514\u2500\u2500 package.json" }, { "id": "run-parachain-node-omni-node", @@ -5079,7 +5079,7 @@ "prerequisites": { "runtime": [ "macOS (Apple Silicon or Intel) or Linux (Ubuntu/Debian) for the pre-built binary path", - "Rust and Cargo (via rustup) if building from source — see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" + "Rust and Cargo (via rustup) if building from source \u2014 see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" ], "network": [ "Internet access to download the binary and to sync the parachain" @@ -5091,7 +5091,7 @@ "order": 1, "action": "Install polkadot-omni-node", "working_directory": ".", - "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust — see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" + "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust \u2014 see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" }, { "order": 2, @@ -5107,7 +5107,7 @@ "order": 3, "action": "Obtain a chain specification", "working_directory": ".", - "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path — you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." + "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path \u2014 you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." }, { "order": 4, @@ -5169,7 +5169,7 @@ }, { "scenario": "Edge case: build from source when no pre-built binary is available for the platform", - "user_says": "I'm on a non-standard Linux architecture — install polkadot-omni-node from source", + "user_says": "I'm on a non-standard Linux architecture \u2014 install polkadot-omni-node from source", "actions": [ "Install Rust via rustup and system dependencies per parachains/install-polkadot-sdk/", "Check crates.io for the latest polkadot-omni-node version", @@ -5205,7 +5205,7 @@ "env_vars": [ { "name": "MNEMONIC", - "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file — do not ask for it in chat.", + "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file \u2014 do not ask for it in chat.", "required": false } ], @@ -5300,7 +5300,7 @@ "scenario": "Edge case: transaction fails with ExtrinsicFailed", "user_says": "My Python transfer script runs but the extrinsic failed", "actions": [ - "Check receipt.is_success — if False, print receipt.error_message for the specific error", + "Check receipt.is_success \u2014 if False, print receipt.error_message for the specific error", "If FundsUnavailable: verify sender balance covers transfer amount plus fees using read_state.py", "If BadOrigin: verify the keypair matches the sender address in the call_params 'dest' field" ], @@ -5330,7 +5330,7 @@ "env_vars": [ { "name": "SECRET_PHRASE", - "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file — do not ask for it in chat.", + "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file \u2014 do not ask for it in chat.", "required": false } ], @@ -5354,7 +5354,7 @@ "order": 3, "action": "Add Subxt dependencies to Cargo.toml", "working_directory": "subxt-example", - "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." + "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" \u2014 the main RPC and codec library\n- subxt-signer = \"0.50.0\" \u2014 provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } \u2014 async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." }, { "order": 4, @@ -5366,7 +5366,7 @@ "order": 5, "action": "Create the main Rust source file", "working_directory": "subxt-example", - "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." + "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs \u2014 each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file \u2014 instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." }, { "order": 6, @@ -5386,9 +5386,9 @@ }, "error_patterns": [ { - "pattern": "error[E0425]: cannot find value / type in scope — after changing metadata", + "pattern": "error[E0425]: cannot find value / type in scope \u2014 after changing metadata", "cause": "The generated types from #[subxt::subxt] changed after re-downloading metadata from a different chain or after a runtime upgrade.", - "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ — check the Subxt docs for the new paths." + "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ \u2014 check the Subxt docs for the new paths." }, { "pattern": "Error: Rpc error: target url is not valid", @@ -5419,7 +5419,7 @@ "slug": "chain-interactions-send-transactions-with-sdks", "title": "Send Transactions with SDKs", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit transactions with multiple SDK clients — context for the Subxt transaction pattern." + "relevance": "How to sign and submit transactions with multiple SDK clients \u2014 context for the Subxt transaction pattern." } ] }, @@ -5470,7 +5470,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -5493,7 +5493,7 @@ "cd hardhat-nft-deployment", "npm init -y" ], - "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead." + "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' \u2014 it is interactive and non-deterministic. Manual scaffolding is used instead." }, { "order": 2, @@ -5519,7 +5519,7 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." }, { "order": 5, @@ -5561,7 +5561,7 @@ ], "expected_output": "MyNFTModule#MyNFT - 0x...", "interactive": true, - "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success." + "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID \u2014 delegate this prompt to the user. Save the contract address printed on success." } ], "reference_code": { @@ -5639,7 +5639,7 @@ { "id": "deploy-erc721-nft-remix", "title": "Deploy an ERC-721 NFT Using Remix IDE", - "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", + "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based \u2014 agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -5654,7 +5654,7 @@ "Polkadot Hub TestNet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas — get from https://faucet.polkadot.io/" + "Testnet PAS tokens for gas \u2014 get from https://faucet.polkadot.io/" ], "wallet": [ "MetaMask browser extension installed and configured for Polkadot Hub TestNet", @@ -5727,7 +5727,7 @@ "slug": "smart-contracts-cookbook-smart-contracts-deploy-nft-nft-hardhat", "title": "Deploy an ERC-721 Using Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", - "relevance": "Alternative CLI-based NFT deployment using Hardhat — better for production or CI/CD workflows." + "relevance": "Alternative CLI-based NFT deployment using Hardhat \u2014 better for production or CI/CD workflows." } ] }, @@ -5759,7 +5759,7 @@ { "id": "set-up-foundry-polkadot-hub", "title": "Use Foundry with Polkadot Hub", - "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", + "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly \u2014 stable release lacks Polkadot chain definitions.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -5776,10 +5776,10 @@ "curl installed" ], "network": [ - "Polkadot Hub TestNet (chain ID 420420417) — supported natively by Foundry nightly as --chain polkadot-testnet" + "Polkadot Hub TestNet (chain ID 420420417) \u2014 supported natively by Foundry nightly as --chain polkadot-testnet" ], "tokens": [ - "Testnet PAS tokens for deployment — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens for deployment \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -5802,7 +5802,7 @@ "foundryup --version nightly" ], "expected_output": "forge Version: (nightly build version string)", - "description": "Install foundryup then the Foundry nightly build. Nightly is required — stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." + "description": "Install foundryup then the Foundry nightly build. Nightly is required \u2014 stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." }, { "order": 2, @@ -5848,7 +5848,7 @@ "forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url https://services.polkadothub-rpc.com/testnet --private-key $PRIVATE_KEY --broadcast" ], "expected_output": "Deployed to: 0x...", - "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address — needed for verification and interaction." + "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address \u2014 needed for verification and interaction." }, { "order": 7, @@ -5950,7 +5950,7 @@ "prerequisites": { "runtime": [ "Unix-based OS (Linux or macOS) or Windows WSL", - "polkadot and polkadot-parachain binaries — downloaded via: zombienet setup polkadot polkadot-parachain" + "polkadot and polkadot-parachain binaries \u2014 downloaded via: zombienet setup polkadot polkadot-parachain" ] }, "env_vars": [], @@ -5990,7 +5990,7 @@ "zombienet spawn network.toml --provider native" ], "expected_output": "Network launched\nRPC endpoints:", - "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal — the network persists until you stop it with Ctrl+C." + "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal \u2014 the network persists until you stop it with Ctrl+C." }, { "order": 5, @@ -6083,7 +6083,7 @@ { "id": "deploy-interact-contracts-web3js", "title": "Deploy and Interact with Smart Contracts Using Web3.js", - "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", + "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset \u2014 for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -6102,7 +6102,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for deployment gas — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens for deployment gas \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -6270,7 +6270,7 @@ "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction" } ], - "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json" + "project_structure": "web3js-project/\n\u251c\u2500\u2500 abis/\n\u251c\u2500\u2500 artifacts/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 scripts/\n\u2502 \u251c\u2500\u2500 compile.js\n\u2502 \u251c\u2500\u2500 connectToProvider.js\n\u2502 \u251c\u2500\u2500 deploy.js\n\u2502 \u2514\u2500\u2500 updateStorage.js\n\u251c\u2500\u2500 contract-address.json\n\u2514\u2500\u2500 package.json" }, { "id": "add-existing-pallet-to-runtime", @@ -6293,7 +6293,7 @@ "Polkadot SDK system dependencies installed (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" ], "network": [ - "No external network required — all compilation and testing is local" + "No external network required \u2014 all compilation and testing is local" ] }, "env_vars": [], @@ -6323,25 +6323,25 @@ "commands": [ "grep 'polkadot-sdk' runtime/Cargo.toml | head -5" ], - "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version — you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." + "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version \u2014 you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." }, { "order": 4, "action": "Add the pallet dependency to runtime/Cargo.toml", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 — the output should not show dependency resolution errors." + "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 \u2014 the output should not show dependency resolution errors." }, { "order": 5, "action": "Implement the pallet's Config trait in runtime/src/lib.rs", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified — the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." + "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified \u2014 the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." }, { "order": 6, "action": "Register the pallet in construct_runtime!", "working_directory": "polkadot-sdk-parachain-template", - "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes — choose carefully as it cannot be changed without a migration. Save the file." + "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes \u2014 choose carefully as it cannot be changed without a migration. Save the file." }, { "order": 7, @@ -6378,7 +6378,7 @@ { "pattern": "error[E0432]: unresolved import `pallet_utility`", "cause": "pallet-utility was not added to runtime/Cargo.toml, or it was added but Cargo has not re-resolved dependencies.", - "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again — Cargo will fetch and compile the new dependency." + "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again \u2014 Cargo will fetch and compile the new dependency." }, { "pattern": "Utility pallet does not appear in Polkadot.js Apps extrinsics", @@ -6435,7 +6435,7 @@ "scenario": "Edge case: compilation fails with missing associated type in Config", "user_says": "I get a compiler error about missing associated types in my Config impl", "actions": [ - "Read the full compiler error — each missing type is listed with 'required by this bound'", + "Read the full compiler error \u2014 each missing type is listed with 'required by this bound'", "Add each missing associated type to the impl block", "Refer to the pallet's source (src/lib.rs) or docs for the expected type definitions", "Re-run cargo build --release" @@ -6466,7 +6466,7 @@ "Familiarity with adding a single pallet to a runtime (see add-existing-pallet-to-runtime)" ], "network": [ - "No external network required — local development only" + "No external network required \u2014 local development only" ] }, "env_vars": [], @@ -6499,7 +6499,7 @@ "order": 4, "action": "Add pallet dependencies to runtime/Cargo.toml", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances — add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" + "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances \u2014 add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" }, { "order": 5, @@ -6511,13 +6511,13 @@ "order": 6, "action": "Implement Config for Council", "working_directory": "polkadot-sdk-parachain-template", - "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage — `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." + "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage \u2014 `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." }, { "order": 7, "action": "Register both instances in construct_runtime!", "working_directory": "polkadot-sdk-parachain-template", - "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics — choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." + "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics \u2014 choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." }, { "order": 8, @@ -6536,7 +6536,7 @@ "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/instances-spec.json", "./target/release/parachain-template-node --dev --tmp --chain /tmp/instances-spec.json" ], - "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics — verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." + "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics \u2014 verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." } ], "reference_code": { @@ -6548,12 +6548,12 @@ "error_patterns": [ { "pattern": "error[E0119]: conflicting implementations of trait `pallet_collective::Config`", - "cause": "Two impl blocks are targeting the same instance type — usually because both use the default instance () instead of Instance1/Instance2.", + "cause": "Two impl blocks are targeting the same instance type \u2014 usually because both use the default instance () instead of Instance1/Instance2.", "resolution": "Ensure each impl block explicitly names a different instance: impl pallet_collective::Config and impl pallet_collective::Config. Type aliases (type TechnicalCommitteeInstance = pallet_collective::Instance1;) help clarify intent." }, { "pattern": "error: the pallet does not implement the Instance trait", - "cause": "The pallet does not support instantiation — it lacks the I: 'static = () generic in its Config trait.", + "cause": "The pallet does not support instantiation \u2014 it lacks the I: 'static = () generic in its Config trait.", "resolution": "Only instantiable pallets (those with a second generic parameter) can be used this way. Check the pallet source or use a different pallet that supports multiple instances." }, { @@ -6569,13 +6569,13 @@ "slug": "parachains-customize-runtime-add-existing-pallets", "title": "Add an Existing Pallet to the Runtime", "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Prerequisite: adding a single pallet to a runtime — covers the Cargo, Config, and construct_runtime! basics." + "relevance": "Prerequisite: adding a single pallet to a runtime \u2014 covers the Cargo, Config, and construct_runtime! basics." }, { "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", "title": "Create a Custom Pallet", "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Building a custom pallet from scratch — next step after mastering pallet integration." + "relevance": "Building a custom pallet from scratch \u2014 next step after mastering pallet integration." }, { "slug": "parachains-install-polkadot-sdk", @@ -6590,7 +6590,7 @@ "scenario": "Common scenario: add TechnicalCommittee and Council as two pallet-collective instances", "user_says": "Add a TechnicalCommittee and a Council to my parachain runtime using pallet-collective", "actions": [ - "Verify pallet-collective has the I: 'static = () generic — confirm it is instantiable", + "Verify pallet-collective has the I: 'static = () generic \u2014 confirm it is instantiable", "Add pallet-collective to runtime/Cargo.toml with default-features = false and add to std features", "Implement pallet_collective::Config as TechnicalCommitteeInstance with committee-appropriate parameters", "Implement pallet_collective::Config as CouncilInstance with council-appropriate parameters", @@ -6598,7 +6598,7 @@ "Run cargo build --release", "Start node in --dev mode and verify both 'technicalCommittee' and 'council' appear in Polkadot.js Apps" ], - "result": "Two independent pallet-collective instances registered — TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" + "result": "Two independent pallet-collective instances registered \u2014 TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" }, { "scenario": "Edge case: runtime compiles but only one instance shows in the UI", @@ -6634,7 +6634,7 @@ "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" ], "network": [ - "No external network required — all work is local" + "No external network required \u2014 all work is local" ] }, "env_vars": [], @@ -6683,7 +6683,7 @@ "order": 6, "action": "Implement pallet_counter::Config in runtime/src/lib.rs", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 — adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." + "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 \u2014 adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." }, { "order": 7, @@ -6700,7 +6700,7 @@ "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/counter-spec.json", "./target/release/parachain-template-node --dev --tmp --chain /tmp/counter-spec.json" ], - "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics — 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." + "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics \u2014 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." } ], "reference_code": { @@ -6713,12 +6713,12 @@ { "pattern": "error[E0412]: cannot find type `ConstU32` in this scope", "cause": "ConstU32 is not imported in runtime/src/lib.rs.", - "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it — search for existing ConstU32 usages to confirm." + "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it \u2014 search for existing ConstU32 usages to confirm." }, { "pattern": "error[E0277]: the trait `pallet_counter::Config` is not implemented", "cause": "The impl pallet_counter::Config for Runtime block is missing or has a missing associated type.", - "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type — add them one by one." + "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type \u2014 add them one by one." }, { "pattern": "error: failed to select a version for `pallet-counter` / could not find Cargo.toml", @@ -6728,7 +6728,7 @@ { "pattern": "CounterOverflow error when submitting increment", "cause": "The counter has reached MaxValue (100 by default).", - "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design — the error confirms the overflow protection is working." + "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design \u2014 the error confirms the overflow protection is working." } ], "supplementary_context": { @@ -6750,7 +6750,7 @@ "slug": "parachains-customize-runtime-add-existing-pallets", "title": "Add an Existing Pallet to the Runtime", "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Adding an existing SDK pallet to a runtime — prerequisite concepts for runtime integration." + "relevance": "Adding an existing SDK pallet to a runtime \u2014 prerequisite concepts for runtime integration." }, { "slug": "parachains-customize-runtime-pallet-development-mock-runtime", @@ -6804,8 +6804,8 @@ "primary_page": "reference/parachains/chain-data.md", "prerequisites": { "runtime": [ - "curl (Method A — available by default on macOS and most Linux distros)", - "subxt CLI (Method B — install with: cargo install subxt-cli)" + "curl (Method A \u2014 available by default on macOS and most Linux distros)", + "subxt CLI (Method B \u2014 install with: cargo install subxt-cli)" ], "network": [ "An accessible RPC endpoint for the target Polkadot SDK node (e.g. https://rpc.polkadot.io for mainnet, or http://127.0.0.1:9944 for a local dev node)" @@ -6820,7 +6820,7 @@ "commands": [ "curl -H \"Content-Type: application/json\" -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\"}' https://rpc.polkadot.io" ], - "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string — not human-readable. Use Method B or C if you need human-readable JSON.", + "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string \u2014 not human-readable. Use Method B or C if you need human-readable JSON.", "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":\"0x6d657461...\",\"id\":1}" }, { @@ -6843,7 +6843,7 @@ "order": 4, "action": "Interpret the metadata output", "working_directory": ".", - "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type → find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions — do not hardcode them." + "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type \u2192 find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions \u2014 do not hardcode them." } ], "reference_code": { @@ -6855,7 +6855,7 @@ { "pattern": "curl returns connection refused or timeout", "cause": "The RPC endpoint URL is incorrect, the node is not running, or the endpoint does not accept HTTP connections.", - "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections — use subxt (Method B) with ws:// or wss:// instead of curl." + "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections \u2014 use subxt (Method B) with ws:// or wss:// instead of curl." }, { "pattern": "subxt metadata fails with 'failed to fetch metadata'", @@ -6875,7 +6875,7 @@ "slug": "reference-tools-subxt", "title": "Subxt Rust API", "url": "https://docs.polkadot.com/reference/tools/subxt.md", - "relevance": "Subxt library usage for generating typed client code from runtime metadata — the primary consumer of metadata in Rust applications." + "relevance": "Subxt library usage for generating typed client code from runtime metadata \u2014 the primary consumer of metadata in Rust applications." }, { "slug": "reference-parachains-data-encoding", @@ -6911,7 +6911,7 @@ { "id": "use-polkadot-js-api", "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", - "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode — new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", + "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode \u2014 new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -6962,7 +6962,7 @@ "printf 'WS_ENDPOINT=\\nMNEMONIC=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly — do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." + "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly \u2014 do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." }, { "order": 3, @@ -7020,13 +7020,13 @@ "slug": "reference-tools-papi", "title": "Polkadot-API", "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Polkadot API (PAPI) — the recommended replacement for @polkadot/api in new projects." + "relevance": "Polkadot API (PAPI) \u2014 the recommended replacement for @polkadot/api in new projects." }, { "slug": "reference-tools-dedot", "title": "Dedot", "url": "https://docs.polkadot.com/reference/tools/dedot.md", - "relevance": "Dedot — modern lightweight alternative to @polkadot/api with better TypeScript inference." + "relevance": "Dedot \u2014 modern lightweight alternative to @polkadot/api with better TypeScript inference." }, { "slug": "chain-interactions-send-transactions-with-sdks", @@ -7063,7 +7063,7 @@ { "id": "deploy-basic-contract-remix", "title": "Deploy a Basic Smart Contract with Remix IDE", - "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", + "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions \u2014 no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7101,7 +7101,7 @@ "order": 2, "action": "Open Remix IDE and locate Storage.sol", "working_directory": ".", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed." + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract \u2014 no changes needed." }, { "order": 3, @@ -7114,7 +7114,7 @@ "order": 4, "action": "Deploy to Polkadot Hub TestNet via MetaMask", "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", + "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission \u2014 click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation \u2014 click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel." }, { @@ -7203,7 +7203,7 @@ { "id": "connect-remix-polkadot", "title": "Connect Remix IDE to Polkadot Hub", - "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", + "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) \u2014 use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7233,13 +7233,13 @@ "order": 2, "action": "Navigate to the Deploy and Run Transactions tab", "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left." + "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond \u2014 the fourth icon from top). The Deploy and Run Transactions panel opens on the left." }, { "order": 3, "action": "Select Injected Provider - MetaMask", "working_directory": ".", - "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." + "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup \u2014 click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." }, { "order": 4, @@ -7346,7 +7346,7 @@ "order": 2, "action": "Enter Polkadot Hub TestNet network details", "working_directory": ".", - "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." + "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank \u2014 no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." }, { "order": 3, @@ -7441,7 +7441,7 @@ { "id": "use-wagmi-polkadot-hub", "title": "Build a Wagmi dApp Connected to Polkadot Hub", - "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo — all code is inlined in the steps.", + "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo \u2014 all code is inlined in the steps.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7460,10 +7460,10 @@ "MetaMask or any EIP-1193 wallet installed in the browser" ], "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + "Small PAS balance for write transactions \u2014 obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -7477,7 +7477,7 @@ "cd wagmi-dapp", "npm install" ], - "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input — `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm — skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." + "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input \u2014 `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm \u2014 skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." }, { "order": 2, @@ -7516,13 +7516,13 @@ "order": 7, "action": "Add Storage contract read interaction using useReadContract", "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type — without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." + "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type \u2014 without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." }, { "order": 8, "action": "Add Storage contract write interaction using useWriteContract", "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from ." + "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee \u2014 get tokens from ." }, { "order": 9, @@ -7539,7 +7539,7 @@ "action": "Connect wallet and test contract interactions in the browser", "working_directory": "wagmi-dapp", "interactive": true, - "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation — approve it. After confirmation, the stored value should update to the new number.", + "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation \u2014 approve it. After confirmation, the stored value should update to the new number.", "expected_output": "Wallet connected, block number displayed, Storage contract read/write working" } ], @@ -7577,13 +7577,13 @@ "slug": "smart-contracts-libraries-viem", "title": "viem for Polkadot Hub Smart Contracts", "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Lower-level viem library for direct contract calls without React hooks — useful when Wagmi is not needed." + "relevance": "Lower-level viem library for direct contract calls without React hooks \u2014 useful when Wagmi is not needed." }, { "slug": "smart-contracts-integrations-wallets", "title": "Wallets for Polkadot Hub", "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required before the wallet connection step works." + "relevance": "How to add Polkadot Hub TestNet to MetaMask \u2014 required before the wallet connection step works." }, { "slug": "smart-contracts-libraries-wagmi", @@ -7609,7 +7609,7 @@ }, { "scenario": "Edge case: user wants to call their own deployed contract instead of the pre-deployed Storage", - "user_says": "I deployed my own ERC-20 token — how do I call it with Wagmi useReadContract?", + "user_says": "I deployed my own ERC-20 token \u2014 how do I call it with Wagmi useReadContract?", "actions": [ "Replace CONTRACT_ADDRESS in StorageRead.tsx with the user's deployed contract address", "Replace STORAGE_ABI with the ERC-20 ABI fragment for the desired function (e.g. balanceOf)", @@ -7623,7 +7623,7 @@ { "id": "deploy-interact-contracts-web3py", "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", - "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", + "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset \u2014 for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7639,19 +7639,19 @@ "Solidity compiler (installed automatically by py-solc-x)" ], "network": [ - "Polkadot Hub TestNet — RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" + "Polkadot Hub TestNet \u2014 RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" ], "tokens": [ - "Small PAS balance for deployment gas — obtain from https://faucet.polkadot.io/" + "Small PAS balance for deployment gas \u2014 obtain from https://faucet.polkadot.io/" ], "wallet": [ - "An EVM private key (PRIVATE_KEY stored in .env — never enter in chat)" + "An EVM private key (PRIVATE_KEY stored in .env \u2014 never enter in chat)" ] }, "env_vars": [ { "name": "PRIVATE_KEY", - "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", + "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat \u2014 they should edit the .env file directly.", "required": true } ], @@ -7674,13 +7674,13 @@ "commands": [ "pip install web3 py-solc-x python-dotenv" ], - "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." + "description": "Install web3 (Web3.py \u2014 Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." }, { "order": 3, "action": "Create the .env file with PRIVATE_KEY", "working_directory": "web3py-contracts", - "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" + "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat \u2014 instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" }, { "order": 4, @@ -7692,17 +7692,17 @@ "order": 5, "action": "Create compile.py to compile the Solidity contract", "working_directory": "web3py-contracts", - "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", + "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful \u2014 compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", "commands": [ "python compile.py" ], - "expected_output": "Compilation successful — compiled.json written." + "expected_output": "Compilation successful \u2014 compiled.json written." }, { "order": 6, "action": "Create deploy.py to deploy the contract", "working_directory": "web3py-contracts", - "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", + "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 \u2014 no substitutions needed.", "commands": [ "python deploy.py" ], @@ -7759,7 +7759,7 @@ "slug": "smart-contracts-libraries-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js library — recommended JavaScript alternative to the sunset Web3.js library." + "relevance": "Ethers.js library \u2014 recommended JavaScript alternative to the sunset Web3.js library." }, { "slug": "smart-contracts-connect", @@ -7785,7 +7785,7 @@ }, { "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", - "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?", + "user_says": "I have my own Solidity file \u2014 how do I deploy it with Web3.py?", "actions": [ "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", @@ -7799,7 +7799,7 @@ { "id": "interact-erc20-precompile-polkadot-hub", "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", - "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", + "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page \u2014 load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7817,10 +7817,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "PAS balance in MetaMask for transfer/approve operations — obtain from https://faucet.polkadot.io/" + "PAS balance in MetaMask for transfer/approve operations \u2014 obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -7830,7 +7830,7 @@ "action": "Find the ERC-20 precompile address from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." + "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address \u2014 it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." }, { "order": 2, @@ -7851,7 +7851,7 @@ "action": "Connect Remix to MetaMask and load the precompile using At Address", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." + "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up \u2014 confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." }, { "order": 5, @@ -7859,14 +7859,14 @@ "working_directory": ".", "interactive": true, "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", - "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18" + "expected_output": "uint256 balance in wei \u2014 should match MetaMask PAS balance * 10^18" }, { "order": 6, "action": "Call approve and transfer functions", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", + "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation \u2014 approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console" } ], @@ -7882,7 +7882,7 @@ "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." }, { - "pattern": "MetaMask: Wrong network — expected Custom (420420417)", + "pattern": "MetaMask: Wrong network \u2014 expected Custom (420420417)", "cause": "MetaMask is still on Ethereum Mainnet or another network.", "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." }, @@ -7905,7 +7905,7 @@ "slug": "smart-contracts-integrations-wallets", "title": "Wallets for Polkadot Hub", "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite for this skill." + "relevance": "How to add Polkadot Hub TestNet to MetaMask \u2014 required prerequisite for this skill." }, { "slug": "smart-contracts-dev-environments-remix", @@ -7946,7 +7946,7 @@ { "id": "interact-storage-precompile-remix", "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", - "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", + "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page \u2014 load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7964,10 +7964,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" + "PAS balance for setBytes write transactions \u2014 obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -7977,7 +7977,7 @@ "action": "Find the Storage precompile address from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address \u2014 it will be used in step 4 as the 'At Address' target in Remix." }, { "order": 2, @@ -8005,7 +8005,7 @@ "action": "Call setBytes to store a value", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value \u2014 for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store \u2014 for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation \u2014 approve it. Wait for the transaction to confirm.", "expected_output": "Transaction confirmed; tx hash shown in Remix console" }, { @@ -8058,7 +8058,7 @@ "slug": "smart-contracts-integrations-wallets", "title": "Wallets for Polkadot Hub", "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite." + "relevance": "How to add Polkadot Hub TestNet to MetaMask \u2014 required prerequisite." } ] }, @@ -8093,7 +8093,7 @@ { "id": "interact-system-precompile-remix", "title": "Interact with the System Precompile on Polkadot Hub via Remix", - "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", + "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values \u2014 the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page \u2014 load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8111,10 +8111,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + "Small PAS balance for write transactions \u2014 obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -8124,7 +8124,7 @@ "action": "Find the System precompile address from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address \u2014 it will be used in step 4 as the 'At Address' target in Remix." }, { "order": 2, @@ -8138,7 +8138,7 @@ "action": "Create the System precompile interface in Remix", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures \u2014 the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." }, { "order": 4, @@ -8152,7 +8152,7 @@ "action": "Call blake2b to hash data", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash \u2014 for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data" }, { @@ -8213,7 +8213,7 @@ "slug": "smart-contracts-precompiles-erc20", "title": "Interact with the ERC20 Precompile", "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", - "relevance": "ERC-20 precompile — often used alongside the System precompile in contract interactions." + "relevance": "ERC-20 precompile \u2014 often used alongside the System precompile in contract interactions." } ] }, @@ -8241,7 +8241,7 @@ "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", "Click call and confirm the result is true" ], - "result": "bool true returned — sr25519 signature verified on-chain via the System precompile" + "result": "bool true returned \u2014 sr25519 signature verified on-chain via the System precompile" } ] }, @@ -8266,10 +8266,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + "Small PAS balance for write transactions \u2014 obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -8279,7 +8279,7 @@ "action": "Find the XCM precompile address and IXcm ABI from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address \u2014 it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." }, { "order": 2, @@ -8293,21 +8293,21 @@ "action": "Create the IXcm interface in Remix and compile it", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) \u2014 the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." }, { "order": 4, "action": "Load the XCM precompile using At Address in Remix", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection \u2014 verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." }, { "order": 5, "action": "Call weighMessage to estimate XCM execution cost", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button \u2014 read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost" }, { @@ -8315,7 +8315,7 @@ "action": "Call execute to run an XCM message locally", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", + "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button \u2014 state-changing). MetaMask will prompt for gas approval \u2014 confirm. The call executes the XCM message in the context of the calling address on the local chain.", "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs" }, { @@ -8323,7 +8323,7 @@ "action": "Call send to dispatch an XCM to another chain", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", + "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas \u2014 confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous \u2014 use the xcm-tools skill to monitor delivery if needed.", "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs" } ], @@ -8425,11 +8425,11 @@ "chain-spec-builder binary: cargo install chain-spec-builder" ], "network": [ - "Paseo TestNet — wss://rpc.ibp.network/paseo", + "Paseo TestNet \u2014 wss://rpc.ibp.network/paseo", "Server with TCP port 30333 open for P2P collator connections" ], "tokens": [ - "PAS tokens from https://faucet.polkadot.io/ — required for para ID reservation and parathread registration fees" + "PAS tokens from https://faucet.polkadot.io/ \u2014 required for para ID reservation and parathread registration fees" ], "wallet": [ "Polkadot.js browser extension (https://polkadot.js.org/extension/) with a PAS-funded account" @@ -8449,7 +8449,7 @@ "action": "Reserve a para ID on Paseo TestNet", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID — record it as PARA_ID (needed in all subsequent steps).", + "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID \u2014 record it as PARA_ID (needed in all subsequent steps).", "expected_output": "Para ID assigned and visible in the Parathreads tab" }, { @@ -8506,7 +8506,7 @@ "commands": [ "polkadot-omni-node generate-node-key --file node-key.dat" ], - "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure — losing it changes the peer ID on next restart." + "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure \u2014 losing it changes the peer ID on next restart." }, { "order": 9, @@ -8525,7 +8525,7 @@ "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"aura\",\"INSERT_AURA_SEED_PHRASE\",\"INSERT_AURA_PUBLIC_KEY_HEX\"],\"id\":1}'", "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"gran\",\"INSERT_GRANDPA_SEED_PHRASE\",\"INSERT_GRANDPA_PUBLIC_KEY_HEX\"],\"id\":1}'" ], - "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history — they contain seed phrases. Run them directly in the server terminal.", + "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history \u2014 they contain seed phrases. Run them directly in the server terminal.", "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1} for each key insertion" } ], @@ -8582,7 +8582,7 @@ "examples": [ { "scenario": "Common scenario: first deployment to Paseo after building the template", - "user_says": "I built the parachain template — how do I deploy it to Paseo TestNet?", + "user_says": "I built the parachain template \u2014 how do I deploy it to Paseo TestNet?", "actions": [ "Get PAS tokens from faucet", "Reserve a para ID via Polkadot.js Apps", @@ -8600,7 +8600,7 @@ "Verify port 30333 is open on the server firewall", "Confirm the startup command includes the '-- --chain paseo' relay chain section", "Add a known Paseo bootnode with '--bootnodes /dns/...' to the startup flags", - "Wait 10 minutes for peer discovery — initial warp sync connection can be slow" + "Wait 10 minutes for peer discovery \u2014 initial warp sync connection can be slow" ], "result": "Collator finds relay chain peers and begins syncing; block production starts after parathread onboards" } @@ -8609,7 +8609,7 @@ { "id": "connect-polkadot-hub-testnet", "title": "Connect to Polkadot Hub and Get Test Tokens", - "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", + "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference \u2014 consult it for endpoint URLs and chain IDs needed by other skills.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8691,7 +8691,7 @@ "examples": [ { "scenario": "Common scenario: set up development environment from scratch", - "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?", + "user_says": "I want to deploy a smart contract on Polkadot Hub \u2014 how do I connect to TestNet?", "actions": [ "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", "Request PAS tokens from https://faucet.polkadot.io/", @@ -8704,15 +8704,903 @@ "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?", "actions": [ "Reference the MainNet network parameters from the source page", - "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" + "Warn: MainNet uses real DOT \u2014 use TestNet (chain ID 420420417) for development and testing" ], "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage" } ] + }, + { + "id": "store-retrieve-data-bulletin-chain", + "title": "Store and Retrieve Data on the Bulletin Chain", + "description": "Stores binary files on the Polkadot Bulletin Chain using PAPI and retrieves them by CID via the IPFS gateway. Use when you need censorship-resistant, IPFS-compatible decentralized storage for images, JSON configs, HTML pages, or any binary data. Trigger phrases: 'store data bulletin chain', 'upload file decentralized Polkadot', 'retrieve bulletin chain CID', 'Bulletin Chain IPFS'. Authorization via Console UI faucet is required before storing (programmatic self-authorization is not supported on TestNet). Key capabilities: project init, PAPI descriptor generation for Bulletin Chain, store transaction submission, CID-based retrieval via IPFS gateway, and data renewal before the 2-week retention expiry. Output: block hash, CID, block number, and transaction index.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/store-data/bulletin-chain.md" + ], + "primary_page": "chain-interactions/store-data/bulletin-chain.md", + "prerequisites": { + "runtime": [ + "Node.js v18+ and npm" + ], + "network": [ + "Polkadot Bulletin Chain TestNet WSS: wss://paseo-bulletin-rpc.polkadot.io" + ], + "tokens": [ + "Bulletin Chain authorization (transactions + bytes quota) \u2014 obtained from the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ (see Get Authorization section; programmatic self-authorization is not available)" + ], + "wallet": [ + "A Polkadot account mnemonic (12-word BIP39 phrase) for an account that has been granted authorization on Bulletin Chain TestNet" + ] + }, + "env_vars": [ + { + "name": "MNEMONIC", + "description": "12-word BIP39 mnemonic for the Polkadot account authorized on the Bulletin Chain TestNet. The account must have active authorization (transactions + bytes quota) granted via the Console UI faucet. Never commit to version control.", + "required": true + } + ], + "project_structure": "bulletin-store-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 store-data.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 package.json\n\u2514\u2500\u2500 node_modules/", + "steps": [ + { + "order": 1, + "action": "Get authorization via Console UI faucet", + "working_directory": ".", + "interactive": true, + "description": "Before storing data, your account needs authorization on the Bulletin Chain (transactions quota + bytes quota). Delegate this step to the user: navigate to https://paritytech.github.io/polkadot-bulletin-chain/, connect their wallet, go to the Faucet page, select Storage Faucet, enter the desired number of transactions and bytes, click Authorize Account, and approve the transaction. Wait for the user to confirm authorization is complete before proceeding. Note: authorization has an expiration block \u2014 unused authorization is not refunded after expiry." + }, + { + "order": 2, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir bulletin-store-example && cd bulletin-store-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory named 'bulletin-store-example' and initialize it as an ESM Node.js project. The `type=module` flag is required for PAPI's ESM-only imports." + }, + { + "order": 3, + "action": "Install dependencies", + "working_directory": "bulletin-store-example", + "commands": [ + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv", + "npm install --save-dev typescript tsx" + ], + "description": "Install polkadot-api (PAPI core), @polkadot-labs/hdkd and @polkadot-labs/hdkd-helpers (SR25519 key derivation), multiformats (CID encoding), dotenv (secret management), and tsx (TypeScript executor)." + }, + { + "order": 4, + "action": "Generate PAPI typed descriptors for Bulletin Chain", + "working_directory": "bulletin-store-example", + "commands": [ + "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" + ], + "expected_output": "Added bulletin chain descriptors", + "description": "Download Bulletin Chain metadata and generate typed descriptors. This connects to the Bulletin Chain WSS endpoint, retrieves the current runtime metadata, and generates typed descriptors in `.papi/descriptors/` that provide compile-time type safety for all pallet interactions including `TransactionStorage.store`." + }, + { + "order": 5, + "action": "Create .env file and .gitignore for mnemonic", + "working_directory": "bulletin-store-example", + "commands": [ + "printf 'MNEMONIC=\\n' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create a .env file with an empty MNEMONIC placeholder. Stop here and ask the user to edit .env directly \u2014 fill in MNEMONIC with their 12-word BIP39 mnemonic for the account that has been authorized on Bulletin Chain TestNet. Do NOT ask for the mnemonic in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 6, + "action": "Fetch and configure the store-data script", + "working_directory": "bulletin-store-example", + "reference_file": "store-data.ts", + "description": "Fetch the reference file and save it as `store-data.ts`. Apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line of the file.\n\n(2) Remove the import of `DEV_PHRASE` from `@polkadot-labs/hdkd-helpers`. Replace the line:\n```typescript\nimport { DEV_PHRASE, entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';\n```\nwith:\n```typescript\nimport { entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';\n```\n\n(3) In the signer derivation section, replace any reference to `DEV_PHRASE` with `process.env.MNEMONIC as string`. For example:\n```typescript\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(process.env.MNEMONIC as string));\n```\n\n(4) Replace `INSERT_IMAGE_PATH` with the actual path to the file the user wants to store (ask the user for this path \u2014 e.g. `'./logo.png'`). Ensure the file exists and is under ~8 MiB.\n\nSave the modified file." + }, + { + "order": 7, + "action": "Run the store script", + "working_directory": "bulletin-store-example", + "commands": [ + "npx tsx store-data.ts" + ], + "expected_output": "Transaction included in block: 0x...", + "description": "Execute the store script. On success, the script prints the block hash. **Save the CID, block number, and transaction index from the output** \u2014 you need the block number and transaction index for any future renewal, and the CID to retrieve the file. The Console UI auto-saves history, but record these values separately as a backup." + }, + { + "order": 8, + "action": "Retrieve the stored file by CID", + "working_directory": "bulletin-store-example", + "description": "Retrieve the stored file using the CID from the previous step. Use the Bulletin Chain IPFS gateway:\n\n```typescript\nconst cid = 'YOUR_CID_HERE';\nconst response = await fetch(`https://paseo-ipfs.polkadot.io/ipfs/${cid}`);\nconst data = await response.arrayBuffer();\nconsole.log(`Retrieved ${data.byteLength} bytes`);\n```\n\nReplace `YOUR_CID_HERE` with the CID string returned in step 7 (e.g., `bafk2bzacea6wlxy...`). Alternatively, open the URL directly in a browser: `https://paseo-ipfs.polkadot.io/ipfs/`. Data is retained for approximately 2 weeks \u2014 renew before expiry to keep it available." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", + "files": [ + { + "path": "store-data.ts", + "description": "Stores a binary file on the Bulletin Chain using PAPI's TransactionStorage.store extrinsic, derives an SR25519 signer from a mnemonic, and prints the block hash and CID on success" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Account has no active authorization", + "cause": "The account has not been granted storage authorization on the Bulletin Chain, or the authorization has expired.", + "resolution": "Return to the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ and authorize the account again with fresh transactions and bytes quota." + }, + { + "pattern": "Error: Not enough authorization bytes remaining", + "cause": "The file size exceeds the remaining byte quota on the account's authorization.", + "resolution": "Request additional authorization via the Console UI faucet, or split the file into smaller chunks (under 4 MiB per transaction)." + }, + { + "pattern": "SyntaxError: Cannot use import statement in a module / ERR_REQUIRE_ESM", + "cause": "The project is not configured as ESM, or dotenv was imported incorrectly.", + "resolution": "Run `npm pkg set type=module` in the project directory. Ensure `import 'dotenv/config';` is used (not `require('dotenv').config()`) since this is an ESM project." + }, + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI descriptors for the Bulletin Chain have not been generated yet.", + "resolution": "Run `npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io` to fetch the chain metadata and generate descriptors." + }, + { + "pattern": "Error connecting to wss://paseo-bulletin-rpc.polkadot.io / WebSocket connection failed", + "cause": "Bulletin Chain TestNet RPC is temporarily unavailable or the endpoint has changed.", + "resolution": "Check the Polkadot developer Discord for network status. Retry after a short wait." + } + ], + "supplementary_context": { + "description": "Load when the user asks about Bulletin Chain concepts, retention details, or advanced retrieval methods.", + "pages": [ + { + "slug": "chain-interactions-accounts-create-account", + "title": "Create a Polkadot Account Programmatically", + "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account.md", + "relevance": "How to create the Polkadot account whose mnemonic is used as the Bulletin Chain signer." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: store an image on the Bulletin Chain", + "user_says": "Store my logo.png on the Polkadot Bulletin Chain", + "actions": [ + "Instruct user to authorize account via Console UI faucet", + "Init bulletin-store-example project with ESM", + "Install polkadot-api, hdkd, hdkd-helpers, multiformats, dotenv, tsx", + "Run npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io", + "Create .env with MNEMONIC placeholder; ask user to fill it in with authorized account mnemonic", + "Fetch store-data.ts; add dotenv import; replace DEV_PHRASE with process.env.MNEMONIC; set FILE_PATH to './logo.png'", + "Run npx tsx store-data.ts", + "Record CID, block number, and transaction index from output" + ], + "result": "File stored on Bulletin Chain; CID returned for retrieval via https://paseo-ipfs.polkadot.io/ipfs/" + }, + { + "scenario": "Edge case: authorization expired before storing", + "user_says": "The script fails saying the account has no authorization", + "actions": [ + "Navigate to https://paritytech.github.io/polkadot-bulletin-chain/", + "Go to Faucet > Storage Faucet and re-authorize the account with fresh transactions and bytes quota", + "Wait for authorization transaction to be included", + "Retry: npx tsx store-data.ts" + ], + "result": "Authorization renewed; store transaction succeeds on retry" + } + ] + }, + { + "id": "deploy-uniswap-v2-core-pvm", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", + "description": "Clones the polkavm-hardhat-examples repository and deploys Uniswap V2 Core contracts (UniswapV2ERC20, UniswapV2Factory, UniswapV2Pair) to Polkadot Hub TestNet using Hardhat. Use when building an AMM or DEX on Polkadot Hub from the polkavm-hardhat-examples reference. Trigger phrases: 'deploy Uniswap V2 Polkadot PVM', 'deploy Factory Pair contracts Polkadot Hub', 'AMM Polkadot Hub polkavm'. Requires testnet PAS tokens and a funded EVM private key. Uses dotenv for private key management. Key capabilities: clone and navigate polkavm-hardhat-examples, compile Solidity contracts, run test suite against local dev node, and deploy UniswapV2ERC20/Factory/Pair to polkadotHubTestNet.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", + "prerequisites": { + "runtime": [ + "Node.js v16+ and npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + ] + }, + "env_vars": [ + { + "name": "AH_PRIV_KEY", + "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", + "required": true + }, + { + "name": "LOCAL_PRIV_KEY", + "description": "0x-prefixed EVM private key for local development node testing. Can be a dev account key from the local node (optional if skipping local testing).", + "required": false + } + ], + "project_structure": "polkavm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v2-polkadot/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV2ERC20.sol\n \u2502 \u251c\u2500\u2500 UniswapV2Factory.sol\n \u2502 \u2514\u2500\u2500 UniswapV2Pair.sol\n \u251c\u2500\u2500 scripts/\n \u2502 \u2514\u2500\u2500 deploy.js\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.js\n \u2514\u2500\u2500 package.json", + "steps": [ + { + "order": 1, + "action": "Clone the repository and navigate to the project", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", + "cd polkavm-hardhat-examples", + "git checkout hardhat-polkadot-evm", + "cd uniswap-v2-polkadot" + ], + "description": "Clone the polkavm-hardhat-examples repository, switch to the `hardhat-polkadot-evm` branch, and navigate into the `uniswap-v2-polkadot` subdirectory. This subdirectory contains the Uniswap V2 Core contracts (Factory, Pair, ERC20), deployment scripts, and a Hardhat configuration already set up to deploy to `polkadotHubTestNet`." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "npm install" + ], + "description": "Install all project dependencies from package.json. The project already includes dotenv as a dependency and the hardhat.config.js already calls `require('dotenv').config()` \u2014 no additional dotenv installation is needed." + }, + { + "order": 3, + "action": "Create .env file and .gitignore for private keys", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with empty placeholders for both private keys. Stop here and ask the user to edit .env directly:\n\n- `AH_PRIV_KEY`: 0x-prefixed EVM private key for their funded Polkadot Hub TestNet account.\n- `LOCAL_PRIV_KEY`: 0x-prefixed EVM private key for a local dev node account (only needed for local testing \u2014 can use a pre-funded dev key from the local node).\n\nDo NOT ask for private keys in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 4, + "action": "Add gas configuration to hardhat.config.js", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "description": "Open `hardhat.config.js` and add `gasPrice: 5000000000000` (5000 gwei \u2014 5x the TestNet base fee of 1000 gwei) to the `polkadotHubTestNet` network block. This prevents 'Priority is too low' errors on TestNet. The `polkadotHubTestNet` network block should look like:\n\n```javascript\npolkadotHubTestNet: {\n url: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n accounts: [process.env.AH_PRIV_KEY],\n gasPrice: 5000000000000,\n},\n```\n\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled contracts successfully", + "description": "Compile all Solidity contracts. On success, compiled artifacts (ABI and bytecode) appear in the `artifacts/` directory. The project uses Solidity 0.8.28." + }, + { + "order": 6, + "action": "Deploy contracts to Polkadot Hub TestNet", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "npx hardhat run scripts/deploy.js --network polkadotHubTestNet" + ], + "expected_output": "Factory deployed to : 0x...", + "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet. The script outputs the deployed contract addresses \u2014 save all three addresses. You need the Factory address to create liquidity pools and the Pair address for subsequent liquidity operations. If the deployment hangs or a transaction appears stuck, check your account balance at https://faucet.polkadot.io/ and verify `gasPrice` is set in hardhat.config.js." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Add `gasPrice: 5000000000000` to the `polkadotHubTestNet` block in hardhat.config.js." + }, + { + "pattern": "Error: Cannot read properties of undefined (reading 'AH_PRIV_KEY') / process.env.AH_PRIV_KEY is undefined", + "cause": ".env file is missing, not populated, or not in the correct directory.", + "resolution": "Ensure .env is in the `uniswap-v2-polkadot/` directory (not the repo root) and contains `AH_PRIV_KEY=0x...` on its own line." + }, + { + "pattern": "Transaction failed / Transaction Already Imported", + "cause": "A previous transaction is stuck in the TestNet mempool, or the transaction was already sent.", + "resolution": "Wait for pending transactions to clear. If stuck, send a replacement transaction at the same nonce with a higher gasPrice (2x the base fee). The TestNet base fee is 1000 gwei." + }, + { + "pattern": "Error: Compiler version mismatch / solidity: '0.8.28' not found", + "cause": "Solidity compiler for 0.8.28 is not installed.", + "resolution": "Run `npx hardhat compile` \u2014 Hardhat downloads the compiler automatically. If it fails to download, check network connectivity." + } + ], + "supplementary_context": { + "description": "Load when the user asks about the Hardhat development environment setup or local testing with the dev node.", + "pages": [ + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Set Up Hardhat for Polkadot Hub (EVM)", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Background on Hardhat configuration for Polkadot Hub, including the polkadotTestnet network setup." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Set Up a Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "How to start the local Revive dev node for running contract tests against a local network before deploying to TestNet." + }, + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", + "title": "Deploy Uniswap V2 Periphery with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "relevance": "Next step: deploy the UniswapV2Router02 contract on top of the Factory deployed in this skill." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V2 Core contracts to Polkadot Hub", + "actions": [ + "Clone polkavm-hardhat-examples, checkout hardhat-polkadot-evm, cd uniswap-v2-polkadot", + "Run npm install", + "Create .env with AH_PRIV_KEY and LOCAL_PRIV_KEY placeholders; ask user to fill in AH_PRIV_KEY", + "Add gasPrice: 5000000000000 to polkadotHubTestNet in hardhat.config.js", + "Run npx hardhat compile", + "Run npx hardhat run scripts/deploy.js --network polkadotHubTestNet", + "Record deployed addresses for UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair" + ], + "result": "All three Uniswap V2 Core contracts deployed to Polkadot Hub TestNet; addresses printed to console" + }, + { + "scenario": "Edge case: transaction stuck or insufficient gas", + "user_says": "Deployment hangs or fails with 'Priority is too low'", + "actions": [ + "Verify AH_PRIV_KEY account has PAS balance at https://faucet.polkadot.io/", + "Open hardhat.config.js and confirm gasPrice: 5000000000000 is in the polkadotHubTestNet block", + "Retry: npx hardhat run scripts/deploy.js --network polkadotHubTestNet" + ], + "result": "Deployment succeeds after gas price is configured correctly" + } + ] + }, + { + "id": "deploy-uniswap-v2-core-evm", + "title": "Deploy Uniswap V2 Core with EVM on Polkadot Hub", + "description": "Clones revm-hardhat-examples and deploys unmodified Uniswap V2 Core contracts (UniswapV2Factory, two test ERC-20 tokens, and a trading pair) to Polkadot Hub TestNet using standard Hardhat and TypeScript via the EVM path (REVM). Use when you need a Factory + Pair deployment on Polkadot Hub with no special compiler plugins. Trigger phrases: 'deploy Uniswap V2 Core EVM Polkadot', 'deploy Factory Pair EVM Polkadot Hub', 'AMM Polkadot Hub EVM Hardhat TypeScript'. Requires testnet PAS tokens and a funded EVM private key. Converts hardhat vars to dotenv. Key capabilities: clone repo, compile Solidity 0.5.16, optionally test locally, and deploy Factory plus two ERC-20 test tokens and a pair to polkadotTestnet.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", + "prerequisites": { + "runtime": [ + "Node.js v22+ and npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v2-core-hardhat/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV2ERC20.sol\n \u2502 \u251c\u2500\u2500 UniswapV2Factory.sol\n \u2502 \u2514\u2500\u2500 UniswapV2Pair.sol\n \u251c\u2500\u2500 scripts/\n \u2502 \u2514\u2500\u2500 deploy.ts\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.ts\n \u2514\u2500\u2500 package.json", + "steps": [ + { + "order": 1, + "action": "Clone the repository and navigate to the project", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout b0a8627059a9d9cb759682310219557550186bc4", + "cd uniswap-v2-core-hardhat" + ], + "description": "Clone the revm-hardhat-examples repository, check out the pinned commit for this tutorial, and navigate into the `uniswap-v2-core-hardhat` subdirectory. This directory contains unmodified Uniswap V2 Core contracts (Solidity 0.5.16), TypeScript tests, and a deployment script that deploys the Factory, two ERC-20 test tokens, and creates a trading pair." + }, + { + "order": 2, + "action": "Install dependencies including dotenv", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install all project dependencies, then additionally install dotenv. dotenv replaces Hardhat's interactive Configuration Variables (`npx hardhat vars set`) which cannot be used in agent shells." + }, + { + "order": 3, + "action": "Create .env file and .gitignore for private key", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty TESTNET_PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for a funded Polkadot Hub TestNet account. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts from vars to dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "description": "Open `hardhat.config.ts` and apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line of the file.\n\n(2) Change the import from:\n```typescript\nimport { HardhatUserConfig, vars } from 'hardhat/config';\n```\nto:\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\n```\n\n(3) Replace the polkadotTestnet `accounts` field. Change:\n```typescript\naccounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : [],\n```\nto:\n```typescript\naccounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : [],\n```\n\n(4) Add `gasPrice: 5000000000000,` (5000 gwei \u2014 5x the TestNet 1000 gwei base fee) to the `polkadotTestnet` network block to prevent 'Priority is too low' errors.\n\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled contracts successfully", + "description": "Compile all Uniswap V2 Core contracts using Solidity 0.5.16 with the optimizer enabled (200 runs). On success, compiled artifacts appear in `artifacts/`." + }, + { + "order": 6, + "action": "Deploy contracts to Polkadot Hub TestNet", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npx hardhat run scripts/deploy.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV2Factory deployed to: 0x...", + "description": "Deploy UniswapV2Factory, two test ERC-20 tokens (10,000 tokens each), and create a trading pair. The script outputs all four contract addresses \u2014 save the Factory, TokenA, TokenB, and Pair addresses. These are needed for subsequent liquidity operations or to build a Router on top of the Factory." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify `gasPrice: 5000000000000` is in the polkadotTestnet block in hardhat.config.ts." + }, + { + "pattern": "TypeError: vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still imports or uses Hardhat's vars system.", + "resolution": "Ensure `import 'dotenv/config';` is the first line, and that `vars` is removed from the import and replaced with `process.env.TESTNET_PRIVATE_KEY`." + }, + { + "pattern": "Error: TESTNET_PRIVATE_KEY is undefined / No accounts configured", + "cause": ".env file is missing or TESTNET_PRIVATE_KEY is not set.", + "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ (not the repo root) and contains `TESTNET_PRIVATE_KEY=0x...`." + }, + { + "pattern": "Transaction stuck / Transaction Already Imported", + "cause": "A previous transaction is pending in the TestNet mempool, or was re-submitted at the same nonce.", + "resolution": "Wait for the pending transaction to confirm or be dropped. If stuck, send a zero-value replacement transaction at the same nonce with 2x the base fee (2000 gwei minimum)." + } + ], + "supplementary_context": { + "description": "Load when the user asks about Hardhat setup, local testing, or deploying the Router on top of the deployed Factory.", + "pages": [ + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Set Up Hardhat for Polkadot Hub (EVM)", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Background on Hardhat configuration for Polkadot Hub." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Set Up a Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "How to start the local Revive dev node for running the test suite against a local network before deploying to TestNet." + }, + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", + "title": "Deploy Uniswap V2 Periphery with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "relevance": "Next step: deploy the UniswapV2Router02 contract that exposes user-facing swap and liquidity functions on top of the Factory." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V2 Core contracts to Polkadot Hub", + "user_says": "Deploy Uniswap V2 Core to Polkadot Hub using EVM", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", + "Modify hardhat.config.ts: add dotenv import, remove vars imports, replace vars.get with process.env, add gasPrice 5000 gwei", + "Run npx hardhat compile", + "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", + "Record Factory, TokenA, TokenB, and Pair addresses" + ], + "result": "UniswapV2Factory and two test ERC-20 tokens deployed; trading pair created; all addresses printed to console" + }, + { + "scenario": "Edge case: run tests against a local dev node before deploying to TestNet", + "user_says": "Run the Uniswap V2 tests against the local node first", + "actions": [ + "Start the local development node following https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "In a new terminal, run: npx hardhat test --network localNode", + "Verify all 28 tests pass", + "Then deploy to TestNet" + ], + "result": "28 tests pass against the local node; deployment to TestNet proceeds with confidence" + } + ] + }, + { + "id": "deploy-uniswap-v2-periphery-evm", + "title": "Deploy Uniswap V2 Periphery (Router) with EVM on Polkadot Hub", + "description": "Clones revm-hardhat-examples and deploys Uniswap V2 Periphery contracts (WETH9, UniswapV2Factory, UniswapV2Router02) to Polkadot Hub TestNet using Hardhat Ignition and the EVM execution path. Use when you need a full Router layer for swaps and liquidity management on Polkadot Hub. Trigger phrases: 'deploy Uniswap V2 Router Polkadot', 'deploy WETH Router EVM Polkadot Hub', 'Uniswap V2 Periphery Polkadot'. Requires testnet PAS tokens and a funded EVM private key. Converts hardhat vars to dotenv. Key capabilities: clone repo, compile multi-version Solidity (0.5.16 + 0.6.6), optionally test locally, and deploy WETH9, Factory, and Router02 via Ignition to polkadotTestnet.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "prerequisites": { + "runtime": [ + "Node.js v22+ and npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v2-periphery-hardhat/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV2Router01.sol\n \u2502 \u251c\u2500\u2500 UniswapV2Router02.sol\n \u2502 \u2514\u2500\u2500 test/\n \u2502 \u251c\u2500\u2500 WETH9.sol\n \u2502 \u2514\u2500\u2500 ERC20.sol\n \u251c\u2500\u2500 ignition/\n \u2502 \u2514\u2500\u2500 modules/\n \u2502 \u2514\u2500\u2500 UniswapV2Router02.ts\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.ts\n \u2514\u2500\u2500 package.json", + "steps": [ + { + "order": 1, + "action": "Clone the repository and navigate to the project", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", + "cd uniswap-v2-periphery-hardhat" + ], + "description": "Clone the revm-hardhat-examples repository, check out the pinned commit for this tutorial, and navigate into `uniswap-v2-periphery-hardhat`. This project depends on the V2 Core contracts through a local file reference (`@uniswap/v2-core: file:../uniswap-v2-core-hardhat`) resolved automatically from the sibling directory in the same repo \u2014 no separate Core clone is needed." + }, + { + "order": 2, + "action": "Install dependencies including dotenv", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install all project dependencies (including the V2 Core local reference), then additionally install dotenv to replace Hardhat's interactive Configuration Variables system." + }, + { + "order": 3, + "action": "Create .env file and .gitignore for private key", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty TESTNET_PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for a funded Polkadot Hub TestNet account. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts from vars to dotenv, add gas and ignition settings", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "description": "Open `hardhat.config.ts` and apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line.\n\n(2) Change the import from:\n```typescript\nimport { HardhatUserConfig, vars } from 'hardhat/config';\n```\nto:\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\n```\n\n(3) Replace the polkadotTestnet `accounts` field:\n```typescript\naccounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : [],\n```\n\n(4) Add `gasPrice: 5000000000000,` (5000 gwei) to the `polkadotTestnet` network block.\n\n(5) Add an `ignition` section at the end of the config object (before `export default config`):\n```typescript\nignition: {\n requiredConfirmations: 1,\n},\n```\n\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled contracts successfully", + "description": "Compile all Periphery contracts using the multi-compiler setup (Solidity 0.5.16 for V2 Core dependency, 0.6.6 for Router contracts). On success, artifacts appear in `artifacts/`." + }, + { + "order": 6, + "action": "Deploy contracts via Ignition to Polkadot Hub TestNet", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", + "interactive": true, + "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. Ignition prompts to confirm the target network name and chain ID \u2014 delegate this confirmation to the user. Ignition deploys Factory and WETH9 in parallel, then Router02. Save all three deployed addresses from the output: WETH9, UniswapV2Factory, and UniswapV2Router02. These addresses are needed to add liquidity and execute swaps via the Router." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify `gasPrice: 5000000000000` is in the polkadotTestnet block in hardhat.config.ts." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of a deployment transaction. Common when requiredConfirmations is 0 or gasPrice is too low.", + "resolution": "Delete ignition/deployments/ directory. Ensure `ignition: { requiredConfirmations: 1 }` and `gasPrice: 5000000000000` are in hardhat.config.ts. Retry the Ignition deploy command." + }, + { + "pattern": "TypeError: vars is not defined", + "cause": "hardhat.config.ts still references Hardhat's vars system.", + "resolution": "Add `import 'dotenv/config';` as first line, remove `vars` from the import, and replace `vars.has/get('TESTNET_PRIVATE_KEY')` with `process.env.TESTNET_PRIVATE_KEY`." + }, + { + "pattern": "Cannot find package '@uniswap/v2-core'", + "cause": "The local V2 Core sibling directory was not found, or npm install did not resolve it.", + "resolution": "Verify the `uniswap-v2-core-hardhat/` directory exists at the same level as `uniswap-v2-periphery-hardhat/` (it was checked out from the same pinned commit in step 1). Run `npm install` again from `uniswap-v2-periphery-hardhat/`." + } + ], + "supplementary_context": { + "description": "Load when the user asks about the Router architecture, V2 Core dependency, or V3 deployment.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", + "title": "Deploy Uniswap V2 Core with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", + "relevance": "The V2 Core Factory and Pair contracts that the Periphery Router builds on top of." + }, + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", + "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "relevance": "Next evolution: deploy the concentrated liquidity Uniswap V3 Factory instead of the uniform-liquidity V2." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Set Up a Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "How to start the local Revive dev node for running the 50-test test suite before deploying to TestNet." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V2 Router to Polkadot Hub", + "user_says": "Deploy the Uniswap V2 Router to Polkadot Hub EVM", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", + "Modify hardhat.config.ts: add dotenv import, remove vars, replace vars.get with process.env, add gasPrice 5000 gwei, add ignition.requiredConfirmations: 1", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet; delegate confirmation to user", + "Record WETH9, UniswapV2Factory, and UniswapV2Router02 addresses" + ], + "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet; addresses printed by Ignition" + }, + { + "scenario": "Edge case: Ignition deployment fails with IGN401 dropped transaction", + "user_says": "Ignition says IGN401 Transaction dropped and retry fails with Transaction Already Imported", + "actions": [ + "Delete the ignition/deployments/ directory", + "Verify hardhat.config.ts has ignition: { requiredConfirmations: 1 } and gasPrice: 5000000000000", + "Retry: npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" + ], + "result": "Ignition re-attempts deployment fresh without stale state; deployment succeeds" + } + ] + }, + { + "id": "deploy-uniswap-v3-core-evm", + "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", + "description": "Clones revm-hardhat-examples and deploys the UniswapV3Factory contract to Polkadot Hub TestNet using Hardhat Ignition and the EVM execution path. Use when you need concentrated liquidity AMM infrastructure on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Polkadot', 'deploy V3 Factory Polkadot Hub EVM', 'concentrated liquidity AMM Polkadot'. Requires testnet PAS tokens and a funded EVM private key. Converts hardhat vars to dotenv. Key capabilities: clone repo, compile Solidity 0.7.6 with metadata hash excluded to meet EIP-170 contract size limit, optionally test locally (187 tests), and deploy UniswapV3Factory via Ignition to polkadotTestnet. Uses ignition.requiredConfirmations: 1 (already configured in the reference repo).", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "prerequisites": { + "runtime": [ + "Node.js v22+ and npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v3-core-hardhat/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV3Factory.sol\n \u2502 \u251c\u2500\u2500 UniswapV3Pool.sol\n \u2502 \u2514\u2500\u2500 libraries/\n \u251c\u2500\u2500 ignition/\n \u2502 \u2514\u2500\u2500 modules/\n \u2502 \u2514\u2500\u2500 UniswapV3Factory.ts\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.ts\n \u2514\u2500\u2500 package.json", + "steps": [ + { + "order": 1, + "action": "Clone the repository and navigate to the project", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", + "cd uniswap-v3-core-hardhat" + ], + "description": "Clone the revm-hardhat-examples repository, check out the pinned commit for this tutorial, and navigate into `uniswap-v3-core-hardhat`. This directory contains unmodified Uniswap V3 Core contracts (Solidity 0.7.6), 16 math library contracts, and an Ignition module that deploys the UniswapV3Factory." + }, + { + "order": 2, + "action": "Install dependencies including dotenv", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install all project dependencies, then additionally install dotenv to replace Hardhat's interactive Configuration Variables system." + }, + { + "order": 3, + "action": "Create .env file and .gitignore for private key", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty TESTNET_PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for a funded Polkadot Hub TestNet account. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts from vars to dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "description": "Open `hardhat.config.ts` and apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line.\n\n(2) Change the import from:\n```typescript\nimport { HardhatUserConfig, vars } from 'hardhat/config';\n```\nto:\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\n```\n\n(3) Replace the polkadotTestnet `accounts` field:\n```typescript\naccounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : [],\n```\n\n(4) Add `gasPrice: 5000000000000,` (5000 gwei) to the `polkadotTestnet` network block.\n\nNote: `ignition: { requiredConfirmations: 1 }` is already present in the config \u2014 do not remove it.\nNote: `bytecodeHash: 'none'` in the Solidity settings is intentional (required to keep UniswapV3Factory under the EIP-170 24KB contract size limit) \u2014 do not change it.\n\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled contracts successfully", + "description": "Compile all V3 Core contracts using Solidity 0.7.6. The build includes 16 math libraries and 17 test helper contracts. `bytecodeHash: 'none'` ensures the Factory stays under the EIP-170 24KB size limit by excluding the metadata hash from the bytecode." + }, + { + "order": 6, + "action": "Deploy UniswapV3Factory via Ignition to Polkadot Hub TestNet", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", + "interactive": true, + "description": "Deploy the UniswapV3Factory contract via Hardhat Ignition. Ignition prompts to confirm the target network and chain ID \u2014 delegate this confirmation to the user. Save the deployed UniswapV3Factory address from the output \u2014 it is the entry point for creating concentrated liquidity pools. The `ignition.requiredConfirmations: 1` setting is already in the config, ensuring reliable deployment on the TestNet." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify `gasPrice: 5000000000000` is in the polkadotTestnet block in hardhat.config.ts." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of the Factory deployment transaction, often due to low gasPrice on TestNet.", + "resolution": "Delete ignition/deployments/ directory. Verify `gasPrice: 5000000000000` and `ignition: { requiredConfirmations: 1 }` in hardhat.config.ts. Retry the Ignition deploy command." + }, + { + "pattern": "TypeError: vars is not defined", + "cause": "hardhat.config.ts still references Hardhat's vars system after the substitution step.", + "resolution": "Add `import 'dotenv/config';` as the first line, remove `vars` from the HardhatUserConfig import, and replace `vars.has/get('TESTNET_PRIVATE_KEY')` with `process.env.TESTNET_PRIVATE_KEY`." + }, + { + "pattern": "Contract size exceeds 24576 bytes / EIP-170 contract size limit", + "cause": "`bytecodeHash` setting was changed or removed from the Solidity compiler settings.", + "resolution": "Restore `metadata: { bytecodeHash: 'none' }` in the Solidity compiler settings in hardhat.config.ts. This is required to keep UniswapV3Factory under the EIP-170 limit and matches the original Uniswap V3 deployment configuration." + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 architecture, concentrated liquidity concepts, or the Periphery contracts.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", + "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", + "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager contracts on top of the V3 Factory." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Set Up a Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "How to start the local Revive dev node for running the 187-test suite (Factory + Pool tests) before deploying to TestNet." + }, + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Set Up Hardhat for Polkadot Hub (EVM)", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Background on Hardhat configuration for Polkadot Hub and the polkadotTestnet network settings." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V3 Factory to Polkadot Hub", + "user_says": "Deploy Uniswap V3 Core to Polkadot Hub TestNet", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", + "Modify hardhat.config.ts: add dotenv import, remove vars, replace vars.get with process.env, add gasPrice 5000 gwei; preserve bytecodeHash: none and ignition.requiredConfirmations: 1", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet; delegate confirmation to user", + "Record UniswapV3Factory address" + ], + "result": "UniswapV3Factory deployed to Polkadot Hub TestNet; factory address printed by Ignition" + }, + { + "scenario": "Edge case: Ignition IGN401 dropped transaction on TestNet", + "user_says": "Ignition fails with IGN401 and retrying gives Transaction Already Imported", + "actions": [ + "Delete ignition/deployments/ directory", + "Verify hardhat.config.ts has ignition: { requiredConfirmations: 1 } and gasPrice: 5000000000000", + "Retry: npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" + ], + "result": "Ignition starts fresh and deploys the Factory successfully" + } + ] } ], "outputs": { "public_root": "/ai/", "skills_dir": "skills" } -} \ No newline at end of file +} diff --git a/skill_candidates.json b/skill_candidates.json index e989e4834..c14fc657b 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -281,7 +281,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent — requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent — bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", + "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent \u2014 requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent \u2014 bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", "scoring": { "signals": [ "P1", @@ -345,7 +345,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:30:00Z", - "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account — no real tokens needed). Self-contained. No cookbook badge.", + "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account \u2014 no real tokens needed). Self-contained. No cookbook badge.", "scoring": { "signals": [ "P1", @@ -490,7 +490,7 @@ }, "priority_score": 11 }, - "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) — prerequisite provisioning is outside current skill capability." + "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) \u2014 prerequisite provisioning is outside current skill capability." }, { "skill_id": "install-polkadot-sdk", @@ -502,7 +502,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch — covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", + "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch \u2014 covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", "scoring": { "signals": [ "P1", @@ -535,7 +535,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T19:00:00Z", - "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent — directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 — requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime).", + "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent \u2014 directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 \u2014 requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime).", "scoring": { "signals": [ "P1", @@ -666,7 +666,7 @@ "category": "guide", "priority": "high", "status": "blocked", - "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 → guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", + "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 \u2192 guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", "scoring": { "signals": [ "P2", @@ -699,7 +699,7 @@ "category": "guide", "priority": "medium", "status": "blocked", - "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 → Rule 6 guide. K1=0 — prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", + "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 \u2192 Rule 6 guide. K1=0 \u2014 prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", "scoring": { "signals": [ "P2", @@ -730,7 +730,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present — numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", + "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present \u2014 numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", "scoring": { "signals": [ "P1", @@ -761,7 +761,7 @@ "category": "tutorial", "priority": "low", "status": "blocked", - "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 — intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", + "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 \u2014 intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -789,7 +789,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 — the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", + "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 \u2014 the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -821,7 +821,7 @@ "category": "tutorial", "priority": "low", "status": "blocked", - "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 — intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", + "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 \u2014 intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -854,7 +854,7 @@ "composite": true, "priority": "medium", "status": "blocked", - "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 → medium.", + "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 \u2192 medium.", "scoring": { "signals": [ "P1", @@ -920,7 +920,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself — delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", + "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself \u2014 delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", "scoring": { "signals": [ "P1", @@ -956,7 +956,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 — requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 → medium.", + "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 \u2014 requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 \u2192 medium.", "scoring": { "signals": [ "P1", @@ -988,7 +988,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 — requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 → medium.", + "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 \u2014 requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 \u2192 medium.", "scoring": { "signals": [ "P1", @@ -1020,7 +1020,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 — requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", + "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 \u2014 requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", "scoring": { "signals": [ "P1", @@ -1053,7 +1053,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 — requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", + "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 \u2014 requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", "scoring": { "signals": [ "P1", @@ -1087,7 +1087,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 — requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", + "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 \u2014 requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", "scoring": { "signals": [ "P1", @@ -1121,7 +1121,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 — requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", + "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 \u2014 requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", "scoring": { "signals": [ "P1", @@ -1231,7 +1231,7 @@ "category": "tutorial", "priority": "low", "status": "built", - "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 — intro explicitly references set-up-the-parachain-template as required prior step. K2=0 — INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", + "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 \u2014 intro explicitly references set-up-the-parachain-template as required prior step. K2=0 \u2014 INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", "scoring": { "signals": [ "P1", @@ -1263,7 +1263,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 — explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", + "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 \u2014 explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", "scoring": { "signals": [ "P1", @@ -1288,7 +1288,7 @@ ], "priority_score": 9 }, - "blocked_reason": "Requires acquiring on-chain coretime on a live network — financial and governance prerequisites make it impractical as a generated skill." + "blocked_reason": "Requires acquiring on-chain coretime on a live network \u2014 financial and governance prerequisites make it impractical as a generated skill." }, { "skill_id": "renew-parachain-coretime", @@ -1299,7 +1299,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 — requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", + "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 \u2014 requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", "scoring": { "signals": [ "P1", @@ -1335,7 +1335,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 — requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 — code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", + "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 \u2014 requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 \u2014 code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", "scoring": { "signals": [ "P1", @@ -1368,7 +1368,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 → Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 — requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", + "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 \u2192 Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 \u2014 requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", "scoring": { "signals": [ "P1", @@ -1404,7 +1404,7 @@ "category": "tutorial", "priority": "high", "status": "blocked", - "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 — tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) → category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", + "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 \u2014 tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) \u2192 category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", "scoring": { "signals": [ "P1", @@ -1445,7 +1445,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present — score 20, no ambiguity. K1=0 — Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 — INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 → medium.", + "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present \u2014 score 20, no ambiguity. K1=0 \u2014 Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 \u2014 INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 \u2192 medium.", "scoring": { "signals": [ "P1", @@ -1477,7 +1477,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic — same endpoint, same output. K3/K4/S3 absent.", + "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic \u2014 same endpoint, same output. K3/K4/S3 absent.", "scoring": { "signals": [ "P1", @@ -1650,7 +1650,7 @@ "category": "guide", "priority": "high", "status": "blocked", - "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) — Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", + "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) \u2014 Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", "scoring": { "signals": [ "P2", @@ -1757,7 +1757,7 @@ "category": "guide", "priority": "high", "status": "built", - "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer — all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", + "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer \u2014 all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", "scoring": { "signals": [ "P2", @@ -1858,7 +1858,7 @@ "category": "guide", "priority": "low", "status": "built", - "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher — flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate → paste address → click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", + "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher \u2014 flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate \u2192 paste address \u2192 click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", "scoring": { "signals": [ "P1", @@ -1994,7 +1994,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) — uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", + "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) \u2014 uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", "scoring": { "signals": [ "P1", @@ -2024,7 +2024,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired — tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", + "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired \u2014 tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", "scoring": { "signals": [ "P1", @@ -2056,7 +2056,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired — workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", + "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired \u2014 workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", "scoring": { "signals": [ "P1", @@ -2088,7 +2088,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired — deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired — code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", + "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired \u2014 deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired \u2014 code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", "scoring": { "signals": [ "P1", @@ -2120,7 +2120,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired — entirely GUI-driven (Remix IDE + MetaMask). K4 not fired — contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", + "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired \u2014 entirely GUI-driven (Remix IDE + MetaMask). K4 not fired \u2014 contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", "scoring": { "signals": [ "P1", @@ -2151,7 +2151,7 @@ "category": "guide", "priority": "high", "status": "built", - "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired — multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired — no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 — not ambiguous.", + "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired \u2014 multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired \u2014 no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 \u2014 not ambiguous.", "scoring": { "signals": [ "P2", @@ -2185,7 +2185,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot — mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", + "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot \u2014 mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", "scoring": { "signals": [ "P1", @@ -2218,7 +2218,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown — P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", + "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown \u2014 P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", "scoring": { "signals": [ "P1", @@ -2441,7 +2441,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) — hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", + "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) \u2014 hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", "scoring": { "signals": [ "P2", @@ -2469,7 +2469,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction — all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", + "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction \u2014 all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", "scoring": { "signals": [ "P1", @@ -2501,7 +2501,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction — all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", + "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction \u2014 all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", "scoring": { "signals": [ "P1", @@ -2532,7 +2532,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural ≥ 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", + "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural \u2265 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", "scoring": { "signals": [ "P1", @@ -2603,7 +2603,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 → medium.", + "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 \u2192 medium.", "scoring": { "signals": [ "P1", @@ -2672,8 +2672,8 @@ ], "category": "tutorial", "priority": "high", - "status": "pending", - "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded — the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", + "status": "built", + "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded \u2014 the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", "scoring": { "signals": [ "P1", @@ -2693,7 +2693,8 @@ "not_applicable": 0 }, "priority_score": 18 - } + }, + "built_at": "2026-05-15T16:00:00Z" }, { "skill_id": "deploy-uniswap-v2-core-pvm", @@ -2703,8 +2704,8 @@ ], "category": "tutorial", "priority": "high", - "status": "pending", - "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded — polkavm-hardhat-examples is listed in context.md.", + "status": "built", + "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded \u2014 polkavm-hardhat-examples is listed in context.md.", "scoring": { "signals": [ "P1", @@ -2727,7 +2728,8 @@ "not_applicable": 0 }, "priority_score": 25 - } + }, + "built_at": "2026-05-15T16:00:00Z" }, { "skill_id": "deploy-uniswap-v2-core-evm", @@ -2737,8 +2739,8 @@ ], "category": "tutorial", "priority": "high", - "status": "pending", - "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded — key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded — revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", + "status": "built", + "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded \u2014 key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded \u2014 revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", "scoring": { "signals": [ "P1", @@ -2760,7 +2762,8 @@ "not_applicable": 0 }, "priority_score": 25 - } + }, + "built_at": "2026-05-15T16:00:00Z" }, { "skill_id": "deploy-uniswap-v2-periphery-evm", @@ -2770,8 +2773,8 @@ ], "category": "tutorial", "priority": "high", - "status": "pending", - "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo — no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded — revm-hardhat-examples is listed in context.md.", + "status": "built", + "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo \u2014 no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded \u2014 revm-hardhat-examples is listed in context.md.", "scoring": { "signals": [ "P1", @@ -2793,7 +2796,8 @@ "not_applicable": 0 }, "priority_score": 25 - } + }, + "built_at": "2026-05-15T16:00:00Z" }, { "skill_id": "deploy-uniswap-v3-core-evm", @@ -2803,7 +2807,7 @@ ], "category": "tutorial", "priority": "high", - "status": "pending", + "status": "built", "notes": "K1=0: test step references external Local Development Node guide; deployment workflow itself is self-contained. K4=3: revm-hardhat-examples repo with pinned commit (3ff28ae). No P3: reader clones the repo, not creates files.", "scoring": { "signals": [ @@ -2827,43 +2831,8 @@ "not_applicable": 0 }, "priority_score": 20 - } - }, - { - "skill_id": "deploy-uniswap-v3-periphery-evm", - "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" - ], - "category": "tutorial", - "priority": "high", - "status": "pending", - "notes": "Full deploy+test workflow for SwapRouter and NonfungiblePositionManager. The 'complete V3 Core tutorial' prereq is a code dependency resolved automatically via local npm file reference in the cloned monorepo; the Ignition module deploys Factory+WETH9+SwapRouter+NFPM in one shot, making this self-contained. Pinned to commit 96696ad15c3cf01b9168a71ad5114f27c34a8726 with docs.test.ts confirming CI-tested code.", - "scoring": { - "signals": [ - "P1", - "P2", - "P3", - "P4", - "R1", - "R2", - "C1", - "K1", - "K2", - "K3", - "K4", - "S1", - "S2", - "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 9, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 25 - } + }, + "built_at": "2026-05-15T16:00:00Z" } ], "supplementary_only": [ @@ -3130,7 +3099,7 @@ "vote-on-referendum", "create-opengov-referendum" ], - "notes": "Conceptual overview of OpenGov origins and tracks — explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", + "notes": "Conceptual overview of OpenGov origins and tracks \u2014 explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", "scoring": { "signals": [ "R3", @@ -3154,7 +3123,7 @@ "configure-runtime-pallet", "set-up-parachain-template" ], - "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points — but the page's primary purpose is deep reference exposition, not a task workflow.", + "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points \u2014 but the page's primary purpose is deep reference exposition, not a task workflow.", "scoring": { "signals": [ "P2", @@ -3360,7 +3329,7 @@ "build-parachain-runtime", "set-up-parachain-template" ], - "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) — rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", + "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) \u2014 rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", "scoring": { "signals": [ "R1", @@ -3413,7 +3382,7 @@ "set-up-parachain-template", "test-parachain-locally" ], - "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local → Paseo → production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", + "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local \u2192 Paseo \u2192 production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", "scoring": { "signals": [ "P1", @@ -3558,7 +3527,7 @@ "purchase-bulk-coretime", "set-up-parachain-template" ], - "notes": "Explains coretime scheduling concepts — bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", + "notes": "Explains coretime scheduling concepts \u2014 bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", "scoring": { "signals": [ "C1", @@ -3580,7 +3549,7 @@ "set-up-validator-node", "stake-dot-tokens" ], - "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps — entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", + "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps \u2014 entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", "scoring": { "signals": [ "C1", @@ -3602,7 +3571,7 @@ "set-up-parachain-template", "purchase-bulk-coretime" ], - "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram — no procedural content.", + "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram \u2014 no procedural content.", "scoring": { "signals": [ "C1", @@ -3623,7 +3592,7 @@ "relevant_to": [ "establish-on-chain-identity" ], - "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) — not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", + "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) \u2014 not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", "scoring": { "signals": [ "P1", @@ -3651,7 +3620,7 @@ "deploy-basic-contract-remix", "deploy-erc20-token-remix" ], - "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps — serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", + "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps \u2014 serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", "scoring": { "signals": [ "C1", @@ -3694,7 +3663,7 @@ "interact-with-chain-polkadart", "use-polkadot-js-api" ], - "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) — reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", + "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) \u2014 reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", "scoring": { "signals": [ "P2", @@ -3721,7 +3690,7 @@ "relevant_to": [ "transfer-assets-parachains" ], - "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual — explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", + "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual \u2014 explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", "scoring": { "signals": [ "P2", @@ -4188,7 +4157,7 @@ { "page": "policies/terms-of-use.md", "category": "not_applicable", - "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet — pure legal boilerplate with no procedural, reference, or conceptual developer content.", + "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet \u2014 pure legal boilerplate with no procedural, reference, or conceptual developer content.", "scoring": { "signals": [ "N2" diff --git a/skill_coverage.json b/skill_coverage.json index c37ee3323..6ccc7d619 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -105,9 +105,11 @@ }, "chain-interactions/store-data/bulletin-chain.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": null, - "skills": [], - "status": "uncovered" + "last_scanned": "2026-05-15T16:00:00Z", + "skills": [ + "store-retrieve-data-bulletin-chain" + ], + "status": "up_to_date" }, "chain-interactions/token-operations/convert-assets.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -705,27 +707,35 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": null, - "skills": [], - "status": "uncovered" + "last_scanned": "2026-05-15T16:00:00Z", + "skills": [ + "deploy-uniswap-v2-core-pvm" + ], + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": null, - "skills": [], - "status": "uncovered" + "last_scanned": "2026-05-15T16:00:00Z", + "skills": [ + "deploy-uniswap-v2-core-evm" + ], + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": null, - "skills": [], - "status": "uncovered" + "last_scanned": "2026-05-15T16:00:00Z", + "skills": [ + "deploy-uniswap-v2-periphery-evm" + ], + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": null, - "skills": [], - "status": "uncovered" + "last_scanned": "2026-05-15T16:00:00Z", + "skills": [ + "deploy-uniswap-v3-core-evm" + ], + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -974,4 +984,4 @@ "status": "up_to_date" } } -} \ No newline at end of file +} From 7a0461b70a3e00957b729e31b0cfc9d4f6bc92ee Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 15:10:23 +0000 Subject: [PATCH 10/26] chore: auto-generate/update skills --- agent_skills_config.json | 892 ++++++++++++++++++--------------------- skill_candidates.json | 51 +-- skill_coverage.json | 14 +- 3 files changed, 418 insertions(+), 539 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 8f4d242d2..9eacbf58e 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "generated": "2026-05-15T16:00:00Z", - "content_hash": "sha256:102fc2efb7582fc21330af06e017972ae25b51ddb1f66ea131fe08a07a4a8907", + "generated": "2026-05-15T15:30:00Z", + "content_hash": "sha256:ef1cb2bf98721bdbe9f835d261011c01ca05b85a93f19c68124e56d2e442e2b5", "skills": [ { "id": "create-polkadot-account", @@ -8713,7 +8713,7 @@ { "id": "store-retrieve-data-bulletin-chain", "title": "Store and Retrieve Data on the Bulletin Chain", - "description": "Stores binary files on the Polkadot Bulletin Chain using PAPI and retrieves them by CID via the IPFS gateway. Use when you need censorship-resistant, IPFS-compatible decentralized storage for images, JSON configs, HTML pages, or any binary data. Trigger phrases: 'store data bulletin chain', 'upload file decentralized Polkadot', 'retrieve bulletin chain CID', 'Bulletin Chain IPFS'. Authorization via Console UI faucet is required before storing (programmatic self-authorization is not supported on TestNet). Key capabilities: project init, PAPI descriptor generation for Bulletin Chain, store transaction submission, CID-based retrieval via IPFS gateway, and data renewal before the 2-week retention expiry. Output: block hash, CID, block number, and transaction index.", + "description": "Authorizes an account on the Polkadot Bulletin Chain, stores a file on-chain via the TransactionStorage pallet using PAPI, retrieves it by CID through the IPFS gateway, and renews storage before expiry. Use when you need decentralized, IPFS-compatible file storage on Polkadot without EVM smart contracts. Key capabilities: Console UI walkthrough for account authorization (Root-origin faucet required); PAPI TypeScript script for programmatic store; CID-based retrieval; renewal workflow. Trigger phrases: 'store data on Bulletin Chain', 'upload file to Polkadot on-chain', 'get CID for image on Polkadot', 'renew Bulletin Chain data'. Authorization must be obtained from the Console UI faucet before PAPI store.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8725,180 +8725,186 @@ "primary_page": "chain-interactions/store-data/bulletin-chain.md", "prerequisites": { "runtime": [ - "Node.js v18+ and npm" + "Node.js v18+", + "npm" ], "network": [ - "Polkadot Bulletin Chain TestNet WSS: wss://paseo-bulletin-rpc.polkadot.io" + "Polkadot Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" ], "tokens": [ - "Bulletin Chain authorization (transactions + bytes quota) \u2014 obtained from the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ (see Get Authorization section; programmatic self-authorization is not available)" + "Account authorization from the Bulletin Chain Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ (authorization is free but requires wallet approval)" ], "wallet": [ - "A Polkadot account mnemonic (12-word BIP39 phrase) for an account that has been granted authorization on Bulletin Chain TestNet" + "A Polkadot account with a browser wallet extension (Polkadot.js, Talisman, SubWallet, or Fearless Wallet) to approve the authorization transaction via the Console UI" ] }, "env_vars": [ { - "name": "MNEMONIC", - "description": "12-word BIP39 mnemonic for the Polkadot account authorized on the Bulletin Chain TestNet. The account must have active authorization (transactions + bytes quota) granted via the Console UI faucet. Never commit to version control.", - "required": true + "name": "SEED_PHRASE", + "description": "12- or 24-word BIP39 mnemonic for the account that was authorized on the Bulletin Chain in the Console UI step. This account must hold authorization (bytes + transactions) before store operations will succeed.", + "required": true, + "secret": true } ], - "project_structure": "bulletin-store-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 store-data.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 package.json\n\u2514\u2500\u2500 node_modules/", "steps": [ { "order": 1, - "action": "Get authorization via Console UI faucet", + "action": "Authorize your account via the Console UI faucet", "working_directory": ".", - "interactive": true, - "description": "Before storing data, your account needs authorization on the Bulletin Chain (transactions quota + bytes quota). Delegate this step to the user: navigate to https://paritytech.github.io/polkadot-bulletin-chain/, connect their wallet, go to the Faucet page, select Storage Faucet, enter the desired number of transactions and bytes, click Authorize Account, and approve the transaction. Wait for the user to confirm authorization is complete before proceeding. Note: authorization has an expiration block \u2014 unused authorization is not refunded after expiry." + "description": "Navigate to the Bulletin Chain Console at https://paritytech.github.io/polkadot-bulletin-chain/ and connect your wallet. Go to the Faucet page, select the Storage Faucet tab, and under 'Authorize Account' enter the number of Transactions and Bytes you need, then click 'Authorize Account'. Approve the transaction in your wallet extension. Verify your authorization balance on the Accounts tab. Note: the 'authorize_account' extrinsic requires Root origin and cannot be called programmatically — the Console UI faucet is the only path. Authorization is consumed as files are stored and expires if unused transactions lapse." }, { "order": 2, "action": "Initialize the project", "working_directory": ".", "commands": [ - "mkdir bulletin-store-example && cd bulletin-store-example", - "npm init -y && npm pkg set type=module" + "mkdir bulletin-store-example", + "cd bulletin-store-example", + "npm init -y", + "npm pkg set type=module" ], - "description": "Create a new directory named 'bulletin-store-example' and initialize it as an ESM Node.js project. The `type=module` flag is required for PAPI's ESM-only imports." + "description": "Create the 'bulletin-store-example' directory and initialize it as an ESM Node.js project. The `type=module` flag is required — PAPI uses ESM imports." }, { "order": 3, "action": "Install dependencies", "working_directory": "bulletin-store-example", "commands": [ - "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv", - "npm install --save-dev typescript tsx" + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv" ], - "description": "Install polkadot-api (PAPI core), @polkadot-labs/hdkd and @polkadot-labs/hdkd-helpers (SR25519 key derivation), multiformats (CID encoding), dotenv (secret management), and tsx (TypeScript executor)." + "description": "Install polkadot-api (PAPI), @polkadot-labs/hdkd and hdkd-helpers for SR25519 key derivation, multiformats for CID decoding, and dotenv for loading the seed phrase from .env." }, { "order": 4, - "action": "Generate PAPI typed descriptors for Bulletin Chain", + "action": "Fetch Bulletin Chain metadata", "working_directory": "bulletin-store-example", "commands": [ "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" ], - "expected_output": "Added bulletin chain descriptors", - "description": "Download Bulletin Chain metadata and generate typed descriptors. This connects to the Bulletin Chain WSS endpoint, retrieves the current runtime metadata, and generates typed descriptors in `.papi/descriptors/` that provide compile-time type safety for all pallet interactions including `TransactionStorage.store`." + "description": "Download Bulletin Chain metadata and generate typed PAPI descriptors with the 'bulletin' export key under @polkadot-api/descriptors. This provides full type safety for all TransactionStorage pallet interactions." }, { "order": 5, - "action": "Create .env file and .gitignore for mnemonic", + "action": "Create the .env file", "working_directory": "bulletin-store-example", - "commands": [ - "printf 'MNEMONIC=\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create a .env file with an empty MNEMONIC placeholder. Stop here and ask the user to edit .env directly \u2014 fill in MNEMONIC with their 12-word BIP39 mnemonic for the account that has been authorized on Bulletin Chain TestNet. Do NOT ask for the mnemonic in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a file named `.env` in the `bulletin-store-example` directory with the following content:\n\n```text\nSEED_PHRASE=\"INSERT_SEED_PHRASE\"\n```\n\nAlso create `.gitignore` with `.env` on its own line so the seed phrase is never committed.\n\nStop and ask the user to edit the `.env` file directly, replacing `INSERT_SEED_PHRASE` with the mnemonic of the account they authorized in step 1. Do NOT ask for the seed phrase in chat. Wait for confirmation before proceeding." }, { "order": 6, - "action": "Fetch and configure the store-data script", + "action": "Fetch the store-data script and apply substitutions", "working_directory": "bulletin-store-example", "reference_file": "store-data.ts", - "description": "Fetch the reference file and save it as `store-data.ts`. Apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line of the file.\n\n(2) Remove the import of `DEV_PHRASE` from `@polkadot-labs/hdkd-helpers`. Replace the line:\n```typescript\nimport { DEV_PHRASE, entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';\n```\nwith:\n```typescript\nimport { entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';\n```\n\n(3) In the signer derivation section, replace any reference to `DEV_PHRASE` with `process.env.MNEMONIC as string`. For example:\n```typescript\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(process.env.MNEMONIC as string));\n```\n\n(4) Replace `INSERT_IMAGE_PATH` with the actual path to the file the user wants to store (ask the user for this path \u2014 e.g. `'./logo.png'`). Ensure the file exists and is under ~8 MiB.\n\nSave the modified file." + "description": "Fetch the reference file and save it as `store-data.ts`. Apply these two substitutions:\n\n1. Replace the `FILE_PATH` placeholder: change `const FILE_PATH = 'INSERT_IMAGE_PATH';` to the path of the file you want to store (e.g., `const FILE_PATH = './logo.png';`).\n\n2. Replace the DEV_PHRASE signer with dotenv. Change the import block:\n```typescript\nimport {\n DEV_PHRASE,\n entropyToMiniSecret,\n mnemonicToEntropy,\n} from '@polkadot-labs/hdkd-helpers';\n```\nto:\n```typescript\nimport 'dotenv/config';\nimport {\n entropyToMiniSecret,\n mnemonicToEntropy,\n} from '@polkadot-labs/hdkd-helpers';\n```\n\nAnd change:\n```typescript\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE));\n```\nto:\n```typescript\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(process.env.SEED_PHRASE!));\n```\n\nThe signer now uses the authorized account from .env instead of the Alice dev account." }, { "order": 7, - "action": "Run the store script", + "action": "Store the file on-chain", "working_directory": "bulletin-store-example", "commands": [ "npx tsx store-data.ts" ], - "expected_output": "Transaction included in block: 0x...", - "description": "Execute the store script. On success, the script prints the block hash. **Save the CID, block number, and transaction index from the output** \u2014 you need the block number and transaction index for any future renewal, and the CID to retrieve the file. The Console UI auto-saves history, but record these values separately as a backup." + "expected_output": "Connected to Bulletin Chain (Polkadot TestNet)\nRead file: ./logo.png (N bytes)\nSubmitting store transaction...\nTransaction included in block: 0x...\nTransaction index: N\n\nImage stored successfully!\nIndex: N\nCID: bafk2bzace...\n\nRetrieve via IPFS gateway:\nhttps://paseo-ipfs.polkadot.io/ipfs/bafk2bzace...", + "description": "Run the store script. On success it prints the CID, block hash, and transaction index. Save the **block hash** and **transaction index** — they are required to renew stored data before it expires (approximately 2 weeks on TestNet)." }, { "order": 8, - "action": "Retrieve the stored file by CID", + "action": "Retrieve the stored file", "working_directory": "bulletin-store-example", - "description": "Retrieve the stored file using the CID from the previous step. Use the Bulletin Chain IPFS gateway:\n\n```typescript\nconst cid = 'YOUR_CID_HERE';\nconst response = await fetch(`https://paseo-ipfs.polkadot.io/ipfs/${cid}`);\nconst data = await response.arrayBuffer();\nconsole.log(`Retrieved ${data.byteLength} bytes`);\n```\n\nReplace `YOUR_CID_HERE` with the CID string returned in step 7 (e.g., `bafk2bzacea6wlxy...`). Alternatively, open the URL directly in a browser: `https://paseo-ipfs.polkadot.io/ipfs/`. Data is retained for approximately 2 weeks \u2014 renew before expiry to keep it available." + "description": "Retrieve your file using the CID from step 7. Option A — fetch via IPFS gateway (programmatic):\n\n```typescript\nconst cid = 'INSERT_CID_FROM_STEP_7';\nconst response = await fetch(`https://paseo-ipfs.polkadot.io/ipfs/${cid}`);\nconst data = await response.arrayBuffer();\nconsole.log(`Retrieved ${data.byteLength} bytes`);\n```\n\nOption B — open the IPFS gateway URL directly in a browser: `https://paseo-ipfs.polkadot.io/ipfs/`.\n\nOption C — use the Console UI Download page at https://paritytech.github.io/polkadot-bulletin-chain/ and enter the CID.", + "expected_output": "File content returned from gateway; byteLength matches the original file size." + }, + { + "order": 9, + "action": "Renew stored data before expiry", + "working_directory": "bulletin-store-example", + "description": "Data is retained for approximately 2 weeks on TestNet. To renew, call `api.tx.TransactionStorage.renew()` with the block number and transaction index recorded in step 7. Add a renewal script or paste this into a TypeScript file:\n\n```typescript\nimport 'dotenv/config';\nimport { createClient } from 'polkadot-api';\nimport { getWsProvider } from 'polkadot-api/ws';\nimport { bulletin } from '@polkadot-api/descriptors';\nimport { sr25519CreateDerive } from '@polkadot-labs/hdkd';\nimport { entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';\nimport { getPolkadotSigner } from 'polkadot-api/signer';\n\nconst BLOCK_NUMBER = INSERT_BLOCK_NUMBER_FROM_STEP_7;\nconst TX_INDEX = INSERT_TX_INDEX_FROM_STEP_7;\n\nconst client = createClient(getWsProvider('wss://paseo-bulletin-rpc.polkadot.io'));\nconst api = client.getTypedApi(bulletin);\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(process.env.SEED_PHRASE!));\nconst derive = sr25519CreateDerive(miniSecret);\nconst hdkdKeyPair = derive('//your-path');\nconst signer = getPolkadotSigner(hdkdKeyPair.publicKey, 'Sr25519', hdkdKeyPair.sign);\n\nconst result = await api.tx.TransactionStorage.renew({ block: BLOCK_NUMBER, index: TX_INDEX }).signAndSubmit(signer);\nconsole.log('Renewed in block:', result.block.hash);\nclient.destroy();\n```\n\nReplace `INSERT_BLOCK_NUMBER_FROM_STEP_7` and `INSERT_TX_INDEX_FROM_STEP_7` with the values recorded in step 7. Each successful renewal emits a 'Renewed' event with a new block number and index — record the new values for subsequent renewals. Using stale values after a renewal will fail." } ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", - "files": [ - { - "path": "store-data.ts", - "description": "Stores a binary file on the Bulletin Chain using PAPI's TransactionStorage.store extrinsic, derives an SR25519 signer from a mnemonic, and prints the block hash and CID on success" - } - ] - }, + "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── descriptors/\n├── store-data.ts\n├── .env\n├── .gitignore\n├── package.json\n└── ", "error_patterns": [ { - "pattern": "Error: Account has no active authorization", - "cause": "The account has not been granted storage authorization on the Bulletin Chain, or the authorization has expired.", - "resolution": "Return to the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ and authorize the account again with fresh transactions and bytes quota." - }, - { - "pattern": "Error: Not enough authorization bytes remaining", - "cause": "The file size exceeds the remaining byte quota on the account's authorization.", - "resolution": "Request additional authorization via the Console UI faucet, or split the file into smaller chunks (under 4 MiB per transaction)." + "pattern": "InsufficientAuthorization / account has no authorization", + "cause": "The signing account has not been authorized on the Bulletin Chain, or all authorized bytes/transactions have been consumed.", + "resolution": "Complete step 1: use the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ to authorize your account. Verify your remaining authorization on the Accounts tab." }, { - "pattern": "SyntaxError: Cannot use import statement in a module / ERR_REQUIRE_ESM", - "cause": "The project is not configured as ESM, or dotenv was imported incorrectly.", - "resolution": "Run `npm pkg set type=module` in the project directory. Ensure `import 'dotenv/config';` is used (not `require('dotenv').config()`) since this is an ESM project." + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI type descriptors were not generated for the Bulletin Chain.", + "resolution": "Run `npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io` in the bulletin-store-example directory (step 4)." }, { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI descriptors for the Bulletin Chain have not been generated yet.", - "resolution": "Run `npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io` to fetch the chain metadata and generate descriptors." + "pattern": "ENOENT: no such file or directory (reading the FILE_PATH)", + "cause": "The file path in store-data.ts does not exist or is incorrect.", + "resolution": "Update FILE_PATH in store-data.ts to point to an existing file. Use an absolute path or a relative path from the bulletin-store-example directory." }, { - "pattern": "Error connecting to wss://paseo-bulletin-rpc.polkadot.io / WebSocket connection failed", - "cause": "Bulletin Chain TestNet RPC is temporarily unavailable or the endpoint has changed.", - "resolution": "Check the Polkadot developer Discord for network status. Retry after a short wait." + "pattern": "Renewal fails with 'UnexpectedStatus' or wrong block/index", + "cause": "Stale block number or transaction index used after a previous renewal.", + "resolution": "Each renewal emits a 'Renewed' event with a new (block, index) pair. Always use the most recent values from the last successful renew transaction — not the original store values." } ], "supplementary_context": { - "description": "Load when the user asks about Bulletin Chain concepts, retention details, or advanced retrieval methods.", + "description": "Load these pages for technical background on Bulletin Chain storage mechanics, size limits, and retrieval methods.", "pages": [ + { + "slug": "reference-polkadot-hub-data-storage", + "title": "Data Storage", + "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage/", + "relevance": "Full technical reference for the Bulletin Chain: all extrinsics, storage items, events, size limits (8 MiB per transaction), and retrieval methods." + }, { "slug": "chain-interactions-accounts-create-account", "title": "Create a Polkadot Account Programmatically", - "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account.md", - "relevance": "How to create the Polkadot account whose mnemonic is used as the Bulletin Chain signer." + "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account/", + "relevance": "How to create a Polkadot SS58 account if the user does not already have one to authorize." } ] }, "examples": [ { - "scenario": "Common scenario: store an image on the Bulletin Chain", - "user_says": "Store my logo.png on the Polkadot Bulletin Chain", + "scenario": "Common scenario: store an image and retrieve it by CID", + "user_says": "Store an image file on the Polkadot Bulletin Chain and give me its IPFS CID", "actions": [ - "Instruct user to authorize account via Console UI faucet", - "Init bulletin-store-example project with ESM", - "Install polkadot-api, hdkd, hdkd-helpers, multiformats, dotenv, tsx", - "Run npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io", - "Create .env with MNEMONIC placeholder; ask user to fill it in with authorized account mnemonic", - "Fetch store-data.ts; add dotenv import; replace DEV_PHRASE with process.env.MNEMONIC; set FILE_PATH to './logo.png'", - "Run npx tsx store-data.ts", - "Record CID, block number, and transaction index from output" + "Authorize account via Console UI faucet (must be done manually — Root origin required)", + "Initialize bulletin-store-example ESM Node.js project with npm", + "Install polkadot-api, hdkd, hdkd-helpers, multiformats, dotenv", + "Fetch Bulletin Chain metadata with `npx papi add bulletin`", + "Create .env with SEED_PHRASE; ask user to fill in their authorized account mnemonic", + "Fetch store-data.ts; substitute FILE_PATH and replace DEV_PHRASE with dotenv SEED_PHRASE", + "Run `npx tsx store-data.ts`; record CID, block number, and transaction index from output", + "Retrieve file via https://paseo-ipfs.polkadot.io/ipfs/" ], - "result": "File stored on Bulletin Chain; CID returned for retrieval via https://paseo-ipfs.polkadot.io/ipfs/" + "result": "File stored on-chain; CID printed (e.g. bafk2bzace...); file accessible at the IPFS gateway URL" }, { - "scenario": "Edge case: authorization expired before storing", - "user_says": "The script fails saying the account has no authorization", + "scenario": "Edge case: stored data nearing expiry, renewal needed", + "user_says": "My Bulletin Chain data is about to expire, how do I renew it?", "actions": [ - "Navigate to https://paritytech.github.io/polkadot-bulletin-chain/", - "Go to Faucet > Storage Faucet and re-authorize the account with fresh transactions and bytes quota", - "Wait for authorization transaction to be included", - "Retry: npx tsx store-data.ts" + "Confirm the user has the block number and transaction index from their original store (or last renewal) transaction", + "Provide the renewal script with substituted BLOCK_NUMBER and TX_INDEX values", + "Run the renewal script; record the new (block, index) pair from the Renewed event for future renewals" ], - "result": "Authorization renewed; store transaction succeeds on retry" + "result": "Retention timer reset; data available for another ~2-week period. New (block, index) pair recorded for next renewal." } - ] + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", + "files": [ + { + "path": "store-data.ts", + "description": "PAPI TypeScript script that connects to the Bulletin Chain, reads a file, submits TransactionStorage.store, and decodes the Stored event to print the CID and block reference" + } + ] + }, + "generated": "2026-05-15T15:30:00Z", + "content_hash": "sha256:c38c3743e7262e82cc82da3bafff3d1c68a5bf29048d98bb8634e94c1a0eb8b2" }, { "id": "deploy-uniswap-v2-core-pvm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", - "description": "Clones the polkavm-hardhat-examples repository and deploys Uniswap V2 Core contracts (UniswapV2ERC20, UniswapV2Factory, UniswapV2Pair) to Polkadot Hub TestNet using Hardhat. Use when building an AMM or DEX on Polkadot Hub from the polkavm-hardhat-examples reference. Trigger phrases: 'deploy Uniswap V2 Polkadot PVM', 'deploy Factory Pair contracts Polkadot Hub', 'AMM Polkadot Hub polkavm'. Requires testnet PAS tokens and a funded EVM private key. Uses dotenv for private key management. Key capabilities: clone and navigate polkavm-hardhat-examples, compile Solidity contracts, run test suite against local dev node, and deploy UniswapV2ERC20/Factory/Pair to polkadotHubTestNet.", + "title": "Deploying Uniswap V2 Core on Polkadot", + "description": "Clones polkavm-hardhat-examples, compiles Uniswap V2 Core contracts (UniswapV2ERC20, UniswapV2Factory, UniswapV2Pair, Solidity 0.8.28) using the @parity/hardhat-polkadot plugin and revive compiler targeting PolkaVM (PVM), runs tests against a local revive-dev-node, and deploys to Polkadot Hub TestNet via the passetHub network. Use when you need AMM-based DEX infrastructure on the PVM execution path. Key capabilities: @parity/hardhat-polkadot plugin config; dotenv-based private key management; UniswapV2Pair deployed via direct JsonRpcProvider to handle contract size. Trigger phrases: 'deploy Uniswap V2 PVM', 'AMM on Polkadot PolkaVM', 'UniswapV2Factory PVM Polkadot'. Requires PAS testnet tokens from https://faucet.polkadot.io/.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8910,32 +8916,34 @@ "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", "prerequisites": { "runtime": [ - "Node.js v16+ and npm", - "Git" + "Node.js v16+", + "npm" ], "network": [ - "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417)" + "Local revive-dev-node for testing (http://127.0.0.1:8545) — see the Local Development Node guide at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", + "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io" ], "tokens": [ - "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "PAS testnet tokens from https://faucet.polkadot.io/ for the account set in AH_PRIV_KEY" ], "wallet": [ - "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + "An EVM-compatible account (MetaMask or similar) with the private key exported for AH_PRIV_KEY" ] }, "env_vars": [ { - "name": "AH_PRIV_KEY", - "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", - "required": true + "name": "LOCAL_PRIV_KEY", + "description": "Private key for the local revive-dev-node account. A default dev key (0x5fb92d6e...) is pre-configured in hardhat.config.js; set this only if you need a different local account.", + "required": false, + "secret": true }, { - "name": "LOCAL_PRIV_KEY", - "description": "0x-prefixed EVM private key for local development node testing. Can be a dev account key from the local node (optional if skipping local testing).", - "required": false + "name": "AH_PRIV_KEY", + "description": "Private key of the account used to deploy contracts to Polkadot Hub TestNet (passetHub network). Export from MetaMask or another EVM wallet. This account must hold PAS testnet tokens.", + "required": true, + "secret": true } ], - "project_structure": "polkavm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v2-polkadot/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV2ERC20.sol\n \u2502 \u251c\u2500\u2500 UniswapV2Factory.sol\n \u2502 \u2514\u2500\u2500 UniswapV2Pair.sol\n \u251c\u2500\u2500 scripts/\n \u2502 \u2514\u2500\u2500 deploy.js\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.js\n \u2514\u2500\u2500 package.json", "steps": [ { "order": 1, @@ -8943,11 +8951,9 @@ "working_directory": ".", "commands": [ "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", - "cd polkavm-hardhat-examples", - "git checkout hardhat-polkadot-evm", - "cd uniswap-v2-polkadot" + "cd polkavm-hardhat-examples/uniswap-v2-polkadot" ], - "description": "Clone the polkavm-hardhat-examples repository, switch to the `hardhat-polkadot-evm` branch, and navigate into the `uniswap-v2-polkadot` subdirectory. This subdirectory contains the Uniswap V2 Core contracts (Factory, Pair, ERC20), deployment scripts, and a Hardhat configuration already set up to deploy to `polkadotHubTestNet`." + "description": "Clone polkavm-hardhat-examples (default branch: master). Navigate to the `uniswap-v2-polkadot` subdirectory which contains the Hardhat project." }, { "order": 2, @@ -8956,127 +8962,155 @@ "commands": [ "npm install" ], - "description": "Install all project dependencies from package.json. The project already includes dotenv as a dependency and the hardhat.config.js already calls `require('dotenv').config()` \u2014 no additional dotenv installation is needed." + "description": "Install all project dependencies including `@parity/hardhat-polkadot` (PolkaVM Hardhat plugin) and the revive Solidity compiler." }, { "order": 3, - "action": "Create .env file and .gitignore for private keys", + "action": "Create the .env file with private keys", "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with empty placeholders for both private keys. Stop here and ask the user to edit .env directly:\n\n- `AH_PRIV_KEY`: 0x-prefixed EVM private key for their funded Polkadot Hub TestNet account.\n- `LOCAL_PRIV_KEY`: 0x-prefixed EVM private key for a local dev node account (only needed for local testing \u2014 can use a pre-funded dev key from the local node).\n\nDo NOT ask for private keys in chat. Wait for the user to confirm .env is filled before proceeding." + "reference_file": ".env.example", + "description": "Fetch the reference file and save it as `.env` (note: save as `.env`, not `.env.example`). The file already contains the correct variable names:\n\n```text\nLOCAL_PRIV_KEY=\"\"\nAH_PRIV_KEY=\"\"\n```\n\nStop and ask the user to edit the `.env` file directly. Do NOT ask for the private key in chat. The user should:\n- Set `AH_PRIV_KEY` to the private key of an account holding PAS testnet tokens on Polkadot Hub TestNet.\n- Optionally set `LOCAL_PRIV_KEY` for a custom local dev account (a default dev key is already in hardhat.config.js if this is left empty).\nWait for confirmation before proceeding." }, { "order": 4, - "action": "Add gas configuration to hardhat.config.js", + "action": "Compile the contracts", "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "description": "Open `hardhat.config.js` and add `gasPrice: 5000000000000` (5000 gwei \u2014 5x the TestNet base fee of 1000 gwei) to the `polkadotHubTestNet` network block. This prevents 'Priority is too low' errors on TestNet. The `polkadotHubTestNet` network block should look like:\n\n```javascript\npolkadotHubTestNet: {\n url: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n accounts: [process.env.AH_PRIV_KEY],\n gasPrice: 5000000000000,\n},\n```\n\nSave the file." + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled N Solidity files successfully (solc 0.8.28)", + "description": "Compile the Uniswap V2 contracts using the revive compiler configured via `@parity/hardhat-polkadot`. Compiled PVM artifacts (ABI and PolkaVM bytecode) are written to the `artifacts` directory." }, { "order": 5, - "action": "Compile the contracts", + "action": "Start the local revive-dev-node", + "working_directory": ".", + "description": "Start the local development node as described at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/. The node must be accessible at http://127.0.0.1:8545 before running tests." + }, + { + "order": 6, + "action": "Run the test suite against the local node", "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", "commands": [ - "npx hardhat compile" + "npx hardhat test --network localNode" ], - "expected_output": "Compiled contracts successfully", - "description": "Compile all Solidity contracts. On success, compiled artifacts (ABI and bytecode) appear in the `artifacts/` directory. The project uses Solidity 0.8.28." + "expected_output": "passing (N tests covering UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair)", + "description": "Run all tests against the local revive-dev-node. Tests cover ERC-20 token operations, factory pair creation, and pair AMM swap operations. If tests time out, verify the local revive-dev-node is running at http://127.0.0.1:8545." }, { - "order": 6, - "action": "Deploy contracts to Polkadot Hub TestNet", + "order": 7, + "action": "Review the deployment script", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "reference_file": "scripts/deploy.js", + "description": "Review `scripts/deploy.js`. It deploys three contracts in sequence: UniswapV2ERC20, UniswapV2Factory (with deployer as fee recipient), and UniswapV2Pair. UniswapV2Pair is deployed using `JsonRpcProvider` with a direct wallet (bypassing Hardhat's default contract size checks which would reject the large PVM bytecode). Output includes the address of all three contracts." + }, + { + "order": 8, + "action": "Deploy to Polkadot Hub TestNet", "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", "commands": [ - "npx hardhat run scripts/deploy.js --network polkadotHubTestNet" + "npx hardhat run scripts/deploy.js --network passetHub" ], - "expected_output": "Factory deployed to : 0x...", - "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet. The script outputs the deployed contract addresses \u2014 save all three addresses. You need the Factory address to create liquidity pools and the Pair address for subsequent liquidity operations. If the deployment hangs or a transaction appears stuck, check your account balance at https://faucet.polkadot.io/ and verify `gasPrice` is set in hardhat.config.js." + "expected_output": "Deploying contracts using 0x...\nDeploying UniswapV2ERC20...\nETH deployed to : 0x...\nDeploying UniswapV2Factory...\nFactory deployed to : 0x...\nDeploying UniswapV2Pair...\nPair deployed to : 0x...", + "description": "Deploy to Polkadot Hub TestNet. The script reads `AH_PRIV_KEY` from `.env` via dotenv. Save all three deployed addresses — they are needed for any subsequent AMM interactions." } ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, + "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── artifacts/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", "error_patterns": [ { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Add `gasPrice: 5000000000000` to the `polkadotHubTestNet` block in hardhat.config.js." + "pattern": "Error: AH_PRIV_KEY is undefined / wallet private key not found", + "cause": ".env file not created or AH_PRIV_KEY not set.", + "resolution": "Create the .env file in the uniswap-v2-polkadot directory (step 3). Set AH_PRIV_KEY to the private key of your PAS-funded account. The hardhat.config.js loads dotenv automatically." }, { - "pattern": "Error: Cannot read properties of undefined (reading 'AH_PRIV_KEY') / process.env.AH_PRIV_KEY is undefined", - "cause": ".env file is missing, not populated, or not in the correct directory.", - "resolution": "Ensure .env is in the `uniswap-v2-polkadot/` directory (not the repo root) and contains `AH_PRIV_KEY=0x...` on its own line." + "pattern": "ECONNREFUSED 127.0.0.1:8545", + "cause": "Local revive-dev-node is not running.", + "resolution": "Start the local development node before running tests. See https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/." }, { - "pattern": "Transaction failed / Transaction Already Imported", - "cause": "A previous transaction is stuck in the TestNet mempool, or the transaction was already sent.", - "resolution": "Wait for pending transactions to clear. If stuck, send a replacement transaction at the same nonce with a higher gasPrice (2x the base fee). The TestNet base fee is 1000 gwei." + "pattern": "insufficient funds / Error: insufficient balance", + "cause": "The AH_PRIV_KEY account has no PAS tokens on Polkadot Hub TestNet.", + "resolution": "Obtain PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account address." }, { - "pattern": "Error: Compiler version mismatch / solidity: '0.8.28' not found", - "cause": "Solidity compiler for 0.8.28 is not installed.", - "resolution": "Run `npx hardhat compile` \u2014 Hardhat downloads the compiler automatically. If it fails to download, check network connectivity." + "pattern": "Error: contract size too large / contract size exceeds limit", + "cause": "UniswapV2Pair PVM bytecode exceeds the deployment size check.", + "resolution": "The deploy.js script deploys UniswapV2Pair via direct JsonRpcProvider to bypass size checks. Ensure you are using the unmodified deploy.js from the reference repo." } ], "supplementary_context": { - "description": "Load when the user asks about the Hardhat development environment setup or local testing with the dev node.", + "description": "Load these pages for background on the PolkaVM execution path, the @parity/hardhat-polkadot plugin setup, and the local development node.", "pages": [ { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Set Up Hardhat for Polkadot Hub (EVM)", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Background on Hardhat configuration for Polkadot Hub, including the polkadotTestnet network setup." + "slug": "smart-contracts-dev-environments-hardhat-polkadot", + "title": "Use Hardhat with Polkadot Hub (PolkaVM)", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot/", + "relevance": "Full guide to configuring @parity/hardhat-polkadot for PolkaVM contract compilation and deployment." }, { "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Set Up a Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "How to start the local Revive dev node for running contract tests against a local network before deploying to TestNet." + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", + "relevance": "How to install and start the local revive-dev-node required for testing in step 5." }, { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", - "title": "Deploy Uniswap V2 Periphery with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", - "relevance": "Next step: deploy the UniswapV2Router02 contract on top of the Factory deployed in this skill." + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet/", + "relevance": "How to obtain PAS testnet tokens for the AH_PRIV_KEY deployer account." } ] }, "examples": [ { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet", - "user_says": "Deploy Uniswap V2 Core contracts to Polkadot Hub", + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet via PVM", + "user_says": "Deploy Uniswap V2 Core contracts on Polkadot Hub using PolkaVM", "actions": [ - "Clone polkavm-hardhat-examples, checkout hardhat-polkadot-evm, cd uniswap-v2-polkadot", + "Clone polkavm-hardhat-examples; navigate to uniswap-v2-polkadot/", "Run npm install", - "Create .env with AH_PRIV_KEY and LOCAL_PRIV_KEY placeholders; ask user to fill in AH_PRIV_KEY", - "Add gasPrice: 5000000000000 to polkadotHubTestNet in hardhat.config.js", + "Fetch .env.example, save as .env; ask user to fill in AH_PRIV_KEY", "Run npx hardhat compile", - "Run npx hardhat run scripts/deploy.js --network polkadotHubTestNet", - "Record deployed addresses for UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair" + "Start local revive-dev-node; run npx hardhat test --network localNode", + "Review scripts/deploy.js", + "Run npx hardhat run scripts/deploy.js --network passetHub; save contract addresses" ], - "result": "All three Uniswap V2 Core contracts deployed to Polkadot Hub TestNet; addresses printed to console" + "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet; three contract addresses printed" }, { - "scenario": "Edge case: transaction stuck or insufficient gas", - "user_says": "Deployment hangs or fails with 'Priority is too low'", + "scenario": "Edge case: user wants to test locally only without TestNet deployment", + "user_says": "Test the Uniswap V2 PVM contracts locally, skip TestNet", "actions": [ - "Verify AH_PRIV_KEY account has PAS balance at https://faucet.polkadot.io/", - "Open hardhat.config.js and confirm gasPrice: 5000000000000 is in the polkadotHubTestNet block", - "Retry: npx hardhat run scripts/deploy.js --network polkadotHubTestNet" + "Clone polkavm-hardhat-examples; navigate to uniswap-v2-polkadot/", + "Run npm install", + "Create .env with LOCAL_PRIV_KEY (AH_PRIV_KEY not needed for local testing)", + "Start local revive-dev-node", + "Run npx hardhat test --network localNode" ], - "result": "Deployment succeeds after gas price is configured correctly" + "result": "All tests pass against the local development node; no TestNet deployment and no PAS tokens needed" } - ] + ], + "reference_code": { + "repo": "polkadot-developers/polkavm-hardhat-examples", + "branch": "master", + "base_path": "uniswap-v2-polkadot", + "files": [ + { + "path": ".env.example", + "description": "Template .env file with LOCAL_PRIV_KEY and AH_PRIV_KEY variable placeholders" + }, + { + "path": "scripts/deploy.js", + "description": "Deploys UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair; uses JsonRpcProvider directly for UniswapV2Pair to bypass contract size restrictions" + } + ] + }, + "generated": "2026-05-15T15:30:00Z", + "content_hash": "sha256:f7cab3d8a1306847479141cfc85e4a7c08f4c2d8a83555788c8b9a82cec77873" }, { "id": "deploy-uniswap-v2-core-evm", - "title": "Deploy Uniswap V2 Core with EVM on Polkadot Hub", - "description": "Clones revm-hardhat-examples and deploys unmodified Uniswap V2 Core contracts (UniswapV2Factory, two test ERC-20 tokens, and a trading pair) to Polkadot Hub TestNet using standard Hardhat and TypeScript via the EVM path (REVM). Use when you need a Factory + Pair deployment on Polkadot Hub with no special compiler plugins. Trigger phrases: 'deploy Uniswap V2 Core EVM Polkadot', 'deploy Factory Pair EVM Polkadot Hub', 'AMM Polkadot Hub EVM Hardhat TypeScript'. Requires testnet PAS tokens and a funded EVM private key. Converts hardhat vars to dotenv. Key capabilities: clone repo, compile Solidity 0.5.16, optionally test locally, and deploy Factory plus two ERC-20 test tokens and a pair to polkadotTestnet.", + "title": "Uniswap V2 Core with EVM on Polkadot", + "description": "Clones revm-hardhat-examples, compiles unmodified Uniswap V2 Core contracts (UniswapV2Factory, UniswapV2Pair, UniswapV2ERC20, Solidity 0.5.16) using standard Hardhat and TypeScript on the EVM path, runs 28 tests against a local development node, and deploys UniswapV2Factory plus two test ERC-20 tokens with a trading pair to Polkadot Hub TestNet via REVM. Use when you need AMM DEX infrastructure on the standard EVM execution path without PVM or revive. Key capabilities: vanilla Hardhat TypeScript config; dotenv-based private key management (converted from Hardhat vars); Hardhat Ignition module for Factory-only deployment. Trigger phrases: 'deploy Uniswap V2 Core EVM', 'AMM on Polkadot EVM', 'UniswapV2Factory Polkadot EVM'. Requires PAS testnet tokens.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -9088,27 +9122,28 @@ "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", "prerequisites": { "runtime": [ - "Node.js v22+ and npm", - "Git" + "Node.js v22+", + "npm" ], "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + "Local development node at http://127.0.0.1:8545 for testing — see https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account" ], "wallet": [ - "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + "An EVM-compatible account (MetaMask or similar) with the private key exported for PRIVATE_KEY in .env" ] }, "env_vars": [ { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", - "required": true + "name": "PRIVATE_KEY", + "description": "Private key of the deployer account on Polkadot Hub TestNet. Required only for TestNet deployment (step 8). Local testing works without it.", + "required": true, + "secret": true } ], - "project_structure": "revm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v2-core-hardhat/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV2ERC20.sol\n \u2502 \u251c\u2500\u2500 UniswapV2Factory.sol\n \u2502 \u2514\u2500\u2500 UniswapV2Pair.sol\n \u251c\u2500\u2500 scripts/\n \u2502 \u2514\u2500\u2500 deploy.ts\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.ts\n \u2514\u2500\u2500 package.json", "steps": [ { "order": 1, @@ -9116,37 +9151,32 @@ "working_directory": ".", "commands": [ "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout b0a8627059a9d9cb759682310219557550186bc4", - "cd uniswap-v2-core-hardhat" + "cd revm-hardhat-examples/uniswap-v2-core-hardhat" ], - "description": "Clone the revm-hardhat-examples repository, check out the pinned commit for this tutorial, and navigate into the `uniswap-v2-core-hardhat` subdirectory. This directory contains unmodified Uniswap V2 Core contracts (Solidity 0.5.16), TypeScript tests, and a deployment script that deploys the Factory, two ERC-20 test tokens, and creates a trading pair." + "description": "Clone revm-hardhat-examples (default branch: master) and navigate to the `uniswap-v2-core-hardhat` subdirectory." }, { "order": 2, - "action": "Install dependencies including dotenv", + "action": "Install dependencies", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", "commands": [ - "npm install", - "npm install dotenv" + "npm install dotenv", + "npm install" ], - "description": "Install all project dependencies, then additionally install dotenv. dotenv replaces Hardhat's interactive Configuration Variables (`npx hardhat vars set`) which cannot be used in agent shells." + "description": "Install dotenv for private key loading, then install all other project dependencies including @nomicfoundation/hardhat-toolbox. Run `npm install dotenv` first to ensure dotenv is available." }, { "order": 3, - "action": "Create .env file and .gitignore for private key", + "action": "Fetch hardhat.config.ts and apply dotenv substitution", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty TESTNET_PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for a funded Polkadot Hub TestNet account. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "reference_file": "hardhat.config.ts", + "description": "Fetch the reference file and save it as `hardhat.config.ts`. Then apply these substitutions to convert from Hardhat vars to dotenv (required — Hardhat's interactive `vars.get()` cannot be used in agent sessions):\n\n1. Replace the import line:\n```typescript\nimport { HardhatUserConfig, vars } from \"hardhat/config\";\n```\nwith:\n```typescript\nimport { HardhatUserConfig } from \"hardhat/config\";\nimport \"dotenv/config\";\n```\n\n2. Replace the `accounts` field in the `polkadotTestnet` network:\n```typescript\naccounts: vars.has(\"TESTNET_PRIVATE_KEY\")\n ? [vars.get(\"TESTNET_PRIVATE_KEY\")]\n : [],\n```\nwith:\n```typescript\naccounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],\n```\n\nAll other config fields (Solidity version 0.5.16 with optimizer, localNode URL, mocha timeout 120000) remain unchanged." }, { "order": 4, - "action": "Convert hardhat.config.ts from vars to dotenv and add gas configuration", + "action": "Create the .env file", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "description": "Open `hardhat.config.ts` and apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line of the file.\n\n(2) Change the import from:\n```typescript\nimport { HardhatUserConfig, vars } from 'hardhat/config';\n```\nto:\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\n```\n\n(3) Replace the polkadotTestnet `accounts` field. Change:\n```typescript\naccounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : [],\n```\nto:\n```typescript\naccounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : [],\n```\n\n(4) Add `gasPrice: 5000000000000,` (5000 gwei \u2014 5x the TestNet 1000 gwei base fee) to the `polkadotTestnet` network block to prevent 'Priority is too low' errors.\n\nSave the file." + "description": "Create a file named `.env` in the `uniswap-v2-core-hardhat` directory with the following content:\n\n```text\nPRIVATE_KEY=\"INSERT_PRIVATE_KEY\"\n```\n\nAlso create `.gitignore` (or append to the existing one) with `.env` to prevent committing the key.\n\nStop and ask the user to edit the `.env` file directly, replacing `INSERT_PRIVATE_KEY` with their Polkadot Hub TestNet account private key (0x-prefixed). Do NOT ask for the key in chat. Wait for confirmation before proceeding. (This step is only required for TestNet deployment; local testing works without a private key.)" }, { "order": 5, @@ -9155,103 +9185,131 @@ "commands": [ "npx hardhat compile" ], - "expected_output": "Compiled contracts successfully", - "description": "Compile all Uniswap V2 Core contracts using Solidity 0.5.16 with the optimizer enabled (200 runs). On success, compiled artifacts appear in `artifacts/`." + "expected_output": "Compiled N Solidity files successfully (solc 0.5.16)", + "description": "Compile the Uniswap V2 Core contracts. The config specifies Solidity 0.5.16 with optimizer enabled (200 runs). Artifacts (ABI and bytecode) are written to the `artifacts` directory." }, { "order": 6, - "action": "Deploy contracts to Polkadot Hub TestNet", + "action": "Start the local development node", + "working_directory": ".", + "description": "Start the local development node as described at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/. The node must be accessible at http://127.0.0.1:8545 before running tests." + }, + { + "order": 7, + "action": "Run the test suite against the local node", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npx hardhat test --network localNode" + ], + "expected_output": "28 passing", + "description": "Run all 28 tests across UniswapV2ERC20.test.ts, UniswapV2Factory.test.ts, and UniswapV2Pair.test.ts. Tests use a 120-second Mocha timeout for Polkadot block times. If tests time out, verify the local development node is running at http://127.0.0.1:8545." + }, + { + "order": 8, + "action": "Review the deployment script and deploy to TestNet", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "reference_file": "scripts/deploy.ts", "commands": [ "npx hardhat run scripts/deploy.ts --network polkadotTestnet" ], - "expected_output": "UniswapV2Factory deployed to: 0x...", - "description": "Deploy UniswapV2Factory, two test ERC-20 tokens (10,000 tokens each), and create a trading pair. The script outputs all four contract addresses \u2014 save the Factory, TokenA, TokenB, and Pair addresses. These are needed for subsequent liquidity operations or to build a Router on top of the Factory." + "expected_output": "Deploying contracts with account: 0x...\nUniswapV2Factory deployed to: 0x...\nTokenA deployed to: 0x...\nTokenB deployed to: 0x...\nPair created at: 0x...", + "description": "Review `scripts/deploy.ts` before running. The script deploys UniswapV2Factory (with deployer as fee recipient), two test ERC-20 tokens (10,000 tokens each at 18 decimals), and calls `factory.createPair()` to create a trading pair. The deployer address and all four contract addresses are printed. Save the output — the Factory and Pair addresses are needed for any periphery or interaction work." } ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.ts\n ├── package.json\n └── tsconfig.json", "error_patterns": [ { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify `gasPrice: 5000000000000` is in the polkadotTestnet block in hardhat.config.ts." + "pattern": "Error: process.env.PRIVATE_KEY is undefined / no accounts configured", + "cause": ".env file not created or PRIVATE_KEY not set.", + "resolution": "Create the .env file in the uniswap-v2-core-hardhat directory (step 4) and set PRIVATE_KEY. Verify that the dotenv substitution in hardhat.config.ts was applied correctly (step 3)." }, { - "pattern": "TypeError: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still imports or uses Hardhat's vars system.", - "resolution": "Ensure `import 'dotenv/config';` is the first line, and that `vars` is removed from the import and replaced with `process.env.TESTNET_PRIVATE_KEY`." + "pattern": "Error: timeout exceeded / test timeout of 120000ms exceeded", + "cause": "Local development node is not running or not reachable at http://127.0.0.1:8545.", + "resolution": "Start the local development node before running tests. See https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/." }, { - "pattern": "Error: TESTNET_PRIVATE_KEY is undefined / No accounts configured", - "cause": ".env file is missing or TESTNET_PRIVATE_KEY is not set.", - "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ (not the repo root) and contains `TESTNET_PRIVATE_KEY=0x...`." + "pattern": "Error: insufficient funds for gas", + "cause": "The PRIVATE_KEY account has no PAS tokens on Polkadot Hub TestNet.", + "resolution": "Obtain PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account." }, { - "pattern": "Transaction stuck / Transaction Already Imported", - "cause": "A previous transaction is pending in the TestNet mempool, or was re-submitted at the same nonce.", - "resolution": "Wait for the pending transaction to confirm or be dropped. If stuck, send a zero-value replacement transaction at the same nonce with 2x the base fee (2000 gwei minimum)." + "pattern": "SyntaxError or TS compilation error after dotenv substitution", + "cause": "The vars.get() substitution in step 3 was incomplete or introduced a syntax error.", + "resolution": "Re-apply the substitutions from step 3 carefully. The final hardhat.config.ts polkadotTestnet accounts entry should read: `accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []`." } ], "supplementary_context": { - "description": "Load when the user asks about Hardhat setup, local testing, or deploying the Router on top of the deployed Factory.", + "description": "Load these pages for background on the EVM execution path, Hardhat configuration, and TestNet token acquisition.", "pages": [ { "slug": "smart-contracts-dev-environments-hardhat", - "title": "Set Up Hardhat for Polkadot Hub (EVM)", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Background on Hardhat configuration for Polkadot Hub." + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat/", + "relevance": "Full guide to Hardhat configuration for Polkadot Hub EVM, including network settings and TypeScript setup." }, { "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Set Up a Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "How to start the local Revive dev node for running the test suite against a local network before deploying to TestNet." + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", + "relevance": "How to install and run the local development node required for test execution in step 6." }, { "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", - "title": "Deploy Uniswap V2 Periphery with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", - "relevance": "Next step: deploy the UniswapV2Router02 contract that exposes user-facing swap and liquidity functions on top of the Factory." + "title": "Uniswap V2 Periphery with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2/", + "relevance": "Next step after Core deployment — deploys WETH9, UniswapV2Factory, and Router02 for full DEX liquidity and swap functionality." } ] }, "examples": [ { - "scenario": "Common scenario: deploy Uniswap V2 Core contracts to Polkadot Hub", - "user_says": "Deploy Uniswap V2 Core to Polkadot Hub using EVM", + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet via EVM", + "user_says": "Deploy Uniswap V2 Core on Polkadot Hub using the EVM execution path", "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", - "Modify hardhat.config.ts: add dotenv import, remove vars imports, replace vars.get with process.env, add gasPrice 5000 gwei", + "Clone revm-hardhat-examples; navigate to uniswap-v2-core-hardhat/", + "Run npm install dotenv && npm install", + "Fetch hardhat.config.ts; apply dotenv substitution (replace vars import and vars.get() with process.env.PRIVATE_KEY)", + "Create .env; ask user to fill in PRIVATE_KEY", "Run npx hardhat compile", - "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", - "Record Factory, TokenA, TokenB, and Pair addresses" + "Start local development node; run npx hardhat test --network localNode (28 tests pass)", + "Review scripts/deploy.ts; run npx hardhat run scripts/deploy.ts --network polkadotTestnet; save all addresses" ], - "result": "UniswapV2Factory and two test ERC-20 tokens deployed; trading pair created; all addresses printed to console" + "result": "UniswapV2Factory, TokenA, TokenB, and trading Pair deployed to Polkadot Hub TestNet; addresses printed" }, { - "scenario": "Edge case: run tests against a local dev node before deploying to TestNet", - "user_says": "Run the Uniswap V2 tests against the local node first", + "scenario": "Edge case: deploy only the Factory using Hardhat Ignition", + "user_says": "I just need the UniswapV2Factory deployed, no test tokens", "actions": [ - "Start the local development node following https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "In a new terminal, run: npx hardhat test --network localNode", - "Verify all 28 tests pass", - "Then deploy to TestNet" + "Ensure the hardhat.config.ts dotenv substitution has been applied (step 3)", + "Run: npx hardhat ignition deploy ./ignition/modules/UniswapV2Factory.ts --network polkadotTestnet", + "Confirm the network name when prompted" ], - "result": "28 tests pass against the local node; deployment to TestNet proceeds with confidence" + "result": "Only UniswapV2Factory deployed; factory address printed" } - ] + ], + "reference_code": { + "repo": "polkadot-developers/revm-hardhat-examples", + "branch": "master", + "base_path": "uniswap-v2-core-hardhat", + "files": [ + { + "path": "hardhat.config.ts", + "description": "Hardhat config with Solidity 0.5.16, localNode and polkadotTestnet networks, Hardhat vars for TESTNET_PRIVATE_KEY (must be converted to dotenv in step 3)" + }, + { + "path": "scripts/deploy.ts", + "description": "Deploys UniswapV2Factory, two ERC-20 test tokens with 10,000 supply each, and creates a trading pair; prints Factory, TokenA, TokenB, and Pair addresses" + } + ] + }, + "generated": "2026-05-15T15:30:00Z", + "content_hash": "sha256:af77b281cf97b9d4924e398dd8b0f9b48c741bd4879052831c92612d2628d831" }, { "id": "deploy-uniswap-v2-periphery-evm", - "title": "Deploy Uniswap V2 Periphery (Router) with EVM on Polkadot Hub", - "description": "Clones revm-hardhat-examples and deploys Uniswap V2 Periphery contracts (WETH9, UniswapV2Factory, UniswapV2Router02) to Polkadot Hub TestNet using Hardhat Ignition and the EVM execution path. Use when you need a full Router layer for swaps and liquidity management on Polkadot Hub. Trigger phrases: 'deploy Uniswap V2 Router Polkadot', 'deploy WETH Router EVM Polkadot Hub', 'Uniswap V2 Periphery Polkadot'. Requires testnet PAS tokens and a funded EVM private key. Converts hardhat vars to dotenv. Key capabilities: clone repo, compile multi-version Solidity (0.5.16 + 0.6.6), optionally test locally, and deploy WETH9, Factory, and Router02 via Ignition to polkadotTestnet.", + "title": "Uniswap V2 Periphery with EVM on Polkadot", + "description": "Clones revm-hardhat-examples, compiles unmodified Uniswap V2 Periphery contracts (WETH9, UniswapV2Router01, UniswapV2Router02, multi-compiler Solidity 0.5.16 + 0.6.6) using standard Hardhat and TypeScript on the EVM path, runs 50 tests against a local development node, and deploys WETH9, UniswapV2Factory, and UniswapV2Router02 to Polkadot Hub TestNet via Hardhat Ignition. Use when you need the full Router layer for liquidity management and token swaps on top of V2 Core. Key capabilities: multi-compiler Hardhat config; local '@uniswap/v2-core' file dependency resolved automatically; dotenv-based private key management (converted from Hardhat vars); Ignition two-batch deployment. Trigger phrases: 'deploy Uniswap V2 Periphery', 'deploy UniswapV2Router02 Polkadot', 'AMM router EVM'. Requires PAS testnet tokens.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -9263,27 +9321,28 @@ "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", "prerequisites": { "runtime": [ - "Node.js v22+ and npm", - "Git" + "Node.js v22+", + "npm" ], "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + "Local development node at http://127.0.0.1:8545 for testing — see https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account" ], "wallet": [ - "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" + "An EVM-compatible account (MetaMask or similar) with the private key exported for PRIVATE_KEY in .env" ] }, "env_vars": [ { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", - "required": true + "name": "PRIVATE_KEY", + "description": "Private key of the deployer account on Polkadot Hub TestNet. Required only for TestNet deployment. Local testing works without it.", + "required": true, + "secret": true } ], - "project_structure": "revm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v2-periphery-hardhat/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV2Router01.sol\n \u2502 \u251c\u2500\u2500 UniswapV2Router02.sol\n \u2502 \u2514\u2500\u2500 test/\n \u2502 \u251c\u2500\u2500 WETH9.sol\n \u2502 \u2514\u2500\u2500 ERC20.sol\n \u251c\u2500\u2500 ignition/\n \u2502 \u2514\u2500\u2500 modules/\n \u2502 \u2514\u2500\u2500 UniswapV2Router02.ts\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.ts\n \u2514\u2500\u2500 package.json", "steps": [ { "order": 1, @@ -9291,37 +9350,32 @@ "working_directory": ".", "commands": [ "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", - "cd uniswap-v2-periphery-hardhat" + "cd revm-hardhat-examples/uniswap-v2-periphery-hardhat" ], - "description": "Clone the revm-hardhat-examples repository, check out the pinned commit for this tutorial, and navigate into `uniswap-v2-periphery-hardhat`. This project depends on the V2 Core contracts through a local file reference (`@uniswap/v2-core: file:../uniswap-v2-core-hardhat`) resolved automatically from the sibling directory in the same repo \u2014 no separate Core clone is needed." + "description": "Clone revm-hardhat-examples (default branch: master) and navigate to the `uniswap-v2-periphery-hardhat` subdirectory. Both `uniswap-v2-core-hardhat/` and `uniswap-v2-periphery-hardhat/` must be present since the Periphery project depends on V2 Core via a local file reference: `'@uniswap/v2-core': 'file:../uniswap-v2-core-hardhat'`." }, { "order": 2, - "action": "Install dependencies including dotenv", + "action": "Install dependencies", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", "commands": [ - "npm install", - "npm install dotenv" + "npm install dotenv", + "npm install" ], - "description": "Install all project dependencies (including the V2 Core local reference), then additionally install dotenv to replace Hardhat's interactive Configuration Variables system." + "description": "Install dotenv for private key loading, then install all other project dependencies. npm resolves the local `@uniswap/v2-core` reference from the sibling `uniswap-v2-core-hardhat/` directory automatically — no separate Core installation needed." }, { "order": 3, - "action": "Create .env file and .gitignore for private key", + "action": "Fetch hardhat.config.ts and apply dotenv substitution", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty TESTNET_PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for a funded Polkadot Hub TestNet account. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "reference_file": "hardhat.config.ts", + "description": "Fetch the reference file and save it as `hardhat.config.ts`. Apply these substitutions to convert from Hardhat vars to dotenv (required — Hardhat's interactive `vars.get()` cannot be used in agent sessions):\n\n1. Replace the import line:\n```typescript\nimport { HardhatUserConfig, vars } from \"hardhat/config\";\n```\nwith:\n```typescript\nimport { HardhatUserConfig } from \"hardhat/config\";\nimport \"dotenv/config\";\n```\n\n2. Replace the `accounts` field in the `polkadotTestnet` network:\n```typescript\naccounts: vars.has(\"TESTNET_PRIVATE_KEY\")\n ? [vars.get(\"TESTNET_PRIVATE_KEY\")]\n : [],\n```\nwith:\n```typescript\naccounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],\n```\n\nAll other config fields (multi-compiler with Solidity 0.5.16 + 0.6.6 both with optimizer enabled, hardhat network with `allowUnlimitedContractSize: true`, localNode URL, mocha timeout 120000) remain unchanged." }, { "order": 4, - "action": "Convert hardhat.config.ts from vars to dotenv, add gas and ignition settings", + "action": "Create the .env file", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "description": "Open `hardhat.config.ts` and apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line.\n\n(2) Change the import from:\n```typescript\nimport { HardhatUserConfig, vars } from 'hardhat/config';\n```\nto:\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\n```\n\n(3) Replace the polkadotTestnet `accounts` field:\n```typescript\naccounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : [],\n```\n\n(4) Add `gasPrice: 5000000000000,` (5000 gwei) to the `polkadotTestnet` network block.\n\n(5) Add an `ignition` section at the end of the config object (before `export default config`):\n```typescript\nignition: {\n requiredConfirmations: 1,\n},\n```\n\nSave the file." + "description": "Create a file named `.env` in the `uniswap-v2-periphery-hardhat` directory with the following content:\n\n```text\nPRIVATE_KEY=\"INSERT_PRIVATE_KEY\"\n```\n\nAlso create `.gitignore` (or append to the existing one) with `.env` to prevent committing the key.\n\nStop and ask the user to edit the `.env` file directly, replacing `INSERT_PRIVATE_KEY` with their Polkadot Hub TestNet account private key (0x-prefixed). Do NOT ask for the key in chat. Wait for confirmation before proceeding. (Skip this step if only testing locally.)" }, { "order": 5, @@ -9330,273 +9384,133 @@ "commands": [ "npx hardhat compile" ], - "expected_output": "Compiled contracts successfully", - "description": "Compile all Periphery contracts using the multi-compiler setup (Solidity 0.5.16 for V2 Core dependency, 0.6.6 for Router contracts). On success, artifacts appear in `artifacts/`." + "expected_output": "Compiled N Solidity files successfully", + "description": "Compile the Periphery contracts with the multi-compiler setup: Solidity 0.5.16 for V2 Core dependency contracts and 0.6.6 for Router contracts. Artifacts are written to the `artifacts` directory." }, { "order": 6, - "action": "Deploy contracts via Ignition to Polkadot Hub TestNet", + "action": "Start the local development node", + "working_directory": ".", + "description": "Start the local development node as described at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/. The node must be accessible at http://127.0.0.1:8545 before running tests. The hardhat network config has `allowUnlimitedContractSize: true` to accommodate Router bytecode size during local testing." + }, + { + "order": 7, + "action": "Run the test suite against the local node", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npx hardhat test --network localNode" + ], + "expected_output": "50 passing", + "description": "Run all 50 tests: 38 in UniswapV2Router01.test.ts (liquidity add/remove, token swaps, ETH swaps, permit-based removal) and 12 in UniswapV2Router02.test.ts (fee-on-transfer token support). Tests use a 120-second Mocha timeout. If tests time out, verify the local development node is running at http://127.0.0.1:8545." + }, + { + "order": 8, + "action": "Review the Ignition deployment module", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "reference_file": "ignition/modules/UniswapV2Router02.ts", + "description": "Review the Ignition module at `ignition/modules/UniswapV2Router02.ts`. It deploys three contracts: WETH9 and UniswapV2Factory in parallel (first batch, both independent), then UniswapV2Router02 with the factory and WETH addresses as constructor arguments (second batch). The deployer account (index 0, from `m.getAccount(0)`) is set as the Factory fee recipient." + }, + { + "order": 9, + "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", "commands": [ "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" ], - "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", - "interactive": true, - "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. Ignition prompts to confirm the target network name and chain ID \u2014 delegate this confirmation to the user. Ignition deploys Factory and WETH9 in parallel, then Router02. Save all three deployed addresses from the output: WETH9, UniswapV2Factory, and UniswapV2Router02. These addresses are needed to add liquidity and execute swaps via the Router." + "expected_output": "UniswapV2Router02Module#WETH9 - 0x...\nUniswapV2Router02Module#UniswapV2Factory - 0x...\nUniswapV2Router02Module#UniswapV2Router02 - 0x...", + "description": "Deploy to Polkadot Hub TestNet (RPC: https://services.polkadothub-rpc.com/testnet). When Ignition prompts to confirm the network, enter the exact network name `polkadotTestnet` and verify the chain ID is 420420417. Ignition deploys in two batches. Save the three addresses from the output (WETH9, UniswapV2Factory, UniswapV2Router02) — they are the entry point for all liquidity and swap operations." } ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, + "project_structure": "revm-hardhat-examples/\n├── uniswap-v2-core-hardhat/\n│ └── contracts/ (local dependency, must be present)\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.ts\n ├── package.json\n └── tsconfig.json", "error_patterns": [ { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify `gasPrice: 5000000000000` is in the polkadotTestnet block in hardhat.config.ts." + "pattern": "Error: Cannot find module '@uniswap/v2-core'", + "cause": "The local V2 Core dependency is not resolved. The sibling `uniswap-v2-core-hardhat/` directory must exist.", + "resolution": "Ensure you cloned the full revm-hardhat-examples repo (not just the periphery subdirectory) and ran `npm install` from within `uniswap-v2-periphery-hardhat/`. Do not clone periphery in isolation." }, { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of a deployment transaction. Common when requiredConfirmations is 0 or gasPrice is too low.", - "resolution": "Delete ignition/deployments/ directory. Ensure `ignition: { requiredConfirmations: 1 }` and `gasPrice: 5000000000000` are in hardhat.config.ts. Retry the Ignition deploy command." + "pattern": "Error: process.env.PRIVATE_KEY is undefined / no accounts configured", + "cause": ".env file not created or PRIVATE_KEY not set, or dotenv substitution in step 3 was not applied.", + "resolution": "Apply the substitution in step 3 to hardhat.config.ts, then create .env with PRIVATE_KEY in step 4. Verify `import \"dotenv/config\"` appears at the top of hardhat.config.ts." }, { - "pattern": "TypeError: vars is not defined", - "cause": "hardhat.config.ts still references Hardhat's vars system.", - "resolution": "Add `import 'dotenv/config';` as first line, remove `vars` from the import, and replace `vars.has/get('TESTNET_PRIVATE_KEY')` with `process.env.TESTNET_PRIVATE_KEY`." + "pattern": "Error: timeout exceeded / test timeout of 120000ms exceeded", + "cause": "Local development node is not running or not reachable at http://127.0.0.1:8545.", + "resolution": "Start the local development node before running tests. See https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/." }, { - "pattern": "Cannot find package '@uniswap/v2-core'", - "cause": "The local V2 Core sibling directory was not found, or npm install did not resolve it.", - "resolution": "Verify the `uniswap-v2-core-hardhat/` directory exists at the same level as `uniswap-v2-periphery-hardhat/` (it was checked out from the same pinned commit in step 1). Run `npm install` again from `uniswap-v2-periphery-hardhat/`." + "pattern": "Ignition deploy fails: confirmation prompt not answered correctly", + "cause": "Ignition requires confirming the network name when deploying to a non-default network.", + "resolution": "When prompted, type the exact network name `polkadotTestnet` and verify the chain ID is 420420417 (Polkadot Hub TestNet)." } ], "supplementary_context": { - "description": "Load when the user asks about the Router architecture, V2 Core dependency, or V3 deployment.", + "description": "Load these pages for background on V2 Core (prerequisite), Hardhat multi-compiler config, and local development node setup.", "pages": [ { "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", - "title": "Deploy Uniswap V2 Core with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", - "relevance": "The V2 Core Factory and Pair contracts that the Periphery Router builds on top of." + "title": "Uniswap V2 Core with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2/", + "relevance": "Prerequisite context: the Periphery Router depends on V2 Core contracts. The Core guide explains Factory and Pair deployment." }, { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", - "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", - "relevance": "Next evolution: deploy the concentrated liquidity Uniswap V3 Factory instead of the uniform-liquidity V2." + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat/", + "relevance": "Full Hardhat configuration guide for Polkadot Hub EVM including multi-compiler setup." }, { "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Set Up a Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "How to start the local Revive dev node for running the 50-test test suite before deploying to TestNet." + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", + "relevance": "How to install and run the local development node required for test execution in step 6." } ] }, "examples": [ { - "scenario": "Common scenario: deploy Uniswap V2 Router to Polkadot Hub", - "user_says": "Deploy the Uniswap V2 Router to Polkadot Hub EVM", + "scenario": "Common scenario: deploy full Uniswap V2 Periphery to Polkadot Hub TestNet", + "user_says": "Deploy the Uniswap V2 Router contracts on Polkadot Hub using EVM and Hardhat Ignition", "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", - "Modify hardhat.config.ts: add dotenv import, remove vars, replace vars.get with process.env, add gasPrice 5000 gwei, add ignition.requiredConfirmations: 1", + "Clone revm-hardhat-examples; navigate to uniswap-v2-periphery-hardhat/", + "Run npm install dotenv && npm install (resolves local V2 Core dependency automatically)", + "Fetch hardhat.config.ts; apply dotenv substitution (replace vars with process.env.PRIVATE_KEY)", + "Create .env; ask user to fill in PRIVATE_KEY", "Run npx hardhat compile", - "Run npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet; delegate confirmation to user", - "Record WETH9, UniswapV2Factory, and UniswapV2Router02 addresses" + "Start local development node; run npx hardhat test --network localNode (50 tests pass)", + "Review ignition/modules/UniswapV2Router02.ts", + "Run npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet; confirm network; save addresses" ], - "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet; addresses printed by Ignition" + "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet via two Ignition batches; three addresses printed" }, { - "scenario": "Edge case: Ignition deployment fails with IGN401 dropped transaction", - "user_says": "Ignition says IGN401 Transaction dropped and retry fails with Transaction Already Imported", + "scenario": "Edge case: use the imperative deploy script instead of Ignition", + "user_says": "I prefer a plain deploy script over Hardhat Ignition", "actions": [ - "Delete the ignition/deployments/ directory", - "Verify hardhat.config.ts has ignition: { requiredConfirmations: 1 } and gasPrice: 5000000000000", - "Retry: npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" - ], - "result": "Ignition re-attempts deployment fresh without stale state; deployment succeeds" - } - ] - }, - { - "id": "deploy-uniswap-v3-core-evm", - "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", - "description": "Clones revm-hardhat-examples and deploys the UniswapV3Factory contract to Polkadot Hub TestNet using Hardhat Ignition and the EVM execution path. Use when you need concentrated liquidity AMM infrastructure on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Polkadot', 'deploy V3 Factory Polkadot Hub EVM', 'concentrated liquidity AMM Polkadot'. Requires testnet PAS tokens and a funded EVM private key. Converts hardhat vars to dotenv. Key capabilities: clone repo, compile Solidity 0.7.6 with metadata hash excluded to meet EIP-170 contract size limit, optionally test locally (187 tests), and deploy UniswapV3Factory via Ignition to polkadotTestnet. Uses ignition.requiredConfirmations: 1 (already configured in the reference repo).", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", - "prerequisites": { - "runtime": [ - "Node.js v22+ and npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded Polkadot Hub TestNet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account on Polkadot Hub TestNet. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n\u2514\u2500\u2500 uniswap-v3-core-hardhat/\n \u251c\u2500\u2500 contracts/\n \u2502 \u251c\u2500\u2500 UniswapV3Factory.sol\n \u2502 \u251c\u2500\u2500 UniswapV3Pool.sol\n \u2502 \u2514\u2500\u2500 libraries/\n \u251c\u2500\u2500 ignition/\n \u2502 \u2514\u2500\u2500 modules/\n \u2502 \u2514\u2500\u2500 UniswapV3Factory.ts\n \u251c\u2500\u2500 test/\n \u251c\u2500\u2500 artifacts/\n \u251c\u2500\u2500 .env\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 hardhat.config.ts\n \u2514\u2500\u2500 package.json", - "steps": [ - { - "order": 1, - "action": "Clone the repository and navigate to the project", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", - "cd uniswap-v3-core-hardhat" - ], - "description": "Clone the revm-hardhat-examples repository, check out the pinned commit for this tutorial, and navigate into `uniswap-v3-core-hardhat`. This directory contains unmodified Uniswap V3 Core contracts (Solidity 0.7.6), 16 math library contracts, and an Ignition module that deploys the UniswapV3Factory." - }, - { - "order": 2, - "action": "Install dependencies including dotenv", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install all project dependencies, then additionally install dotenv to replace Hardhat's interactive Configuration Variables system." - }, - { - "order": 3, - "action": "Create .env file and .gitignore for private key", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" + "Ensure the dotenv substitution in hardhat.config.ts has been applied (step 3) and .env is set", + "Run: npx hardhat run scripts/deploy.ts --network polkadotTestnet", + "The script deploys WETH9, UniswapV2Factory, UniswapV2Router02, two ERC-20 test tokens, and creates a trading pair" ], - "description": "Create a .env file with an empty TESTNET_PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for a funded Polkadot Hub TestNet account. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts from vars to dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "description": "Open `hardhat.config.ts` and apply all of the following modifications:\n\n(1) Add `import 'dotenv/config';` as the very first line.\n\n(2) Change the import from:\n```typescript\nimport { HardhatUserConfig, vars } from 'hardhat/config';\n```\nto:\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\n```\n\n(3) Replace the polkadotTestnet `accounts` field:\n```typescript\naccounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : [],\n```\n\n(4) Add `gasPrice: 5000000000000,` (5000 gwei) to the `polkadotTestnet` network block.\n\nNote: `ignition: { requiredConfirmations: 1 }` is already present in the config \u2014 do not remove it.\nNote: `bytecodeHash: 'none'` in the Solidity settings is intentional (required to keep UniswapV3Factory under the EIP-170 24KB contract size limit) \u2014 do not change it.\n\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled contracts successfully", - "description": "Compile all V3 Core contracts using Solidity 0.7.6. The build includes 16 math libraries and 17 test helper contracts. `bytecodeHash: 'none'` ensures the Factory stays under the EIP-170 24KB size limit by excluding the metadata hash from the bytecode." - }, - { - "order": 6, - "action": "Deploy UniswapV3Factory via Ignition to Polkadot Hub TestNet", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", - "interactive": true, - "description": "Deploy the UniswapV3Factory contract via Hardhat Ignition. Ignition prompts to confirm the target network and chain ID \u2014 delegate this confirmation to the user. Save the deployed UniswapV3Factory address from the output \u2014 it is the entry point for creating concentrated liquidity pools. The `ignition.requiredConfirmations: 1` setting is already in the config, ensuring reliable deployment on the TestNet." + "result": "All six contracts deployed sequentially; WETH, Factory, Router, TokenA, TokenB, and Pair addresses printed" } ], "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify `gasPrice: 5000000000000` is in the polkadotTestnet block in hardhat.config.ts." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of the Factory deployment transaction, often due to low gasPrice on TestNet.", - "resolution": "Delete ignition/deployments/ directory. Verify `gasPrice: 5000000000000` and `ignition: { requiredConfirmations: 1 }` in hardhat.config.ts. Retry the Ignition deploy command." - }, - { - "pattern": "TypeError: vars is not defined", - "cause": "hardhat.config.ts still references Hardhat's vars system after the substitution step.", - "resolution": "Add `import 'dotenv/config';` as the first line, remove `vars` from the HardhatUserConfig import, and replace `vars.has/get('TESTNET_PRIVATE_KEY')` with `process.env.TESTNET_PRIVATE_KEY`." - }, - { - "pattern": "Contract size exceeds 24576 bytes / EIP-170 contract size limit", - "cause": "`bytecodeHash` setting was changed or removed from the Solidity compiler settings.", - "resolution": "Restore `metadata: { bytecodeHash: 'none' }` in the Solidity compiler settings in hardhat.config.ts. This is required to keep UniswapV3Factory under the EIP-170 limit and matches the original Uniswap V3 deployment configuration." - } - ], - "supplementary_context": { - "description": "Load when the user asks about V3 architecture, concentrated liquidity concepts, or the Periphery contracts.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", - "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", - "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager contracts on top of the V3 Factory." - }, + "repo": "polkadot-developers/revm-hardhat-examples", + "branch": "master", + "base_path": "uniswap-v2-periphery-hardhat", + "files": [ { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Set Up a Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "How to start the local Revive dev node for running the 187-test suite (Factory + Pool tests) before deploying to TestNet." + "path": "hardhat.config.ts", + "description": "Hardhat config with multi-compiler (Solidity 0.5.16 + 0.6.6), hardhat/localNode/polkadotTestnet network configs, Hardhat vars for TESTNET_PRIVATE_KEY (must be converted to dotenv in step 3)" }, { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Set Up Hardhat for Polkadot Hub (EVM)", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Background on Hardhat configuration for Polkadot Hub and the polkadotTestnet network settings." + "path": "ignition/modules/UniswapV2Router02.ts", + "description": "Hardhat Ignition module deploying WETH9 and UniswapV2Factory in parallel (batch 1), then UniswapV2Router02 with factory and WETH addresses (batch 2)" } ] }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V3 Factory to Polkadot Hub", - "user_says": "Deploy Uniswap V3 Core to Polkadot Hub TestNet", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", - "Modify hardhat.config.ts: add dotenv import, remove vars, replace vars.get with process.env, add gasPrice 5000 gwei; preserve bytecodeHash: none and ignition.requiredConfirmations: 1", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet; delegate confirmation to user", - "Record UniswapV3Factory address" - ], - "result": "UniswapV3Factory deployed to Polkadot Hub TestNet; factory address printed by Ignition" - }, - { - "scenario": "Edge case: Ignition IGN401 dropped transaction on TestNet", - "user_says": "Ignition fails with IGN401 and retrying gives Transaction Already Imported", - "actions": [ - "Delete ignition/deployments/ directory", - "Verify hardhat.config.ts has ignition: { requiredConfirmations: 1 } and gasPrice: 5000000000000", - "Retry: npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" - ], - "result": "Ignition starts fresh and deploys the Factory successfully" - } - ] + "generated": "2026-05-15T15:30:00Z", + "content_hash": "sha256:a6be4ba82c68b70d5b48c46ed3490c6f441ed9e65ab849ddb92c01259c717d1c" } ], "outputs": { diff --git a/skill_candidates.json b/skill_candidates.json index c14fc657b..48cecb508 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -2673,7 +2673,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded \u2014 the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", + "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded — the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", "scoring": { "signals": [ "P1", @@ -2694,7 +2694,7 @@ }, "priority_score": 18 }, - "built_at": "2026-05-15T16:00:00Z" + "built_at": "2026-05-15T15:30:00Z" }, { "skill_id": "deploy-uniswap-v2-core-pvm", @@ -2705,7 +2705,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded \u2014 polkavm-hardhat-examples is listed in context.md.", + "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded — polkavm-hardhat-examples is listed in context.md.", "scoring": { "signals": [ "P1", @@ -2729,7 +2729,7 @@ }, "priority_score": 25 }, - "built_at": "2026-05-15T16:00:00Z" + "built_at": "2026-05-15T15:30:00Z" }, { "skill_id": "deploy-uniswap-v2-core-evm", @@ -2740,7 +2740,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded \u2014 key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded \u2014 revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", + "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded — key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded — revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", "scoring": { "signals": [ "P1", @@ -2763,7 +2763,7 @@ }, "priority_score": 25 }, - "built_at": "2026-05-15T16:00:00Z" + "built_at": "2026-05-15T15:30:00Z" }, { "skill_id": "deploy-uniswap-v2-periphery-evm", @@ -2774,7 +2774,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo \u2014 no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded \u2014 revm-hardhat-examples is listed in context.md.", + "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo — no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded — revm-hardhat-examples is listed in context.md.", "scoring": { "signals": [ "P1", @@ -2797,42 +2797,7 @@ }, "priority_score": 25 }, - "built_at": "2026-05-15T16:00:00Z" - }, - { - "skill_id": "deploy-uniswap-v3-core-evm", - "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" - ], - "category": "tutorial", - "priority": "high", - "status": "built", - "notes": "K1=0: test step references external Local Development Node guide; deployment workflow itself is self-contained. K4=3: revm-hardhat-examples repo with pinned commit (3ff28ae). No P3: reader clones the repo, not creates files.", - "scoring": { - "signals": [ - "P1", - "P2", - "P4", - "R2", - "C1", - "C2", - "K2", - "K3", - "K4", - "S1", - "S2", - "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 4, - "conceptual": 8, - "not_applicable": 0 - }, - "priority_score": 20 - }, - "built_at": "2026-05-15T16:00:00Z" + "built_at": "2026-05-15T15:30:00Z" } ], "supplementary_only": [ diff --git a/skill_coverage.json b/skill_coverage.json index 6ccc7d619..b10a1ec3b 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,11 +1,11 @@ { "schema_version": "1", - "generated": "2026-05-15T15:00:00Z", + "generated": "2026-05-15T15:30:00Z", "summary": { "total_candidates": 142, - "up_to_date": 56, + "up_to_date": 60, "stale": 0, - "uncovered": 63, + "uncovered": 59, "reviewed_no_skill": 23 }, "pages": { @@ -105,7 +105,7 @@ }, "chain-interactions/store-data/bulletin-chain.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T16:00:00Z", + "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "store-retrieve-data-bulletin-chain" ], @@ -707,7 +707,7 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T16:00:00Z", + "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-core-pvm" ], @@ -715,7 +715,7 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T16:00:00Z", + "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-core-evm" ], @@ -723,7 +723,7 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T16:00:00Z", + "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-periphery-evm" ], From 9852f21c29fcb74a911d7ad2d5146d461d9d434b Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 15:20:20 +0000 Subject: [PATCH 11/26] chore: auto-generate/update skills --- agent_skills_config.json | 854 ++++++++++++++++++++------------------- skill_candidates.json | 71 ++++ skill_coverage.json | 2 +- 3 files changed, 521 insertions(+), 406 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 9eacbf58e..900882429 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", "generated": "2026-05-15T15:30:00Z", - "content_hash": "sha256:ef1cb2bf98721bdbe9f835d261011c01ca05b85a93f19c68124e56d2e442e2b5", + "content_hash": "sha256:1e942f0bea9b650ee643e6d7957d81984fe9990af06b540e4e1736c2850e16a9", "skills": [ { "id": "create-polkadot-account", @@ -8713,7 +8713,7 @@ { "id": "store-retrieve-data-bulletin-chain", "title": "Store and Retrieve Data on the Bulletin Chain", - "description": "Authorizes an account on the Polkadot Bulletin Chain, stores a file on-chain via the TransactionStorage pallet using PAPI, retrieves it by CID through the IPFS gateway, and renews storage before expiry. Use when you need decentralized, IPFS-compatible file storage on Polkadot without EVM smart contracts. Key capabilities: Console UI walkthrough for account authorization (Root-origin faucet required); PAPI TypeScript script for programmatic store; CID-based retrieval; renewal workflow. Trigger phrases: 'store data on Bulletin Chain', 'upload file to Polkadot on-chain', 'get CID for image on Polkadot', 'renew Bulletin Chain data'. Authorization must be obtained from the Console UI faucet before PAPI store.", + "description": "Stores a file on the Polkadot Bulletin Chain using PAPI TypeScript and retrieves it by CID via the IPFS gateway. Use when you need decentralized IPFS-compatible file storage for dApp assets, NFT metadata, or files under 8 MiB. Requires a Polkadot account authorized via the Bulletin Chain Console UI faucet (no programmatic self-authorization). Trigger phrases: 'store file Bulletin Chain', 'PAPI TransactionStorage store', 'Polkadot decentralized storage', 'Bulletin Chain IPFS'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8725,37 +8725,37 @@ "primary_page": "chain-interactions/store-data/bulletin-chain.md", "prerequisites": { "runtime": [ - "Node.js v18+", + "Node.js v18 or later", "npm" ], "network": [ - "Polkadot Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" + "Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" ], "tokens": [ - "Account authorization from the Bulletin Chain Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ (authorization is free but requires wallet approval)" + "Bulletin Chain storage authorization (not DOT/PAS) — obtained from Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/. The authorize_account extrinsic requires Root origin; self-authorization is not available programmatically." ], "wallet": [ - "A Polkadot account with a browser wallet extension (Polkadot.js, Talisman, SubWallet, or Fearless Wallet) to approve the authorization transaction via the Console UI" + "A Polkadot account (SS58) with active Bulletin Chain authorization, plus its 12-word mnemonic for signing" ] }, "env_vars": [ { - "name": "SEED_PHRASE", - "description": "12- or 24-word BIP39 mnemonic for the account that was authorized on the Bulletin Chain in the Console UI step. This account must hold authorization (bytes + transactions) before store operations will succeed.", - "required": true, - "secret": true + "name": "MNEMONIC", + "description": "12-word mnemonic of the authorized Polkadot account. Do NOT ask user to paste mnemonic in chat — they must edit .env directly.", + "required": true } ], + "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── (generated Bulletin Chain descriptors)\n├── store-data.ts\n├── .env\n├── .gitignore\n├── node_modules/\n└── package.json", "steps": [ { "order": 1, - "action": "Authorize your account via the Console UI faucet", + "action": "Authorize account via Bulletin Chain Console UI", "working_directory": ".", - "description": "Navigate to the Bulletin Chain Console at https://paritytech.github.io/polkadot-bulletin-chain/ and connect your wallet. Go to the Faucet page, select the Storage Faucet tab, and under 'Authorize Account' enter the number of Transactions and Bytes you need, then click 'Authorize Account'. Approve the transaction in your wallet extension. Verify your authorization balance on the Accounts tab. Note: the 'authorize_account' extrinsic requires Root origin and cannot be called programmatically — the Console UI faucet is the only path. Authorization is consumed as files are stored and expires if unused transactions lapse." + "description": "This step requires manual browser interaction — delegate to the user.\n\n1. Open https://paritytech.github.io/polkadot-bulletin-chain/ and click Connect to link a wallet (Polkadot.js, Talisman, SubWallet, or Fearless).\n2. Go to Faucet > Storage Faucet tab.\n3. Under Authorize Account, enter the number of Transactions and Bytes needed, click Authorize Account, and approve in the wallet extension.\n4. Verify authorization on the Accounts tab — note the expiration block number.\n\nNote: Authorization expires at a block. Once expired, data cannot be renewed without re-authorizing." }, { "order": 2, - "action": "Initialize the project", + "action": "Create and initialize the project", "working_directory": ".", "commands": [ "mkdir bulletin-store-example", @@ -8763,148 +8763,130 @@ "npm init -y", "npm pkg set type=module" ], - "description": "Create the 'bulletin-store-example' directory and initialize it as an ESM Node.js project. The `type=module` flag is required — PAPI uses ESM imports." + "description": "Create a new Node.js ESM project. The ESM module type is required for polkadot-api and hdkd packages." }, { "order": 3, "action": "Install dependencies", "working_directory": "bulletin-store-example", "commands": [ - "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv" + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv tsx" ], - "description": "Install polkadot-api (PAPI), @polkadot-labs/hdkd and hdkd-helpers for SR25519 key derivation, multiformats for CID decoding, and dotenv for loading the seed phrase from .env." + "description": "Install polkadot-api (typed chain client), hdkd and hdkd-helpers (key derivation), multiformats (CID decoding), dotenv (secure mnemonic loading), and tsx (TypeScript runner)." }, { "order": 4, - "action": "Fetch Bulletin Chain metadata", + "action": "Add Bulletin Chain metadata", "working_directory": "bulletin-store-example", "commands": [ "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" ], - "description": "Download Bulletin Chain metadata and generate typed PAPI descriptors with the 'bulletin' export key under @polkadot-api/descriptors. This provides full type safety for all TransactionStorage pallet interactions." + "expected_output": "Descriptors generated successfully", + "description": "Connect to the Bulletin Chain RPC, download chain metadata, and generate typed descriptors in .papi/. These descriptors provide compile-time type safety for all pallet interactions." }, { "order": 5, - "action": "Create the .env file", + "action": "Create .env with mnemonic placeholder", "working_directory": "bulletin-store-example", - "description": "Create a file named `.env` in the `bulletin-store-example` directory with the following content:\n\n```text\nSEED_PHRASE=\"INSERT_SEED_PHRASE\"\n```\n\nAlso create `.gitignore` with `.env` on its own line so the seed phrase is never committed.\n\nStop and ask the user to edit the `.env` file directly, replacing `INSERT_SEED_PHRASE` with the mnemonic of the account they authorized in step 1. Do NOT ask for the seed phrase in chat. Wait for confirmation before proceeding." + "commands": [ + "printf 'MNEMONIC=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty MNEMONIC placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in MNEMONIC with the 12-word mnemonic of the authorized account. Do NOT ask for the mnemonic in chat. Wait for user confirmation before proceeding." }, { "order": 6, - "action": "Fetch the store-data script and apply substitutions", + "action": "Create store-data.ts", "working_directory": "bulletin-store-example", "reference_file": "store-data.ts", - "description": "Fetch the reference file and save it as `store-data.ts`. Apply these two substitutions:\n\n1. Replace the `FILE_PATH` placeholder: change `const FILE_PATH = 'INSERT_IMAGE_PATH';` to the path of the file you want to store (e.g., `const FILE_PATH = './logo.png';`).\n\n2. Replace the DEV_PHRASE signer with dotenv. Change the import block:\n```typescript\nimport {\n DEV_PHRASE,\n entropyToMiniSecret,\n mnemonicToEntropy,\n} from '@polkadot-labs/hdkd-helpers';\n```\nto:\n```typescript\nimport 'dotenv/config';\nimport {\n entropyToMiniSecret,\n mnemonicToEntropy,\n} from '@polkadot-labs/hdkd-helpers';\n```\n\nAnd change:\n```typescript\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE));\n```\nto:\n```typescript\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(process.env.SEED_PHRASE!));\n```\n\nThe signer now uses the authorized account from .env instead of the Alice dev account." + "description": "Fetch the reference file and save as store-data.ts. Then apply these modifications:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Remove `DEV_PHRASE` from the `@polkadot-labs/hdkd-helpers` import (keep `entropyToMiniSecret` and `mnemonicToEntropy`).\n3. Change `mnemonicToEntropy(DEV_PHRASE)` to `mnemonicToEntropy(process.env.MNEMONIC as string)`.\n4. Replace `'INSERT_IMAGE_PATH'` with the path to the file the user wants to store (ask the user for this path before running).\nSave the file." }, { "order": 7, - "action": "Store the file on-chain", + "action": "Run the store script", "working_directory": "bulletin-store-example", "commands": [ "npx tsx store-data.ts" ], - "expected_output": "Connected to Bulletin Chain (Polkadot TestNet)\nRead file: ./logo.png (N bytes)\nSubmitting store transaction...\nTransaction included in block: 0x...\nTransaction index: N\n\nImage stored successfully!\nIndex: N\nCID: bafk2bzace...\n\nRetrieve via IPFS gateway:\nhttps://paseo-ipfs.polkadot.io/ipfs/bafk2bzace...", - "description": "Run the store script. On success it prints the CID, block hash, and transaction index. Save the **block hash** and **transaction index** — they are required to renew stored data before it expires (approximately 2 weeks on TestNet)." - }, - { - "order": 8, - "action": "Retrieve the stored file", - "working_directory": "bulletin-store-example", - "description": "Retrieve your file using the CID from step 7. Option A — fetch via IPFS gateway (programmatic):\n\n```typescript\nconst cid = 'INSERT_CID_FROM_STEP_7';\nconst response = await fetch(`https://paseo-ipfs.polkadot.io/ipfs/${cid}`);\nconst data = await response.arrayBuffer();\nconsole.log(`Retrieved ${data.byteLength} bytes`);\n```\n\nOption B — open the IPFS gateway URL directly in a browser: `https://paseo-ipfs.polkadot.io/ipfs/`.\n\nOption C — use the Console UI Download page at https://paritytech.github.io/polkadot-bulletin-chain/ and enter the CID.", - "expected_output": "File content returned from gateway; byteLength matches the original file size." - }, - { - "order": 9, - "action": "Renew stored data before expiry", - "working_directory": "bulletin-store-example", - "description": "Data is retained for approximately 2 weeks on TestNet. To renew, call `api.tx.TransactionStorage.renew()` with the block number and transaction index recorded in step 7. Add a renewal script or paste this into a TypeScript file:\n\n```typescript\nimport 'dotenv/config';\nimport { createClient } from 'polkadot-api';\nimport { getWsProvider } from 'polkadot-api/ws';\nimport { bulletin } from '@polkadot-api/descriptors';\nimport { sr25519CreateDerive } from '@polkadot-labs/hdkd';\nimport { entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';\nimport { getPolkadotSigner } from 'polkadot-api/signer';\n\nconst BLOCK_NUMBER = INSERT_BLOCK_NUMBER_FROM_STEP_7;\nconst TX_INDEX = INSERT_TX_INDEX_FROM_STEP_7;\n\nconst client = createClient(getWsProvider('wss://paseo-bulletin-rpc.polkadot.io'));\nconst api = client.getTypedApi(bulletin);\nconst miniSecret = entropyToMiniSecret(mnemonicToEntropy(process.env.SEED_PHRASE!));\nconst derive = sr25519CreateDerive(miniSecret);\nconst hdkdKeyPair = derive('//your-path');\nconst signer = getPolkadotSigner(hdkdKeyPair.publicKey, 'Sr25519', hdkdKeyPair.sign);\n\nconst result = await api.tx.TransactionStorage.renew({ block: BLOCK_NUMBER, index: TX_INDEX }).signAndSubmit(signer);\nconsole.log('Renewed in block:', result.block.hash);\nclient.destroy();\n```\n\nReplace `INSERT_BLOCK_NUMBER_FROM_STEP_7` and `INSERT_TX_INDEX_FROM_STEP_7` with the values recorded in step 7. Each successful renewal emits a 'Renewed' event with a new block number and index — record the new values for subsequent renewals. Using stale values after a renewal will fail." + "expected_output": "Image stored successfully! CID: bafk2bzace...", + "description": "Run the script to submit the file to the Bulletin Chain. On success it prints the block hash, transaction index, CID, and an IPFS gateway URL. Save the CID and (block, index) pair — needed to retrieve or renew the stored data." } ], - "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── descriptors/\n├── store-data.ts\n├── .env\n├── .gitignore\n├── package.json\n└── ", + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", + "files": [ + { + "path": "store-data.ts", + "description": "PAPI TypeScript script that connects to Bulletin Chain, reads a local file, and submits it via TransactionStorage.store. Uses DEV_PHRASE by default — replace with process.env.MNEMONIC as instructed in step 6." + } + ] + }, "error_patterns": [ { - "pattern": "InsufficientAuthorization / account has no authorization", - "cause": "The signing account has not been authorized on the Bulletin Chain, or all authorized bytes/transactions have been consumed.", - "resolution": "Complete step 1: use the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ to authorize your account. Verify your remaining authorization on the Accounts tab." + "pattern": "Error: account has no authorization", + "cause": "The signing account has no active authorization on the Bulletin Chain, or it has expired.", + "resolution": "Re-authorize via the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ before retrying." }, { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated for the Bulletin Chain.", - "resolution": "Run `npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io` in the bulletin-store-example directory (step 4)." + "pattern": "file too large / data exceeds per-transaction limit", + "cause": "File exceeds the ~8 MiB per-transaction size limit.", + "resolution": "Split into chunks under 8 MiB and submit multiple store transactions, recording (block, index) per chunk." }, { - "pattern": "ENOENT: no such file or directory (reading the FILE_PATH)", - "cause": "The file path in store-data.ts does not exist or is incorrect.", - "resolution": "Update FILE_PATH in store-data.ts to point to an existing file. Use an absolute path or a relative path from the bulletin-store-example directory." + "pattern": "MNEMONIC is undefined / Cannot derive key from undefined", + "cause": ".env not populated or dotenv not loaded.", + "resolution": "Verify .env contains MNEMONIC= and that `import 'dotenv/config';` is the first line of store-data.ts." }, { - "pattern": "Renewal fails with 'UnexpectedStatus' or wrong block/index", - "cause": "Stale block number or transaction index used after a previous renewal.", - "resolution": "Each renewal emits a 'Renewed' event with a new (block, index) pair. Always use the most recent values from the last successful renew transaction — not the original store values." + "pattern": "WebSocket connection failed / ECONNREFUSED wss://paseo-bulletin-rpc.polkadot.io", + "cause": "Network issue or RPC temporarily unavailable.", + "resolution": "Check internet connectivity and retry after a few minutes. Monitor the Polkadot Discord for outage announcements." } ], "supplementary_context": { - "description": "Load these pages for technical background on Bulletin Chain storage mechanics, size limits, and retrieval methods.", + "description": "Load when the user asks about Bulletin Chain architecture, data retention, retrieval methods (P2P vs IPFS gateway), or the renewal workflow.", "pages": [ { "slug": "reference-polkadot-hub-data-storage", "title": "Data Storage", - "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage/", - "relevance": "Full technical reference for the Bulletin Chain: all extrinsics, storage items, events, size limits (8 MiB per transaction), and retrieval methods." - }, - { - "slug": "chain-interactions-accounts-create-account", - "title": "Create a Polkadot Account Programmatically", - "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account/", - "relevance": "How to create a Polkadot SS58 account if the user does not already have one to authorize." + "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage.md", + "relevance": "Technical reference for TransactionStorage pallet extrinsics, storage size limits, retention period, retrieval methods (IPFS gateway, P2P Helia), and renewal mechanics." } ] }, "examples": [ { - "scenario": "Common scenario: store an image and retrieve it by CID", - "user_says": "Store an image file on the Polkadot Bulletin Chain and give me its IPFS CID", + "scenario": "Common scenario: store an image for an NFT collection", + "user_says": "Store my NFT artwork on the Polkadot Bulletin Chain and get its IPFS CID", "actions": [ - "Authorize account via Console UI faucet (must be done manually — Root origin required)", - "Initialize bulletin-store-example ESM Node.js project with npm", - "Install polkadot-api, hdkd, hdkd-helpers, multiformats, dotenv", - "Fetch Bulletin Chain metadata with `npx papi add bulletin`", - "Create .env with SEED_PHRASE; ask user to fill in their authorized account mnemonic", - "Fetch store-data.ts; substitute FILE_PATH and replace DEV_PHRASE with dotenv SEED_PHRASE", - "Run `npx tsx store-data.ts`; record CID, block number, and transaction index from output", - "Retrieve file via https://paseo-ipfs.polkadot.io/ipfs/" + "Authorize Polkadot account via Console UI faucet", + "Set up Node.js ESM project and install polkadot-api + hdkd + dotenv", + "Run npx papi add bulletin to generate chain descriptors", + "Create .env with MNEMONIC, modify store-data.ts to use process.env.MNEMONIC", + "Set FILE_PATH to the image path, run npx tsx store-data.ts", + "Receive CID for use in NFT metadata" ], - "result": "File stored on-chain; CID printed (e.g. bafk2bzace...); file accessible at the IPFS gateway URL" + "result": "File stored on-chain; CID like bafk2bzacea6wlxy... retrievable at https://paseo-ipfs.polkadot.io/ipfs/" }, { - "scenario": "Edge case: stored data nearing expiry, renewal needed", - "user_says": "My Bulletin Chain data is about to expire, how do I renew it?", + "scenario": "Edge case: stored data approaches expiry", + "user_says": "My stored data is about to expire — how do I renew it?", "actions": [ - "Confirm the user has the block number and transaction index from their original store (or last renewal) transaction", - "Provide the renewal script with substituted BLOCK_NUMBER and TX_INDEX values", - "Run the renewal script; record the new (block, index) pair from the Renewed event for future renewals" + "Retrieve the (block, index) pair from when the data was stored", + "Confirm expiration block has not yet passed (check Bulletin Chain Explorer)", + "Call TransactionStorage.renew({block, index}) using PAPI with the same authorized signer", + "Record new (block, index) from the Renewed event for future renewals" ], - "result": "Retention timer reset; data available for another ~2-week period. New (block, index) pair recorded for next renewal." + "result": "Retention timer reset; original CID remains valid for another full retention period" } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", - "files": [ - { - "path": "store-data.ts", - "description": "PAPI TypeScript script that connects to the Bulletin Chain, reads a file, submits TransactionStorage.store, and decodes the Stored event to print the CID and block reference" - } - ] - }, - "generated": "2026-05-15T15:30:00Z", - "content_hash": "sha256:c38c3743e7262e82cc82da3bafff3d1c68a5bf29048d98bb8634e94c1a0eb8b2" + ] }, { "id": "deploy-uniswap-v2-core-pvm", - "title": "Deploying Uniswap V2 Core on Polkadot", - "description": "Clones polkavm-hardhat-examples, compiles Uniswap V2 Core contracts (UniswapV2ERC20, UniswapV2Factory, UniswapV2Pair, Solidity 0.8.28) using the @parity/hardhat-polkadot plugin and revive compiler targeting PolkaVM (PVM), runs tests against a local revive-dev-node, and deploys to Polkadot Hub TestNet via the passetHub network. Use when you need AMM-based DEX infrastructure on the PVM execution path. Key capabilities: @parity/hardhat-polkadot plugin config; dotenv-based private key management; UniswapV2Pair deployed via direct JsonRpcProvider to handle contract size. Trigger phrases: 'deploy Uniswap V2 PVM', 'AMM on Polkadot PolkaVM', 'UniswapV2Factory PVM Polkadot'. Requires PAS testnet tokens from https://faucet.polkadot.io/.", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", + "description": "Clones the polkavm-hardhat-examples repo, compiles Uniswap V2 Factory and Pair contracts to the Polkadot Virtual Machine (PVM) using the Hardhat Polkadot plugin and resolc compiler, and deploys to Polkadot Hub TestNet. Use when deploying a DEX AMM factory on Polkadot Hub with PVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 PVM', 'UniswapV2Factory PVM Polkadot', 'deploy AMM Polkadot Hub PVM', 'polkavm-hardhat-examples uniswap'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8916,44 +8898,43 @@ "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", "prerequisites": { "runtime": [ - "Node.js v16+", - "npm" + "Node.js v16 or later", + "npm", + "Git" ], "network": [ - "Local revive-dev-node for testing (http://127.0.0.1:8545) — see the Local Development Node guide at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", - "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io" + "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "PAS testnet tokens from https://faucet.polkadot.io/ for the account set in AH_PRIV_KEY" + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" ], "wallet": [ - "An EVM-compatible account (MetaMask or similar) with the private key exported for AH_PRIV_KEY" + "An EVM private key (0x-prefixed) for a funded testnet account (AH_PRIV_KEY)" ] }, "env_vars": [ { - "name": "LOCAL_PRIV_KEY", - "description": "Private key for the local revive-dev-node account. A default dev key (0x5fb92d6e...) is pre-configured in hardhat.config.js; set this only if you need a different local account.", - "required": false, - "secret": true + "name": "AH_PRIV_KEY", + "description": "0x-prefixed EVM private key for the account deploying to Polkadot Hub TestNet (passetHub network). Must be funded with testnet PAS. Do NOT ask user to paste key in chat — they must edit .env directly.", + "required": true }, { - "name": "AH_PRIV_KEY", - "description": "Private key of the account used to deploy contracts to Polkadot Hub TestNet (passetHub network). Export from MetaMask or another EVM wallet. This account must hold PAS testnet tokens.", - "required": true, - "secret": true + "name": "LOCAL_PRIV_KEY", + "description": "0x-prefixed EVM private key for local testing. For local-only use, Alice's dev key (0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133) is the default in the config. Not required for TestNet deployment.", + "required": false } ], + "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", "steps": [ { "order": 1, - "action": "Clone the repository and navigate to the project", + "action": "Clone the polkavm-hardhat-examples repository", "working_directory": ".", "commands": [ "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", "cd polkavm-hardhat-examples/uniswap-v2-polkadot" ], - "description": "Clone polkavm-hardhat-examples (default branch: master). Navigate to the `uniswap-v2-polkadot` subdirectory which contains the Hardhat project." + "description": "Clone the repository and navigate to the Uniswap V2 project. The hardhat.config.js already uses dotenv (require('dotenv').config()) and process.env variables — no Hardhat vars conversion needed." }, { "order": 2, @@ -8962,14 +8943,16 @@ "commands": [ "npm install" ], - "description": "Install all project dependencies including `@parity/hardhat-polkadot` (PolkaVM Hardhat plugin) and the revive Solidity compiler." + "description": "Install all project dependencies including @parity/hardhat-polkadot and the resolc PVM compiler." }, { "order": 3, - "action": "Create the .env file with private keys", + "action": "Create .env with private key placeholders", "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "reference_file": ".env.example", - "description": "Fetch the reference file and save it as `.env` (note: save as `.env`, not `.env.example`). The file already contains the correct variable names:\n\n```text\nLOCAL_PRIV_KEY=\"\"\nAH_PRIV_KEY=\"\"\n```\n\nStop and ask the user to edit the `.env` file directly. Do NOT ask for the private key in chat. The user should:\n- Set `AH_PRIV_KEY` to the private key of an account holding PAS testnet tokens on Polkadot Hub TestNet.\n- Optionally set `LOCAL_PRIV_KEY` for a custom local dev account (a default dev key is already in hardhat.config.js if this is left empty).\nWait for confirmation before proceeding." + "commands": [ + "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env" + ], + "description": "Create .env with empty placeholders. The .gitignore already includes .env. Stop here and ask the user to edit .env directly — fill in AH_PRIV_KEY with the 0x-prefixed private key of the account that will deploy to Polkadot Hub TestNet. Do NOT ask for the key in chat. For LOCAL_PRIV_KEY, Alice's dev key is the default in hardhat.config.js for local testing. Wait for confirmation before proceeding." }, { "order": 4, @@ -8978,139 +8961,95 @@ "commands": [ "npx hardhat compile" ], - "expected_output": "Compiled N Solidity files successfully (solc 0.8.28)", - "description": "Compile the Uniswap V2 contracts using the revive compiler configured via `@parity/hardhat-polkadot`. Compiled PVM artifacts (ABI and PolkaVM bytecode) are written to the `artifacts` directory." + "expected_output": "Compiled 1 Solidity file successfully", + "description": "Compile the Uniswap V2 contracts to PVM bytecode using the Hardhat Polkadot plugin and the resolc compiler. Compiled artifacts (ABI and PVM bytecode) appear in the artifacts/ directory." }, { "order": 5, - "action": "Start the local revive-dev-node", - "working_directory": ".", - "description": "Start the local development node as described at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/. The node must be accessible at http://127.0.0.1:8545 before running tests." - }, - { - "order": 6, - "action": "Run the test suite against the local node", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npx hardhat test --network localNode" - ], - "expected_output": "passing (N tests covering UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair)", - "description": "Run all tests against the local revive-dev-node. Tests cover ERC-20 token operations, factory pair creation, and pair AMM swap operations. If tests time out, verify the local revive-dev-node is running at http://127.0.0.1:8545." - }, - { - "order": 7, - "action": "Review the deployment script", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "reference_file": "scripts/deploy.js", - "description": "Review `scripts/deploy.js`. It deploys three contracts in sequence: UniswapV2ERC20, UniswapV2Factory (with deployer as fee recipient), and UniswapV2Pair. UniswapV2Pair is deployed using `JsonRpcProvider` with a direct wallet (bypassing Hardhat's default contract size checks which would reject the large PVM bytecode). Output includes the address of all three contracts." - }, - { - "order": 8, "action": "Deploy to Polkadot Hub TestNet", "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", "commands": [ "npx hardhat run scripts/deploy.js --network passetHub" ], - "expected_output": "Deploying contracts using 0x...\nDeploying UniswapV2ERC20...\nETH deployed to : 0x...\nDeploying UniswapV2Factory...\nFactory deployed to : 0x...\nDeploying UniswapV2Pair...\nPair deployed to : 0x...", - "description": "Deploy to Polkadot Hub TestNet. The script reads `AH_PRIV_KEY` from `.env` via dotenv. Save all three deployed addresses — they are needed for any subsequent AMM interactions." + "expected_output": "Factory deployed to : 0x...", + "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet via the passetHub network config. The script outputs the deployed addresses — save them, especially the Factory address, which is needed for creating liquidity pairs.\n\nNote: The documentation shows `--network polkadotHubTestNet` but the actual network name in hardhat.config.js is `passetHub`." } ], - "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── artifacts/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, "error_patterns": [ { - "pattern": "Error: AH_PRIV_KEY is undefined / wallet private key not found", - "cause": ".env file not created or AH_PRIV_KEY not set.", - "resolution": "Create the .env file in the uniswap-v2-polkadot directory (step 3). Set AH_PRIV_KEY to the private key of your PAS-funded account. The hardhat.config.js loads dotenv automatically." + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account not funded with testnet PAS, or network base fee (1000 gwei on Polkadot Hub TestNet) exceeds the transaction gas price.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. If gas issues persist, add gasPrice: 5000000000000 (5000 gwei) to the passetHub network block in hardhat.config.js." }, { - "pattern": "ECONNREFUSED 127.0.0.1:8545", - "cause": "Local revive-dev-node is not running.", - "resolution": "Start the local development node before running tests. See https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/." + "pattern": "Error: AH_PRIV_KEY is undefined / invalid private key", + "cause": ".env file not populated or not loaded by dotenv.", + "resolution": "Verify .env exists in uniswap-v2-polkadot/ and contains AH_PRIV_KEY=0x. The config uses require('dotenv').config() automatically." }, { - "pattern": "insufficient funds / Error: insufficient balance", - "cause": "The AH_PRIV_KEY account has no PAS tokens on Polkadot Hub TestNet.", - "resolution": "Obtain PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account address." + "pattern": "Error: network passetHub not found / unknown network", + "cause": "Using wrong --network flag; documentation shows polkadotHubTestNet but the config defines passetHub.", + "resolution": "Use --network passetHub instead of --network polkadotHubTestNet." }, { - "pattern": "Error: contract size too large / contract size exceeds limit", - "cause": "UniswapV2Pair PVM bytecode exceeds the deployment size check.", - "resolution": "The deploy.js script deploys UniswapV2Pair via direct JsonRpcProvider to bypass size checks. Ensure you are using the unmodified deploy.js from the reference repo." + "pattern": "Compilation error: resolc not found / PVM compiler unavailable", + "cause": "The resolc compiler is not installed or not accessible.", + "resolution": "Run npm install to reinstall @parity/hardhat-polkadot and its bundled resolc compiler. Verify package.json includes the plugin." } ], "supplementary_context": { - "description": "Load these pages for background on the PolkaVM execution path, the @parity/hardhat-polkadot plugin setup, and the local development node.", + "description": "Load when the user asks about PVM vs EVM differences, local development setup, or testing the deployed contracts.", "pages": [ - { - "slug": "smart-contracts-dev-environments-hardhat-polkadot", - "title": "Use Hardhat with Polkadot Hub (PolkaVM)", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot/", - "relevance": "Full guide to configuring @parity/hardhat-polkadot for PolkaVM contract compilation and deployment." - }, { "slug": "smart-contracts-dev-environments-local-dev-node", "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", - "relevance": "How to install and start the local revive-dev-node required for testing in step 5." + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local Polkadot development node and ETH-RPC adapter to test the Uniswap V2 PVM contracts locally before deploying to TestNet." }, { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet/", - "relevance": "How to obtain PAS testnet tokens for the AH_PRIV_KEY deployer account." + "slug": "smart-contracts-dev-environments-hardhat-polkadot", + "title": "Set Up Hardhat with the Polkadot Plugin", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot.md", + "relevance": "Configure the @parity/hardhat-polkadot plugin and resolc compiler for PVM smart contract development." } ] }, "examples": [ { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet via PVM", - "user_says": "Deploy Uniswap V2 Core contracts on Polkadot Hub using PolkaVM", + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using PVM", + "user_says": "Deploy Uniswap V2 on Polkadot Hub using PVM", "actions": [ - "Clone polkavm-hardhat-examples; navigate to uniswap-v2-polkadot/", + "Clone polkavm-hardhat-examples and cd uniswap-v2-polkadot", "Run npm install", - "Fetch .env.example, save as .env; ask user to fill in AH_PRIV_KEY", - "Run npx hardhat compile", - "Start local revive-dev-node; run npx hardhat test --network localNode", - "Review scripts/deploy.js", - "Run npx hardhat run scripts/deploy.js --network passetHub; save contract addresses" + "Create .env with AH_PRIV_KEY for testnet deployment", + "Run npx hardhat compile to compile to PVM bytecode", + "Run npx hardhat run scripts/deploy.js --network passetHub", + "Save deployed Factory and Pair addresses" ], - "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet; three contract addresses printed" + "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet with PVM bytecode" }, { - "scenario": "Edge case: user wants to test locally only without TestNet deployment", - "user_says": "Test the Uniswap V2 PVM contracts locally, skip TestNet", + "scenario": "Edge case: deployment fails with insufficient funds", + "user_says": "My Uniswap V2 deployment is failing with insufficient funds", "actions": [ - "Clone polkavm-hardhat-examples; navigate to uniswap-v2-polkadot/", - "Run npm install", - "Create .env with LOCAL_PRIV_KEY (AH_PRIV_KEY not needed for local testing)", - "Start local revive-dev-node", - "Run npx hardhat test --network localNode" + "Confirm account has testnet PAS at https://faucet.polkadot.io/", + "If gas price issue, add gasPrice: 5000000000000 to passetHub config in hardhat.config.js", + "Retry deployment with npx hardhat run scripts/deploy.js --network passetHub" ], - "result": "All tests pass against the local development node; no TestNet deployment and no PAS tokens needed" + "result": "Deployment succeeds after funding the account and optionally raising gas price" } - ], - "reference_code": { - "repo": "polkadot-developers/polkavm-hardhat-examples", - "branch": "master", - "base_path": "uniswap-v2-polkadot", - "files": [ - { - "path": ".env.example", - "description": "Template .env file with LOCAL_PRIV_KEY and AH_PRIV_KEY variable placeholders" - }, - { - "path": "scripts/deploy.js", - "description": "Deploys UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair; uses JsonRpcProvider directly for UniswapV2Pair to bypass contract size restrictions" - } - ] - }, - "generated": "2026-05-15T15:30:00Z", - "content_hash": "sha256:f7cab3d8a1306847479141cfc85e4a7c08f4c2d8a83555788c8b9a82cec77873" + ] }, { "id": "deploy-uniswap-v2-core-evm", - "title": "Uniswap V2 Core with EVM on Polkadot", - "description": "Clones revm-hardhat-examples, compiles unmodified Uniswap V2 Core contracts (UniswapV2Factory, UniswapV2Pair, UniswapV2ERC20, Solidity 0.5.16) using standard Hardhat and TypeScript on the EVM path, runs 28 tests against a local development node, and deploys UniswapV2Factory plus two test ERC-20 tokens with a trading pair to Polkadot Hub TestNet via REVM. Use when you need AMM DEX infrastructure on the standard EVM execution path without PVM or revive. Key capabilities: vanilla Hardhat TypeScript config; dotenv-based private key management (converted from Hardhat vars); Hardhat Ignition module for Factory-only deployment. Trigger phrases: 'deploy Uniswap V2 Core EVM', 'AMM on Polkadot EVM', 'UniswapV2Factory Polkadot EVM'. Requires PAS testnet tokens.", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Core contracts (Factory and Pair) with standard Hardhat and TypeScript, converts Hardhat vars to dotenv for agent-compatible key management, and deploys to Polkadot Hub TestNet via the EVM execution path. Use when deploying a DEX AMM factory on Polkadot Hub using standard EVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 EVM Polkadot', 'UniswapV2Factory EVM Polkadot Hub', 'deploy AMM Polkadot REVM'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -9122,61 +9061,66 @@ "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", "prerequisites": { "runtime": [ - "Node.js v22+", - "npm" + "Node.js v22 or later", + "npm", + "Git" ], "network": [ - "Local development node at http://127.0.0.1:8545 for testing — see https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account" + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" ], "wallet": [ - "An EVM-compatible account (MetaMask or similar) with the private key exported for PRIVATE_KEY in .env" + "An EVM private key (0x-prefixed) for a funded testnet account" ] }, "env_vars": [ { - "name": "PRIVATE_KEY", - "description": "Private key of the deployer account on Polkadot Hub TestNet. Required only for TestNet deployment (step 8). Local testing works without it.", - "required": true, - "secret": true + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true } ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", "steps": [ { "order": 1, - "action": "Clone the repository and navigate to the project", + "action": "Clone the revm-hardhat-examples repository", "working_directory": ".", "commands": [ "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples/uniswap-v2-core-hardhat" + "cd revm-hardhat-examples", + "git checkout b0a8627059a9d9cb759682310219557550186bc4", + "cd uniswap-v2-core-hardhat" ], - "description": "Clone revm-hardhat-examples (default branch: master) and navigate to the `uniswap-v2-core-hardhat` subdirectory." + "description": "Clone the repository, check out the pinned commit (tested configuration), and navigate to the Uniswap V2 Core project." }, { "order": 2, "action": "Install dependencies", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", "commands": [ - "npm install dotenv", - "npm install" + "npm install", + "npm install dotenv" ], - "description": "Install dotenv for private key loading, then install all other project dependencies including @nomicfoundation/hardhat-toolbox. Run `npm install dotenv` first to ensure dotenv is available." + "description": "Install project dependencies, then additionally install dotenv. dotenv is required to replace Hardhat's interactive vars system with a .env-based private key approach usable in agent shells." }, { "order": 3, - "action": "Fetch hardhat.config.ts and apply dotenv substitution", + "action": "Create .env with private key placeholder", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "reference_file": "hardhat.config.ts", - "description": "Fetch the reference file and save it as `hardhat.config.ts`. Then apply these substitutions to convert from Hardhat vars to dotenv (required — Hardhat's interactive `vars.get()` cannot be used in agent sessions):\n\n1. Replace the import line:\n```typescript\nimport { HardhatUserConfig, vars } from \"hardhat/config\";\n```\nwith:\n```typescript\nimport { HardhatUserConfig } from \"hardhat/config\";\nimport \"dotenv/config\";\n```\n\n2. Replace the `accounts` field in the `polkadotTestnet` network:\n```typescript\naccounts: vars.has(\"TESTNET_PRIVATE_KEY\")\n ? [vars.get(\"TESTNET_PRIVATE_KEY\")]\n : [],\n```\nwith:\n```typescript\naccounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],\n```\n\nAll other config fields (Solidity version 0.5.16 with optimizer, localNode URL, mocha timeout 120000) remain unchanged." + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key for the testnet deployer account. Do NOT ask for the key in chat. Wait for confirmation before proceeding." }, { "order": 4, - "action": "Create the .env file", + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "description": "Create a file named `.env` in the `uniswap-v2-core-hardhat` directory with the following content:\n\n```text\nPRIVATE_KEY=\"INSERT_PRIVATE_KEY\"\n```\n\nAlso create `.gitignore` (or append to the existing one) with `.env` to prevent committing the key.\n\nStop and ask the user to edit the `.env` file directly, replacing `INSERT_PRIVATE_KEY` with their Polkadot Hub TestNet account private key (0x-prefixed). Do NOT ask for the key in chat. Wait for confirmation before proceeding. (This step is only required for TestNet deployment; local testing works without a private key.)" + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee) to prevent 'Priority is too low' errors.\n5. Confirm `ignition.requiredConfirmations` is 1 (not 0) — zero-confirmation only works on local dev nodes with instant finality.\nSave the file." }, { "order": 5, @@ -9185,131 +9129,97 @@ "commands": [ "npx hardhat compile" ], - "expected_output": "Compiled N Solidity files successfully (solc 0.5.16)", - "description": "Compile the Uniswap V2 Core contracts. The config specifies Solidity 0.5.16 with optimizer enabled (200 runs). Artifacts (ABI and bytecode) are written to the `artifacts` directory." + "expected_output": "Compiled 3 Solidity files successfully", + "description": "Compile UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair (Solidity 0.5.16). Artifacts (ABI and EVM bytecode) appear in the artifacts/ directory." }, { "order": 6, - "action": "Start the local development node", - "working_directory": ".", - "description": "Start the local development node as described at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/. The node must be accessible at http://127.0.0.1:8545 before running tests." - }, - { - "order": 7, - "action": "Run the test suite against the local node", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npx hardhat test --network localNode" - ], - "expected_output": "28 passing", - "description": "Run all 28 tests across UniswapV2ERC20.test.ts, UniswapV2Factory.test.ts, and UniswapV2Pair.test.ts. Tests use a 120-second Mocha timeout for Polkadot block times. If tests time out, verify the local development node is running at http://127.0.0.1:8545." - }, - { - "order": 8, - "action": "Review the deployment script and deploy to TestNet", + "action": "Deploy to Polkadot Hub TestNet", "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "reference_file": "scripts/deploy.ts", "commands": [ "npx hardhat run scripts/deploy.ts --network polkadotTestnet" ], - "expected_output": "Deploying contracts with account: 0x...\nUniswapV2Factory deployed to: 0x...\nTokenA deployed to: 0x...\nTokenB deployed to: 0x...\nPair created at: 0x...", - "description": "Review `scripts/deploy.ts` before running. The script deploys UniswapV2Factory (with deployer as fee recipient), two test ERC-20 tokens (10,000 tokens each at 18 decimals), and calls `factory.createPair()` to create a trading pair. The deployer address and all four contract addresses are printed. Save the output — the Factory and Pair addresses are needed for any periphery or interaction work." + "expected_output": "UniswapV2Factory deployed to: 0x...", + "description": "Deploy UniswapV2Factory, two test ERC-20 tokens, and create a trading pair. The script outputs all deployed addresses. Save the Factory address — needed for creating additional pairs and for the Periphery (Router) deployment." } ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.ts\n ├── package.json\n └── tsconfig.json", + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, "error_patterns": [ { - "pattern": "Error: process.env.PRIVATE_KEY is undefined / no accounts configured", - "cause": ".env file not created or PRIVATE_KEY not set.", - "resolution": "Create the .env file in the uniswap-v2-core-hardhat directory (step 4) and set PRIVATE_KEY. Verify that the dotenv substitution in hardhat.config.ts was applied correctly (step 3)." + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." }, { - "pattern": "Error: timeout exceeded / test timeout of 120000ms exceeded", - "cause": "Local development node is not running or not reachable at http://127.0.0.1:8545.", - "resolution": "Start the local development node before running tests. See https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/." + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses vars.get() instead of dotenv.", + "resolution": "Ensure `import 'dotenv/config';` is the first line of hardhat.config.ts and replace vars.get('TESTNET_PRIVATE_KEY') with process.env.TESTNET_PRIVATE_KEY." }, { - "pattern": "Error: insufficient funds for gas", - "cause": "The PRIVATE_KEY account has no PAS tokens on Polkadot Hub TestNet.", - "resolution": "Obtain PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account." + "pattern": "TESTNET_PRIVATE_KEY is undefined", + "cause": ".env not populated or not in the correct directory.", + "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ and contains TESTNET_PRIVATE_KEY=0x." }, { - "pattern": "SyntaxError or TS compilation error after dotenv substitution", - "cause": "The vars.get() substitution in step 3 was incomplete or introduced a syntax error.", - "resolution": "Re-apply the substitutions from step 3 carefully. The final hardhat.config.ts polkadotTestnet accounts entry should read: `accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []`." + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of deployment — missing gasPrice or requiredConfirmations: 0.", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy." } ], "supplementary_context": { - "description": "Load these pages for background on the EVM execution path, Hardhat configuration, and TestNet token acquisition.", + "description": "Load when the user asks about the V2 Periphery Router deployment, local testing, or Hardhat configuration for Polkadot.", "pages": [ { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat/", - "relevance": "Full guide to Hardhat configuration for Polkadot Hub EVM, including network settings and TypeScript setup." + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", + "title": "Uniswap V2 Periphery with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "relevance": "Next step: deploy the Uniswap V2 Router (WETH9, Router02) on top of the deployed V2 Core." }, { "slug": "smart-contracts-dev-environments-local-dev-node", "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", - "relevance": "How to install and run the local development node required for test execution in step 6." - }, - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", - "title": "Uniswap V2 Periphery with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2/", - "relevance": "Next step after Core deployment — deploys WETH9, UniswapV2Factory, and Router02 for full DEX liquidity and swap functionality." + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local Polkadot development node to run the test suite against a local node before TestNet deployment." } ] }, "examples": [ { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet via EVM", - "user_says": "Deploy Uniswap V2 Core on Polkadot Hub using the EVM execution path", + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using EVM", + "user_says": "Deploy Uniswap V2 on Polkadot Hub using EVM Hardhat", "actions": [ - "Clone revm-hardhat-examples; navigate to uniswap-v2-core-hardhat/", - "Run npm install dotenv && npm install", - "Fetch hardhat.config.ts; apply dotenv substitution (replace vars import and vars.get() with process.env.PRIVATE_KEY)", - "Create .env; ask user to fill in PRIVATE_KEY", + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", "Run npx hardhat compile", - "Start local development node; run npx hardhat test --network localNode (28 tests pass)", - "Review scripts/deploy.ts; run npx hardhat run scripts/deploy.ts --network polkadotTestnet; save all addresses" + "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", + "Save Factory, TokenA, TokenB, and Pair addresses" ], - "result": "UniswapV2Factory, TokenA, TokenB, and trading Pair deployed to Polkadot Hub TestNet; addresses printed" + "result": "UniswapV2Factory, two test ERC-20 tokens, and a trading pair deployed to Polkadot Hub TestNet via EVM" }, { - "scenario": "Edge case: deploy only the Factory using Hardhat Ignition", - "user_says": "I just need the UniswapV2Factory deployed, no test tokens", + "scenario": "Edge case: deployment stuck with IGN401 Transaction dropped", + "user_says": "Ignition says my V2 Core deployment transaction was dropped", "actions": [ - "Ensure the hardhat.config.ts dotenv substitution has been applied (step 3)", - "Run: npx hardhat ignition deploy ./ignition/modules/UniswapV2Factory.ts --network polkadotTestnet", - "Confirm the network name when prompted" + "Verify gasPrice: 5000000000000 is set in polkadotTestnet network config", + "Delete ignition/deployments/ directory", + "Confirm TESTNET_PRIVATE_KEY account still has PAS balance", + "Redeploy with npx hardhat run scripts/deploy.ts --network polkadotTestnet" ], - "result": "Only UniswapV2Factory deployed; factory address printed" + "result": "Clean deployment after resolving gas configuration and clearing stale Ignition state" } - ], - "reference_code": { - "repo": "polkadot-developers/revm-hardhat-examples", - "branch": "master", - "base_path": "uniswap-v2-core-hardhat", - "files": [ - { - "path": "hardhat.config.ts", - "description": "Hardhat config with Solidity 0.5.16, localNode and polkadotTestnet networks, Hardhat vars for TESTNET_PRIVATE_KEY (must be converted to dotenv in step 3)" - }, - { - "path": "scripts/deploy.ts", - "description": "Deploys UniswapV2Factory, two ERC-20 test tokens with 10,000 supply each, and creates a trading pair; prints Factory, TokenA, TokenB, and Pair addresses" - } - ] - }, - "generated": "2026-05-15T15:30:00Z", - "content_hash": "sha256:af77b281cf97b9d4924e398dd8b0f9b48c741bd4879052831c92612d2628d831" + ] }, { "id": "deploy-uniswap-v2-periphery-evm", - "title": "Uniswap V2 Periphery with EVM on Polkadot", - "description": "Clones revm-hardhat-examples, compiles unmodified Uniswap V2 Periphery contracts (WETH9, UniswapV2Router01, UniswapV2Router02, multi-compiler Solidity 0.5.16 + 0.6.6) using standard Hardhat and TypeScript on the EVM path, runs 50 tests against a local development node, and deploys WETH9, UniswapV2Factory, and UniswapV2Router02 to Polkadot Hub TestNet via Hardhat Ignition. Use when you need the full Router layer for liquidity management and token swaps on top of V2 Core. Key capabilities: multi-compiler Hardhat config; local '@uniswap/v2-core' file dependency resolved automatically; dotenv-based private key management (converted from Hardhat vars); Ignition two-batch deployment. Trigger phrases: 'deploy Uniswap V2 Periphery', 'deploy UniswapV2Router02 Polkadot', 'AMM router EVM'. Requires PAS testnet tokens.", + "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Periphery contracts (WETH9, Router02) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when adding the Router layer (liquidity, swaps, slippage protection) on top of Uniswap V2 Core. V2 Core Solidity dependency is a local file reference — no pre-deployed core required. Trigger phrases: 'deploy Uniswap V2 Router EVM Polkadot', 'UniswapV2Router02 Polkadot Hub', 'Uniswap periphery Polkadot REVM'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -9321,61 +9231,66 @@ "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", "prerequisites": { "runtime": [ - "Node.js v22+", - "npm" + "Node.js v22 or later", + "npm", + "Git" ], "network": [ - "Local development node at http://127.0.0.1:8545 for testing — see https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "PAS testnet tokens from https://faucet.polkadot.io/ for the deployer account" + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" ], "wallet": [ - "An EVM-compatible account (MetaMask or similar) with the private key exported for PRIVATE_KEY in .env" + "An EVM private key (0x-prefixed) for a funded testnet account" ] }, "env_vars": [ { - "name": "PRIVATE_KEY", - "description": "Private key of the deployer account on Polkadot Hub TestNet. Required only for TestNet deployment. Local testing works without it.", - "required": true, - "secret": true + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true } ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", "steps": [ { "order": 1, - "action": "Clone the repository and navigate to the project", + "action": "Clone the revm-hardhat-examples repository", "working_directory": ".", "commands": [ "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples/uniswap-v2-periphery-hardhat" + "cd revm-hardhat-examples", + "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", + "cd uniswap-v2-periphery-hardhat" ], - "description": "Clone revm-hardhat-examples (default branch: master) and navigate to the `uniswap-v2-periphery-hardhat` subdirectory. Both `uniswap-v2-core-hardhat/` and `uniswap-v2-periphery-hardhat/` must be present since the Periphery project depends on V2 Core via a local file reference: `'@uniswap/v2-core': 'file:../uniswap-v2-core-hardhat'`." + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V2 Periphery project. The sibling directory uniswap-v2-core-hardhat/ (at the same commit) is automatically used for the local V2 Core Solidity dependency." }, { "order": 2, "action": "Install dependencies", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", "commands": [ - "npm install dotenv", - "npm install" + "npm install", + "npm install dotenv" ], - "description": "Install dotenv for private key loading, then install all other project dependencies. npm resolves the local `@uniswap/v2-core` reference from the sibling `uniswap-v2-core-hardhat/` directory automatically — no separate Core installation needed." + "description": "Install project dependencies including the local V2 Core reference (resolved automatically from ../uniswap-v2-core-hardhat/). Then install dotenv to replace Hardhat's interactive vars system." }, { "order": 3, - "action": "Fetch hardhat.config.ts and apply dotenv substitution", + "action": "Create .env with private key placeholder", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "reference_file": "hardhat.config.ts", - "description": "Fetch the reference file and save it as `hardhat.config.ts`. Apply these substitutions to convert from Hardhat vars to dotenv (required — Hardhat's interactive `vars.get()` cannot be used in agent sessions):\n\n1. Replace the import line:\n```typescript\nimport { HardhatUserConfig, vars } from \"hardhat/config\";\n```\nwith:\n```typescript\nimport { HardhatUserConfig } from \"hardhat/config\";\nimport \"dotenv/config\";\n```\n\n2. Replace the `accounts` field in the `polkadotTestnet` network:\n```typescript\naccounts: vars.has(\"TESTNET_PRIVATE_KEY\")\n ? [vars.get(\"TESTNET_PRIVATE_KEY\")]\n : [],\n```\nwith:\n```typescript\naccounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],\n```\n\nAll other config fields (multi-compiler with Solidity 0.5.16 + 0.6.6 both with optimizer enabled, hardhat network with `allowUnlimitedContractSize: true`, localNode URL, mocha timeout 120000) remain unchanged." + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." }, { "order": 4, - "action": "Create the .env file", + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "description": "Create a file named `.env` in the `uniswap-v2-periphery-hardhat` directory with the following content:\n\n```text\nPRIVATE_KEY=\"INSERT_PRIVATE_KEY\"\n```\n\nAlso create `.gitignore` (or append to the existing one) with `.env` to prevent committing the key.\n\nStop and ask the user to edit the `.env` file directly, replacing `INSERT_PRIVATE_KEY` with their Polkadot Hub TestNet account private key (0x-prefixed). Do NOT ask for the key in chat. Wait for confirmation before proceeding. (Skip this step if only testing locally.)" + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee).\n5. Confirm `ignition.requiredConfirmations` is 1.\nSave the file." }, { "order": 5, @@ -9384,133 +9299,262 @@ "commands": [ "npx hardhat compile" ], - "expected_output": "Compiled N Solidity files successfully", - "description": "Compile the Periphery contracts with the multi-compiler setup: Solidity 0.5.16 for V2 Core dependency contracts and 0.6.6 for Router contracts. Artifacts are written to the `artifacts` directory." + "expected_output": "Compiled 5 Solidity files successfully", + "description": "Compile the Uniswap V2 Periphery contracts using a multi-compiler setup (Solidity 0.5.16 for V2 Core dependency and 0.6.6 for Router contracts). Artifacts appear in the artifacts/ directory." }, { "order": 6, - "action": "Start the local development node", - "working_directory": ".", - "description": "Start the local development node as described at https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/. The node must be accessible at http://127.0.0.1:8545 before running tests. The hardhat network config has `allowUnlimitedContractSize: true` to accommodate Router bytecode size during local testing." - }, - { - "order": 7, - "action": "Run the test suite against the local node", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npx hardhat test --network localNode" - ], - "expected_output": "50 passing", - "description": "Run all 50 tests: 38 in UniswapV2Router01.test.ts (liquidity add/remove, token swaps, ETH swaps, permit-based removal) and 12 in UniswapV2Router02.test.ts (fee-on-transfer token support). Tests use a 120-second Mocha timeout. If tests time out, verify the local development node is running at http://127.0.0.1:8545." - }, - { - "order": 8, - "action": "Review the Ignition deployment module", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "reference_file": "ignition/modules/UniswapV2Router02.ts", - "description": "Review the Ignition module at `ignition/modules/UniswapV2Router02.ts`. It deploys three contracts: WETH9 and UniswapV2Factory in parallel (first batch, both independent), then UniswapV2Router02 with the factory and WETH addresses as constructor arguments (second batch). The deployer account (index 0, from `m.getAccount(0)`) is set as the Factory fee recipient." - }, - { - "order": 9, "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", "commands": [ "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" ], - "expected_output": "UniswapV2Router02Module#WETH9 - 0x...\nUniswapV2Router02Module#UniswapV2Factory - 0x...\nUniswapV2Router02Module#UniswapV2Router02 - 0x...", - "description": "Deploy to Polkadot Hub TestNet (RPC: https://services.polkadothub-rpc.com/testnet). When Ignition prompts to confirm the network, enter the exact network name `polkadotTestnet` and verify the chain ID is 420420417. Ignition deploys in two batches. Save the three addresses from the output (WETH9, UniswapV2Factory, UniswapV2Router02) — they are the entry point for all liquidity and swap operations." + "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", + "interactive": true, + "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 using Hardhat Ignition. Ignition will prompt to confirm the target network name and chain ID — delegate this confirmation to the user. After confirmation, contracts are deployed in two batches (Factory+WETH9 in parallel, then Router02). Save all three deployed addresses." } ], - "project_structure": "revm-hardhat-examples/\n├── uniswap-v2-core-hardhat/\n│ └── contracts/ (local dependency, must be present)\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.ts\n ├── package.json\n └── tsconfig.json", + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, "error_patterns": [ { - "pattern": "Error: Cannot find module '@uniswap/v2-core'", - "cause": "The local V2 Core dependency is not resolved. The sibling `uniswap-v2-core-hardhat/` directory must exist.", - "resolution": "Ensure you cloned the full revm-hardhat-examples repo (not just the periphery subdirectory) and ran `npm install` from within `uniswap-v2-periphery-hardhat/`. Do not clone periphery in isolation." + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." }, { - "pattern": "Error: process.env.PRIVATE_KEY is undefined / no accounts configured", - "cause": ".env file not created or PRIVATE_KEY not set, or dotenv substitution in step 3 was not applied.", - "resolution": "Apply the substitution in step 3 to hardhat.config.ts, then create .env with PRIVATE_KEY in step 4. Verify `import \"dotenv/config\"` appears at the top of hardhat.config.ts." + "pattern": "Error: Cannot find module '@uniswap/v2-core' / ENOENT uniswap-v2-core-hardhat", + "cause": "The local V2 Core Solidity dependency (../../uniswap-v2-core-hardhat) is not accessible. Likely cloning only the periphery subdirectory or checking out different commits for each.", + "resolution": "Ensure the full revm-hardhat-examples repo is cloned and both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ are at the same commit. Run npm install from uniswap-v2-periphery-hardhat/." }, { - "pattern": "Error: timeout exceeded / test timeout of 120000ms exceeded", - "cause": "Local development node is not running or not reachable at http://127.0.0.1:8545.", - "resolution": "Start the local development node before running tests. See https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/." + "pattern": "vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses Hardhat vars.get() instead of dotenv.", + "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." }, { - "pattern": "Ignition deploy fails: confirmation prompt not answered correctly", - "cause": "Ignition requires confirming the network name when deploying to a non-default network.", - "resolution": "When prompted, type the exact network name `polkadotTestnet` and verify the chain ID is 420420417 (Polkadot Hub TestNet)." + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost transaction state — missing gasPrice or requiredConfirmations: 0.", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1, then redeploy." } ], "supplementary_context": { - "description": "Load these pages for background on V2 Core (prerequisite), Hardhat multi-compiler config, and local development node setup.", + "description": "Load when the user asks about the V2 Core prerequisite, testing the Router, or V3 upgrades.", "pages": [ { "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", "title": "Uniswap V2 Core with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2/", - "relevance": "Prerequisite context: the Periphery Router depends on V2 Core contracts. The Core guide explains Factory and Pair deployment." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat/", - "relevance": "Full Hardhat configuration guide for Polkadot Hub EVM including multi-compiler setup." + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", + "relevance": "V2 Core tutorial covering Factory and Pair deployment — the Periphery Router builds on top of the same Core contracts included as a local dependency." }, { "slug": "smart-contracts-dev-environments-local-dev-node", "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/", - "relevance": "How to install and run the local development node required for test execution in step 6." + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local development node to run the test suite against localNode before TestNet deployment." } ] }, "examples": [ { - "scenario": "Common scenario: deploy full Uniswap V2 Periphery to Polkadot Hub TestNet", - "user_says": "Deploy the Uniswap V2 Router contracts on Polkadot Hub using EVM and Hardhat Ignition", + "scenario": "Common scenario: deploy Uniswap V2 Periphery Router to Polkadot Hub TestNet", + "user_says": "Deploy the Uniswap V2 Router on Polkadot Hub EVM", "actions": [ - "Clone revm-hardhat-examples; navigate to uniswap-v2-periphery-hardhat/", - "Run npm install dotenv && npm install (resolves local V2 Core dependency automatically)", - "Fetch hardhat.config.ts; apply dotenv substitution (replace vars with process.env.PRIVATE_KEY)", - "Create .env; ask user to fill in PRIVATE_KEY", + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", "Run npx hardhat compile", - "Start local development node; run npx hardhat test --network localNode (50 tests pass)", - "Review ignition/modules/UniswapV2Router02.ts", - "Run npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet; confirm network; save addresses" + "Run npx hardhat ignition deploy with --network polkadotTestnet, confirm when prompted", + "Save WETH9, Factory, and Router02 addresses" ], - "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet via two Ignition batches; three addresses printed" + "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet" }, { - "scenario": "Edge case: use the imperative deploy script instead of Ignition", - "user_says": "I prefer a plain deploy script over Hardhat Ignition", + "scenario": "Edge case: npm install fails with missing V2 Core module", + "user_says": "npm install is failing because it cannot find @uniswap/v2-core", "actions": [ - "Ensure the dotenv substitution in hardhat.config.ts has been applied (step 3) and .env is set", - "Run: npx hardhat run scripts/deploy.ts --network polkadotTestnet", - "The script deploys WETH9, UniswapV2Factory, UniswapV2Router02, two ERC-20 test tokens, and creates a trading pair" + "Verify the full repo is cloned (both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ must exist)", + "Ensure git checkout used the same commit for the whole repo (a871364...)", + "Re-run npm install from uniswap-v2-periphery-hardhat/ — the local file reference resolves to ../uniswap-v2-core-hardhat/" ], - "result": "All six contracts deployed sequentially; WETH, Factory, Router, TokenA, TokenB, and Pair addresses printed" + "result": "npm install succeeds and @uniswap/v2-core is resolved from the sibling directory" + } + ] + }, + { + "id": "deploy-uniswap-v3-core-evm", + "title": "Deploy Uniswap V3 Core on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Core contracts (UniswapV3Factory) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. V3 config requires bytecodeHash set to none to keep Factory under the 24KB EIP-170 contract size limit. Use when deploying a concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 EVM Polkadot', 'UniswapV3Factory Polkadot Hub', 'concentrated liquidity Polkadot REVM'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "prerequisites": { + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── NoDelegateCall.sol\n │ ├── UniswapV3Factory.sol\n │ ├── UniswapV3Pool.sol\n │ └── UniswapV3PoolDeployer.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "steps": [ + { + "order": 1, + "action": "Clone the revm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", + "cd uniswap-v3-core-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Core project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies then add dotenv to replace Hardhat's interactive vars system with .env-based private key management." + }, + { + "order": 3, + "action": "Create .env with private key placeholder", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei).\n5. Confirm `ignition.requiredConfirmations` is 1.\n6. Preserve the existing `bytecodeHash: 'none'` in the Solidity compiler settings — this is required to keep UniswapV3Factory under the EIP-170 24KB contract size limit.\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 5 Solidity files successfully", + "description": "Compile UniswapV3Factory, UniswapV3Pool, UniswapV3PoolDeployer, NoDelegateCall, and math libraries (Solidity 0.7.6). The bytecodeHash: 'none' setting in the Solidity config is critical — without it, UniswapV3Factory exceeds the EIP-170 24KB contract size limit." + }, + { + "order": 6, + "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", + "interactive": true, + "description": "Deploy UniswapV3Factory using Hardhat Ignition. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save the deployed Factory address — it is the entry point for creating V3 pools and is needed for the Periphery (SwapRouter, NonfungiblePositionManager) deployment." } ], "reference_code": { - "repo": "polkadot-developers/revm-hardhat-examples", - "branch": "master", - "base_path": "uniswap-v2-periphery-hardhat", - "files": [ + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: contract code too large / Contract creation code storage out of gas", + "cause": "bytecodeHash is not set to 'none', causing UniswapV3Factory to exceed the EIP-170 24KB limit.", + "resolution": "In hardhat.config.ts under the Solidity compiler settings, ensure metadata: { bytecodeHash: 'none' } is present. Recompile after the fix." + }, + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." + }, + { + "pattern": "vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses Hardhat vars.get().", + "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost transaction state.", + "resolution": "Delete ignition/deployments/, verify gas config (gasPrice: 5000000000000, requiredConfirmations: 1), and redeploy." + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 Periphery (SwapRouter, NFPM) deployment, local testing, or concentrated liquidity mechanics.", + "pages": [ { - "path": "hardhat.config.ts", - "description": "Hardhat config with multi-compiler (Solidity 0.5.16 + 0.6.6), hardhat/localNode/polkadotTestnet network configs, Hardhat vars for TESTNET_PRIVATE_KEY (must be converted to dotenv in step 3)" + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", + "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", + "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager on top of the deployed V3 Factory." }, { - "path": "ignition/modules/UniswapV2Router02.ts", - "description": "Hardhat Ignition module deploying WETH9 and UniswapV2Factory in parallel (batch 1), then UniswapV2Router02 with factory and WETH addresses (batch 2)" + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local development node to run the 187-test V3 suite before TestNet deployment." } ] }, - "generated": "2026-05-15T15:30:00Z", - "content_hash": "sha256:a6be4ba82c68b70d5b48c46ed3490c6f441ed9e65ab849ddb92c01259c717d1c" + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V3 Core to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V3 on Polkadot Hub EVM", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice, preserve bytecodeHash: 'none'", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy UniswapV3Factory.ts --network polkadotTestnet, confirm when prompted", + "Save deployed UniswapV3Factory address" + ], + "result": "UniswapV3Factory deployed to Polkadot Hub TestNet — entry point for creating V3 concentrated-liquidity pools" + }, + { + "scenario": "Edge case: compilation fails with contract too large", + "user_says": "Hardhat compilation fails saying UniswapV3Factory is too large", + "actions": [ + "Check hardhat.config.ts Solidity compiler settings for the existence of metadata: { bytecodeHash: 'none' }", + "If missing, add it under settings: { optimizer: {...}, metadata: { bytecodeHash: 'none' } }", + "Recompile with npx hardhat compile" + ], + "result": "Compilation succeeds after setting bytecodeHash: 'none' to exclude metadata hash and keep contract under the 24KB EIP-170 limit" + } + ] } ], "outputs": { diff --git a/skill_candidates.json b/skill_candidates.json index 48cecb508..5116dc295 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -2798,6 +2798,77 @@ "priority_score": 25 }, "built_at": "2026-05-15T15:30:00Z" + }, + { + "skill_id": "deploy-uniswap-v3-core-evm", + "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + ], + "category": "tutorial", + "priority": "high", + "status": "built", + "notes": "K1=0: test step references external Local Development Node guide; deployment workflow itself is self-contained. K4=3: revm-hardhat-examples repo with pinned commit (3ff28ae). No P3: reader clones the repo, not creates files.", + "scoring": { + "signals": [ + "P1", + "P2", + "P4", + "R2", + "C1", + "C2", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 15, + "reference": 4, + "conceptual": 8, + "not_applicable": 0 + }, + "priority_score": 20 + }, + "built_at": "2026-05-15T15:30:00Z" + }, + { + "skill_id": "deploy-uniswap-v3-periphery-evm", + "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "category": "tutorial", + "priority": "high", + "status": "pending", + "notes": "Full deploy+test workflow for SwapRouter and NonfungiblePositionManager. The 'complete V3 Core tutorial' prereq is a code dependency resolved automatically via local npm file reference in the cloned monorepo; the Ignition module deploys Factory+WETH9+SwapRouter+NFPM in one shot, making this self-contained. Pinned to commit 96696ad15c3cf01b9168a71ad5114f27c34a8726 with docs.test.ts confirming CI-tested code.", + "scoring": { + "signals": [ + "P1", + "P2", + "P3", + "P4", + "R1", + "R2", + "C1", + "K1", + "K2", + "K3", + "K4", + "S1", + "S2", + "S3" + ], + "category_scores": { + "procedural": 20, + "reference": 9, + "conceptual": 5, + "not_applicable": 0 + }, + "priority_score": 25 + } } ], "supplementary_only": [ diff --git a/skill_coverage.json b/skill_coverage.json index b10a1ec3b..16111bedb 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -731,7 +731,7 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T16:00:00Z", + "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v3-core-evm" ], From a0bb1030436f4a8f95b997948316153970f5e2f7 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 15:28:25 +0000 Subject: [PATCH 12/26] chore: auto-generate/update skills --- agent_skills_config.json | 766 ++++++++++++++++++++++++--------------- skill_candidates.json | 119 +++--- skill_coverage.json | 14 +- 3 files changed, 543 insertions(+), 356 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 900882429..d688ade78 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "generated": "2026-05-15T15:30:00Z", - "content_hash": "sha256:1e942f0bea9b650ee643e6d7957d81984fe9990af06b540e4e1736c2850e16a9", + "generated": "2026-05-15T16:00:00Z", + "content_hash": "sha256:1474320d5d053319d2f0cd541ff17349c37e62629d95b7a5944a08bc01885551", "skills": [ { "id": "create-polkadot-account", @@ -32,7 +32,7 @@ "mkdir account-creator && cd account-creator", "npm init -y && npm pkg set type=module" ], - "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required \u2014 the @polkadot packages use ESM imports." + "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required — the @polkadot packages use ESM imports." }, { "order": 2, @@ -48,7 +48,7 @@ "order": 3, "action": "Fetch the account creation script", "working_directory": "account-creator", - "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed \u2014 the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", + "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed — the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", "reference_file": "create-account.ts" }, { @@ -59,7 +59,7 @@ "npx tsx create-account.ts" ], "expected_output": "Address: \nMnemonic: <12-word BIP39 phrase>", - "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both \u2014 the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." + "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both — the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." } ], "reference_code": { @@ -131,12 +131,12 @@ "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console" } ], - "project_structure": "account-creator/\n\u251c\u2500\u2500 create-account.ts\n\u2514\u2500\u2500 package.json" + "project_structure": "account-creator/\n├── create-account.ts\n└── package.json" }, { "id": "query-account-info-sdks", "title": "Query Account Information with SDKs", - "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required \u2014 read-only operation against any funded or unfunded address.", + "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -184,7 +184,7 @@ "commands": [ "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly \u2014 note the name you choose, as it must match the import in query-account.ts." + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly — note the name you choose, as it must match the import in query-account.ts." }, { "order": 4, @@ -273,12 +273,12 @@ "result": "After funding, account data is returned correctly" } ], - "project_structure": "papi-query-account-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 query-account.ts\n\u2514\u2500\u2500 package.json" + "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json" }, { "id": "query-chain-state-sdks", "title": "Query On-Chain State with SDKs", - "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info \u2014 including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required \u2014 read-only.", + "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -427,12 +427,12 @@ "result": "USDC metadata and address balance returned" } ], - "project_structure": "papi-query-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 query-asset.ts\n\u251c\u2500\u2500 query-balance.ts\n\u2514\u2500\u2500 package.json" + "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json" }, { "id": "call-runtime-apis-sdks", "title": "Call Runtime APIs with SDKs", - "description": "Executes Polkadot SDK runtime APIs \u2014 AccountNonceApi.account_nonce and Metadata.metadata_versions \u2014 using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required \u2014 read-only operation.", + "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -535,7 +535,7 @@ "slug": "chain-interactions-query-data-query-sdks", "title": "Query On-Chain State with SDKs", "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "Storage-based queries using the same PAPI setup \u2014 compare with runtime API calls." + "relevance": "Storage-based queries using the same PAPI setup — compare with runtime API calls." }, { "slug": "reference-tools-papi", @@ -560,16 +560,16 @@ }, { "scenario": "Edge case: nonce shows 0 for a new address", - "user_says": "The nonce is 0 \u2014 is that correct?", + "user_says": "The nonce is 0 — is that correct?", "actions": [ "Explain that nonce 0 means the account has not yet sent any transactions", "Note that AccountNonceApi returns 0 for any address that has never submitted a transaction, including unfunded accounts", - "No action required \u2014 the nonce increments with each submitted transaction" + "No action required — the nonce increments with each submitted transaction" ], "result": "User understands nonce 0 is correct for a new or never-transacted account" } ], - "project_structure": "papi-runtime-api-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 runtime-apis.ts\n\u2514\u2500\u2500 package.json" + "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json" }, { "id": "send-transactions-sdks", @@ -593,7 +593,7 @@ "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://asset-hub-paseo.dotters.network for testnet)" ], "tokens": [ - "Testnet PAS tokens in the sender account \u2014 get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" + "Testnet PAS tokens in the sender account — get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" ], "wallet": [ "An SR25519 account mnemonic phrase for the sender account, funded with testnet PAS" @@ -654,7 +654,7 @@ "echo 'SENDER_MNEMONIC=\\nWS_ENDPOINT=wss://asset-hub-paseo.dotters.network\\nDEST_ADDRESS=' > .env", "echo '.env' > .gitignore" ], - "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly \u2014 fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." + "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly — fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." }, { "order": 5, @@ -677,7 +677,7 @@ "npx tsx send-transfer.ts" ], "expected_output": "Connected to Polkadot Testnet\nSigning and submitting transaction...\nTransaction submitted with hash: 0x", - "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash \u2014 needed in step 8 to verify." + "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash — needed in step 8 to verify." } ], "reference_code": { @@ -710,7 +710,7 @@ { "pattern": "Transaction submitted but not confirmed / stuck pending", "cause": "Polkadot Hub TestNet can be unstable and drop transactions under load.", - "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction \u2014 if you see 'nonce too low', the previous transaction was included despite appearing to fail." + "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction — if you see 'nonce too low', the previous transaction was included despite appearing to fail." } ], "supplementary_context": { @@ -761,7 +761,7 @@ "result": "Account funded; transaction succeeds on retry" } ], - "project_structure": "papi-send-tx-example/\n\u251c\u2500\u2500 .papi/\n\u2502 \u2514\u2500\u2500 descriptors/\n\u251c\u2500\u2500 send-transfer.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 package.json" + "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json" }, { "id": "install-polkadot-sdk", @@ -842,7 +842,7 @@ "./target/release/polkadot --version" ], "expected_output": "polkadot 1.x.x-xxxxxxx", - "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string \u2014 needed when matching binary versions in parachain template setup." + "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string — needed when matching binary versions in parachain template setup." } ], "error_patterns": [ @@ -901,7 +901,7 @@ "Run cargo build --release --locked", "Verify with ./target/release/polkadot --version" ], - "result": "Polkadot binary prints version string \u2014 SDK toolchain ready for parachain development" + "result": "Polkadot binary prints version string — SDK toolchain ready for parachain development" }, { "scenario": "Edge case: build runs out of memory on a machine with limited RAM", @@ -941,7 +941,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -963,7 +963,7 @@ "mkdir hardhat-deployment && cd hardhat-deployment", "npm init -y" ], - "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage \u2014 interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." + "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage — interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." }, { "order": 2, @@ -973,7 +973,7 @@ "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", "npm install dotenv" ], - "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required \u2014 the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." + "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." }, { "order": 3, @@ -992,21 +992,21 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." }, { "order": 5, "action": "Fetch and configure hardhat.config.ts", "working_directory": "hardhat-deployment", "reference_file": "hardhat.config.ts", - "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei \u2014 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes \u2014 on TestNet it causes IGN401 dropped-transaction errors)." + "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors)." }, { "order": 6, "action": "Fetch Storage.sol contract", "working_directory": "hardhat-deployment", "reference_file": "Storage.sol", - "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed \u2014 this is a generic Storage contract with get/set functions." + "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed — this is a generic Storage contract with get/set functions." }, { "order": 7, @@ -1023,7 +1023,7 @@ "action": "Fetch the Ignition deployment module", "working_directory": "hardhat-deployment", "reference_file": "storage.ts", - "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed \u2014 the module deploys the Storage contract by name." + "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed — the module deploys the Storage contract by name." }, { "order": 9, @@ -1032,9 +1032,9 @@ "commands": [ "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" ], - "expected_output": "Hardhat Ignition \ud83d\ude80 ... Storage deployed to: 0x...", + "expected_output": "Hardhat Ignition 🚀 ... Storage deployed to: 0x...", "interactive": true, - "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID \u2014 delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end \u2014 needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." + "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end — needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." } ], "reference_code": { @@ -1121,12 +1121,12 @@ "result": "Fresh deployment succeeds with correct gas configuration" } ], - "project_structure": "hardhat-deployment/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 ignition/\n\u2502 \u2514\u2500\u2500 modules/\n\u2502 \u2514\u2500\u2500 Storage.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 hardhat.config.ts\n\u2514\u2500\u2500 package.json" + "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json" }, { "id": "deploy-erc20-token-hardhat", "title": "Deploy an ERC-20 Token Using Hardhat", - "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ \u2014 requires evmVersion cancun.", + "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -1146,7 +1146,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas fees and test transactions \u2014 get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" + "Testnet PAS tokens for gas fees and test transactions — get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -1188,13 +1188,13 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." }, { "order": 4, "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", "working_directory": "revm-hardhat-examples/erc20-hardhat", - "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei \u2014 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block \u2014 required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." + "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." }, { "order": 5, @@ -1214,7 +1214,7 @@ "npx hardhat test --network polkadotTestnet" ], "expected_output": "7 passing", - "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output \u2014 it confirms the contract logic is correct before deployment." + "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output — it confirms the contract logic is correct before deployment." }, { "order": 7, @@ -1225,7 +1225,7 @@ ], "expected_output": "MyToken deployed to: 0x...", "interactive": true, - "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID \u2014 delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success \u2014 needed for token interaction and verification." + "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success — needed for token interaction and verification." } ], "error_patterns": [ @@ -1321,7 +1321,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -1354,7 +1354,7 @@ "npm install --save-dev @parity/resolc", "npm install dotenv" ], - "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them \u2014 use latest." + "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them — use latest." }, { "order": 3, @@ -1364,7 +1364,7 @@ "npx hardhat-polkadot init" ], "interactive": true, - "description": "Run the project creation wizard. This is an interactive command \u2014 delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." + "description": "Run the project creation wizard. This is an interactive command — delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." }, { "order": 4, @@ -1384,13 +1384,13 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." }, { "order": 6, "action": "Fetch and configure hardhat.config.ts for TestNet", "working_directory": "hardhat-pvm-example", - "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei \u2014 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." + "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." }, { "order": 7, @@ -1495,11 +1495,11 @@ "primary_page": "smart-contracts/dev-environments/hardhat.md", "prerequisites": { "runtime": [ - "Node.js LTS (18.x, 20.x, or 22.x \u2014 even major version numbers only)", + "Node.js LTS (18.x, 20.x, or 22.x — even major version numbers only)", "npm, pnpm, or yarn" ], "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account \u2014 needed before deploying contracts" + "An EVM private key (0x-prefixed) for a funded testnet account — needed before deploying contracts" ] }, "env_vars": [ @@ -1518,7 +1518,7 @@ "mkdir hardhat-example && cd hardhat-example", "npm init -y" ], - "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage \u2014 the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." + "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage — the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." }, { "order": 2, @@ -1550,13 +1550,13 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable \u2014 the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable — the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." }, { "order": 5, "action": "Fetch and configure hardhat.config.ts", "working_directory": "hardhat-example", - "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei \u2014 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." + "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." } ], "reference_code": { @@ -1634,7 +1634,7 @@ { "id": "query-chain-data-sidecar-rest", "title": "Query On-Chain Data with Sidecar REST API", - "description": "Runs the Substrate API Sidecar REST service connected to Polkadot Hub TestNet and queries on-chain data via curl. Use when you need account balances, asset info, or block data without writing SDK code. Covers Sidecar installation, startup with a TestNet endpoint, and the primary query endpoints: /accounts/{addr}/balance-info, /assets/{id}/asset-info, and /blocks/{id}. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required \u2014 read-only operations only.", + "description": "Runs the Substrate API Sidecar REST service connected to Polkadot Hub TestNet and queries on-chain data via curl. Use when you need account balances, asset info, or block data without writing SDK code. Covers Sidecar installation, startup with a TestNet endpoint, and the primary query endpoints: /accounts/{addr}/balance-info, /assets/{id}/asset-info, and /blocks/{id}. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required — read-only operations only.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -1648,7 +1648,7 @@ "runtime": [ "Node.js v18+ and npm (to install and run @substrate/api-sidecar)", "curl (pre-installed on macOS and Linux; use WSL on Windows)", - "jq (optional, for formatted JSON output \u2014 install with: apt install jq or brew install jq)" + "jq (optional, for formatted JSON output — install with: apt install jq or brew install jq)" ], "network": [ "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network" @@ -1673,7 +1673,7 @@ "SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network substrate-api-sidecar" ], "expected_output": "SAS listening on http://127.0.0.1:8080/", - "description": "Start Sidecar with SAS_SUBSTRATE_URL pointing to the Polkadot Hub TestNet WebSocket endpoint. The service listens on http://127.0.0.1:8080/ by default. Run this in a dedicated terminal and keep it running while executing the following steps. If using npx: SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network npx @substrate/api-sidecar. Polkadot Hub TestNet can be unstable \u2014 if the WebSocket connection drops, restart Sidecar." + "description": "Start Sidecar with SAS_SUBSTRATE_URL pointing to the Polkadot Hub TestNet WebSocket endpoint. The service listens on http://127.0.0.1:8080/ by default. Run this in a dedicated terminal and keep it running while executing the following steps. If using npx: SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network npx @substrate/api-sidecar. Polkadot Hub TestNet can be unstable — if the WebSocket connection drops, restart Sidecar." }, { "order": 3, @@ -1683,7 +1683,7 @@ "curl -s \"http://127.0.0.1:8080/accounts/INSERT_SS58_ADDRESS/balance-info\" | jq ." ], "expected_output": "{\"at\":{\"hash\":\"0x...\",\"height\":\"12345\"},\"nonce\":\"0\",\"tokenSymbol\":\"PAS\",\"free\":\"...\",\"reserved\":\"...\"}", - "description": "Query the balance-info endpoint. Replace 'INSERT_SS58_ADDRESS' with the target SS58 address. Response fields: free \u2014 spendable planck; reserved \u2014 locked/staked; miscFrozen / feeFrozen \u2014 frozen amounts; nonce \u2014 transaction count. Divide raw planck values by 10^10 to convert to PAS. Omit '| jq .' if jq is not installed." + "description": "Query the balance-info endpoint. Replace 'INSERT_SS58_ADDRESS' with the target SS58 address. Response fields: free — spendable planck; reserved — locked/staked; miscFrozen / feeFrozen — frozen amounts; nonce — transaction count. Divide raw planck values by 10^10 to convert to PAS. Omit '| jq .' if jq is not installed." }, { "order": 4, @@ -1704,7 +1704,7 @@ "curl -s \"http://127.0.0.1:8080/assets/1984/asset-info\" | jq ." ], "expected_output": "{\"at\":{...},\"items\":[{\"id\":\"1984\",\"name\":\"USDT\",\"symbol\":\"USDT\",\"decimals\":\"6\"}]}", - "description": "Query metadata for asset ID 1984 (USDT on Polkadot Hub). Replace '1984' with any registered asset ID. If the asset does not exist on TestNet, items will be empty \u2014 this is expected since not all mainnet assets are bridged to TestNet." + "description": "Query metadata for asset ID 1984 (USDT on Polkadot Hub). Replace '1984' with any registered asset ID. If the asset does not exist on TestNet, items will be empty — this is expected since not all mainnet assets are bridged to TestNet." } ], "reference_code": { @@ -1725,7 +1725,7 @@ }, { "pattern": "balance-info returns all-zero values for free/reserved/nonce", - "cause": "The queried account has no on-chain entry \u2014 it has never received any tokens.", + "cause": "The queried account has no on-chain entry — it has never received any tokens.", "resolution": "Fund the account at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation. Re-query the endpoint." }, { @@ -1741,7 +1741,7 @@ "slug": "chain-interactions-query-data-query-sdks", "title": "Query On-Chain State with SDKs", "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "SDK-based alternative to Sidecar \u2014 query the same on-chain data using PAPI or Polkadot.js TypeScript code." + "relevance": "SDK-based alternative to Sidecar — query the same on-chain data using PAPI or Polkadot.js TypeScript code." }, { "slug": "reference-tools-sidecar", @@ -1784,7 +1784,7 @@ { "id": "calculate-transaction-fees-papi", "title": "Estimate Transaction Fees with PAPI", - "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getPaymentInfo method. Use when you need to preview gas costs before submitting \u2014 for fee UIs, budget checks, or preflight validation. Returns partial_fee in planck, human-readable PAS, and weight breakdown. Also covers the Polkadot.js paymentInfo alternative described in the source page. Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getPaymentInfo PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required \u2014 fee estimation is read-only.", + "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getPaymentInfo method. Use when you need to preview gas costs before submitting — for fee UIs, budget checks, or preflight validation. Returns partial_fee in planck, human-readable PAS, and weight breakdown. Also covers the Polkadot.js paymentInfo alternative described in the source page. Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getPaymentInfo PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required — fee estimation is read-only.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -1812,7 +1812,7 @@ "mkdir papi-fee-estimate && cd papi-fee-estimate", "npm init -y && npm pkg set type=module" ], - "description": "Create a new directory 'papi-fee-estimate' and initialize it as an ESM Node.js project. The type=module flag is required \u2014 polkadot-api uses ESM imports." + "description": "Create a new directory 'papi-fee-estimate' and initialize it as an ESM Node.js project. The type=module flag is required — polkadot-api uses ESM imports." }, { "order": 2, @@ -1831,13 +1831,13 @@ "commands": [ "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. Polkadot Hub TestNet can be unstable \u2014 retry after a few minutes if the connection fails." + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. Polkadot Hub TestNet can be unstable — retry after a few minutes if the connection fails." }, { "order": 4, "action": "Create the fee estimation script", "working_directory": "papi-fee-estimate", - "description": "Create a file named 'estimate-fees.ts' in papi-fee-estimate with this content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { polkadotTestNet } from \"@polkadot-api/descriptors\";\n\nconst WS_ENDPOINT = \"INSERT_WS_ENDPOINT\";\nconst SENDER_ADDRESS = \"INSERT_SENDER_ADDRESS\";\nconst DEST_ADDRESS = \"INSERT_DEST_ADDRESS\";\n\nasync function main() {\n const client = createClient(getWsProvider(WS_ENDPOINT));\n const api = client.getTypedApi(polkadotTestNet);\n const tx = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: DEST_ADDRESS },\n value: 10_000_000_000n,\n });\n const info = await tx.getPaymentInfo(SENDER_ADDRESS);\n console.log(\"Fee (planck):\", info.partial_fee.toString());\n console.log(\"Fee (PAS):\", (Number(info.partial_fee) / 1e10).toFixed(6));\n console.log(\"Weight ref_time:\", info.weight.ref_time.toString());\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nAfter creating the file, apply these substitutions: (1) Replace INSERT_WS_ENDPOINT with your WS endpoint (e.g. 'wss://asset-hub-paseo.dotters.network'). (2) Replace INSERT_SENDER_ADDRESS with any valid funded SS58 address \u2014 sender address affects fee because weight depends on account state. (3) Replace INSERT_DEST_ADDRESS with any valid SS58 recipient address. Save the file." + "description": "Create a file named 'estimate-fees.ts' in papi-fee-estimate with this content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { polkadotTestNet } from \"@polkadot-api/descriptors\";\n\nconst WS_ENDPOINT = \"INSERT_WS_ENDPOINT\";\nconst SENDER_ADDRESS = \"INSERT_SENDER_ADDRESS\";\nconst DEST_ADDRESS = \"INSERT_DEST_ADDRESS\";\n\nasync function main() {\n const client = createClient(getWsProvider(WS_ENDPOINT));\n const api = client.getTypedApi(polkadotTestNet);\n const tx = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: DEST_ADDRESS },\n value: 10_000_000_000n,\n });\n const info = await tx.getPaymentInfo(SENDER_ADDRESS);\n console.log(\"Fee (planck):\", info.partial_fee.toString());\n console.log(\"Fee (PAS):\", (Number(info.partial_fee) / 1e10).toFixed(6));\n console.log(\"Weight ref_time:\", info.weight.ref_time.toString());\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nAfter creating the file, apply these substitutions: (1) Replace INSERT_WS_ENDPOINT with your WS endpoint (e.g. 'wss://asset-hub-paseo.dotters.network'). (2) Replace INSERT_SENDER_ADDRESS with any valid funded SS58 address — sender address affects fee because weight depends on account state. (3) Replace INSERT_DEST_ADDRESS with any valid SS58 recipient address. Save the file." }, { "order": 5, @@ -1869,7 +1869,7 @@ { "pattern": "TypeError: tx.getPaymentInfo is not a function", "cause": "Descriptor export name does not match the import, or polkadot-api version is below 1.0.0.", - "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api \u2014 version 1.0.0+ required." + "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api — version 1.0.0+ required." }, { "pattern": "SyntaxError: Cannot use import statement in a module", @@ -1884,7 +1884,7 @@ "slug": "chain-interactions-send-transactions-with-sdks", "title": "Send Transactions with SDKs", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit the same transaction whose fee was estimated \u2014 the natural next step." + "relevance": "How to sign and submit the same transaction whose fee was estimated — the natural next step." }, { "slug": "reference-tools-papi", @@ -1909,9 +1909,9 @@ }, { "scenario": "Edge case: fee appears unusually high compared to Ethereum", - "user_says": "The fee estimate shows 0.001 PAS \u2014 that seems expensive", + "user_says": "The fee estimate shows 0.001 PAS — that seems expensive", "actions": [ - "Explain that Polkadot Hub TestNet has a base fee of 1000 gwei \u2014 significantly higher than Ethereum mainnet", + "Explain that Polkadot Hub TestNet has a base fee of 1000 gwei — significantly higher than Ethereum mainnet", "Note that PAS testnet fees do not reflect real economic cost", "Get testnet tokens from https://faucet.polkadot.io/ if the sender balance is insufficient" ], @@ -1941,7 +1941,7 @@ "Destination parachain WebSocket RPC (e.g., wss://sys.ibp.network/people-paseo for People Chain Paseo)" ], "tokens": [ - "Paseo testnet PAS in the sender account \u2014 get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." + "Paseo testnet PAS in the sender account — get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." ], "wallet": [ "SR25519 account mnemonic phrase for the sender account, funded with Paseo testnet PAS" @@ -1999,7 +1999,7 @@ "order": 4, "action": "Create the XCM transfer script", "working_directory": "paraspell-xcm-transfer", - "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains \u2014 chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." + "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains — chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." }, { "order": 5, @@ -2009,7 +2009,7 @@ "npx tsx xcm-transfer.ts" ], "expected_output": "Sender: ...\nDry-run: OK\nEstimated fee: ...\nStatus: InBlock\nFinalized: 0x...", - "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first \u2014 if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." + "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first — if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." } ], "reference_code": { @@ -2035,8 +2035,8 @@ }, { "pattern": "Transaction submitted but never finalizes", - "cause": "Paseo TestNet instability \u2014 the TestNet can drop transactions under load.", - "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script \u2014 nonce: -1 fetches the current nonce automatically." + "cause": "Paseo TestNet instability — the TestNet can drop transactions under load.", + "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script — nonce: -1 fetches the current nonce automatically." } ], "supplementary_context": { @@ -2091,7 +2091,7 @@ { "id": "pay-fees-alternative-token", "title": "Pay Transaction Fees with an Alternative Token", - "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode \u2014 no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", + "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode — no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -2104,10 +2104,10 @@ "prerequisites": { "runtime": [ "Node.js v18+ and npm", - "npx (bundled with npm v5.2+ \u2014 no separate install needed)" + "npx (bundled with npm v5.2+ — no separate install needed)" ], "network": [ - "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network \u2014 Chopsticks downloads state on first run" + "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network — Chopsticks downloads state on first run" ] }, "env_vars": [], @@ -2128,7 +2128,7 @@ "order": 2, "action": "Create Chopsticks configuration", "working_directory": "fee-proxy-demo", - "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key \u2014 this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." + "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key — this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." }, { "order": 3, @@ -2138,7 +2138,7 @@ "npx @acala-network/chopsticks --config=chopsticks.yml" ], "expected_output": "Listening on port 8000", - "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state \u2014 subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." + "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." }, { "order": 4, @@ -2153,7 +2153,7 @@ "order": 5, "action": "Create the fee-proxy script", "working_directory": "fee-proxy-demo", - "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy \u2014 pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." + "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy — pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." }, { "order": 6, @@ -2206,7 +2206,7 @@ "slug": "chain-interactions-send-transactions-calculate-transaction-fees", "title": "Calculate Transaction Fees", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", - "relevance": "How to estimate fees for a standard transaction \u2014 compare with the fee proxy approach to understand the difference." + "relevance": "How to estimate fees for a standard transaction — compare with the fee proxy approach to understand the difference." } ] }, @@ -2267,7 +2267,7 @@ "order": 1, "action": "Choose deployment method and sync type", "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table \u2014 replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." + "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table — replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." }, { "order": 2, @@ -2295,7 +2295,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" ], "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync \u2014 initial sync can take hours to days depending on hardware." + "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." }, { "order": 5, @@ -2343,13 +2343,13 @@ "slug": "node-infrastructure-run-a-node-polkadot-hub-rpc", "title": "Run an RPC Node for Polkadot Hub", "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/polkadot-hub-rpc.md", - "relevance": "How to run an RPC node for Polkadot Hub specifically \u2014 different chain spec and optional Ethereum RPC adapter." + "relevance": "How to run an RPC node for Polkadot Hub specifically — different chain spec and optional Ethereum RPC adapter." }, { "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", "title": "Set Up Secure WebSocket", "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx \u2014 required for production endpoints." + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." }, { "slug": "node-infrastructure-run-a-node-relay-chain-full-node", @@ -2436,14 +2436,14 @@ "npx @acala-network/chopsticks --config=chopsticks.yml" ], "expected_output": "Listening on port 8000", - "description": "Start Chopsticks in a dedicated terminal. It downloads chain state from the configured endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state \u2014 subsequent runs use db.sqlite cache. Keep this terminal open during all following steps." + "description": "Start Chopsticks in a dedicated terminal. It downloads chain state from the configured endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite cache. Keep this terminal open during all following steps." }, { "order": 4, "action": "Locate the XCM call data on Subscan", "working_directory": ".", "interactive": true, - "description": "This step is manual. Open Subscan for the source chain (e.g., https://polkadot.subscan.io or https://assethub-polkadot.subscan.io). Navigate to the extrinsic or event containing the XCM you want to replay. Find the encoded call data hex string \u2014 it typically appears in the 'Call Data' or 'Params' section of the extrinsic detail page. Copy the full hex string (starts with '0x'). You will use this in step 5. Paste it into a local file named 'call-data.txt' for reference." + "description": "This step is manual. Open Subscan for the source chain (e.g., https://polkadot.subscan.io or https://assethub-polkadot.subscan.io). Navigate to the extrinsic or event containing the XCM you want to replay. Find the encoded call data hex string — it typically appears in the 'Call Data' or 'Params' section of the extrinsic detail page. Copy the full hex string (starts with '0x'). You will use this in step 5. Paste it into a local file named 'call-data.txt' for reference." }, { "order": 5, @@ -2453,7 +2453,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"xcmPallet_dryRunCall\",\"params\":[\"INSERT_SENDER_ADDRESS\", \"INSERT_CALL_DATA_HEX\"]}' http://localhost:8000" ], "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"executionResult\":{\"Ok\":{}},\"emittedEvents\":[],...},\"id\":1}", - "description": "Replace INSERT_SENDER_ADDRESS with the SS58 address that submitted the original XCM and INSERT_CALL_DATA_HEX with the hex string from Subscan (step 4). The response's executionResult indicates success or failure along with the failure reason if any. If the RPC method is not available on the forked chain, use 'xcmPallet_dryRunXcm' as an alternative \u2014 check the source page for the correct method name and parameter signature for your chain version. Chopsticks must be running (step 3) for this to work." + "description": "Replace INSERT_SENDER_ADDRESS with the SS58 address that submitted the original XCM and INSERT_CALL_DATA_HEX with the hex string from Subscan (step 4). The response's executionResult indicates success or failure along with the failure reason if any. If the RPC method is not available on the forked chain, use 'xcmPallet_dryRunXcm' as an alternative — check the source page for the correct method name and parameter signature for your chain version. Chopsticks must be running (step 3) for this to work." } ], "reference_code": { @@ -2490,7 +2490,7 @@ "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", "title": "XCM Fee Estimation", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "How to estimate XCM fees before submission \u2014 helps diagnose BuyExecution failures in a dry-run." + "relevance": "How to estimate XCM fees before submission — helps diagnose BuyExecution failures in a dry-run." }, { "slug": "reference-tools-chopsticks", @@ -2502,7 +2502,7 @@ "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", "title": "Transfer Assets Between Parachains", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "relevance": "How to submit a real XCM transfer between parachains \u2014 use after debugging with Chopsticks." + "relevance": "How to submit a real XCM transfer between parachains — use after debugging with Chopsticks." } ] }, @@ -2535,7 +2535,7 @@ { "id": "estimate-xcm-fees-teleport", "title": "Estimate XCM Fees for Asset Teleport", - "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required \u2014 Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", + "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required — Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -2573,7 +2573,7 @@ "order": 2, "action": "Create Chopsticks configs for source and destination chains", "working_directory": "xcm-fee-estimate", - "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 \u2014 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 \u2014 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." + "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 — 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 — 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." }, { "order": 3, @@ -2600,7 +2600,7 @@ "order": 5, "action": "Create the fee estimation script", "working_directory": "xcm-fee-estimate", - "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples \u2014 consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." + "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples — consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." }, { "order": 6, @@ -2704,7 +2704,7 @@ "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", "prerequisites": { "runtime": [ - "Rust toolchain with wasm32-unknown-unknown target \u2014 complete the install-polkadot-sdk skill first", + "Rust toolchain with wasm32-unknown-unknown target — complete the install-polkadot-sdk skill first", "Git", "Disk: at least 5 GB free for the build artifacts", "RAM: 8 GB minimum; 16 GB recommended for parallel compilation" @@ -2726,7 +2726,7 @@ "order": 2, "action": "Review the template directory structure", "working_directory": "polkadot-sdk-parachain-template", - "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup \u2014 the default configuration targets a local development network." + "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup — the default configuration targets a local development network." }, { "order": 3, @@ -2746,7 +2746,7 @@ "./target/release/parachain-template-node --version" ], "expected_output": "parachain-template-node 0.1.0-...", - "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully \u2014 review the cargo build output for compilation errors." + "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully — review the cargo build output for compilation errors." }, { "order": 5, @@ -2756,7 +2756,7 @@ "./target/release/parachain-template-node --dev" ], "expected_output": "Running in --dev mode\n...\nIdle (0 peers)", - "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain \u2014 the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." + "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain — the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." } ], "reference_code": { @@ -2778,7 +2778,7 @@ }, { "pattern": "Killed / process killed during compilation / OOM", - "cause": "Insufficient RAM for parallel Rust compilation \u2014 cargo uses one thread per CPU core by default.", + "cause": "Insufficient RAM for parallel Rust compilation — cargo uses one thread per CPU core by default.", "resolution": "Reduce parallel compilation jobs: CARGO_BUILD_JOBS=2 cargo build --release. Consider adding swap space on Linux: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile." }, { @@ -2866,7 +2866,7 @@ "order": 1, "action": "Choose deployment method and sync type", "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync \u2014 check https://snapshots.polkadot.io or community providers for current snapshots." + "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync — check https://snapshots.polkadot.io or community providers for current snapshots." }, { "order": 2, @@ -2904,7 +2904,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" ], "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync \u2014 initial sync can take hours to days depending on hardware." + "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." }, { "order": 6, @@ -2915,7 +2915,7 @@ "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" ], "expected_output": "{\"result\":\"Polkadot Asset Hub\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", - "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 \u2014 should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." + "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 — should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." } ], "reference_code": { @@ -2958,7 +2958,7 @@ "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", "title": "Set Up Secure WebSocket", "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx \u2014 required for production endpoints." + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." }, { "slug": "node-infrastructure-run-a-node-relay-chain-full-node", @@ -2982,11 +2982,11 @@ "result": "Polkadot Hub RPC node running, synced, serving requests on ws://localhost:9944" }, { - "scenario": "Edge case: Ethereum tooling cannot connect \u2014 eth_chainId returns wrong value", + "scenario": "Edge case: Ethereum tooling cannot connect — eth_chainId returns wrong value", "user_says": "MetaMask connects to port 8545 but shows the wrong chain ID", "actions": [ "Verify the eth-rpc adapter is configured with the correct --chain flag matching the running node", - "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) \u2014 confirm eth_chainId returns this value", + "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) — confirm eth_chainId returns this value", "If chain ID is wrong, restart the eth-rpc adapter with --chain=asset-hub-paseo for TestNet or --chain=asset-hub-polkadot for mainnet", "Update MetaMask network settings with the correct chain ID and RPC URL http://localhost:8545" ], @@ -3053,7 +3053,7 @@ "chmod +x polkadot polkadot-prepare-worker polkadot-execute-worker", "sudo mv polkadot polkadot-prepare-worker polkadot-execute-worker /usr/local/bin/" ], - "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 \u2014 check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE \u2014 APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE \u2014 Docker:\n docker pull parity/polkadot:stable2512-2" + "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 — check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE — APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE — Docker:\n docker pull parity/polkadot:stable2512-2" }, { "order": 4, @@ -3102,7 +3102,7 @@ "slug": "node-infrastructure-run-a-validator-requirements", "title": "Validator Requirements", "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/requirements.md", - "relevance": "Minimum hardware and skill requirements before starting a validator \u2014 read before this skill." + "relevance": "Minimum hardware and skill requirements before starting a validator — read before this skill." }, { "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-key-management", @@ -3153,7 +3153,7 @@ { "id": "set-up-chopsticks-fork", "title": "Set Up and Use Chopsticks for Chain Forking", - "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC \u2014 MetaMask cannot connect to a Chopsticks fork.", + "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -3188,7 +3188,7 @@ "order": 2, "action": "Create a fork configuration file", "working_directory": ".", - "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs \u2014 download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts \u2014 useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." + "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs — download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts — useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." }, { "order": 3, @@ -3259,13 +3259,13 @@ "slug": "parachains-testing-fork-a-parachain", "title": "Fork a Parachain Using Chopsticks", "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", - "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks \u2014 same tool, focused on the parachain testing workflow with a CI-backed example." + "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks — same tool, focused on the parachain testing workflow with a CI-backed example." }, { "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", "title": "Replay and Dry Run XCMs", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "Using Chopsticks to replay and dry-run XCM messages \u2014 advanced XCM debugging workflow." + "relevance": "Using Chopsticks to replay and dry-run XCM messages — advanced XCM debugging workflow." }, { "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", @@ -3302,7 +3302,7 @@ { "id": "set-up-e2e-testing-moonwall", "title": "Set Up End-to-End Testing with Moonwall", - "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain \u2014 local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", + "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain — local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -3341,7 +3341,7 @@ "moonwall init" ], "interactive": true, - "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user \u2014 do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." + "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user — do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." }, { "order": 3, @@ -3353,7 +3353,7 @@ "order": 4, "action": "Write a test suite", "working_directory": ".", - "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run \u2014 do not create API connections manually." + "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run — do not create API connections manually." }, { "order": 5, @@ -3427,7 +3427,7 @@ "scenario": "Edge case: test environment fails to start", "user_says": "Moonwall says it cannot connect to the node endpoint", "actions": [ - "Check the foundation type in moonwall.config \u2014 for 'dev', verify binPath is correct and the binary has execute permission", + "Check the foundation type in moonwall.config — for 'dev', verify binPath is correct and the binary has execute permission", "For 'read_only' or 'chopsticks', start the node or Chopsticks fork manually and confirm it is listening on the configured endpoint", "Increase the connection timeout in moonwall.config if the node starts slowly" ], @@ -3458,7 +3458,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required for contract deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for contract deployment gas" ], "wallet": [ "MetaMask configured for Polkadot Hub TestNet (chainId 420420417)", @@ -3468,7 +3468,7 @@ "env_vars": [ { "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly \u2014 never paste in chat.", + "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", "required": true } ], @@ -3506,7 +3506,7 @@ "order": 4, "action": "Update hardhat.config.ts for TestNet and security", "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string \u2014 Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings \u2014 required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 \u2014 zero causes IGN401 errors on TestNet).\nSave the file." + "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file." }, { "order": 5, @@ -3526,7 +3526,7 @@ "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" ], "expected_output": "Storage deployed to: 0x...", - "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address \u2014 needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json \u2014 if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." + "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." }, { "order": 7, @@ -3578,7 +3578,7 @@ { "pattern": "IGN401 / Transaction dropped", "cause": "Ignition lost track of the transaction. The transaction may have succeeded or may be stuck in the mempool.", - "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded \u2014 use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." + "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded — use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." }, { "pattern": "invalid opcode: MCOPY", @@ -3639,7 +3639,7 @@ "user_says": "Ignition says IGN401 but I don't know if the contract deployed", "actions": [ "Check ignition/deployments//deployed_addresses.json for a Storage address", - "If address is present: deployment succeeded \u2014 use that address in contract.ts and proceed to step 7", + "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" ], "result": "Contract address recovered from deployment state or clean redeployment completed" @@ -3664,10 +3664,10 @@ "A modern web browser (Chrome or Firefox recommended)" ], "network": [ - "Polkadot Hub TestNet (chainId 420420417) \u2014 configured in MetaMask" + "Polkadot Hub TestNet (chainId 420420417) — configured in MetaMask" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required to pay deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ — required to pay deployment gas" ], "wallet": [ "MetaMask browser extension installed and unlocked", @@ -3692,7 +3692,7 @@ "order": 3, "action": "Configure the Solidity compiler in Remix", "working_directory": ".", - "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown \u2014 required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." + "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown — required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." }, { "order": 4, @@ -3704,13 +3704,13 @@ "order": 5, "action": "Connect MetaMask to Remix and deploy", "working_directory": ".", - "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect \u2014 click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation \u2014 review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10\u201330 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." + "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect — click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation — review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10–30 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." }, { "order": 6, "action": "Verify the deployment and mint tokens", "working_directory": ".", - "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' \u2014 should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." + "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' — should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." } ], "reference_code": { @@ -3748,7 +3748,7 @@ "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", "title": "Deploy an ERC-20 Using Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "CLI-based ERC-20 deployment using Hardhat \u2014 use when the user prefers a terminal workflow over Remix." + "relevance": "CLI-based ERC-20 deployment using Hardhat — use when the user prefers a terminal workflow over Remix." }, { "slug": "smart-contracts-connect", @@ -3780,7 +3780,7 @@ }, { "scenario": "Edge case: user has no testnet PAS tokens", - "user_says": "Deployment failed \u2014 gas fee transaction rejected", + "user_says": "Deployment failed — gas fee transaction rejected", "actions": [ "Direct user to https://faucet.polkadot.io/ and ask them to enter their MetaMask address", "Wait for confirmation that PAS tokens have arrived (check MetaMask balance)", @@ -3812,7 +3812,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required for deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" ], "wallet": [ "0x-prefixed EVM private key for a funded testnet account" @@ -3821,7 +3821,7 @@ "env_vars": [ { "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly \u2014 never paste in chat.", + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", "required": true }, { @@ -3874,7 +3874,7 @@ "action": "Fetch the Storage.sol contract", "working_directory": "ethers-deploy", "reference_file": "Storage.sol", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target \u2014 a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." }, { "order": 6, @@ -3906,7 +3906,7 @@ "commands": [ "node deploy.js" ], - "description": "Run the deployment script. Save the deployed contract address from the output \u2014 needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." + "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." }, { "order": 10, @@ -3984,7 +3984,7 @@ "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", "title": "Deploy a Basic Contract with Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Hardhat-based deployment workflow \u2014 use when the project needs a full Hardhat setup rather than lightweight scripts." + "relevance": "Hardhat-based deployment workflow — use when the project needs a full Hardhat setup rather than lightweight scripts." } ] }, @@ -4008,13 +4008,13 @@ "user_says": "Deploy failed with transaction underpriced", "actions": [ "Open deploy.ts and add gasPrice override to the deployment transaction options", - "Set gasPrice: ethers.parseUnits('5000', 'gwei') \u2014 5x the 1000 gwei TestNet base fee", + "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", "Retry with npx tsx deploy.ts" ], "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet" } ], - "project_structure": "ethers-deploy/\n\u251c\u2500\u2500 artifacts/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 checkStorage.js\n\u251c\u2500\u2500 compile.js\n\u251c\u2500\u2500 connectToProvider.js\n\u251c\u2500\u2500 deploy.js\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 package.json" + "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json" }, { "id": "deploy-contracts-viem", @@ -4038,7 +4038,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ \u2014 required for deployment gas" + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" ], "wallet": [ "0x-prefixed EVM private key for a funded testnet account" @@ -4047,7 +4047,7 @@ "env_vars": [ { "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly \u2014 never paste in chat.", + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", "required": true } ], @@ -4061,7 +4061,7 @@ "npm init -y && npm pkg set type=module", "mkdir -p src contracts abis artifacts" ], - "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) \u2014 the reference scripts assume this layout for relative paths." + "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) — the reference scripts assume this layout for relative paths." }, { "order": 2, @@ -4088,7 +4088,7 @@ "action": "Fetch the chain configuration", "working_directory": "viem-deploy", "reference_file": "chainConfig.ts", - "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) \u2014 apply the same substitutions consistently in all three files." + "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) — apply the same substitutions consistently in all three files." }, { "order": 5, @@ -4109,7 +4109,7 @@ "action": "Fetch the wallet client setup", "working_directory": "viem-deploy", "reference_file": "createWallet.ts", - "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders \u2014 including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." + "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders — including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." }, { "order": 8, @@ -4221,13 +4221,13 @@ "slug": "smart-contracts-libraries-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js equivalent workflow \u2014 use when the user prefers Ethers.js v6 over Viem." + "relevance": "Ethers.js equivalent workflow — use when the user prefers Ethers.js v6 over Viem." }, { "slug": "smart-contracts-cookbook-dapps-zero-to-hero", "title": "Zero to Hero Smart Contract DApp", "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend \u2014 the natural next step after deploying a contract with Viem." + "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend — the natural next step after deploying a contract with Viem." } ] }, @@ -4258,7 +4258,7 @@ "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee" } ], - "project_structure": "viem-deploy/\n\u251c\u2500\u2500 abis/\n\u251c\u2500\u2500 artifacts/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 chainConfig.ts\n\u2502 \u251c\u2500\u2500 compile.ts\n\u2502 \u251c\u2500\u2500 createClient.ts\n\u2502 \u251c\u2500\u2500 createWallet.ts\n\u2502 \u251c\u2500\u2500 deploy.ts\n\u2502 \u2514\u2500\u2500 interact.ts\n\u251c\u2500\u2500 .env\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 package.json" + "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json" }, { "id": "set-up-pallet-mock-runtime", @@ -4279,7 +4279,7 @@ "Disk: at least 5 GB free for build artifacts", "Rust toolchain (stable, via rustup)", "Cargo", - "Completed the Make a Custom Pallet guide \u2014 the counter pallet must exist in pallets/pallet-custom" + "Completed the Make a Custom Pallet guide — the counter pallet must exist in pallets/pallet-custom" ], "network": [], "tokens": [], @@ -4347,7 +4347,7 @@ { "pattern": "error[E0277]: the trait bound is not satisfied / missing associated type in Config impl", "cause": "The mock.rs Config implementation is missing an associated type that the pallet's Config trait declares (e.g., WeightInfo).", - "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder \u2014 for WeightInfo: type WeightInfo = ();" + "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder — for WeightInfo: type WeightInfo = ();" }, { "pattern": "error[E0412]: cannot find type in scope / unresolved import", @@ -4400,7 +4400,7 @@ "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments." } ], - "project_structure": "polkadot-sdk-parachain-template/\n\u2514\u2500\u2500 pallets/\n \u2514\u2500\u2500 pallet-custom/\n \u2514\u2500\u2500 src/\n \u2514\u2500\u2500 mock.rs" + "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs" }, { "id": "unit-test-frame-pallet", @@ -4421,8 +4421,8 @@ "Disk: at least 5 GB free for build artifacts", "Rust toolchain (stable, via rustup)", "Cargo", - "Completed the Make a Custom Pallet guide \u2014 counter pallet in pallets/pallet-custom", - "Completed the Mock Your Runtime guide \u2014 mock.rs must exist in pallets/pallet-custom/src" + "Completed the Make a Custom Pallet guide — counter pallet in pallets/pallet-custom", + "Completed the Mock Your Runtime guide — mock.rs must exist in pallets/pallet-custom/src" ], "network": [], "tokens": [], @@ -4467,19 +4467,19 @@ "order": 5, "action": "Write basic operation and event emission tests", "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion \u2014 events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() \u2014 uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() \u2014 uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() \u2014 uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" + "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion — events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() — uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() — uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() — uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" }, { "order": 6, "action": "Write error condition and access control tests", "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() \u2014 uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() \u2014 uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() \u2014 uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() \u2014 uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" + "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() — uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() — uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() — uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() — uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" }, { "order": 7, "action": "Write genesis configuration and interaction tracking tests", "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() \u2014 uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() \u2014 calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() \u2014 account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" + "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() — uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() — calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() — account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" }, { "order": 8, @@ -4505,7 +4505,7 @@ "resolution": "Add System::set_block_number(1); as the first line inside the execute_with closure for any test that checks events." }, { - "pattern": "error: assert_noop! failed \u2014 storage was modified", + "pattern": "error: assert_noop! failed — storage was modified", "cause": "The dispatchable modified storage before returning an error, violating the no-op contract.", "resolution": "This is a pallet bug: all preconditions must be checked before any storage mutations. Fix the pallet extrinsic, not the test." }, @@ -4543,7 +4543,7 @@ "Write increment_works, set_counter_value_works, decrement_works with System::set_block_number(1) for event checks", "Write error tests: overflow, underflow, root-only access control", "Write genesis_config_works and interaction tracking tests", - "Run cargo test --package pallet-custom \u2014 expect 15 passing tests" + "Run cargo test --package pallet-custom — expect 15 passing tests" ], "result": "All 15 tests pass including 2 auto-generated mock tests." }, @@ -4658,7 +4658,7 @@ "cargo build --release --features runtime-benchmarks" ], "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking \u2014 do not use for production deployment." + "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking — do not use for production deployment." }, { "order": 10, @@ -4674,7 +4674,7 @@ "order": 11, "action": "Create the weight Handlebars template file", "working_directory": "polkadot-sdk-parachain-template", - "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content \u2014 needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} \u00b1{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions \u2014 you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." + "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content — needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions — you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." }, { "order": 12, @@ -4708,7 +4708,7 @@ { "pattern": "Error: No benchmarks found for pallet_custom", "cause": "The pallet was not registered in define_benchmarks! in runtime/src/benchmarks.rs, or the pallet name is misspelled.", - "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! \u2014 use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." + "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! — use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." }, { "pattern": "could not compile parachain-template-runtime / runtime-benchmarks feature error", @@ -4767,7 +4767,7 @@ { "id": "set-up-local-dev-node", "title": "Set Up a Local Development Node", - "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation \u2014 the local node exposes a subset of the Ethereum JSON-RPC API.", + "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -4780,7 +4780,7 @@ "prerequisites": { "runtime": [ "Rust toolchain (stable + nightly via rustup) with wasm32-unknown-unknown target", - "Polkadot SDK build dependencies \u2014 complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", + "Polkadot SDK build dependencies — complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", "Git", "At least 20 GB free disk space for the release build" ], @@ -4828,7 +4828,7 @@ "./target/release/revive-dev-node --dev" ], "interactive": true, - "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running \u2014 open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" + "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running — open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" }, { "order": 5, @@ -4900,8 +4900,8 @@ "user_says": "The build has been running for over an hour and my machine is unresponsive", "actions": [ "Cancel the build with Ctrl+C", - "Check free disk space (df -h) \u2014 at least 20 GB required", - "Check free RAM \u2014 at least 8 GB recommended", + "Check free disk space (df -h) — at least 20 GB required", + "Check free RAM — at least 8 GB recommended", "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" ], "result": "Build completes more slowly with reduced CPU and memory usage." @@ -4973,7 +4973,7 @@ "npx tsx main.ts" ], "expected_output": "Free balance: ", - "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts \u2014 it will not throw an error." + "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts — it will not throw an error." }, { "order": 6, @@ -5061,7 +5061,7 @@ "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs." } ], - "project_structure": "dedot-example/\n\u251c\u2500\u2500 main.ts\n\u251c\u2500\u2500 send-tx.ts\n\u2514\u2500\u2500 package.json" + "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json" }, { "id": "run-parachain-node-omni-node", @@ -5079,7 +5079,7 @@ "prerequisites": { "runtime": [ "macOS (Apple Silicon or Intel) or Linux (Ubuntu/Debian) for the pre-built binary path", - "Rust and Cargo (via rustup) if building from source \u2014 see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" + "Rust and Cargo (via rustup) if building from source — see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" ], "network": [ "Internet access to download the binary and to sync the parachain" @@ -5091,7 +5091,7 @@ "order": 1, "action": "Install polkadot-omni-node", "working_directory": ".", - "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust \u2014 see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" + "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust — see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" }, { "order": 2, @@ -5107,7 +5107,7 @@ "order": 3, "action": "Obtain a chain specification", "working_directory": ".", - "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path \u2014 you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." + "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path — you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." }, { "order": 4, @@ -5169,7 +5169,7 @@ }, { "scenario": "Edge case: build from source when no pre-built binary is available for the platform", - "user_says": "I'm on a non-standard Linux architecture \u2014 install polkadot-omni-node from source", + "user_says": "I'm on a non-standard Linux architecture — install polkadot-omni-node from source", "actions": [ "Install Rust via rustup and system dependencies per parachains/install-polkadot-sdk/", "Check crates.io for the latest polkadot-omni-node version", @@ -5205,7 +5205,7 @@ "env_vars": [ { "name": "MNEMONIC", - "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file \u2014 do not ask for it in chat.", + "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file — do not ask for it in chat.", "required": false } ], @@ -5300,7 +5300,7 @@ "scenario": "Edge case: transaction fails with ExtrinsicFailed", "user_says": "My Python transfer script runs but the extrinsic failed", "actions": [ - "Check receipt.is_success \u2014 if False, print receipt.error_message for the specific error", + "Check receipt.is_success — if False, print receipt.error_message for the specific error", "If FundsUnavailable: verify sender balance covers transfer amount plus fees using read_state.py", "If BadOrigin: verify the keypair matches the sender address in the call_params 'dest' field" ], @@ -5330,7 +5330,7 @@ "env_vars": [ { "name": "SECRET_PHRASE", - "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file \u2014 do not ask for it in chat.", + "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file — do not ask for it in chat.", "required": false } ], @@ -5354,7 +5354,7 @@ "order": 3, "action": "Add Subxt dependencies to Cargo.toml", "working_directory": "subxt-example", - "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" \u2014 the main RPC and codec library\n- subxt-signer = \"0.50.0\" \u2014 provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } \u2014 async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." + "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." }, { "order": 4, @@ -5366,7 +5366,7 @@ "order": 5, "action": "Create the main Rust source file", "working_directory": "subxt-example", - "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs \u2014 each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file \u2014 instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." + "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." }, { "order": 6, @@ -5386,9 +5386,9 @@ }, "error_patterns": [ { - "pattern": "error[E0425]: cannot find value / type in scope \u2014 after changing metadata", + "pattern": "error[E0425]: cannot find value / type in scope — after changing metadata", "cause": "The generated types from #[subxt::subxt] changed after re-downloading metadata from a different chain or after a runtime upgrade.", - "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ \u2014 check the Subxt docs for the new paths." + "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ — check the Subxt docs for the new paths." }, { "pattern": "Error: Rpc error: target url is not valid", @@ -5419,7 +5419,7 @@ "slug": "chain-interactions-send-transactions-with-sdks", "title": "Send Transactions with SDKs", "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit transactions with multiple SDK clients \u2014 context for the Subxt transaction pattern." + "relevance": "How to sign and submit transactions with multiple SDK clients — context for the Subxt transaction pattern." } ] }, @@ -5470,7 +5470,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas fees \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -5493,7 +5493,7 @@ "cd hardhat-nft-deployment", "npm init -y" ], - "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' \u2014 it is interactive and non-deterministic. Manual scaffolding is used instead." + "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead." }, { "order": 2, @@ -5519,7 +5519,7 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly \u2014 fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." }, { "order": 5, @@ -5561,7 +5561,7 @@ ], "expected_output": "MyNFTModule#MyNFT - 0x...", "interactive": true, - "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID \u2014 delegate this prompt to the user. Save the contract address printed on success." + "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success." } ], "reference_code": { @@ -5639,7 +5639,7 @@ { "id": "deploy-erc721-nft-remix", "title": "Deploy an ERC-721 NFT Using Remix IDE", - "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based \u2014 agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", + "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -5654,7 +5654,7 @@ "Polkadot Hub TestNet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for gas \u2014 get from https://faucet.polkadot.io/" + "Testnet PAS tokens for gas — get from https://faucet.polkadot.io/" ], "wallet": [ "MetaMask browser extension installed and configured for Polkadot Hub TestNet", @@ -5727,7 +5727,7 @@ "slug": "smart-contracts-cookbook-smart-contracts-deploy-nft-nft-hardhat", "title": "Deploy an ERC-721 Using Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", - "relevance": "Alternative CLI-based NFT deployment using Hardhat \u2014 better for production or CI/CD workflows." + "relevance": "Alternative CLI-based NFT deployment using Hardhat — better for production or CI/CD workflows." } ] }, @@ -5759,7 +5759,7 @@ { "id": "set-up-foundry-polkadot-hub", "title": "Use Foundry with Polkadot Hub", - "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly \u2014 stable release lacks Polkadot chain definitions.", + "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -5776,10 +5776,10 @@ "curl installed" ], "network": [ - "Polkadot Hub TestNet (chain ID 420420417) \u2014 supported natively by Foundry nightly as --chain polkadot-testnet" + "Polkadot Hub TestNet (chain ID 420420417) — supported natively by Foundry nightly as --chain polkadot-testnet" ], "tokens": [ - "Testnet PAS tokens for deployment \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens for deployment — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -5802,7 +5802,7 @@ "foundryup --version nightly" ], "expected_output": "forge Version: (nightly build version string)", - "description": "Install foundryup then the Foundry nightly build. Nightly is required \u2014 stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." + "description": "Install foundryup then the Foundry nightly build. Nightly is required — stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." }, { "order": 2, @@ -5848,7 +5848,7 @@ "forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url https://services.polkadothub-rpc.com/testnet --private-key $PRIVATE_KEY --broadcast" ], "expected_output": "Deployed to: 0x...", - "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address \u2014 needed for verification and interaction." + "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address — needed for verification and interaction." }, { "order": 7, @@ -5950,7 +5950,7 @@ "prerequisites": { "runtime": [ "Unix-based OS (Linux or macOS) or Windows WSL", - "polkadot and polkadot-parachain binaries \u2014 downloaded via: zombienet setup polkadot polkadot-parachain" + "polkadot and polkadot-parachain binaries — downloaded via: zombienet setup polkadot polkadot-parachain" ] }, "env_vars": [], @@ -5990,7 +5990,7 @@ "zombienet spawn network.toml --provider native" ], "expected_output": "Network launched\nRPC endpoints:", - "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal \u2014 the network persists until you stop it with Ctrl+C." + "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal — the network persists until you stop it with Ctrl+C." }, { "order": 5, @@ -6083,7 +6083,7 @@ { "id": "deploy-interact-contracts-web3js", "title": "Deploy and Interact with Smart Contracts Using Web3.js", - "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset \u2014 for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", + "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", "version": "1.0.1", "chain_role": "isolated", "invocation": "user", @@ -6102,7 +6102,7 @@ "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" ], "tokens": [ - "Testnet PAS tokens for deployment gas \u2014 get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + "Testnet PAS tokens for deployment gas — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" ], "wallet": [ "An EVM private key (0x-prefixed) for a funded testnet account" @@ -6270,7 +6270,7 @@ "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction" } ], - "project_structure": "web3js-project/\n\u251c\u2500\u2500 abis/\n\u251c\u2500\u2500 artifacts/\n\u251c\u2500\u2500 contracts/\n\u2502 \u2514\u2500\u2500 Storage.sol\n\u251c\u2500\u2500 scripts/\n\u2502 \u251c\u2500\u2500 compile.js\n\u2502 \u251c\u2500\u2500 connectToProvider.js\n\u2502 \u251c\u2500\u2500 deploy.js\n\u2502 \u2514\u2500\u2500 updateStorage.js\n\u251c\u2500\u2500 contract-address.json\n\u2514\u2500\u2500 package.json" + "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json" }, { "id": "add-existing-pallet-to-runtime", @@ -6293,7 +6293,7 @@ "Polkadot SDK system dependencies installed (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" ], "network": [ - "No external network required \u2014 all compilation and testing is local" + "No external network required — all compilation and testing is local" ] }, "env_vars": [], @@ -6323,25 +6323,25 @@ "commands": [ "grep 'polkadot-sdk' runtime/Cargo.toml | head -5" ], - "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version \u2014 you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." + "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version — you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." }, { "order": 4, "action": "Add the pallet dependency to runtime/Cargo.toml", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 \u2014 the output should not show dependency resolution errors." + "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 — the output should not show dependency resolution errors." }, { "order": 5, "action": "Implement the pallet's Config trait in runtime/src/lib.rs", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified \u2014 the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." + "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified — the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." }, { "order": 6, "action": "Register the pallet in construct_runtime!", "working_directory": "polkadot-sdk-parachain-template", - "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes \u2014 choose carefully as it cannot be changed without a migration. Save the file." + "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes — choose carefully as it cannot be changed without a migration. Save the file." }, { "order": 7, @@ -6378,7 +6378,7 @@ { "pattern": "error[E0432]: unresolved import `pallet_utility`", "cause": "pallet-utility was not added to runtime/Cargo.toml, or it was added but Cargo has not re-resolved dependencies.", - "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again \u2014 Cargo will fetch and compile the new dependency." + "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again — Cargo will fetch and compile the new dependency." }, { "pattern": "Utility pallet does not appear in Polkadot.js Apps extrinsics", @@ -6435,7 +6435,7 @@ "scenario": "Edge case: compilation fails with missing associated type in Config", "user_says": "I get a compiler error about missing associated types in my Config impl", "actions": [ - "Read the full compiler error \u2014 each missing type is listed with 'required by this bound'", + "Read the full compiler error — each missing type is listed with 'required by this bound'", "Add each missing associated type to the impl block", "Refer to the pallet's source (src/lib.rs) or docs for the expected type definitions", "Re-run cargo build --release" @@ -6466,7 +6466,7 @@ "Familiarity with adding a single pallet to a runtime (see add-existing-pallet-to-runtime)" ], "network": [ - "No external network required \u2014 local development only" + "No external network required — local development only" ] }, "env_vars": [], @@ -6499,7 +6499,7 @@ "order": 4, "action": "Add pallet dependencies to runtime/Cargo.toml", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances \u2014 add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" + "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances — add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" }, { "order": 5, @@ -6511,13 +6511,13 @@ "order": 6, "action": "Implement Config for Council", "working_directory": "polkadot-sdk-parachain-template", - "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage \u2014 `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." + "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage — `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." }, { "order": 7, "action": "Register both instances in construct_runtime!", "working_directory": "polkadot-sdk-parachain-template", - "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics \u2014 choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." + "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics — choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." }, { "order": 8, @@ -6536,7 +6536,7 @@ "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/instances-spec.json", "./target/release/parachain-template-node --dev --tmp --chain /tmp/instances-spec.json" ], - "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics \u2014 verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." + "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics — verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." } ], "reference_code": { @@ -6548,12 +6548,12 @@ "error_patterns": [ { "pattern": "error[E0119]: conflicting implementations of trait `pallet_collective::Config`", - "cause": "Two impl blocks are targeting the same instance type \u2014 usually because both use the default instance () instead of Instance1/Instance2.", + "cause": "Two impl blocks are targeting the same instance type — usually because both use the default instance () instead of Instance1/Instance2.", "resolution": "Ensure each impl block explicitly names a different instance: impl pallet_collective::Config and impl pallet_collective::Config. Type aliases (type TechnicalCommitteeInstance = pallet_collective::Instance1;) help clarify intent." }, { "pattern": "error: the pallet does not implement the Instance trait", - "cause": "The pallet does not support instantiation \u2014 it lacks the I: 'static = () generic in its Config trait.", + "cause": "The pallet does not support instantiation — it lacks the I: 'static = () generic in its Config trait.", "resolution": "Only instantiable pallets (those with a second generic parameter) can be used this way. Check the pallet source or use a different pallet that supports multiple instances." }, { @@ -6569,13 +6569,13 @@ "slug": "parachains-customize-runtime-add-existing-pallets", "title": "Add an Existing Pallet to the Runtime", "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Prerequisite: adding a single pallet to a runtime \u2014 covers the Cargo, Config, and construct_runtime! basics." + "relevance": "Prerequisite: adding a single pallet to a runtime — covers the Cargo, Config, and construct_runtime! basics." }, { "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", "title": "Create a Custom Pallet", "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Building a custom pallet from scratch \u2014 next step after mastering pallet integration." + "relevance": "Building a custom pallet from scratch — next step after mastering pallet integration." }, { "slug": "parachains-install-polkadot-sdk", @@ -6590,7 +6590,7 @@ "scenario": "Common scenario: add TechnicalCommittee and Council as two pallet-collective instances", "user_says": "Add a TechnicalCommittee and a Council to my parachain runtime using pallet-collective", "actions": [ - "Verify pallet-collective has the I: 'static = () generic \u2014 confirm it is instantiable", + "Verify pallet-collective has the I: 'static = () generic — confirm it is instantiable", "Add pallet-collective to runtime/Cargo.toml with default-features = false and add to std features", "Implement pallet_collective::Config as TechnicalCommitteeInstance with committee-appropriate parameters", "Implement pallet_collective::Config as CouncilInstance with council-appropriate parameters", @@ -6598,7 +6598,7 @@ "Run cargo build --release", "Start node in --dev mode and verify both 'technicalCommittee' and 'council' appear in Polkadot.js Apps" ], - "result": "Two independent pallet-collective instances registered \u2014 TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" + "result": "Two independent pallet-collective instances registered — TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" }, { "scenario": "Edge case: runtime compiles but only one instance shows in the UI", @@ -6634,7 +6634,7 @@ "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" ], "network": [ - "No external network required \u2014 all work is local" + "No external network required — all work is local" ] }, "env_vars": [], @@ -6683,7 +6683,7 @@ "order": 6, "action": "Implement pallet_counter::Config in runtime/src/lib.rs", "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 \u2014 adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." + "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 — adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." }, { "order": 7, @@ -6700,7 +6700,7 @@ "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/counter-spec.json", "./target/release/parachain-template-node --dev --tmp --chain /tmp/counter-spec.json" ], - "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics \u2014 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." + "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics — 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." } ], "reference_code": { @@ -6713,12 +6713,12 @@ { "pattern": "error[E0412]: cannot find type `ConstU32` in this scope", "cause": "ConstU32 is not imported in runtime/src/lib.rs.", - "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it \u2014 search for existing ConstU32 usages to confirm." + "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it — search for existing ConstU32 usages to confirm." }, { "pattern": "error[E0277]: the trait `pallet_counter::Config` is not implemented", "cause": "The impl pallet_counter::Config for Runtime block is missing or has a missing associated type.", - "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type \u2014 add them one by one." + "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type — add them one by one." }, { "pattern": "error: failed to select a version for `pallet-counter` / could not find Cargo.toml", @@ -6728,7 +6728,7 @@ { "pattern": "CounterOverflow error when submitting increment", "cause": "The counter has reached MaxValue (100 by default).", - "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design \u2014 the error confirms the overflow protection is working." + "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design — the error confirms the overflow protection is working." } ], "supplementary_context": { @@ -6750,7 +6750,7 @@ "slug": "parachains-customize-runtime-add-existing-pallets", "title": "Add an Existing Pallet to the Runtime", "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Adding an existing SDK pallet to a runtime \u2014 prerequisite concepts for runtime integration." + "relevance": "Adding an existing SDK pallet to a runtime — prerequisite concepts for runtime integration." }, { "slug": "parachains-customize-runtime-pallet-development-mock-runtime", @@ -6804,8 +6804,8 @@ "primary_page": "reference/parachains/chain-data.md", "prerequisites": { "runtime": [ - "curl (Method A \u2014 available by default on macOS and most Linux distros)", - "subxt CLI (Method B \u2014 install with: cargo install subxt-cli)" + "curl (Method A — available by default on macOS and most Linux distros)", + "subxt CLI (Method B — install with: cargo install subxt-cli)" ], "network": [ "An accessible RPC endpoint for the target Polkadot SDK node (e.g. https://rpc.polkadot.io for mainnet, or http://127.0.0.1:9944 for a local dev node)" @@ -6820,7 +6820,7 @@ "commands": [ "curl -H \"Content-Type: application/json\" -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\"}' https://rpc.polkadot.io" ], - "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string \u2014 not human-readable. Use Method B or C if you need human-readable JSON.", + "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string — not human-readable. Use Method B or C if you need human-readable JSON.", "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":\"0x6d657461...\",\"id\":1}" }, { @@ -6843,7 +6843,7 @@ "order": 4, "action": "Interpret the metadata output", "working_directory": ".", - "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type \u2192 find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions \u2014 do not hardcode them." + "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type → find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions — do not hardcode them." } ], "reference_code": { @@ -6855,7 +6855,7 @@ { "pattern": "curl returns connection refused or timeout", "cause": "The RPC endpoint URL is incorrect, the node is not running, or the endpoint does not accept HTTP connections.", - "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections \u2014 use subxt (Method B) with ws:// or wss:// instead of curl." + "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections — use subxt (Method B) with ws:// or wss:// instead of curl." }, { "pattern": "subxt metadata fails with 'failed to fetch metadata'", @@ -6875,7 +6875,7 @@ "slug": "reference-tools-subxt", "title": "Subxt Rust API", "url": "https://docs.polkadot.com/reference/tools/subxt.md", - "relevance": "Subxt library usage for generating typed client code from runtime metadata \u2014 the primary consumer of metadata in Rust applications." + "relevance": "Subxt library usage for generating typed client code from runtime metadata — the primary consumer of metadata in Rust applications." }, { "slug": "reference-parachains-data-encoding", @@ -6911,7 +6911,7 @@ { "id": "use-polkadot-js-api", "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", - "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode \u2014 new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", + "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode — new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -6962,7 +6962,7 @@ "printf 'WS_ENDPOINT=\\nMNEMONIC=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly \u2014 do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." + "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly — do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." }, { "order": 3, @@ -7020,13 +7020,13 @@ "slug": "reference-tools-papi", "title": "Polkadot-API", "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Polkadot API (PAPI) \u2014 the recommended replacement for @polkadot/api in new projects." + "relevance": "Polkadot API (PAPI) — the recommended replacement for @polkadot/api in new projects." }, { "slug": "reference-tools-dedot", "title": "Dedot", "url": "https://docs.polkadot.com/reference/tools/dedot.md", - "relevance": "Dedot \u2014 modern lightweight alternative to @polkadot/api with better TypeScript inference." + "relevance": "Dedot — modern lightweight alternative to @polkadot/api with better TypeScript inference." }, { "slug": "chain-interactions-send-transactions-with-sdks", @@ -7063,7 +7063,7 @@ { "id": "deploy-basic-contract-remix", "title": "Deploy a Basic Smart Contract with Remix IDE", - "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions \u2014 no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", + "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7101,7 +7101,7 @@ "order": 2, "action": "Open Remix IDE and locate Storage.sol", "working_directory": ".", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract \u2014 no changes needed." + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed." }, { "order": 3, @@ -7114,7 +7114,7 @@ "order": 4, "action": "Deploy to Polkadot Hub TestNet via MetaMask", "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission \u2014 click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation \u2014 click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", + "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel." }, { @@ -7203,7 +7203,7 @@ { "id": "connect-remix-polkadot", "title": "Connect Remix IDE to Polkadot Hub", - "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) \u2014 use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", + "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7233,13 +7233,13 @@ "order": 2, "action": "Navigate to the Deploy and Run Transactions tab", "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond \u2014 the fourth icon from top). The Deploy and Run Transactions panel opens on the left." + "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left." }, { "order": 3, "action": "Select Injected Provider - MetaMask", "working_directory": ".", - "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup \u2014 click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." + "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." }, { "order": 4, @@ -7346,7 +7346,7 @@ "order": 2, "action": "Enter Polkadot Hub TestNet network details", "working_directory": ".", - "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank \u2014 no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." + "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." }, { "order": 3, @@ -7441,7 +7441,7 @@ { "id": "use-wagmi-polkadot-hub", "title": "Build a Wagmi dApp Connected to Polkadot Hub", - "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo \u2014 all code is inlined in the steps.", + "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo — all code is inlined in the steps.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7460,10 +7460,10 @@ "MetaMask or any EIP-1193 wallet installed in the browser" ], "network": [ - "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Small PAS balance for write transactions \u2014 obtain from https://faucet.polkadot.io/" + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -7477,7 +7477,7 @@ "cd wagmi-dapp", "npm install" ], - "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input \u2014 `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm \u2014 skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." + "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input — `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm — skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." }, { "order": 2, @@ -7516,13 +7516,13 @@ "order": 7, "action": "Add Storage contract read interaction using useReadContract", "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type \u2014 without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." + "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type — without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." }, { "order": 8, "action": "Add Storage contract write interaction using useWriteContract", "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee \u2014 get tokens from ." + "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from ." }, { "order": 9, @@ -7539,7 +7539,7 @@ "action": "Connect wallet and test contract interactions in the browser", "working_directory": "wagmi-dapp", "interactive": true, - "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation \u2014 approve it. After confirmation, the stored value should update to the new number.", + "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation — approve it. After confirmation, the stored value should update to the new number.", "expected_output": "Wallet connected, block number displayed, Storage contract read/write working" } ], @@ -7577,13 +7577,13 @@ "slug": "smart-contracts-libraries-viem", "title": "viem for Polkadot Hub Smart Contracts", "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Lower-level viem library for direct contract calls without React hooks \u2014 useful when Wagmi is not needed." + "relevance": "Lower-level viem library for direct contract calls without React hooks — useful when Wagmi is not needed." }, { "slug": "smart-contracts-integrations-wallets", "title": "Wallets for Polkadot Hub", "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask \u2014 required before the wallet connection step works." + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required before the wallet connection step works." }, { "slug": "smart-contracts-libraries-wagmi", @@ -7609,7 +7609,7 @@ }, { "scenario": "Edge case: user wants to call their own deployed contract instead of the pre-deployed Storage", - "user_says": "I deployed my own ERC-20 token \u2014 how do I call it with Wagmi useReadContract?", + "user_says": "I deployed my own ERC-20 token — how do I call it with Wagmi useReadContract?", "actions": [ "Replace CONTRACT_ADDRESS in StorageRead.tsx with the user's deployed contract address", "Replace STORAGE_ABI with the ERC-20 ABI fragment for the desired function (e.g. balanceOf)", @@ -7623,7 +7623,7 @@ { "id": "deploy-interact-contracts-web3py", "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", - "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset \u2014 for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", + "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7639,19 +7639,19 @@ "Solidity compiler (installed automatically by py-solc-x)" ], "network": [ - "Polkadot Hub TestNet \u2014 RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" + "Polkadot Hub TestNet — RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" ], "tokens": [ - "Small PAS balance for deployment gas \u2014 obtain from https://faucet.polkadot.io/" + "Small PAS balance for deployment gas — obtain from https://faucet.polkadot.io/" ], "wallet": [ - "An EVM private key (PRIVATE_KEY stored in .env \u2014 never enter in chat)" + "An EVM private key (PRIVATE_KEY stored in .env — never enter in chat)" ] }, "env_vars": [ { "name": "PRIVATE_KEY", - "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat \u2014 they should edit the .env file directly.", + "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", "required": true } ], @@ -7674,13 +7674,13 @@ "commands": [ "pip install web3 py-solc-x python-dotenv" ], - "description": "Install web3 (Web3.py \u2014 Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." + "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." }, { "order": 3, "action": "Create the .env file with PRIVATE_KEY", "working_directory": "web3py-contracts", - "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat \u2014 instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" + "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" }, { "order": 4, @@ -7692,17 +7692,17 @@ "order": 5, "action": "Create compile.py to compile the Solidity contract", "working_directory": "web3py-contracts", - "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful \u2014 compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", + "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", "commands": [ "python compile.py" ], - "expected_output": "Compilation successful \u2014 compiled.json written." + "expected_output": "Compilation successful — compiled.json written." }, { "order": 6, "action": "Create deploy.py to deploy the contract", "working_directory": "web3py-contracts", - "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 \u2014 no substitutions needed.", + "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", "commands": [ "python deploy.py" ], @@ -7759,7 +7759,7 @@ "slug": "smart-contracts-libraries-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js library \u2014 recommended JavaScript alternative to the sunset Web3.js library." + "relevance": "Ethers.js library — recommended JavaScript alternative to the sunset Web3.js library." }, { "slug": "smart-contracts-connect", @@ -7785,7 +7785,7 @@ }, { "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", - "user_says": "I have my own Solidity file \u2014 how do I deploy it with Web3.py?", + "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?", "actions": [ "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", @@ -7799,7 +7799,7 @@ { "id": "interact-erc20-precompile-polkadot-hub", "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", - "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page \u2014 load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", + "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7817,10 +7817,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "PAS balance in MetaMask for transfer/approve operations \u2014 obtain from https://faucet.polkadot.io/" + "PAS balance in MetaMask for transfer/approve operations — obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -7830,7 +7830,7 @@ "action": "Find the ERC-20 precompile address from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address \u2014 it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." + "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." }, { "order": 2, @@ -7851,7 +7851,7 @@ "action": "Connect Remix to MetaMask and load the precompile using At Address", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up \u2014 confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." + "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." }, { "order": 5, @@ -7859,14 +7859,14 @@ "working_directory": ".", "interactive": true, "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", - "expected_output": "uint256 balance in wei \u2014 should match MetaMask PAS balance * 10^18" + "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18" }, { "order": 6, "action": "Call approve and transfer functions", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation \u2014 approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", + "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console" } ], @@ -7882,7 +7882,7 @@ "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." }, { - "pattern": "MetaMask: Wrong network \u2014 expected Custom (420420417)", + "pattern": "MetaMask: Wrong network — expected Custom (420420417)", "cause": "MetaMask is still on Ethereum Mainnet or another network.", "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." }, @@ -7905,7 +7905,7 @@ "slug": "smart-contracts-integrations-wallets", "title": "Wallets for Polkadot Hub", "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask \u2014 required prerequisite for this skill." + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite for this skill." }, { "slug": "smart-contracts-dev-environments-remix", @@ -7946,7 +7946,7 @@ { "id": "interact-storage-precompile-remix", "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", - "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page \u2014 load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", + "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -7964,10 +7964,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "PAS balance for setBytes write transactions \u2014 obtain from https://faucet.polkadot.io/" + "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -7977,7 +7977,7 @@ "action": "Find the Storage precompile address from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address \u2014 it will be used in step 4 as the 'At Address' target in Remix." + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." }, { "order": 2, @@ -8005,7 +8005,7 @@ "action": "Call setBytes to store a value", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value \u2014 for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store \u2014 for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation \u2014 approve it. Wait for the transaction to confirm.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", "expected_output": "Transaction confirmed; tx hash shown in Remix console" }, { @@ -8058,7 +8058,7 @@ "slug": "smart-contracts-integrations-wallets", "title": "Wallets for Polkadot Hub", "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask \u2014 required prerequisite." + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite." } ] }, @@ -8093,7 +8093,7 @@ { "id": "interact-system-precompile-remix", "title": "Interact with the System Precompile on Polkadot Hub via Remix", - "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values \u2014 the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page \u2014 load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", + "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8111,10 +8111,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Small PAS balance for write transactions \u2014 obtain from https://faucet.polkadot.io/" + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -8124,7 +8124,7 @@ "action": "Find the System precompile address from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address \u2014 it will be used in step 4 as the 'At Address' target in Remix." + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." }, { "order": 2, @@ -8138,7 +8138,7 @@ "action": "Create the System precompile interface in Remix", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures \u2014 the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." }, { "order": 4, @@ -8152,7 +8152,7 @@ "action": "Call blake2b to hash data", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash \u2014 for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data" }, { @@ -8213,7 +8213,7 @@ "slug": "smart-contracts-precompiles-erc20", "title": "Interact with the ERC20 Precompile", "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", - "relevance": "ERC-20 precompile \u2014 often used alongside the System precompile in contract interactions." + "relevance": "ERC-20 precompile — often used alongside the System precompile in contract interactions." } ] }, @@ -8241,7 +8241,7 @@ "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", "Click call and confirm the result is true" ], - "result": "bool true returned \u2014 sr25519 signature verified on-chain via the System precompile" + "result": "bool true returned — sr25519 signature verified on-chain via the System precompile" } ] }, @@ -8266,10 +8266,10 @@ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" ], "network": [ - "Polkadot Hub TestNet \u2014 chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" ], "tokens": [ - "Small PAS balance for write transactions \u2014 obtain from https://faucet.polkadot.io/" + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" ] }, "env_vars": [], @@ -8279,7 +8279,7 @@ "action": "Find the XCM precompile address and IXcm ABI from the source page", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address \u2014 it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." }, { "order": 2, @@ -8293,21 +8293,21 @@ "action": "Create the IXcm interface in Remix and compile it", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) \u2014 the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." }, { "order": 4, "action": "Load the XCM precompile using At Address in Remix", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection \u2014 verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." }, { "order": 5, "action": "Call weighMessage to estimate XCM execution cost", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button \u2014 read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost" }, { @@ -8315,7 +8315,7 @@ "action": "Call execute to run an XCM message locally", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button \u2014 state-changing). MetaMask will prompt for gas approval \u2014 confirm. The call executes the XCM message in the context of the calling address on the local chain.", + "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs" }, { @@ -8323,7 +8323,7 @@ "action": "Call send to dispatch an XCM to another chain", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas \u2014 confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous \u2014 use the xcm-tools skill to monitor delivery if needed.", + "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs" } ], @@ -8425,11 +8425,11 @@ "chain-spec-builder binary: cargo install chain-spec-builder" ], "network": [ - "Paseo TestNet \u2014 wss://rpc.ibp.network/paseo", + "Paseo TestNet — wss://rpc.ibp.network/paseo", "Server with TCP port 30333 open for P2P collator connections" ], "tokens": [ - "PAS tokens from https://faucet.polkadot.io/ \u2014 required for para ID reservation and parathread registration fees" + "PAS tokens from https://faucet.polkadot.io/ — required for para ID reservation and parathread registration fees" ], "wallet": [ "Polkadot.js browser extension (https://polkadot.js.org/extension/) with a PAS-funded account" @@ -8449,7 +8449,7 @@ "action": "Reserve a para ID on Paseo TestNet", "working_directory": ".", "interactive": true, - "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID \u2014 record it as PARA_ID (needed in all subsequent steps).", + "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID — record it as PARA_ID (needed in all subsequent steps).", "expected_output": "Para ID assigned and visible in the Parathreads tab" }, { @@ -8506,7 +8506,7 @@ "commands": [ "polkadot-omni-node generate-node-key --file node-key.dat" ], - "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure \u2014 losing it changes the peer ID on next restart." + "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure — losing it changes the peer ID on next restart." }, { "order": 9, @@ -8525,7 +8525,7 @@ "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"aura\",\"INSERT_AURA_SEED_PHRASE\",\"INSERT_AURA_PUBLIC_KEY_HEX\"],\"id\":1}'", "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"gran\",\"INSERT_GRANDPA_SEED_PHRASE\",\"INSERT_GRANDPA_PUBLIC_KEY_HEX\"],\"id\":1}'" ], - "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history \u2014 they contain seed phrases. Run them directly in the server terminal.", + "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history — they contain seed phrases. Run them directly in the server terminal.", "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1} for each key insertion" } ], @@ -8582,7 +8582,7 @@ "examples": [ { "scenario": "Common scenario: first deployment to Paseo after building the template", - "user_says": "I built the parachain template \u2014 how do I deploy it to Paseo TestNet?", + "user_says": "I built the parachain template — how do I deploy it to Paseo TestNet?", "actions": [ "Get PAS tokens from faucet", "Reserve a para ID via Polkadot.js Apps", @@ -8600,7 +8600,7 @@ "Verify port 30333 is open on the server firewall", "Confirm the startup command includes the '-- --chain paseo' relay chain section", "Add a known Paseo bootnode with '--bootnodes /dns/...' to the startup flags", - "Wait 10 minutes for peer discovery \u2014 initial warp sync connection can be slow" + "Wait 10 minutes for peer discovery — initial warp sync connection can be slow" ], "result": "Collator finds relay chain peers and begins syncing; block production starts after parathread onboards" } @@ -8609,7 +8609,7 @@ { "id": "connect-polkadot-hub-testnet", "title": "Connect to Polkadot Hub and Get Test Tokens", - "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference \u2014 consult it for endpoint URLs and chain IDs needed by other skills.", + "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", "version": "1.0.0", "chain_role": "isolated", "invocation": "user", @@ -8691,7 +8691,7 @@ "examples": [ { "scenario": "Common scenario: set up development environment from scratch", - "user_says": "I want to deploy a smart contract on Polkadot Hub \u2014 how do I connect to TestNet?", + "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?", "actions": [ "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", "Request PAS tokens from https://faucet.polkadot.io/", @@ -8704,7 +8704,7 @@ "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?", "actions": [ "Reference the MainNet network parameters from the source page", - "Warn: MainNet uses real DOT \u2014 use TestNet (chain ID 420420417) for development and testing" + "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" ], "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage" } @@ -9555,6 +9555,190 @@ "result": "Compilation succeeds after setting bytecodeHash: 'none' to exclude metadata hash and keep contract under the 24KB EIP-170 limit" } ] + }, + { + "id": "deploy-uniswap-v3-periphery-evm", + "title": "Deploy Uniswap V3 Periphery on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Periphery contracts (SwapRouter, NonfungiblePositionManager) with Hardhat, converts Hardhat vars to dotenv, and deploys all four contracts (UniswapV3Factory, WETH9, SwapRouter, NonfungiblePositionManager) to Polkadot Hub TestNet in a single Hardhat Ignition run. The V3 Core contracts are resolved automatically from a local sibling package reference. Use when building a full-stack concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Periphery Polkadot', 'SwapRouter NonfungiblePositionManager Polkadot Hub', 'Uniswap V3 full deployment EVM Polkadot'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", + "prerequisites": { + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-periphery-hardhat/\n ├── contracts/\n │ ├── SwapRouter.sol\n │ ├── NonfungiblePositionManager.sol\n │ ├── NonfungibleTokenPositionDescriptor.sol\n │ ├── base/\n │ ├── interfaces/\n │ ├── lens/\n │ ├── libraries/\n │ └── test/\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Periphery.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n ├── package.json\n └── tsconfig.json", + "steps": [ + { + "order": 1, + "action": "Clone the revm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 96696ad15c3cf01b9168a71ad5114f27c34a8726", + "cd uniswap-v3-periphery-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Periphery project. The monorepo layout is critical: the Periphery project depends on V3 Core contracts through a local file reference (`\"@uniswap/v3-core\": \"file:../uniswap-v3-core-hardhat\"`), so both sibling directories must be present." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies. `npm install` resolves the local `@uniswap/v3-core` sibling package automatically — no separate step is needed. Then add `dotenv` to replace Hardhat's interactive vars system with .env-based private key management." + }, + { + "order": 3, + "action": "Create .env with private key placeholder", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from \"hardhat/config\";` to `import { HardhatUserConfig } from \"hardhat/config\";`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet base fee).\n5. Confirm `ignition.requiredConfirmations` is 1 (already set).\n6. Preserve `bytecodeHash: \"none\"` in the Solidity compiler settings — required so the compiled UniswapV3Pool bytecode hash matches the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol, enabling correct CREATE2 pool address derivation.\n7. Preserve `allowUnlimitedContractSize: true` for the hardhat network — several Periphery contracts exceed the 24KB EIP-170 limit and require this for local testing.\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 39 Solidity files successfully", + "description": "Compile SwapRouter, NonfungiblePositionManager, and all supporting periphery contracts (Solidity 0.7.6). The `bytecodeHash: 'none'` setting ensures the Pool bytecode hash matches PoolAddress.sol's hardcoded constant, which is required for CREATE2 pool address computation to work correctly during swaps and LP operations." + }, + { + "order": 6, + "action": "Deploy all contracts to Polkadot Hub TestNet via Hardhat Ignition", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV3PeripheryModule#UniswapV3Factory deployed at 0x...\nUniswapV3PeripheryModule#WETH9 deployed at 0x...\nUniswapV3PeripheryModule#SwapRouter deployed at 0x...\nUniswapV3PeripheryModule#NonfungiblePositionManager deployed at 0x...", + "interactive": true, + "description": "Deploy all four contracts using Hardhat Ignition. The module deploys UniswapV3Factory and WETH9 in the first batch (in parallel), then SwapRouter and NonfungiblePositionManager once their dependencies are ready. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save all four deployed addresses: they are needed for any downstream swap or LP interaction." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." + }, + { + "pattern": "vars is not defined / Cannot read properties of undefined (reading 'has')", + "cause": "hardhat.config.ts still uses Hardhat vars.get() or vars.has().", + "resolution": "Add `import 'dotenv/config';` as the first line and replace `vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of a pending transaction due to TestNet latency or gas underpricing.", + "resolution": "Delete `ignition/deployments/`, verify gasPrice is 5000000000000 and requiredConfirmations is 1, then redeploy. If the deployment receipt exists in deployed_addresses.json but code is missing at the address, wait and verify via eth_getCode before retrying." + }, + { + "pattern": "POOL_INIT_CODE_HASH mismatch / pool address computation incorrect", + "cause": "bytecodeHash is not set to 'none', causing the compiled Pool bytecode to differ from the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol.", + "resolution": "In hardhat.config.ts under Solidity compiler settings, ensure `metadata: { bytecodeHash: 'none' }` is present. Recompile after the fix." + }, + { + "pattern": "Error: cannot estimate gas / contract deployment failed during testing", + "cause": "The hardhat network is missing allowUnlimitedContractSize: true, causing Periphery contracts that exceed 24KB to fail during local testing.", + "resolution": "In hardhat.config.ts, ensure `networks.hardhat.allowUnlimitedContractSize = true` is set. This flag only applies to the in-process Hardhat network used for testing; it is not needed for TestNet deployment." + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 Core as a prerequisite, testing against a local development node, or the Hardhat environment setup.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", + "title": "Uniswap V3 Core with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "relevance": "The V3 Core contracts (UniswapV3Factory, UniswapV3Pool) are resolved automatically via local npm reference — no separate Core tutorial step required, but this page explains the Factory/Pool architecture the Periphery builds on." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Required for running the 39-test suite (SwapRouter and NonfungiblePositionManager tests) against a local Polkadot node via `npx hardhat test --network localNode` before TestNet deployment." + }, + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Reference for Hardhat network configuration, Ignition deployment options, and gas settings specific to Polkadot Hub." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy all Uniswap V3 Periphery contracts to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V3 SwapRouter and NonfungiblePositionManager to Polkadot Hub", + "actions": [ + "Clone revm-hardhat-examples, check out pinned commit, cd uniswap-v3-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY placeholder, ask user to fill it in", + "Convert hardhat.config.ts: add dotenv import, replace vars.get with process.env, add gasPrice: 5000000000000", + "Run npx hardhat compile (expects 39 Solidity files compiled successfully)", + "Run npx hardhat ignition deploy UniswapV3Periphery.ts --network polkadotTestnet, confirm when prompted", + "Save deployed addresses for UniswapV3Factory, WETH9, SwapRouter, and NonfungiblePositionManager" + ], + "result": "All four Uniswap V3 Periphery contracts deployed to Polkadot Hub TestNet — SwapRouter for token swaps and NonfungiblePositionManager for concentrated liquidity LP positions" + }, + { + "scenario": "Edge case: deployment fails with IGN401 or Transaction Already Imported", + "user_says": "Ignition reports the transaction was dropped and retrying gives 'Transaction Already Imported'", + "actions": [ + "Do not retry at the same gas price — that will fail again with the same error", + "Check ignition/deployments/ and deployed_addresses.json for any partial state", + "Verify the contract is not already deployed: run eth_getCode at any addresses in deployed_addresses.json", + "If not deployed, delete the ignition/deployments/ directory", + "Confirm gasPrice: 5000000000000 and requiredConfirmations: 1 are set in hardhat.config.ts", + "Re-run: npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" + ], + "result": "Deployment completes cleanly after removing stale Ignition state and confirming gas configuration" + } + ] } ], "outputs": { diff --git a/skill_candidates.json b/skill_candidates.json index 5116dc295..413d440e4 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,6 +1,6 @@ { "schema_version": "2", - "generated": "2026-05-15T14:58:47Z", + "generated": "2026-05-15T16:00:00Z", "project_id": "polkadot-docs", "scoring_rubric_version": "1.0", "candidates": [ @@ -281,7 +281,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent \u2014 requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent \u2014 bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", + "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent — requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent — bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", "scoring": { "signals": [ "P1", @@ -345,7 +345,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:30:00Z", - "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account \u2014 no real tokens needed). Self-contained. No cookbook badge.", + "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account — no real tokens needed). Self-contained. No cookbook badge.", "scoring": { "signals": [ "P1", @@ -490,7 +490,7 @@ }, "priority_score": 11 }, - "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) \u2014 prerequisite provisioning is outside current skill capability." + "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) — prerequisite provisioning is outside current skill capability." }, { "skill_id": "install-polkadot-sdk", @@ -502,7 +502,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch \u2014 covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", + "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch — covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", "scoring": { "signals": [ "P1", @@ -535,7 +535,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T19:00:00Z", - "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent \u2014 directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 \u2014 requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime).", + "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent — directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 — requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime).", "scoring": { "signals": [ "P1", @@ -666,7 +666,7 @@ "category": "guide", "priority": "high", "status": "blocked", - "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 \u2192 guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", + "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 → guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", "scoring": { "signals": [ "P2", @@ -699,7 +699,7 @@ "category": "guide", "priority": "medium", "status": "blocked", - "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 \u2192 Rule 6 guide. K1=0 \u2014 prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", + "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 → Rule 6 guide. K1=0 — prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", "scoring": { "signals": [ "P2", @@ -730,7 +730,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present \u2014 numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", + "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present — numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", "scoring": { "signals": [ "P1", @@ -761,7 +761,7 @@ "category": "tutorial", "priority": "low", "status": "blocked", - "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 \u2014 intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", + "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 — intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -789,7 +789,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 \u2014 the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", + "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 — the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -821,7 +821,7 @@ "category": "tutorial", "priority": "low", "status": "blocked", - "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 \u2014 intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", + "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 — intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", "scoring": { "signals": [ "P1", @@ -854,7 +854,7 @@ "composite": true, "priority": "medium", "status": "blocked", - "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 \u2192 medium.", + "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 → medium.", "scoring": { "signals": [ "P1", @@ -920,7 +920,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself \u2014 delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", + "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself — delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", "scoring": { "signals": [ "P1", @@ -956,7 +956,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 \u2014 requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 \u2192 medium.", + "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 — requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 → medium.", "scoring": { "signals": [ "P1", @@ -988,7 +988,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 \u2014 requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 \u2192 medium.", + "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 — requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 → medium.", "scoring": { "signals": [ "P1", @@ -1020,7 +1020,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 \u2014 requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", + "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 — requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", "scoring": { "signals": [ "P1", @@ -1053,7 +1053,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 \u2014 requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", + "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 — requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", "scoring": { "signals": [ "P1", @@ -1087,7 +1087,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 \u2014 requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", + "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 — requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", "scoring": { "signals": [ "P1", @@ -1121,7 +1121,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 \u2014 requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", + "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 — requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", "scoring": { "signals": [ "P1", @@ -1231,7 +1231,7 @@ "category": "tutorial", "priority": "low", "status": "built", - "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 \u2014 intro explicitly references set-up-the-parachain-template as required prior step. K2=0 \u2014 INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", + "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 — intro explicitly references set-up-the-parachain-template as required prior step. K2=0 — INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", "scoring": { "signals": [ "P1", @@ -1263,7 +1263,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 \u2014 explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template \u2192 deploy-to-polkadot \u2192 obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", + "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 — explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", "scoring": { "signals": [ "P1", @@ -1288,7 +1288,7 @@ ], "priority_score": 9 }, - "blocked_reason": "Requires acquiring on-chain coretime on a live network \u2014 financial and governance prerequisites make it impractical as a generated skill." + "blocked_reason": "Requires acquiring on-chain coretime on a live network — financial and governance prerequisites make it impractical as a generated skill." }, { "skill_id": "renew-parachain-coretime", @@ -1299,7 +1299,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 \u2014 requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", + "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 — requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", "scoring": { "signals": [ "P1", @@ -1335,7 +1335,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 \u2014 requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 \u2014 code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", + "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 — requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 — code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", "scoring": { "signals": [ "P1", @@ -1368,7 +1368,7 @@ "category": "guide", "priority": "low", "status": "blocked", - "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 \u2192 Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 \u2014 requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", + "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 → Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 — requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", "scoring": { "signals": [ "P1", @@ -1404,7 +1404,7 @@ "category": "tutorial", "priority": "high", "status": "blocked", - "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 \u2014 tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) \u2192 category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", + "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 — tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) → category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", "scoring": { "signals": [ "P1", @@ -1445,7 +1445,7 @@ "category": "tutorial", "priority": "medium", "status": "blocked", - "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present \u2014 score 20, no ambiguity. K1=0 \u2014 Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 \u2014 INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 \u2192 medium.", + "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present — score 20, no ambiguity. K1=0 — Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 — INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 → medium.", "scoring": { "signals": [ "P1", @@ -1477,7 +1477,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic \u2014 same endpoint, same output. K3/K4/S3 absent.", + "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic — same endpoint, same output. K3/K4/S3 absent.", "scoring": { "signals": [ "P1", @@ -1650,7 +1650,7 @@ "category": "guide", "priority": "high", "status": "blocked", - "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) \u2014 Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", + "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) — Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", "scoring": { "signals": [ "P2", @@ -1757,7 +1757,7 @@ "category": "guide", "priority": "high", "status": "built", - "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer \u2014 all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", + "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer — all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", "scoring": { "signals": [ "P2", @@ -1858,7 +1858,7 @@ "category": "guide", "priority": "low", "status": "built", - "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher \u2014 flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate \u2192 paste address \u2192 click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", + "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher — flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate → paste address → click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", "scoring": { "signals": [ "P1", @@ -1994,7 +1994,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) \u2014 uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", + "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) — uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", "scoring": { "signals": [ "P1", @@ -2024,7 +2024,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired \u2014 tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", + "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired — tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", "scoring": { "signals": [ "P1", @@ -2056,7 +2056,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired \u2014 workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", + "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired — workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", "scoring": { "signals": [ "P1", @@ -2088,7 +2088,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired \u2014 deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired \u2014 code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", + "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired — deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired — code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", "scoring": { "signals": [ "P1", @@ -2120,7 +2120,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired \u2014 entirely GUI-driven (Remix IDE + MetaMask). K4 not fired \u2014 contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", + "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired — entirely GUI-driven (Remix IDE + MetaMask). K4 not fired — contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", "scoring": { "signals": [ "P1", @@ -2151,7 +2151,7 @@ "category": "guide", "priority": "high", "status": "built", - "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired \u2014 multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired \u2014 no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 \u2014 not ambiguous.", + "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired — multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired — no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 — not ambiguous.", "scoring": { "signals": [ "P2", @@ -2185,7 +2185,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot \u2014 mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", + "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot — mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", "scoring": { "signals": [ "P1", @@ -2218,7 +2218,7 @@ "priority": "high", "status": "built", "built_at": "2026-04-21T18:00:00Z", - "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown \u2014 P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", + "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown — P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", "scoring": { "signals": [ "P1", @@ -2441,7 +2441,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) \u2014 hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", + "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) — hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", "scoring": { "signals": [ "P2", @@ -2469,7 +2469,7 @@ "category": "tutorial", "priority": "high", "status": "built", - "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction \u2014 all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", + "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction — all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", "scoring": { "signals": [ "P1", @@ -2501,7 +2501,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction \u2014 all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", + "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction — all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", "scoring": { "signals": [ "P1", @@ -2532,7 +2532,7 @@ "category": "guide", "priority": "medium", "status": "built", - "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural \u2265 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", + "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural ≥ 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", "scoring": { "signals": [ "P1", @@ -2603,7 +2603,7 @@ "category": "tutorial", "priority": "medium", "status": "built", - "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 \u2192 medium.", + "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 → medium.", "scoring": { "signals": [ "P1", @@ -2842,7 +2842,7 @@ ], "category": "tutorial", "priority": "high", - "status": "pending", + "status": "built", "notes": "Full deploy+test workflow for SwapRouter and NonfungiblePositionManager. The 'complete V3 Core tutorial' prereq is a code dependency resolved automatically via local npm file reference in the cloned monorepo; the Ignition module deploys Factory+WETH9+SwapRouter+NFPM in one shot, making this self-contained. Pinned to commit 96696ad15c3cf01b9168a71ad5114f27c34a8726 with docs.test.ts confirming CI-tested code.", "scoring": { "signals": [ @@ -2868,7 +2868,8 @@ "not_applicable": 0 }, "priority_score": 25 - } + }, + "built_at": "2026-05-15T16:00:00Z" } ], "supplementary_only": [ @@ -3135,7 +3136,7 @@ "vote-on-referendum", "create-opengov-referendum" ], - "notes": "Conceptual overview of OpenGov origins and tracks \u2014 explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", + "notes": "Conceptual overview of OpenGov origins and tracks — explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", "scoring": { "signals": [ "R3", @@ -3159,7 +3160,7 @@ "configure-runtime-pallet", "set-up-parachain-template" ], - "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points \u2014 but the page's primary purpose is deep reference exposition, not a task workflow.", + "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points — but the page's primary purpose is deep reference exposition, not a task workflow.", "scoring": { "signals": [ "P2", @@ -3365,7 +3366,7 @@ "build-parachain-runtime", "set-up-parachain-template" ], - "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) \u2014 rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", + "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) — rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", "scoring": { "signals": [ "R1", @@ -3418,7 +3419,7 @@ "set-up-parachain-template", "test-parachain-locally" ], - "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local \u2192 Paseo \u2192 production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", + "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local → Paseo → production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", "scoring": { "signals": [ "P1", @@ -3563,7 +3564,7 @@ "purchase-bulk-coretime", "set-up-parachain-template" ], - "notes": "Explains coretime scheduling concepts \u2014 bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", + "notes": "Explains coretime scheduling concepts — bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", "scoring": { "signals": [ "C1", @@ -3585,7 +3586,7 @@ "set-up-validator-node", "stake-dot-tokens" ], - "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps \u2014 entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", + "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps — entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", "scoring": { "signals": [ "C1", @@ -3607,7 +3608,7 @@ "set-up-parachain-template", "purchase-bulk-coretime" ], - "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram \u2014 no procedural content.", + "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram — no procedural content.", "scoring": { "signals": [ "C1", @@ -3628,7 +3629,7 @@ "relevant_to": [ "establish-on-chain-identity" ], - "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) \u2014 not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", + "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) — not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", "scoring": { "signals": [ "P1", @@ -3656,7 +3657,7 @@ "deploy-basic-contract-remix", "deploy-erc20-token-remix" ], - "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps \u2014 serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", + "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps — serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", "scoring": { "signals": [ "C1", @@ -3699,7 +3700,7 @@ "interact-with-chain-polkadart", "use-polkadot-js-api" ], - "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) \u2014 reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", + "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) — reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", "scoring": { "signals": [ "P2", @@ -3726,7 +3727,7 @@ "relevant_to": [ "transfer-assets-parachains" ], - "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual \u2014 explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", + "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual — explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", "scoring": { "signals": [ "P2", @@ -4193,7 +4194,7 @@ { "page": "policies/terms-of-use.md", "category": "not_applicable", - "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet \u2014 pure legal boilerplate with no procedural, reference, or conceptual developer content.", + "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet — pure legal boilerplate with no procedural, reference, or conceptual developer content.", "scoring": { "signals": [ "N2" diff --git a/skill_coverage.json b/skill_coverage.json index 16111bedb..88ef9130e 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,11 +1,11 @@ { "schema_version": "1", - "generated": "2026-05-15T15:30:00Z", + "generated": "2026-05-15T16:00:00Z", "summary": { "total_candidates": 142, - "up_to_date": 60, + "up_to_date": 62, "stale": 0, - "uncovered": 59, + "uncovered": 57, "reviewed_no_skill": 23 }, "pages": { @@ -739,9 +739,11 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": null, - "skills": [], - "status": "uncovered" + "last_scanned": "2026-05-15T16:00:00Z", + "skills": [ + "deploy-uniswap-v3-periphery-evm" + ], + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", From 077e0a5e2d2fe77b168b3c257d541ec71e7b7a7b Mon Sep 17 00:00:00 2001 From: Aljosa Makevic Date: Mon, 18 May 2026 16:49:45 +0200 Subject: [PATCH 13/26] chore: migrate skill_coverage statuses to granular vocabulary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the legacy `reviewed_no_skill` status (which conflated blocked / not_applicable / supplementary) with the granular reason sourced from skill_candidates.json. Ran via: python3 agent-ready-docs-hub/migrate_coverage_status.py \ polkadot-docs --mkdocs-root . Migration result: before: 23 reviewed_no_skill after: 23 blocked All 23 are pages whose skills were blocked in the previous PR cycle (transfer-assets-into-polkadot, convert-assets, register-foreign- asset, register-local-asset, run-a-collator, bootnode, etc.). None mapped to not_applicable or supplementary — those classifications were never assigned `reviewed_no_skill` by the legacy logic; they sat as `uncovered`, and the updated generate_coverage.py will pick them up correctly on the next workflow run. Companion to agent-ready-docs-hub@a3ace06 which updates the writer (generate_coverage.py) to emit the granular statuses natively. That PR must merge before the next workflow run, otherwise the old writer will re-emit `reviewed_no_skill` and undo this migration. --- skill_coverage.json | 50 +++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/skill_coverage.json b/skill_coverage.json index 88ef9130e..bf4b68b03 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -6,7 +6,9 @@ "up_to_date": 62, "stale": 0, "uncovered": 57, - "reviewed_no_skill": 23 + "blocked": 23, + "not_applicable": 0, + "supplementary": 0 }, "pages": { "chain-interactions/accounts/create-account.md": { @@ -77,7 +79,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -115,19 +117,19 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "chain-interactions/token-operations/register-foreign-asset.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "chain-interactions/token-operations/register-local-asset.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "get-support.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -139,7 +141,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-node/parachain-rpc.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -161,25 +163,25 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-node/relay-chain/full-node.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-node/relay-chain/secure-wss.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -193,7 +195,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -205,7 +207,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -223,7 +225,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "node-infrastructure/run-a-validator/requirements.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -335,13 +337,13 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/interoperability/channels-with-system-parachains.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/interoperability/get-started.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -361,7 +363,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -375,13 +377,13 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/runtime-maintenance/runtime-upgrades.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/runtime-maintenance/storage-migrations.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -393,19 +395,19 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/testing/fork-a-parachain.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "parachains/testing/run-a-parachain-network.md": { "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "policies/ai-chatbot-policy.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -637,7 +639,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "reference/tools/polkadot-js-api.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -651,7 +653,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "reference/tools/py-substrate-interface.md": { "last_edited": "2026-05-15T14:44:17+00:00", @@ -843,7 +845,7 @@ "last_edited": "2026-05-15T14:44:17+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], - "status": "reviewed_no_skill" + "status": "blocked" }, "smart-contracts/for-eth-devs/accounts.md": { "last_edited": "2026-05-15T14:44:17+00:00", From 3a3090876bcd79ccc525119ae7e0b05e14d8ff9d Mon Sep 17 00:00:00 2001 From: Aljosa Makevic Date: Tue, 19 May 2026 10:20:09 +0200 Subject: [PATCH 14/26] chore: mark 44 skills stale for project_structure backfill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ran the new stale-sweep tool against pages whose skills are missing the project_structure field — which 44 skills are, post PR #1679 (only the 6 new + 11 incidentally-touched skills got it; the other 44 weren't stale and so never picked up the field). python3 agent-ready-docs-hub/mark_skills_stale.py polkadot-docs \\ --mkdocs-root . --missing-field project_structure Selected skills: 44 Pages flipped to stale (last_scanned -> null): 44 Pages already stale (no-op): 0 The next workflow run will compute status="stale" from the timestamp mismatch and drain these via the generate-skills job. Per the batch-tuning change (workflows-private#15, batch-size 5→7, stale counts against batch limit), the agent will process at most 7 skills per chained iteration: 44 stale ÷ 7 per batch ≈ 7 chained iterations Wall time ≈ 7 × (~5-10 min/batch + 60s rate-limit pause) Total cost ≈ 7 × ~$1-2/iteration After completion, `report_schema_drift.py` should show project_structure at 61/61 (100%) and the next stale-sweep will report nothing to do. --- skill_coverage.json | 88 ++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/skill_coverage.json b/skill_coverage.json index bf4b68b03..0216f480e 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -29,7 +29,7 @@ }, "chain-interactions/query-data/query-rest.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "query-chain-data-sidecar-rest" ], @@ -53,7 +53,7 @@ }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "calculate-transaction-fees-papi" ], @@ -61,7 +61,7 @@ }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "replay-dry-run-xcm-chopsticks" ], @@ -69,7 +69,7 @@ }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "estimate-xcm-fees-teleport" ], @@ -83,7 +83,7 @@ }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "transfer-assets-parachains-paraspell" ], @@ -91,7 +91,7 @@ }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "pay-fees-alternative-token" ], @@ -145,7 +145,7 @@ }, "node-infrastructure/run-a-node/parachain-rpc.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "run-parachain-rpc-node" ], @@ -153,7 +153,7 @@ }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "run-polkadot-hub-rpc-node" ], @@ -185,7 +185,7 @@ }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-polkadot-validator-node" ], @@ -249,7 +249,7 @@ }, "parachains/customize-runtime/add-existing-pallets.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "add-existing-pallet-to-runtime" ], @@ -257,7 +257,7 @@ }, "parachains/customize-runtime/add-pallet-instances.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "configure-multiple-pallet-instances" ], @@ -271,7 +271,7 @@ }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "benchmark-frame-pallet" ], @@ -279,7 +279,7 @@ }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "create-frame-pallet" ], @@ -295,7 +295,7 @@ }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "unit-test-frame-pallet" ], @@ -309,7 +309,7 @@ }, "parachains/install-polkadot-sdk.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "install-polkadot-sdk" ], @@ -353,7 +353,7 @@ }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-parachain-to-polkadot-testnet" ], @@ -367,7 +367,7 @@ }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-parachain-template" ], @@ -471,7 +471,7 @@ }, "reference/parachains/chain-data.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "retrieve-runtime-metadata" ], @@ -587,7 +587,7 @@ }, "reference/tools/chopsticks.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-chopsticks-fork" ], @@ -609,7 +609,7 @@ }, "reference/tools/moonwall.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-e2e-testing-moonwall" ], @@ -617,7 +617,7 @@ }, "reference/tools/omninode.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "run-parachain-node-omni-node" ], @@ -643,7 +643,7 @@ }, "reference/tools/polkadot-js-api.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "use-polkadot-js-api" ], @@ -657,7 +657,7 @@ }, "reference/tools/py-substrate-interface.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "interact-polkadot-node-py-substrate" ], @@ -671,7 +671,7 @@ }, "reference/tools/subxt.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "interact-polkadot-node-subxt" ], @@ -685,7 +685,7 @@ }, "reference/tools/zombienet.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "spawn-test-network-zombienet" ], @@ -693,7 +693,7 @@ }, "smart-contracts/connect.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "connect-polkadot-hub-testnet" ], @@ -701,7 +701,7 @@ }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "build-dapp-viem-nextjs" ], @@ -757,7 +757,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-basic-contract-remix" ], @@ -765,7 +765,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-erc20-token-hardhat" ], @@ -773,7 +773,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-erc20-token-remix" ], @@ -781,7 +781,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-erc721-nft-hardhat" ], @@ -789,7 +789,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-erc721-nft-remix" ], @@ -797,7 +797,7 @@ }, "smart-contracts/dev-environments/foundry.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-foundry-polkadot-hub" ], @@ -805,7 +805,7 @@ }, "smart-contracts/dev-environments/hardhat-polkadot.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-hardhat-pvm" ], @@ -813,7 +813,7 @@ }, "smart-contracts/dev-environments/hardhat.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-hardhat-evm" ], @@ -821,7 +821,7 @@ }, "smart-contracts/dev-environments/local-dev-node.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "set-up-local-dev-node" ], @@ -829,7 +829,7 @@ }, "smart-contracts/dev-environments/remix.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "connect-remix-polkadot" ], @@ -897,7 +897,7 @@ }, "smart-contracts/integrations/wallets.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "connect-wallet-polkadot-hub" ], @@ -921,7 +921,7 @@ }, "smart-contracts/libraries/wagmi.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "use-wagmi-polkadot-hub" ], @@ -937,7 +937,7 @@ }, "smart-contracts/libraries/web3-py.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "deploy-interact-contracts-web3py" ], @@ -951,7 +951,7 @@ }, "smart-contracts/precompiles/erc20.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "interact-erc20-precompile-polkadot-hub" ], @@ -965,7 +965,7 @@ }, "smart-contracts/precompiles/storage.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "interact-storage-precompile-remix" ], @@ -973,7 +973,7 @@ }, "smart-contracts/precompiles/system.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "interact-system-precompile-remix" ], @@ -981,7 +981,7 @@ }, "smart-contracts/precompiles/xcm.md": { "last_edited": "2026-05-15T14:44:17+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": null, "skills": [ "interact-xcm-precompile-remix" ], From 46770ac2bed6afed50efba081966263b508723db Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 08:28:13 +0000 Subject: [PATCH 15/26] chore: refresh skill coverage and alerts --- ref_repo_change_alerts.json | 2 +- repo_state.json | 2 +- skill_coverage.json | 534 ++++++++++++++++++------------------ 3 files changed, 269 insertions(+), 269 deletions(-) diff --git a/ref_repo_change_alerts.json b/ref_repo_change_alerts.json index 06c7d7b2b..ee985ddcd 100644 --- a/ref_repo_change_alerts.json +++ b/ref_repo_change_alerts.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated": "2026-05-15T14:44:38Z", + "generated": "2026-05-19T08:28:13Z", "summary": { "reference_changes": 0, "skills_affected": 0 diff --git a/repo_state.json b/repo_state.json index 90e710a7a..f627fdb3b 100644 --- a/repo_state.json +++ b/repo_state.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "last_checked": "2026-05-15T14:44:38Z", + "last_checked": "2026-05-19T08:28:13Z", "docs_repo": { "branch": "master", "last_checked_commit": "cc6c24ebf10e1e4e620ad152ae9af9e3d19aa6ae" diff --git a/skill_coverage.json b/skill_coverage.json index 0216f480e..d8e044598 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,991 +1,991 @@ { "schema_version": "1", - "generated": "2026-05-15T16:00:00Z", + "generated": "2026-05-19T08:28:13Z", "summary": { "total_candidates": 142, - "up_to_date": 62, - "stale": 0, - "uncovered": 57, + "up_to_date": 0, + "stale": 62, + "uncovered": 0, "blocked": 23, - "not_applicable": 0, - "supplementary": 0 + "not_applicable": 12, + "supplementary": 45 }, "pages": { "chain-interactions/accounts/create-account.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "create-polkadot-account" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/accounts/query-accounts.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "query-account-info-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/query-data/query-rest.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "query-chain-data-sidecar-rest" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/query-data/query-sdks.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "query-chain-state-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/query-data/runtime-api-calls.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "call-runtime-apis-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "calculate-transaction-fees-papi" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "replay-dry-run-xcm-chopsticks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "estimate-xcm-fees-teleport" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "transfer-assets-parachains-paraspell" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "pay-fees-alternative-token" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/send-transactions/with-sdks.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "send-transactions-sdks" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/store-data/bulletin-chain.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "store-retrieve-data-bulletin-chain" ], - "status": "up_to_date" + "status": "stale" }, "chain-interactions/token-operations/convert-assets.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "chain-interactions/token-operations/register-foreign-asset.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "chain-interactions/token-operations/register-local-asset.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "get-support.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "node-infrastructure/run-a-collator.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-node/parachain-rpc.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "run-parachain-rpc-node" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "run-polkadot-hub-rpc-node" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-node/relay-chain/bootnode.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-node/relay-chain/full-node.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-node/relay-chain/secure-wss.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-polkadot-validator-node" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "node-infrastructure/run-a-validator/operational-tasks/general-management.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "node-infrastructure/run-a-validator/operational-tasks/upgrade-your-node.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/requirements.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "onboard-polkadot-validator" ], - "status": "up_to_date" + "status": "stale" }, "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "node-infrastructure/run-a-validator/staking-mechanics/rewards.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "parachains/customize-runtime/add-existing-pallets.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "add-existing-pallet-to-runtime" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/add-pallet-instances.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "configure-multiple-pallet-instances" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/add-smart-contract-functionality.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "benchmark-frame-pallet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "create-frame-pallet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/pallet-development/mock-runtime.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-pallet-mock-runtime" ], - "status": "up_to_date" + "status": "stale" }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "unit-test-frame-pallet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/get-started.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "parachains/install-polkadot-sdk.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "install-polkadot-sdk" ], - "status": "up_to_date" + "status": "stale" }, "parachains/integrations/indexers.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "parachains/integrations/oracles.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "parachains/integrations/wallets.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "parachains/interoperability/channels-between-parachains.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "parachains/interoperability/channels-with-system-parachains.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "parachains/interoperability/get-started.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-parachain-to-polkadot-testnet" ], - "status": "up_to_date" + "status": "stale" }, "parachains/launch-a-parachain/obtain-coretime.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "blocked" }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-parachain-template" ], - "status": "up_to_date" + "status": "stale" }, "parachains/runtime-maintenance/coretime-renewal.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "parachains/runtime-maintenance/runtime-upgrades.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "parachains/runtime-maintenance/storage-migrations.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "parachains/runtime-maintenance/unlock-parachains.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "parachains/testing/fork-a-parachain.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], "status": "blocked" }, "parachains/testing/run-a-parachain-network.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "policies/ai-chatbot-policy.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "policies/cookie-policy.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "policies/privacy-policy.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "policies/terms-of-use.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "reference/glossary.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/governance/origins-tracks.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/accounts.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/blocks-transactions-fees/blocks.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/blocks-transactions-fees/fees.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/blocks-transactions-fees/transactions.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/chain-data.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "retrieve-runtime-metadata" ], - "status": "up_to_date" + "status": "stale" }, "reference/parachains/consensus/async-backing.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/consensus/elastic-scaling.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/consensus/inclusion-pipeline.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/cryptography.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/data-encoding.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/interoperability.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/networks.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/node-and-runtime.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/parachains/randomness.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/assets.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/bridging.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/collectives-and-daos.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/consensus-and-security/agile-coretime.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/consensus-and-security/pos-consensus.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/consensus-and-security/relay-chain.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/data-storage.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/people-and-identity.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/polkadot-hub/smart-contracts.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/tools/chopsticks.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-chopsticks-fork" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/dedot.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-with-chain-dedot" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/light-clients.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/tools/moonwall.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-e2e-testing-moonwall" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/omninode.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "run-parachain-node-omni-node" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/papi.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/tools/paraspell.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/tools/polkadart.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [], "status": "blocked" }, "reference/tools/polkadot-js-api.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "use-polkadot-js-api" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/pop-cli.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "reference/tools/py-substrate-interface.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "interact-polkadot-node-py-substrate" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/sidecar.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "reference/tools/subxt.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "interact-polkadot-node-subxt" ], - "status": "up_to_date" + "status": "stale" }, "reference/tools/xcm-tools.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "reference/tools/zombienet.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "spawn-test-network-zombienet" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/connect.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "connect-polkadot-hub-testnet" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "build-dapp-viem-nextjs" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-core-pvm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-core-evm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-periphery-evm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v3-core-evm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T16:00:00Z", "skills": [ "deploy-uniswap-v3-periphery-evm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-basic-contract-hardhat" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-basic-contract-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-erc20-token-hardhat" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-erc20-token-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-erc721-nft-hardhat" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-erc721-nft-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/foundry.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-foundry-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/hardhat-polkadot.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-hardhat-pvm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/hardhat.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-hardhat-evm" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/local-dev-node.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "set-up-local-dev-node" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/dev-environments/remix.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "connect-remix-polkadot" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/explorers.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/faucet.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "smart-contracts/for-eth-devs/accounts.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/for-eth-devs/blocks-transactions-fees.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/for-eth-devs/contract-deployment.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/for-eth-devs/dual-vm-stack.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/for-eth-devs/evm-vs-pvm.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/for-eth-devs/gas-model.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/for-eth-devs/json-rpc-apis.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/get-started.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "smart-contracts/integrations/wallets.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "connect-wallet-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/ethers-js.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-contracts-ethers-js" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/viem.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-contracts-viem" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/wagmi.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "use-wagmi-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/web3-js.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-interact-contracts-web3js" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/libraries/web3-py.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "deploy-interact-contracts-web3py" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/overview.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "not_applicable" }, "smart-contracts/precompiles/erc20.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "interact-erc20-precompile-polkadot-hub" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/precompiles/eth-native.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [], - "status": "uncovered" + "status": "supplementary" }, "smart-contracts/precompiles/storage.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "interact-storage-precompile-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/precompiles/system.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "interact-system-precompile-remix" ], - "status": "up_to_date" + "status": "stale" }, "smart-contracts/precompiles/xcm.md": { - "last_edited": "2026-05-15T14:44:17+00:00", + "last_edited": "2026-05-19T08:20:09+00:00", "last_scanned": null, "skills": [ "interact-xcm-precompile-remix" ], - "status": "up_to_date" + "status": "stale" } } } From bc522004e12c3ab418eef4a1476e848c00fa5f2f Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 08:40:20 +0000 Subject: [PATCH 16/26] chore: auto-generate/update skills --- agent_skills_config.json | 19556 +++++++++++++++++++------------------ skill_coverage.json | 34 +- 2 files changed, 9832 insertions(+), 9758 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index d688ade78..b76763d07 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,9748 +1,9822 @@ { - "schema_version": "0.1", - "generated": "2026-05-15T16:00:00Z", - "content_hash": "sha256:1474320d5d053319d2f0cd541ff17349c37e62629d95b7a5944a08bc01885551", - "skills": [ - { - "id": "create-polkadot-account", - "title": "Create a Polkadot Account Programmatically", - "description": "Generates a new Polkadot SR25519 account (mnemonic, public key, SS58 address) using @polkadot/util-crypto and @polkadot/keyring. Use when you need to create a wallet, automate key generation, or produce a signing account for testing. Supports TypeScript (primary), Python (substrate-interface), and Rust (sp-core). Trigger phrases: 'create a Polkadot account', 'generate wallet keypair', 'new account', 'key generation'. Output: console-printed 12-word BIP39 mnemonic and SS58 address. No network connection required.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/accounts/create-account.md" - ], - "primary_page": "chain-interactions/accounts/create-account.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir account-creator && cd account-creator", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required — the @polkadot packages use ESM imports." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "account-creator", - "commands": [ - "npm install @polkadot/util-crypto @polkadot/keyring", - "npm install --save-dev typescript tsx" - ], - "description": "Install @polkadot/util-crypto (WASM-backed mnemonic generation and cryptoWaitReady), @polkadot/keyring (SR25519 keyring), and tsx (TypeScript executor)." - }, - { - "order": 3, - "action": "Fetch the account creation script", - "working_directory": "account-creator", - "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed — the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", - "reference_file": "create-account.ts" - }, - { - "order": 4, - "action": "Run the account creation script", - "working_directory": "account-creator", - "commands": [ - "npx tsx create-account.ts" - ], - "expected_output": "Address: \nMnemonic: <12-word BIP39 phrase>", - "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both — the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/accounts/create-account", - "files": [ - { - "path": "create-account.ts", - "description": "Generates a new SR25519 keypair using mnemonicGenerate(); prints SS58 address and mnemonic" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Unable to initialize the WASM interface", - "cause": "cryptoWaitReady() was not awaited before keyring operations.", - "resolution": "Ensure cryptoWaitReady() is called and awaited at the start of main(). The reference file already does this; verify the file was saved correctly." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "The project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the account-creator directory to enable ES modules." - }, - { - "pattern": "Error: Cannot find module '@polkadot/util-crypto'", - "cause": "Dependencies not installed.", - "resolution": "Run 'npm install @polkadot/util-crypto @polkadot/keyring' in the account-creator directory." - } - ], - "supplementary_context": { - "description": "Load these pages when the user asks about next steps after account creation or about account concepts.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to use the newly created account to sign and submit transactions on Polkadot Hub." - }, - { - "slug": "chain-interactions-accounts-query-accounts", + "schema_version": "0.1", + "generated": "2026-05-19T12:00:00Z", + "content_hash": "sha256:3449894b97efb0b7d50d8921cfa164c68a51a6adb9a3f5766f7e69f50772d673", + "skills": [ + { + "id": "create-polkadot-account", + "title": "Create a Polkadot Account Programmatically", + "description": "Generates a new Polkadot SR25519 account (mnemonic, public key, SS58 address) using @polkadot/util-crypto and @polkadot/keyring. Use when you need to create a wallet, automate key generation, or produce a signing account for testing. Supports TypeScript (primary), Python (substrate-interface), and Rust (sp-core). Trigger phrases: 'create a Polkadot account', 'generate wallet keypair', 'new account', 'key generation'. Output: console-printed 12-word BIP39 mnemonic and SS58 address. No network connection required.", + "version": "1.0.2", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/accounts/create-account.md" + ], + "primary_page": "chain-interactions/accounts/create-account.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir account-creator && cd account-creator", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required — the @polkadot packages use ESM imports." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "account-creator", + "commands": [ + "npm install @polkadot/util-crypto @polkadot/keyring", + "npm install --save-dev typescript tsx" + ], + "description": "Install @polkadot/util-crypto (WASM-backed mnemonic generation and cryptoWaitReady), @polkadot/keyring (SR25519 keyring), and tsx (TypeScript executor)." + }, + { + "order": 3, + "action": "Fetch the account creation script", + "working_directory": "account-creator", + "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed — the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", + "reference_file": "create-account.ts" + }, + { + "order": 4, + "action": "Run the account creation script", + "working_directory": "account-creator", + "commands": [ + "npx tsx create-account.ts" + ], + "expected_output": "Address: \nMnemonic: <12-word BIP39 phrase>", + "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both — the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/accounts/create-account", + "files": [ + { + "path": "create-account.ts", + "description": "Generates a new SR25519 keypair using mnemonicGenerate(); prints SS58 address and mnemonic" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Unable to initialize the WASM interface", + "cause": "cryptoWaitReady() was not awaited before keyring operations.", + "resolution": "Ensure cryptoWaitReady() is called and awaited at the start of main(). The reference file already does this; verify the file was saved correctly." + }, + { + "pattern": "SyntaxError: Cannot use import statement in a module", + "cause": "The project is not configured as ESM.", + "resolution": "Run 'npm pkg set type=module' in the account-creator directory to enable ES modules." + }, + { + "pattern": "Error: Cannot find module '@polkadot/util-crypto'", + "cause": "Dependencies not installed.", + "resolution": "Run 'npm install @polkadot/util-crypto @polkadot/keyring' in the account-creator directory." + } + ], + "supplementary_context": { + "description": "Load these pages when the user asks about next steps after account creation or about account concepts.", + "pages": [ + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "How to use the newly created account to sign and submit transactions on Polkadot Hub." + }, + { + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", + "relevance": "How to query the balance and account state of the newly created account." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: generate a fresh wallet", + "user_says": "Create a new Polkadot account for me", + "actions": [ + "Scaffold 'account-creator/' as an ESM Node.js project", + "Install @polkadot/util-crypto and @polkadot/keyring", + "Fetch and save create-account.ts", + "Run npx tsx create-account.ts" + ], + "result": "A fresh SR25519 mnemonic phrase and SS58 address printed to console" + }, + { + "scenario": "Edge case: user wants Python instead of TypeScript", + "user_says": "Generate a Polkadot account using Python", + "actions": [ + "Create a Python virtual environment in 'account-creator-python/'", + "Install substrate-interface via pip", + "Create create_account.py using generate_mnemonic() and Keypair.create_from_mnemonic()", + "Run python create_account.py" + ], + "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console" + } + ], + "project_structure": "account-creator/\n├── create-account.ts\n└── package.json" + }, + { + "id": "query-account-info-sdks", "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "How to query the balance and account state of the newly created account." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: generate a fresh wallet", - "user_says": "Create a new Polkadot account for me", - "actions": [ - "Scaffold 'account-creator/' as an ESM Node.js project", - "Install @polkadot/util-crypto and @polkadot/keyring", - "Fetch and save create-account.ts", - "Run npx tsx create-account.ts" - ], - "result": "A fresh SR25519 mnemonic phrase and SS58 address printed to console" - }, - { - "scenario": "Edge case: user wants Python instead of TypeScript", - "user_says": "Generate a Polkadot account using Python", - "actions": [ - "Create a Python virtual environment in 'account-creator-python/'", - "Install substrate-interface via pip", - "Create create_account.py using generate_mnemonic() and Keypair.create_from_mnemonic()", - "Run python create_account.py" - ], - "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console" - } - ], - "project_structure": "account-creator/\n├── create-account.ts\n└── package.json" - }, - { - "id": "query-account-info-sdks", - "title": "Query Account Information with SDKs", - "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/accounts/query-accounts.md" - ], - "primary_page": "chain-interactions/accounts/query-accounts.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub or compatible chain (e.g., wss://asset-hub-paseo.dotters.network for TestNet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-query-account-example && cd papi-query-account-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-query-account-example", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors", - "working_directory": "papi-query-account-example", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly — note the name you choose, as it must match the import in query-account.ts." - }, - { - "order": 4, - "action": "Fetch the query script", - "working_directory": "papi-query-account-example", - "reference_file": "papi/query-account.ts", - "description": "Fetch the reference file and save it as 'query-account.ts'. Then make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ACCOUNT_ADDRESS' with the SS58 address you want to query. Save the changes before running." - }, - { - "order": 5, - "action": "Run the query script", - "working_directory": "papi-query-account-example", - "commands": [ - "npx tsx query-account.ts" - ], - "expected_output": "Account Information with Nonce, Consumers, Providers, Free Balance, Reserved Balance, Frozen Balance, and Total Balance", - "description": "Execute the script. Output will display account nonce, balance breakdown (free/reserved/frozen), and total balance in raw PAS units and human-readable format (divide raw value by 10^10 for PAS)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/accounts/query-account", - "files": [ - { - "path": "papi/query-account.ts", - "description": "PAPI TypeScript script that queries System.Account storage and prints balance details" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED", - "cause": "Incorrect or unreachable WebSocket endpoint URL.", - "resolution": "Verify the WebSocket URL in query-account.ts is correct and the endpoint is accessible. For TestNet, use 'wss://asset-hub-paseo.dotters.network'." - }, - { - "pattern": "Account not found / undefined accountInfo", - "cause": "The queried address has never been funded and has no on-chain entry.", - "resolution": "The account exists only once it receives tokens. Query a funded address, or fund the address using the Polkadot faucet at https://faucet.polkadot.io/ first." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs background on account data fields or wants to send transactions from the queried account.", - "pages": [ - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI API reference for understanding query methods and typed API usage." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to send a transaction from the account after verifying its balance." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: check balance of an address", - "user_says": "Query the balance of this Polkadot address: 1abc...", - "actions": [ - "Scaffold 'papi-query-account-example/' ESM project", - "Install polkadot-api and tsx", - "Generate descriptors with npx papi add polkadotTestNet", - "Fetch query-account.ts; substitute WS endpoint and account address", - "Run npx tsx query-account.ts" - ], - "result": "Free/reserved/frozen balance and nonce printed for the specified address" - }, - { - "scenario": "Edge case: address not found (zero-balance unfunded account)", - "user_says": "Query the account but it shows no data", - "actions": [ - "Explain that Polkadot Hub only stores accounts that have been funded (existential deposit met)", - "Direct user to https://faucet.polkadot.io/ to fund the account with testnet PAS tokens", - "Re-run query-account.ts after account is funded" - ], - "result": "After funding, account data is returned correctly" - } - ], - "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json" - }, - { - "id": "query-chain-state-sdks", - "title": "Query On-Chain State with SDKs", - "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/query-data/query-sdks.md" - ], - "primary_page": "chain-interactions/query-data/query-sdks.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://polkadot-asset-hub-rpc.polkadot.io for mainnet or wss://asset-hub-paseo.dotters.network for testnet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-query-example && cd papi-query-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-query-example' and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-query-example", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors for Polkadot Hub", - "working_directory": "papi-query-example", - "commands": [ - "npx papi add pah -n polkadot_asset_hub" - ], - "description": "Generate compile-time type descriptors using the well-known 'polkadot_asset_hub' chain name. This produces the 'pah' export in @polkadot-api/descriptors. For a different network, use the appropriate -n value from the PAPI well-known chains list, or use -w with a WebSocket URL." - }, - { - "order": 4, - "action": "Fetch and configure the balance query script", - "working_directory": "papi-query-example", - "reference_file": "papi/query-balance.ts", - "description": "Fetch the reference file and save it as 'query-balance.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://polkadot-asset-hub-rpc.polkadot.io' for mainnet). (2) Replace 'INSERT_ADDRESS' with the SS58 address whose balance you want to query. Save the file." - }, - { - "order": 5, - "action": "Fetch and configure the asset query script", - "working_directory": "papi-query-example", - "reference_file": "papi/query-asset.ts", - "description": "Fetch the reference file and save it as 'query-asset.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with the same WebSocket endpoint used above. (2) Replace 'INSERT_ADDRESS' with the address to check for USDT balance. The script queries asset ID 1984 (USDT) by default; change the asset ID constant if targeting a different asset. Save the file." - }, - { - "order": 6, - "action": "Run the scripts", - "working_directory": "papi-query-example", - "commands": [ - "npx tsx query-balance.ts", - "npx tsx query-asset.ts" - ], - "expected_output": "Account nonce and balance info, then asset metadata and balance for the specified address", - "description": "Execute both scripts in sequence. query-balance.ts shows the native PAS balance. query-asset.ts shows the USDT asset metadata and the address's USDT balance (0 if not holding USDT)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/query-data/query-sdks", - "files": [ - { - "path": "papi/query-balance.ts", - "description": "Queries System.Account storage for native PAS balance using the 'pah' descriptor" - }, - { - "path": "papi/query-asset.ts", - "description": "Queries Assets pallet for USDT (asset ID 1984) metadata and address balance" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated.", - "resolution": "Run 'npx papi add pah -n polkadot_asset_hub' in the project directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED", - "cause": "Incorrect or unreachable WebSocket endpoint.", - "resolution": "Verify the WebSocket URL. For Polkadot Hub mainnet use 'wss://polkadot-asset-hub-rpc.polkadot.io', for testnet use 'wss://asset-hub-paseo.dotters.network'." - }, - { - "pattern": "query-asset returns null or empty for the address", - "cause": "The address does not hold the queried asset (USDT asset ID 1984).", - "resolution": "This is expected for addresses with no USDT balance. The metadata query will still succeed; only the per-address balance query returns empty." - } - ], - "supplementary_context": { - "description": "Load these pages for deeper understanding of chain storage structure or related SDK tools.", - "pages": [ - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI API reference for understanding storage query methods and typed API patterns." - }, - { - "slug": "chain-interactions-query-data-runtime-api-calls", - "title": "Runtime API Calls", - "url": "https://docs.polkadot.com/chain-interactions/query-data/runtime-api-calls.md", - "relevance": "How to call runtime APIs for computed results (e.g., nonce, fee estimation) beyond static storage reads." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: read native balance of an address", - "user_says": "Query the DOT/PAS balance for address 15abc... on Polkadot Hub", - "actions": [ - "Scaffold 'papi-query-example/' ESM project", - "Install polkadot-api and tsx", - "Run npx papi add pah -n polkadot_asset_hub", - "Fetch query-balance.ts; substitute endpoint and address", - "Run npx tsx query-balance.ts" - ], - "result": "Account nonce, free/reserved/frozen PAS balance printed to console" - }, - { - "scenario": "Edge case: query a non-standard asset ID", - "user_says": "Query USDC balance instead of USDT", - "actions": [ - "Fetch query-asset.ts and follow standard setup", - "In query-asset.ts, change the asset ID constant from 1984 (USDT) to the USDC asset ID on the target chain", - "Run npx tsx query-asset.ts" - ], - "result": "USDC metadata and address balance returned" - } - ], - "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json" - }, - { - "id": "call-runtime-apis-sdks", - "title": "Call Runtime APIs with SDKs", - "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/query-data/runtime-api-calls.md" - ], - "primary_page": "chain-interactions/query-data/runtime-api-calls.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub TestNet (e.g., wss://asset-hub-paseo.dotters.network)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-runtime-api-example && cd papi-runtime-api-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-runtime-api-example' and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-runtime-api-example", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors for Polkadot Hub TestNet", - "working_directory": "papi-runtime-api-example", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the runtime-apis.ts script. If targeting a different network, replace the WebSocket URL and update the descriptor name accordingly." - }, - { - "order": 4, - "action": "Fetch the runtime API script", - "working_directory": "papi-runtime-api-example", - "reference_file": "papi/runtime-apis.ts", - "description": "Fetch the reference file and save it as 'runtime-apis.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ADDRESS' with a valid SS58 address to query the nonce for. Save the file." - }, - { - "order": 5, - "action": "Run the runtime API script", - "working_directory": "papi-runtime-api-example", - "commands": [ - "npx tsx runtime-apis.ts" - ], - "expected_output": "Account Nonce: \nSupported Metadata Versions: []", - "description": "Execute the script. It calls AccountNonceApi.account_nonce for the provided address and Metadata.metadata_versions. Nonce for a new address is 0. Metadata versions list shows supported runtime metadata formats (e.g., [14, 15])." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/query-data/runtime-api-calls", - "files": [ - { - "path": "papi/runtime-apis.ts", - "description": "Calls AccountNonceApi.account_nonce and Metadata.metadata_versions using PAPI typed API" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." - }, - { - "pattern": "api.apis.AccountNonceApi is undefined", - "cause": "The target chain does not expose AccountNonceApi, or the descriptor was generated for a different chain.", - "resolution": "Verify the chain exposes AccountNonceApi in its runtime metadata. For Polkadot Hub TestNet, this API is available. If you generated descriptors for a different chain, regenerate with the correct endpoint." - }, - { - "pattern": "WebSocket connection failed / timeout on papi add", - "cause": "The testnet endpoint is temporarily unavailable.", - "resolution": "Polkadot Hub TestNet can be unstable. Wait a few minutes and retry. If the endpoint remains down, check the Polkadot Discord status channel. Alternatively, try a different public endpoint." - } - ], - "supplementary_context": { - "description": "Load these pages for background on available runtime APIs or to compare runtime API calls with storage queries.", - "pages": [ - { - "slug": "chain-interactions-query-data-query-sdks", + "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", + "version": "1.0.2", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/accounts/query-accounts.md" + ], + "primary_page": "chain-interactions/accounts/query-accounts.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for Polkadot Hub or compatible chain (e.g., wss://asset-hub-paseo.dotters.network for TestNet)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir papi-query-account-example && cd papi-query-account-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory and initialize an ESM Node.js project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "papi-query-account-example", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling." + }, + { + "order": 3, + "action": "Generate PAPI type descriptors", + "working_directory": "papi-query-account-example", + "commands": [ + "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly — note the name you choose, as it must match the import in query-account.ts." + }, + { + "order": 4, + "action": "Fetch the query script", + "working_directory": "papi-query-account-example", + "reference_file": "papi/query-account.ts", + "description": "Fetch the reference file and save it as 'query-account.ts'. Then make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ACCOUNT_ADDRESS' with the SS58 address you want to query. Save the changes before running." + }, + { + "order": 5, + "action": "Run the query script", + "working_directory": "papi-query-account-example", + "commands": [ + "npx tsx query-account.ts" + ], + "expected_output": "Account Information with Nonce, Consumers, Providers, Free Balance, Reserved Balance, Frozen Balance, and Total Balance", + "description": "Execute the script. Output will display account nonce, balance breakdown (free/reserved/frozen), and total balance in raw PAS units and human-readable format (divide raw value by 10^10 for PAS)." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/accounts/query-account", + "files": [ + { + "path": "papi/query-account.ts", + "description": "PAPI TypeScript script that queries System.Account storage and prints balance details" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI type descriptors were not generated.", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." + }, + { + "pattern": "WebSocket connection failed / ECONNREFUSED", + "cause": "Incorrect or unreachable WebSocket endpoint URL.", + "resolution": "Verify the WebSocket URL in query-account.ts is correct and the endpoint is accessible. For TestNet, use 'wss://asset-hub-paseo.dotters.network'." + }, + { + "pattern": "Account not found / undefined accountInfo", + "cause": "The queried address has never been funded and has no on-chain entry.", + "resolution": "The account exists only once it receives tokens. Query a funded address, or fund the address using the Polkadot faucet at https://faucet.polkadot.io/ first." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs background on account data fields or wants to send transactions from the queried account.", + "pages": [ + { + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md", + "relevance": "Full PAPI API reference for understanding query methods and typed API usage." + }, + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "How to send a transaction from the account after verifying its balance." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: check balance of an address", + "user_says": "Query the balance of this Polkadot address: 1abc...", + "actions": [ + "Scaffold 'papi-query-account-example/' ESM project", + "Install polkadot-api and tsx", + "Generate descriptors with npx papi add polkadotTestNet", + "Fetch query-account.ts; substitute WS endpoint and account address", + "Run npx tsx query-account.ts" + ], + "result": "Free/reserved/frozen balance and nonce printed for the specified address" + }, + { + "scenario": "Edge case: address not found (zero-balance unfunded account)", + "user_says": "Query the account but it shows no data", + "actions": [ + "Explain that Polkadot Hub only stores accounts that have been funded (existential deposit met)", + "Direct user to https://faucet.polkadot.io/ to fund the account with testnet PAS tokens", + "Re-run query-account.ts after account is funded" + ], + "result": "After funding, account data is returned correctly" + } + ], + "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json" + }, + { + "id": "query-chain-state-sdks", "title": "Query On-Chain State with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "Storage-based queries using the same PAPI setup — compare with runtime API calls." - }, - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI reference listing all available runtime APIs and their method signatures." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: get account nonce before constructing a transaction", - "user_says": "Call the AccountNonceApi to get the nonce for my address", - "actions": [ - "Scaffold 'papi-runtime-api-example/' ESM project", - "Install polkadot-api and tsx", - "Generate descriptors with npx papi add polkadotTestNet", - "Fetch runtime-apis.ts; substitute WS endpoint and address", - "Run npx tsx runtime-apis.ts" - ], - "result": "Account nonce and supported metadata versions printed to console" - }, - { - "scenario": "Edge case: nonce shows 0 for a new address", - "user_says": "The nonce is 0 — is that correct?", - "actions": [ - "Explain that nonce 0 means the account has not yet sent any transactions", - "Note that AccountNonceApi returns 0 for any address that has never submitted a transaction, including unfunded accounts", - "No action required — the nonce increments with each submitted transaction" - ], - "result": "User understands nonce 0 is correct for a new or never-transacted account" - } - ], - "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json" - }, - { - "id": "send-transactions-sdks", - "title": "Send Transactions with SDKs", - "description": "Constructs, signs, and submits a Balances.transfer_keep_alive extrinsic to Polkadot Hub using the PAPI TypeScript library. Use when you need to send tokens programmatically from a funded account. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Requires a funded account mnemonic and testnet PAS tokens. Trigger phrases: 'send a transaction', 'transfer tokens programmatically', 'sign and submit extrinsic', 'send PAS balance'. Uses dotenv to protect the sender mnemonic.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/with-sdks.md" - ], - "primary_page": "chain-interactions/send-transactions/with-sdks.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://asset-hub-paseo.dotters.network for testnet)" - ], - "tokens": [ - "Testnet PAS tokens in the sender account — get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" - ], - "wallet": [ - "An SR25519 account mnemonic phrase for the sender account, funded with testnet PAS" - ] - }, - "env_vars": [ - { - "name": "SENDER_MNEMONIC", - "description": "12-word BIP39 mnemonic phrase for the sender account. Must be the mnemonic for a funded account. Never commit this value to version control.", - "required": true - }, - { - "name": "WS_ENDPOINT", - "description": "WebSocket RPC endpoint for the target chain (e.g., wss://asset-hub-paseo.dotters.network).", - "required": true - }, - { - "name": "DEST_ADDRESS", - "description": "SS58-encoded recipient address for the balance transfer.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-send-tx-example && cd papi-send-tx-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-send-tx-example' and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-send-tx-example", - "commands": [ - "npm install polkadot-api @polkadot/util-crypto @polkadot/keyring dotenv", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api, @polkadot/keyring (for SR25519 signing), dotenv (for secure credential loading), and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors", - "working_directory": "papi-send-tx-example", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the send-transfer script. If targeting a different network, replace the endpoint and update the descriptor name in step 5." - }, - { - "order": 4, - "action": "Create .env and .gitignore for secrets", - "working_directory": "papi-send-tx-example", - "commands": [ - "echo 'SENDER_MNEMONIC=\\nWS_ENDPOINT=wss://asset-hub-paseo.dotters.network\\nDEST_ADDRESS=' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly — fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." - }, - { - "order": 5, - "action": "Fetch and adapt the send-transfer script", - "working_directory": "papi-send-tx-example", - "reference_file": "papi/send-transfer.ts", - "description": "Fetch the reference file and save it as 'send-transfer.ts'. Then apply these modifications: (1) Add 'import \"dotenv/config\";' as the very first line of the file. (2) Replace 'const POLKADOT_TESTNET_RPC = \\'INSERT_WS_ENDPOINT\\';' with 'const POLKADOT_TESTNET_RPC = process.env.WS_ENDPOINT!;'. (3) Replace 'const SENDER_MNEMONIC = \\'INSERT_MNEMONIC\\';' with 'const SENDER_MNEMONIC = process.env.SENDER_MNEMONIC!;'. (4) Replace 'const DEST_ADDRESS = \\'INSERT_DEST_ADDRESS\\';' with 'const DEST_ADDRESS = process.env.DEST_ADDRESS!;'. Save the file." - }, - { - "order": 6, - "action": "Verify the sender account is funded", - "working_directory": "papi-send-tx-example", - "description": "Before running, confirm the sender account has testnet PAS tokens. If not, direct the user to https://faucet.polkadot.io/?parachain=1111, paste the sender address, and request tokens. Wait for the faucet transaction to confirm (typically 1-2 minutes) before proceeding." - }, - { - "order": 7, - "action": "Submit the transaction", - "working_directory": "papi-send-tx-example", - "commands": [ - "npx tsx send-transfer.ts" - ], - "expected_output": "Connected to Polkadot Testnet\nSigning and submitting transaction...\nTransaction submitted with hash: 0x", - "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash — needed in step 8 to verify." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/send-transactions/with-sdks", - "files": [ - { - "path": "papi/send-transfer.ts", - "description": "PAPI TypeScript script that signs and submits a Balances.transfer_keep_alive extrinsic" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." - }, - { - "pattern": "Invalid mnemonic phrase / Error creating keypair", - "cause": "SENDER_MNEMONIC in .env is incorrect, has extra spaces, or is not a valid BIP39 phrase.", - "resolution": "Open .env, verify SENDER_MNEMONIC is a valid 12-word BIP39 phrase with single spaces between words, and no leading/trailing spaces." - }, - { - "pattern": "Error: 1010: Invalid Transaction: Inability to pay some fees", - "cause": "The sender account has insufficient PAS balance to cover the transfer amount plus gas fees.", - "resolution": "Get testnet tokens from https://faucet.polkadot.io/?parachain=1111. Alternatively, reduce the AMOUNT constant in send-transfer.ts to 1_000_000_000n (0.1 PAS)." - }, - { - "pattern": "Transaction submitted but not confirmed / stuck pending", - "cause": "Polkadot Hub TestNet can be unstable and drop transactions under load.", - "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction — if you see 'nonce too low', the previous transaction was included despite appearing to fail." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to fund an account, understand fees, or verify the submitted transaction.", - "pages": [ - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "How to obtain testnet PAS tokens from the Polkadot faucet." - }, - { - "slug": "chain-interactions-send-transactions-calculate-transaction-fees", - "title": "Calculate Transaction Fees", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", - "relevance": "How to estimate transaction fees before submitting to avoid insufficient balance errors." - }, - { - "slug": "chain-interactions-accounts-create-account", - "title": "Create an Account", - "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account.md", - "relevance": "How to create a new Polkadot account if the user does not yet have a sender mnemonic." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: send a small amount of PAS to another address", - "user_says": "Send 1 PAS from my funded account to address 15xyz...", - "actions": [ - "Scaffold 'papi-send-tx-example/' ESM project", - "Install polkadot-api, keyring, dotenv", - "Generate descriptors with npx papi add polkadotTestNet", - "Create .env and .gitignore; ask user to fill SENDER_MNEMONIC and DEST_ADDRESS", - "Fetch and adapt send-transfer.ts with dotenv substitutions", - "Verify sender is funded; run npx tsx send-transfer.ts" - ], - "result": "Transaction hash printed; 1 PAS (10^10 planck) transferred to the destination address" - }, - { - "scenario": "Edge case: account has no testnet tokens", - "user_says": "The transaction fails with 'inability to pay fees'", - "actions": [ - "Direct user to https://faucet.polkadot.io/?parachain=1111 with their sender address", - "Wait for faucet to credit the account (1-2 minutes)", - "Re-run send-transfer.ts" - ], - "result": "Account funded; transaction succeeds on retry" - } - ], - "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json" - }, - { - "id": "install-polkadot-sdk", - "title": "Install the Polkadot SDK", - "description": "Installs all Polkadot SDK build dependencies and compiles the SDK from source on macOS, Linux (Ubuntu/Debian/Arch/Fedora/OpenSUSE), and Windows WSL. Use when setting up a new development machine for Polkadot parachain or runtime development. Covers OS package installation, Rust toolchain setup, wasm32 target addition, git clone, and a full cargo build. Trigger phrases: 'install Polkadot SDK', 'set up parachain dev environment', 'install Rust for Polkadot', 'build polkadot-sdk', 'set up Substrate environment'. Expected outcome: polkadot binary executes and prints its version string.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/install-polkadot-sdk.md" - ], - "primary_page": "parachains/install-polkadot-sdk.md", - "prerequisites": { - "runtime": [ - "macOS 10.7+, Ubuntu/Debian/Arch/Fedora/OpenSUSE Linux, or Windows 10 v2004+ with WSL2", - "At least 10 GB free disk space and 8 GB RAM (16 GB recommended for parallel compilation)", - "Broadband internet connection (downloads several GB of Rust crates and dependencies)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install OS-level system dependencies", - "working_directory": ".", - "description": "Install the packages required to compile the Polkadot SDK. Run the command block for your OS:\n\nmacOS: First install Homebrew if missing (/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\"), then run: brew install protobuf openssl cmake\n\nUbuntu or Debian: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler\n\nArch Linux: pacman -Syu --needed --noconfirm curl git clang make protobuf\n\nFedora: sudo dnf update && sudo dnf install clang curl git openssl-devel make protobuf-compiler\n\nOpenSUSE: sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf\n\nWindows WSL (Ubuntu): open the WSL Ubuntu terminal, then run: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler" - }, - { - "order": 2, - "action": "Install Rust via rustup", - "working_directory": ".", - "commands": [ - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", - "source ~/.cargo/env" - ], - "description": "Download and run the rustup installer. When prompted, accept the default installation (press Enter or type 1). After installation completes, source the Cargo environment so the current shell session can find the Rust toolchain. On Windows WSL, use source ~/.cargo/env; on some Linux distributions use source $HOME/.cargo/env." - }, - { - "order": 3, - "action": "Configure the Rust toolchain", - "working_directory": ".", - "commands": [ - "rustup default stable", - "rustup update", - "rustup target add wasm32-unknown-unknown", - "rustup component add rust-src" - ], - "expected_output": "info: setting default toolchain to 'stable-...'", - "description": "Set the stable toolchain as default, update it to the latest release, and add the WebAssembly compilation target plus the Rust source component. Both wasm32-unknown-unknown and rust-src are required for building Substrate/Polkadot SDK runtime WASM blobs." - }, - { - "order": 4, - "action": "Clone the Polkadot SDK repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk.git", - "cd polkadot-sdk" - ], - "description": "Clone the official Polkadot SDK repository. The clone is several hundred MB and may take a few minutes on a slower connection. All subsequent steps run from inside the polkadot-sdk directory." - }, - { - "order": 5, - "action": "Compile the Polkadot SDK", - "working_directory": "polkadot-sdk", - "commands": [ - "cargo build --release --locked" - ], - "expected_output": "Compiling polkadot ... Finished release [optimized] target(s)", - "description": "Build the entire SDK in release mode. This compiles all components and verifies the full toolchain. Expect 20-60 minutes on typical developer hardware. A successful build produces binaries in target/release/ including polkadot, polkadot-parachain, polkadot-omni-node, and substrate-node." - }, - { - "order": 6, - "action": "Verify the build", - "working_directory": "polkadot-sdk", - "commands": [ - "./target/release/polkadot --version" - ], - "expected_output": "polkadot 1.x.x-xxxxxxx", - "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string — needed when matching binary versions in parachain template setup." - } - ], - "error_patterns": [ - { - "pattern": "error: linker 'cc' not found", - "cause": "C compiler and build tools are not installed.", - "resolution": "On Ubuntu/Debian run: sudo apt install build-essential clang. On macOS run: xcode-select --install. On Fedora run: sudo dnf install gcc." - }, - { - "pattern": "error: failed to run custom build command for 'openssl-sys'", - "cause": "OpenSSL development headers are missing.", - "resolution": "On Ubuntu/Debian run: sudo apt install libssl-dev. On macOS run: brew install openssl. On Fedora run: sudo dnf install openssl-devel." - }, - { - "pattern": "error[E0463]: can't find crate for 'std' ... target wasm32-unknown-unknown", - "cause": "The wasm32-unknown-unknown target is not installed.", - "resolution": "Run: rustup target add wasm32-unknown-unknown && rustup component add rust-src" - }, - { - "pattern": "LLVM ERROR: out of memory / build killed / OOM", - "cause": "Insufficient RAM for the default number of parallel compilation jobs.", - "resolution": "Limit parallelism: cargo build --release --locked -j 4. On Linux you can also temporarily increase swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile" - }, - { - "pattern": "error: failed to compile protobuf files / protoc not found", - "cause": "The protobuf compiler is missing.", - "resolution": "On Ubuntu/Debian: sudo apt install protobuf-compiler. On macOS: brew install protobuf. On Fedora: sudo dnf install protobuf-compiler." - } - ], - "supplementary_context": { - "description": "Load when the user asks about next steps after installing the SDK or about the parachain template.", - "pages": [ - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", + "version": "1.0.2", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/query-data/query-sdks.md" + ], + "primary_page": "chain-interactions/query-data/query-sdks.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://polkadot-asset-hub-rpc.polkadot.io for mainnet or wss://asset-hub-paseo.dotters.network for testnet)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir papi-query-example && cd papi-query-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'papi-query-example' and initialize an ESM Node.js project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "papi-query-example", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling." + }, + { + "order": 3, + "action": "Generate PAPI type descriptors for Polkadot Hub", + "working_directory": "papi-query-example", + "commands": [ + "npx papi add pah -n polkadot_asset_hub" + ], + "description": "Generate compile-time type descriptors using the well-known 'polkadot_asset_hub' chain name. This produces the 'pah' export in @polkadot-api/descriptors. For a different network, use the appropriate -n value from the PAPI well-known chains list, or use -w with a WebSocket URL." + }, + { + "order": 4, + "action": "Fetch and configure the balance query script", + "working_directory": "papi-query-example", + "reference_file": "papi/query-balance.ts", + "description": "Fetch the reference file and save it as 'query-balance.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://polkadot-asset-hub-rpc.polkadot.io' for mainnet). (2) Replace 'INSERT_ADDRESS' with the SS58 address whose balance you want to query. Save the file." + }, + { + "order": 5, + "action": "Fetch and configure the asset query script", + "working_directory": "papi-query-example", + "reference_file": "papi/query-asset.ts", + "description": "Fetch the reference file and save it as 'query-asset.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with the same WebSocket endpoint used above. (2) Replace 'INSERT_ADDRESS' with the address to check for USDT balance. The script queries asset ID 1984 (USDT) by default; change the asset ID constant if targeting a different asset. Save the file." + }, + { + "order": 6, + "action": "Run the scripts", + "working_directory": "papi-query-example", + "commands": [ + "npx tsx query-balance.ts", + "npx tsx query-asset.ts" + ], + "expected_output": "Account nonce and balance info, then asset metadata and balance for the specified address", + "description": "Execute both scripts in sequence. query-balance.ts shows the native PAS balance. query-asset.ts shows the USDT asset metadata and the address's USDT balance (0 if not holding USDT)." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/query-data/query-sdks", + "files": [ + { + "path": "papi/query-balance.ts", + "description": "Queries System.Account storage for native PAS balance using the 'pah' descriptor" + }, + { + "path": "papi/query-asset.ts", + "description": "Queries Assets pallet for USDT (asset ID 1984) metadata and address balance" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI type descriptors were not generated.", + "resolution": "Run 'npx papi add pah -n polkadot_asset_hub' in the project directory." + }, + { + "pattern": "WebSocket connection failed / ECONNREFUSED", + "cause": "Incorrect or unreachable WebSocket endpoint.", + "resolution": "Verify the WebSocket URL. For Polkadot Hub mainnet use 'wss://polkadot-asset-hub-rpc.polkadot.io', for testnet use 'wss://asset-hub-paseo.dotters.network'." + }, + { + "pattern": "query-asset returns null or empty for the address", + "cause": "The address does not hold the queried asset (USDT asset ID 1984).", + "resolution": "This is expected for addresses with no USDT balance. The metadata query will still succeed; only the per-address balance query returns empty." + } + ], + "supplementary_context": { + "description": "Load these pages for deeper understanding of chain storage structure or related SDK tools.", + "pages": [ + { + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md", + "relevance": "Full PAPI API reference for understanding storage query methods and typed API patterns." + }, + { + "slug": "chain-interactions-query-data-runtime-api-calls", + "title": "Runtime API Calls", + "url": "https://docs.polkadot.com/chain-interactions/query-data/runtime-api-calls.md", + "relevance": "How to call runtime APIs for computed results (e.g., nonce, fee estimation) beyond static storage reads." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: read native balance of an address", + "user_says": "Query the DOT/PAS balance for address 15abc... on Polkadot Hub", + "actions": [ + "Scaffold 'papi-query-example/' ESM project", + "Install polkadot-api and tsx", + "Run npx papi add pah -n polkadot_asset_hub", + "Fetch query-balance.ts; substitute endpoint and address", + "Run npx tsx query-balance.ts" + ], + "result": "Account nonce, free/reserved/frozen PAS balance printed to console" + }, + { + "scenario": "Edge case: query a non-standard asset ID", + "user_says": "Query USDC balance instead of USDT", + "actions": [ + "Fetch query-asset.ts and follow standard setup", + "In query-asset.ts, change the asset ID constant from 1984 (USDT) to the USDC asset ID on the target chain", + "Run npx tsx query-asset.ts" + ], + "result": "USDC metadata and address balance returned" + } + ], + "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json" + }, + { + "id": "call-runtime-apis-sdks", + "title": "Call Runtime APIs with SDKs", + "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", + "version": "1.0.2", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/query-data/runtime-api-calls.md" + ], + "primary_page": "chain-interactions/query-data/runtime-api-calls.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for Polkadot Hub TestNet (e.g., wss://asset-hub-paseo.dotters.network)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir papi-runtime-api-example && cd papi-runtime-api-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'papi-runtime-api-example' and initialize an ESM Node.js project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "papi-runtime-api-example", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling." + }, + { + "order": 3, + "action": "Generate PAPI type descriptors for Polkadot Hub TestNet", + "working_directory": "papi-runtime-api-example", + "commands": [ + "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the runtime-apis.ts script. If targeting a different network, replace the WebSocket URL and update the descriptor name accordingly." + }, + { + "order": 4, + "action": "Fetch the runtime API script", + "working_directory": "papi-runtime-api-example", + "reference_file": "papi/runtime-apis.ts", + "description": "Fetch the reference file and save it as 'runtime-apis.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ADDRESS' with a valid SS58 address to query the nonce for. Save the file." + }, + { + "order": 5, + "action": "Run the runtime API script", + "working_directory": "papi-runtime-api-example", + "commands": [ + "npx tsx runtime-apis.ts" + ], + "expected_output": "Account Nonce: \nSupported Metadata Versions: []", + "description": "Execute the script. It calls AccountNonceApi.account_nonce for the provided address and Metadata.metadata_versions. Nonce for a new address is 0. Metadata versions list shows supported runtime metadata formats (e.g., [14, 15])." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/query-data/runtime-api-calls", + "files": [ + { + "path": "papi/runtime-apis.ts", + "description": "Calls AccountNonceApi.account_nonce and Metadata.metadata_versions using PAPI typed API" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI type descriptors not generated.", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." + }, + { + "pattern": "api.apis.AccountNonceApi is undefined", + "cause": "The target chain does not expose AccountNonceApi, or the descriptor was generated for a different chain.", + "resolution": "Verify the chain exposes AccountNonceApi in its runtime metadata. For Polkadot Hub TestNet, this API is available. If you generated descriptors for a different chain, regenerate with the correct endpoint." + }, + { + "pattern": "WebSocket connection failed / timeout on papi add", + "cause": "The testnet endpoint is temporarily unavailable.", + "resolution": "Polkadot Hub TestNet can be unstable. Wait a few minutes and retry. If the endpoint remains down, check the Polkadot Discord status channel. Alternatively, try a different public endpoint." + } + ], + "supplementary_context": { + "description": "Load these pages for background on available runtime APIs or to compare runtime API calls with storage queries.", + "pages": [ + { + "slug": "chain-interactions-query-data-query-sdks", + "title": "Query On-Chain State with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", + "relevance": "Storage-based queries using the same PAPI setup — compare with runtime API calls." + }, + { + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md", + "relevance": "Full PAPI reference listing all available runtime APIs and their method signatures." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: get account nonce before constructing a transaction", + "user_says": "Call the AccountNonceApi to get the nonce for my address", + "actions": [ + "Scaffold 'papi-runtime-api-example/' ESM project", + "Install polkadot-api and tsx", + "Generate descriptors with npx papi add polkadotTestNet", + "Fetch runtime-apis.ts; substitute WS endpoint and address", + "Run npx tsx runtime-apis.ts" + ], + "result": "Account nonce and supported metadata versions printed to console" + }, + { + "scenario": "Edge case: nonce shows 0 for a new address", + "user_says": "The nonce is 0 — is that correct?", + "actions": [ + "Explain that nonce 0 means the account has not yet sent any transactions", + "Note that AccountNonceApi returns 0 for any address that has never submitted a transaction, including unfunded accounts", + "No action required — the nonce increments with each submitted transaction" + ], + "result": "User understands nonce 0 is correct for a new or never-transacted account" + } + ], + "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json" + }, + { + "id": "send-transactions-sdks", + "title": "Send Transactions with SDKs", + "description": "Constructs, signs, and submits a Balances.transfer_keep_alive extrinsic to Polkadot Hub using the PAPI TypeScript library. Use when you need to send tokens programmatically from a funded account. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Requires a funded account mnemonic and testnet PAS tokens. Trigger phrases: 'send a transaction', 'transfer tokens programmatically', 'sign and submit extrinsic', 'send PAS balance'. Uses dotenv to protect the sender mnemonic.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/send-transactions/with-sdks.md" + ], + "primary_page": "chain-interactions/send-transactions/with-sdks.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://asset-hub-paseo.dotters.network for testnet)" + ], + "tokens": [ + "Testnet PAS tokens in the sender account — get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" + ], + "wallet": [ + "An SR25519 account mnemonic phrase for the sender account, funded with testnet PAS" + ] + }, + "env_vars": [ + { + "name": "SENDER_MNEMONIC", + "description": "12-word BIP39 mnemonic phrase for the sender account. Must be the mnemonic for a funded account. Never commit this value to version control.", + "required": true + }, + { + "name": "WS_ENDPOINT", + "description": "WebSocket RPC endpoint for the target chain (e.g., wss://asset-hub-paseo.dotters.network).", + "required": true + }, + { + "name": "DEST_ADDRESS", + "description": "SS58-encoded recipient address for the balance transfer.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir papi-send-tx-example && cd papi-send-tx-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'papi-send-tx-example' and initialize an ESM Node.js project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "papi-send-tx-example", + "commands": [ + "npm install polkadot-api @polkadot/util-crypto @polkadot/keyring dotenv", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api, @polkadot/keyring (for SR25519 signing), dotenv (for secure credential loading), and TypeScript tooling." + }, + { + "order": 3, + "action": "Generate PAPI type descriptors", + "working_directory": "papi-send-tx-example", + "commands": [ + "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the send-transfer script. If targeting a different network, replace the endpoint and update the descriptor name in step 5." + }, + { + "order": 4, + "action": "Create .env and .gitignore for secrets", + "working_directory": "papi-send-tx-example", + "commands": [ + "echo 'SENDER_MNEMONIC=\\nWS_ENDPOINT=wss://asset-hub-paseo.dotters.network\\nDEST_ADDRESS=' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly — fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." + }, + { + "order": 5, + "action": "Fetch and adapt the send-transfer script", + "working_directory": "papi-send-tx-example", + "reference_file": "papi/send-transfer.ts", + "description": "Fetch the reference file and save it as 'send-transfer.ts'. Then apply these modifications: (1) Add 'import \"dotenv/config\";' as the very first line of the file. (2) Replace 'const POLKADOT_TESTNET_RPC = \\'INSERT_WS_ENDPOINT\\';' with 'const POLKADOT_TESTNET_RPC = process.env.WS_ENDPOINT!;'. (3) Replace 'const SENDER_MNEMONIC = \\'INSERT_MNEMONIC\\';' with 'const SENDER_MNEMONIC = process.env.SENDER_MNEMONIC!;'. (4) Replace 'const DEST_ADDRESS = \\'INSERT_DEST_ADDRESS\\';' with 'const DEST_ADDRESS = process.env.DEST_ADDRESS!;'. Save the file." + }, + { + "order": 6, + "action": "Verify the sender account is funded", + "working_directory": "papi-send-tx-example", + "description": "Before running, confirm the sender account has testnet PAS tokens. If not, direct the user to https://faucet.polkadot.io/?parachain=1111, paste the sender address, and request tokens. Wait for the faucet transaction to confirm (typically 1-2 minutes) before proceeding." + }, + { + "order": 7, + "action": "Submit the transaction", + "working_directory": "papi-send-tx-example", + "commands": [ + "npx tsx send-transfer.ts" + ], + "expected_output": "Connected to Polkadot Testnet\nSigning and submitting transaction...\nTransaction submitted with hash: 0x", + "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash — needed in step 8 to verify." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/send-transactions/with-sdks", + "files": [ + { + "path": "papi/send-transfer.ts", + "description": "PAPI TypeScript script that signs and submits a Balances.transfer_keep_alive extrinsic" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI type descriptors not generated.", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." + }, + { + "pattern": "Invalid mnemonic phrase / Error creating keypair", + "cause": "SENDER_MNEMONIC in .env is incorrect, has extra spaces, or is not a valid BIP39 phrase.", + "resolution": "Open .env, verify SENDER_MNEMONIC is a valid 12-word BIP39 phrase with single spaces between words, and no leading/trailing spaces." + }, + { + "pattern": "Error: 1010: Invalid Transaction: Inability to pay some fees", + "cause": "The sender account has insufficient PAS balance to cover the transfer amount plus gas fees.", + "resolution": "Get testnet tokens from https://faucet.polkadot.io/?parachain=1111. Alternatively, reduce the AMOUNT constant in send-transfer.ts to 1_000_000_000n (0.1 PAS)." + }, + { + "pattern": "Transaction submitted but not confirmed / stuck pending", + "cause": "Polkadot Hub TestNet can be unstable and drop transactions under load.", + "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction — if you see 'nonce too low', the previous transaction was included despite appearing to fail." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to fund an account, understand fees, or verify the submitted transaction.", + "pages": [ + { + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md", + "relevance": "How to obtain testnet PAS tokens from the Polkadot faucet." + }, + { + "slug": "chain-interactions-send-transactions-calculate-transaction-fees", + "title": "Calculate Transaction Fees", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", + "relevance": "How to estimate transaction fees before submitting to avoid insufficient balance errors." + }, + { + "slug": "chain-interactions-accounts-create-account", + "title": "Create an Account", + "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account.md", + "relevance": "How to create a new Polkadot account if the user does not yet have a sender mnemonic." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: send a small amount of PAS to another address", + "user_says": "Send 1 PAS from my funded account to address 15xyz...", + "actions": [ + "Scaffold 'papi-send-tx-example/' ESM project", + "Install polkadot-api, keyring, dotenv", + "Generate descriptors with npx papi add polkadotTestNet", + "Create .env and .gitignore; ask user to fill SENDER_MNEMONIC and DEST_ADDRESS", + "Fetch and adapt send-transfer.ts with dotenv substitutions", + "Verify sender is funded; run npx tsx send-transfer.ts" + ], + "result": "Transaction hash printed; 1 PAS (10^10 planck) transferred to the destination address" + }, + { + "scenario": "Edge case: account has no testnet tokens", + "user_says": "The transaction fails with 'inability to pay fees'", + "actions": [ + "Direct user to https://faucet.polkadot.io/?parachain=1111 with their sender address", + "Wait for faucet to credit the account (1-2 minutes)", + "Re-run send-transfer.ts" + ], + "result": "Account funded; transaction succeeds on retry" + } + ], + "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json" + }, + { + "id": "install-polkadot-sdk", + "title": "Install the Polkadot SDK", + "description": "Installs all Polkadot SDK build dependencies and compiles the SDK from source on macOS, Linux (Ubuntu/Debian/Arch/Fedora/OpenSUSE), and Windows WSL. Use when setting up a new development machine for Polkadot parachain or runtime development. Covers OS package installation, Rust toolchain setup, wasm32 target addition, git clone, and a full cargo build. Trigger phrases: 'install Polkadot SDK', 'set up parachain dev environment', 'install Rust for Polkadot', 'build polkadot-sdk', 'set up Substrate environment'. Expected outcome: polkadot binary executes and prints its version string.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/install-polkadot-sdk.md" + ], + "primary_page": "parachains/install-polkadot-sdk.md", + "prerequisites": { + "runtime": [ + "macOS 10.7+, Ubuntu/Debian/Arch/Fedora/OpenSUSE Linux, or Windows 10 v2004+ with WSL2", + "At least 10 GB free disk space and 8 GB RAM (16 GB recommended for parallel compilation)", + "Broadband internet connection (downloads several GB of Rust crates and dependencies)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Install OS-level system dependencies", + "working_directory": ".", + "description": "Install the packages required to compile the Polkadot SDK. Run the command block for your OS:\n\nmacOS: First install Homebrew if missing (/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\"), then run: brew install protobuf openssl cmake\n\nUbuntu or Debian: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler\n\nArch Linux: pacman -Syu --needed --noconfirm curl git clang make protobuf\n\nFedora: sudo dnf update && sudo dnf install clang curl git openssl-devel make protobuf-compiler\n\nOpenSUSE: sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf\n\nWindows WSL (Ubuntu): open the WSL Ubuntu terminal, then run: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler" + }, + { + "order": 2, + "action": "Install Rust via rustup", + "working_directory": ".", + "commands": [ + "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + "source ~/.cargo/env" + ], + "description": "Download and run the rustup installer. When prompted, accept the default installation (press Enter or type 1). After installation completes, source the Cargo environment so the current shell session can find the Rust toolchain. On Windows WSL, use source ~/.cargo/env; on some Linux distributions use source $HOME/.cargo/env." + }, + { + "order": 3, + "action": "Configure the Rust toolchain", + "working_directory": ".", + "commands": [ + "rustup default stable", + "rustup update", + "rustup target add wasm32-unknown-unknown", + "rustup component add rust-src" + ], + "expected_output": "info: setting default toolchain to 'stable-...'", + "description": "Set the stable toolchain as default, update it to the latest release, and add the WebAssembly compilation target plus the Rust source component. Both wasm32-unknown-unknown and rust-src are required for building Substrate/Polkadot SDK runtime WASM blobs." + }, + { + "order": 4, + "action": "Clone the Polkadot SDK repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk.git", + "cd polkadot-sdk" + ], + "description": "Clone the official Polkadot SDK repository. The clone is several hundred MB and may take a few minutes on a slower connection. All subsequent steps run from inside the polkadot-sdk directory." + }, + { + "order": 5, + "action": "Compile the Polkadot SDK", + "working_directory": "polkadot-sdk", + "commands": [ + "cargo build --release --locked" + ], + "expected_output": "Compiling polkadot ... Finished release [optimized] target(s)", + "description": "Build the entire SDK in release mode. This compiles all components and verifies the full toolchain. Expect 20-60 minutes on typical developer hardware. A successful build produces binaries in target/release/ including polkadot, polkadot-parachain, polkadot-omni-node, and substrate-node." + }, + { + "order": 6, + "action": "Verify the build", + "working_directory": "polkadot-sdk", + "commands": [ + "./target/release/polkadot --version" + ], + "expected_output": "polkadot 1.x.x-xxxxxxx", + "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string — needed when matching binary versions in parachain template setup." + } + ], + "error_patterns": [ + { + "pattern": "error: linker 'cc' not found", + "cause": "C compiler and build tools are not installed.", + "resolution": "On Ubuntu/Debian run: sudo apt install build-essential clang. On macOS run: xcode-select --install. On Fedora run: sudo dnf install gcc." + }, + { + "pattern": "error: failed to run custom build command for 'openssl-sys'", + "cause": "OpenSSL development headers are missing.", + "resolution": "On Ubuntu/Debian run: sudo apt install libssl-dev. On macOS run: brew install openssl. On Fedora run: sudo dnf install openssl-devel." + }, + { + "pattern": "error[E0463]: can't find crate for 'std' ... target wasm32-unknown-unknown", + "cause": "The wasm32-unknown-unknown target is not installed.", + "resolution": "Run: rustup target add wasm32-unknown-unknown && rustup component add rust-src" + }, + { + "pattern": "LLVM ERROR: out of memory / build killed / OOM", + "cause": "Insufficient RAM for the default number of parallel compilation jobs.", + "resolution": "Limit parallelism: cargo build --release --locked -j 4. On Linux you can also temporarily increase swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile" + }, + { + "pattern": "error: failed to compile protobuf files / protoc not found", + "cause": "The protobuf compiler is missing.", + "resolution": "On Ubuntu/Debian: sudo apt install protobuf-compiler. On macOS: brew install protobuf. On Fedora: sudo dnf install protobuf-compiler." + } + ], + "supplementary_context": { + "description": "Load when the user asks about next steps after installing the SDK or about the parachain template.", + "pages": [ + { + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", + "relevance": "Next step after SDK installation: clone and run the Polkadot SDK parachain template locally." + }, + { + "slug": "parachains-get-started", + "title": "Get Started with Parachain Development", + "url": "https://docs.polkadot.com/parachains/get-started.md", + "relevance": "Overview of parachain development paths and starting points available after SDK setup." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: fresh Ubuntu developer machine", + "user_says": "Install the Polkadot SDK on my Linux machine", + "actions": [ + "Run apt install for required packages (git, clang, libssl-dev, etc.)", + "Install Rust via rustup and source ~/.cargo/env", + "Run rustup default stable && rustup target add wasm32-unknown-unknown && rustup component add rust-src", + "Clone https://github.com/paritytech/polkadot-sdk.git and cd polkadot-sdk", + "Run cargo build --release --locked", + "Verify with ./target/release/polkadot --version" + ], + "result": "Polkadot binary prints version string — SDK toolchain ready for parachain development" + }, + { + "scenario": "Edge case: build runs out of memory on a machine with limited RAM", + "user_says": "The build keeps getting killed or runs out of memory", + "actions": [ + "Re-run with reduced parallelism: cargo build --release --locked -j 4", + "If still failing on Linux, add swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile, then retry" + ], + "result": "Build completes with reduced parallel jobs or additional swap space" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + } + }, + { + "id": "deploy-basic-contract-hardhat", + "title": "Deploy a Basic Smart Contract with Hardhat", + "description": "Scaffolds a Hardhat v2 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + ], + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", + "prerequisites": { + "runtime": [ + "Node.js v22.13.1 or later", + "npm" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account. Must correspond to a testnet account funded with PAS tokens. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create project directory and initialize npm", + "working_directory": ".", + "commands": [ + "mkdir hardhat-deployment && cd hardhat-deployment", + "npm init -y" + ], + "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage — interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." + }, + { + "order": 2, + "action": "Install Hardhat and dependencies", + "working_directory": "hardhat-deployment", + "commands": [ + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", + "npm install dotenv" + ], + "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." + }, + { + "order": 3, + "action": "Create project directory structure", + "working_directory": "hardhat-deployment", + "commands": [ + "mkdir -p contracts ignition/modules" + ], + "description": "Create the contracts and ignition/modules directories that Hardhat expects. The test directory is optional and not needed for this deployment workflow." + }, + { + "order": 4, + "action": "Create .env file and .gitignore for private key", + "working_directory": "hardhat-deployment", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." + }, + { + "order": 5, + "action": "Fetch and configure hardhat.config.ts", + "working_directory": "hardhat-deployment", + "reference_file": "hardhat.config.ts", + "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors)." + }, + { + "order": 6, + "action": "Fetch Storage.sol contract", + "working_directory": "hardhat-deployment", + "reference_file": "Storage.sol", + "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed — this is a generic Storage contract with get/set functions." + }, + { + "order": 7, + "action": "Compile the contract", + "working_directory": "hardhat-deployment", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 1 Solidity file successfully", + "description": "Compile Storage.sol. Successful output shows 'Compiled 1 Solidity file successfully' and creates artifacts in the artifacts/ directory. If compilation fails with 'invalid opcode: MCOPY', add 'evmVersion: \"cancun\"' inside the solidity.settings.evmVersion field of hardhat.config.ts and recompile." + }, + { + "order": 8, + "action": "Fetch the Ignition deployment module", + "working_directory": "hardhat-deployment", + "reference_file": "storage.ts", + "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed — the module deploys the Storage contract by name." + }, + { + "order": 9, + "action": "Deploy the contract to Polkadot Hub TestNet", + "working_directory": "hardhat-deployment", + "commands": [ + "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" + ], + "expected_output": "Hardhat Ignition 🚀 ... Storage deployed to: 0x...", + "interactive": true, + "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end — needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat", + "files": [ + { + "path": "hardhat.config.ts", + "description": "Hardhat config with polkadotTestnet network (RPC, chain ID, accounts via vars). Requires dotenv conversion and gasPrice addition per skill steps." + }, + { + "path": "Storage.sol", + "description": "Simple Storage contract with uint256 store/retrieve functions" + }, + { + "path": "storage.ts", + "description": "Hardhat Ignition module that deploys the Storage contract" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: vars is not defined / Cannot read properties of undefined (reading 'get')", + "cause": "The hardhat.config.ts still contains vars.get('PRIVATE_KEY') instead of the dotenv substitution.", + "resolution": "Open hardhat.config.ts, replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string, and add 'import \"dotenv/config\";' as the first line of the file." + }, + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "The deployer account has no testnet PAS tokens, or the gasPrice in hardhat.config.ts is below the network minimum (1000 gwei base fee).", + "resolution": "Get testnet tokens from https://faucet.polkadot.io/. Also verify the polkadotTestnet network block in hardhat.config.ts includes gasPrice: 5000000000000 (5000 gwei)." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported on retry", + "cause": "Ignition lost track of the deployment transaction. Often caused by requiredConfirmations: 0 or gasPrice below the base fee.", + "resolution": "First check if the contract was actually deployed: look in ignition/deployments/ for deployed_addresses.json. If deployed, import the existing deployment. If not deployed: (1) delete the ignition/deployments/ directory, (2) ensure hardhat.config.ts has gasPrice: 5000000000000 and ignition.requiredConfirmations: 1, (3) redeploy." + }, + { + "pattern": "CompilationError: Invalid opcode: MCOPY", + "cause": "The Solidity contract uses OpenZeppelin v5.4.0+ which requires the Cancun EVM version.", + "resolution": "In hardhat.config.ts, add evmVersion: 'cancun' inside the solidity compiler settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }" + } + ], + "supplementary_context": { + "description": "Load when the user wants to build on this deployment or needs to connect a frontend.", + "pages": [ + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", + "title": "Deploy an ERC-20 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", + "relevance": "Next step: deploy an ERC-20 token using the same Hardhat workflow." + }, + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Hardhat configuration reference for Polkadot Hub including verification setup." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy a first smart contract to Polkadot Hub", + "user_says": "Deploy a Storage contract to Polkadot Hub TestNet using Hardhat", + "actions": [ + "Scaffold hardhat-deployment/ with npm init -y", + "Install hardhat, toolbox, dotenv", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Fetch and configure hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set requiredConfirmations: 1", + "Fetch contracts/Storage.sol", + "Run npx hardhat compile", + "Fetch ignition/modules/Storage.ts", + "Run npx hardhat ignition deploy --network polkadotTestnet; delegate confirmation prompt to user" + ], + "result": "Storage contract deployed; address printed to console" + }, + { + "scenario": "Edge case: deployment transaction gets stuck (IGN401)", + "user_says": "Ignition says the transaction was dropped and retrying gives 'Transaction Already Imported'", + "actions": [ + "Check ignition/deployments/ for deployed_addresses.json to determine if contract was actually deployed", + "If not deployed: delete ignition/deployments/, verify gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy" + ], + "result": "Fresh deployment succeeds with correct gas configuration" + } + ], + "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json" + }, + { + "id": "deploy-erc20-token-hardhat", + "title": "Deploy an ERC-20 Token Using Hardhat", + "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" + ], + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", + "prerequisites": { + "runtime": [ + "Node.js v22.13.1 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees and test transactions — get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer/test account. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Clone the ERC-20 template repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples/", + "cd revm-hardhat-examples/erc20-hardhat" + ], + "description": "Clone the revm-hardhat-examples repository and navigate into the erc20-hardhat subdirectory. This template contains a pre-configured Hardhat project with an OpenZeppelin ERC-20 contract, test suite, and Ignition deployment module." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/erc20-hardhat", + "commands": [ + "npm i", + "npm install dotenv" + ], + "description": "Install all template dependencies from package.json, then additionally install dotenv. dotenv is needed to replace Hardhat's interactive vars system (which cannot be used in agent shells) with a .env-based private key approach." + }, + { + "order": 3, + "action": "Create .env file and .gitignore for private key", + "working_directory": "revm-hardhat-examples/erc20-hardhat", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/erc20-hardhat", + "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." + }, + { + "order": 5, + "action": "Compile the contract", + "working_directory": "revm-hardhat-examples/erc20-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 1 Solidity file successfully", + "description": "Compile the ERC-20 contract. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in the solidity compiler settings and recompile." + }, + { + "order": 6, + "action": "Run the test suite", + "working_directory": "revm-hardhat-examples/erc20-hardhat", + "commands": [ + "npx hardhat test --network polkadotTestnet" + ], + "expected_output": "7 passing", + "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output — it confirms the contract logic is correct before deployment." + }, + { + "order": 7, + "action": "Deploy the ERC-20 contract", + "working_directory": "revm-hardhat-examples/erc20-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet" + ], + "expected_output": "MyToken deployed to: 0x...", + "interactive": true, + "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success — needed for token interaction and verification." + } + ], + "error_patterns": [ + { + "pattern": "CompilationError: Invalid opcode: MCOPY", + "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode which requires Cancun EVM version. The hardhat.config.ts does not specify evmVersion: 'cancun'.", + "resolution": "In hardhat.config.ts, set evmVersion: 'cancun' inside solidity.settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }. Then recompile." + }, + { + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + }, + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of deployment, commonly due to missing gasPrice or requiredConfirmations: 0.", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." + } + ], + "supplementary_context": { + "description": "Load when the user wants to build a dApp on top of the deployed ERC-20 or interact with it.", + "pages": [ + { + "slug": "smart-contracts-cookbook-dapps-zero-to-hero", + "title": "Zero to Hero Smart Contract DApp", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", + "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract." + }, + { + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", + "relevance": "How to interact with the deployed ERC-20 contract using Ethers.js." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy a new ERC-20 token to Polkadot Hub", + "user_says": "Deploy an ERC-20 token to Polkadot Hub TestNet", + "actions": [ + "Clone revm-hardhat-examples and cd erc20-hardhat", + "Run npm i && npm install dotenv", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Modify hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set evmVersion cancun, confirm requiredConfirmations: 1", + "Run npx hardhat compile", + "Run npx hardhat test --network polkadotTestnet to verify contract logic", + "Run npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet; delegate confirmation to user" + ], + "result": "ERC-20 token contract deployed; address printed to console" + }, + { + "scenario": "Edge case: compilation fails with MCOPY opcode error", + "user_says": "Compilation fails with 'Invalid opcode: MCOPY'", + "actions": [ + "Open hardhat.config.ts", + "Change the solidity field to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", + "Run npx hardhat compile again" + ], + "result": "Compilation succeeds with Cancun EVM version" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + } + }, + { + "id": "set-up-hardhat-pvm", + "title": "Set Up Hardhat with the Polkadot Plugin (PVM)", + "description": "Sets up a Hardhat project using the @parity/hardhat-polkadot plugin and the resolc compiler to compile Solidity to PolkaVM (PVM) bytecode. Use when targeting Polkadot Hub's PVM runtime rather than the standard EVM runtime. Covers project initialization, resolc installation, dotenv private key setup, TestNet config with gasPrice, and a compile-verify step. Trigger phrases: 'Hardhat PVM', 'hardhat-polkadot plugin', 'compile to PolkaVM', 'resolc Hardhat', 'PVM smart contract Polkadot'. Do NOT use this skill for standard EVM deployments; use set-up-hardhat-evm instead.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/dev-environments/hardhat-polkadot.md" + ], + "primary_page": "smart-contracts/dev-environments/hardhat-polkadot.md", + "prerequisites": { + "runtime": [ + "Node.js v22.5+ and npm v10.9.0+ (required for @parity/hardhat-polkadot compatibility)", + "npm or pnpm or yarn" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create project directory and initialize npm", + "working_directory": ".", + "commands": [ + "mkdir hardhat-pvm-example && cd hardhat-pvm-example", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project." + }, + { + "order": 2, + "action": "Install the Polkadot Hardhat plugin and resolc compiler", + "working_directory": "hardhat-pvm-example", + "commands": [ + "npm install --save-dev @parity/hardhat-polkadot", + "npm install --save-dev @parity/resolc", + "npm install dotenv" + ], + "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them — use latest." + }, + { + "order": 3, + "action": "Initialize the Hardhat PVM project structure", + "working_directory": "hardhat-pvm-example", + "commands": [ + "npx hardhat-polkadot init" + ], + "interactive": true, + "description": "Run the project creation wizard. This is an interactive command — delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." + }, + { + "order": 4, + "action": "Install all project dependencies after init", + "working_directory": "hardhat-pvm-example", + "commands": [ + "npm install", + "echo '/ignition/deployments/' >> .gitignore" + ], + "description": "Install any additional dependencies added by the init wizard, then add ignition/deployments/ to .gitignore to avoid committing deployment state to version control." + }, + { + "order": 5, + "action": "Create .env file and .gitignore entry for private key", + "working_directory": "hardhat-pvm-example", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 6, + "action": "Fetch and configure hardhat.config.ts for TestNet", + "working_directory": "hardhat-pvm-example", + "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." + }, + { + "order": 7, + "action": "Compile the sample contract to verify setup", + "working_directory": "hardhat-pvm-example", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled ... Solidity file(s) successfully", + "description": "Compile the sample contract generated by the init wizard. Successful compilation confirms resolc is working and the project structure is correct. The compiled artifacts appear in artifacts/contracts/. If compilation fails with resolc errors, verify @parity/resolc is installed and the version matches what the hardhat.config.ts specifies in the resolc field." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find module '@parity/hardhat-polkadot' / resolc not found", + "cause": "The Polkadot plugin or resolc compiler was not installed.", + "resolution": "Run: npm install --save-dev @parity/hardhat-polkadot @parity/resolc in the project directory." + }, + { + "pattern": "Compilation fails with resolc errors / unsupported Solidity version", + "cause": "The resolc compiler version does not support the contract's Solidity pragma version.", + "resolution": "Check the @parity/resolc version installed and the Solidity pragma in the contract. Update the version field in hardhat.config.ts resolc settings to match what is installed, or downgrade the Solidity pragma in the contract to a supported version." + }, + { + "pattern": "Error: vars is not defined", + "cause": "hardhat.config.ts uses Hardhat's vars.get() system instead of dotenv.", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + }, + { + "pattern": "Deployment hangs on local node / requiredConfirmations: 0", + "cause": "The ignition config has requiredConfirmations: 0, which causes Ignition to misinterpret pending transactions as dropped on local nodes that don't produce blocks instantly.", + "resolution": "Set ignition.requiredConfirmations: 1 in hardhat.config.ts." + }, + { + "pattern": "Binary permission issues on macOS (binary is quarantined)", + "cause": "macOS Gatekeeper quarantines downloaded binaries for dev-node and eth-rpc adapter.", + "resolution": "Run: xattr -d com.apple.quarantine /path/to/binary. Also ensure executable permission: chmod +x /path/to/binary." + } + ], + "supplementary_context": { + "description": "Load when the user wants to deploy a contract after setting up the PVM environment, or needs to understand PVM vs EVM differences.", + "pages": [ + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", + "relevance": "Next step: deploy a basic contract once the Hardhat PVM environment is set up." + }, + { + "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", + "title": "EVM vs PVM", + "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md", + "relevance": "Understand the differences between EVM and PVM to choose the right compilation target." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: set up a PVM Hardhat project from scratch", + "user_says": "Set up Hardhat with the Polkadot PVM plugin", + "actions": [ + "Create hardhat-pvm-example/ and run npm init -y", + "Install @parity/hardhat-polkadot, @parity/resolc, dotenv", + "Run npx hardhat-polkadot init wizard (delegate to user)", + "Run npm install and add ignition/deployments/ to .gitignore", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Fetch and configure hardhat.config.ts: add dotenv, replace vars.get, add gasPrice 5000 gwei, requiredConfirmations: 1", + "Run npx hardhat compile to verify setup" + ], + "result": "PVM Hardhat project compiles successfully; ready to deploy contracts to Polkadot Hub" + }, + { + "scenario": "Edge case: user is on macOS and binary is quarantined", + "user_says": "The dev-node binary is blocked by macOS Gatekeeper", + "actions": [ + "Run: xattr -d com.apple.quarantine /path/to/dev-node", + "Run: chmod +x /path/to/dev-node", + "Restart the local node" + ], + "result": "Binary executes without quarantine errors" + } + ] + }, + { + "id": "set-up-hardhat-evm", + "title": "Set Up Hardhat for Polkadot Hub (EVM)", + "description": "Initializes a Hardhat v2 TypeScript project for the standard Polkadot Hub EVM runtime (not PVM). Configures the polkadotTestnet network with correct RPC URL, chain ID (420420417), gasPrice (5000 gwei), and dotenv-based private key management. Optionally configures Blockscout or Routescan contract verification. Use when setting up a reusable EVM development environment before deploying any contract. Trigger phrases: 'set up Hardhat for Polkadot EVM', 'configure Hardhat Polkadot Hub', 'Hardhat EVM Polkadot setup', 'Hardhat network config Polkadot'. Do NOT use for PVM; use set-up-hardhat-pvm instead.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/dev-environments/hardhat.md" + ], + "primary_page": "smart-contracts/dev-environments/hardhat.md", + "prerequisites": { + "runtime": [ + "Node.js LTS (18.x, 20.x, or 22.x — even major version numbers only)", + "npm, pnpm, or yarn" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account — needed before deploying contracts" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key used in the polkadotTestnet accounts array. Fill before running any deployment. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create project directory and initialize npm", + "working_directory": ".", + "commands": [ + "mkdir hardhat-example && cd hardhat-example", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage — the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." + }, + { + "order": 2, + "action": "Install Hardhat and dependencies", + "working_directory": "hardhat-example", + "commands": [ + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", + "npm install dotenv" + ], + "description": "Install Hardhat v2, the Hardhat Toolbox (includes ethers.js, chai, and testing utilities), TypeScript support, and dotenv. The dotenv package replaces Hardhat's interactive 'npx hardhat vars set' mechanism, which cannot be used in agent shells." + }, + { + "order": 3, + "action": "Create project directory structure", + "working_directory": "hardhat-example", + "commands": [ + "mkdir -p contracts ignition/modules test", + "echo '/ignition/deployments/' >> .gitignore", + "echo '/artifacts/' >> .gitignore", + "echo '/cache/' >> .gitignore" + ], + "description": "Create the standard Hardhat directory structure and add common output directories to .gitignore." + }, + { + "order": 4, + "action": "Create .env file and .gitignore entry for private key", + "working_directory": "hardhat-example", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable — the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." + }, + { + "order": 5, + "action": "Fetch and configure hardhat.config.ts", + "working_directory": "hardhat-example", + "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: vars is not defined", + "cause": "hardhat.config.ts still uses Hardhat's vars.get() system.", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + }, + { + "pattern": "HardhatError: HH8: There's one or more errors in your config / Cannot read properties of undefined (reading 'get')", + "cause": "The vars import is present but vars is undefined because the interactive vars store was never populated.", + "resolution": "Remove 'import { vars } from \"hardhat/config\";' and all vars.get() calls. Replace with process.env.PRIVATE_KEY loaded by dotenv." + }, + { + "pattern": "Error: insufficient funds for intrinsic transaction cost on deployment", + "cause": "gasPrice is missing or below the TestNet base fee of 1000 gwei.", + "resolution": "Add gasPrice: 5000000000000 to the polkadotTestnet network block in hardhat.config.ts. Also ensure the deployer account has testnet PAS from https://faucet.polkadot.io/." + }, + { + "pattern": "Cannot find module 'ts-node' / TypeScript compilation error on npx hardhat", + "cause": "TypeScript dependencies were not installed.", + "resolution": "Run: npm install --save-dev typescript ts-node @types/node in the project directory." + } + ], + "supplementary_context": { + "description": "Load when the user is ready to deploy a contract or wants to verify a deployed contract.", + "pages": [ + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", + "relevance": "Next step: deploy a basic Storage contract using the configured Hardhat project." + }, + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", + "title": "Deploy an ERC-20 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", + "relevance": "Deploy an ERC-20 token using the configured Hardhat environment." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: configure a Hardhat project for Polkadot Hub from scratch", + "user_says": "Set up a Hardhat project for Polkadot Hub EVM", + "actions": [ + "Create hardhat-example/ and run npm init -y", + "Install hardhat@^2.27.0, hardhat-toolbox, typescript, ts-node, dotenv", + "Create contracts/, ignition/modules/, test/ directories and set .gitignore", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Fetch hardhat.config.ts from cookbook; add dotenv import, replace vars.get, add gasPrice 5000 gwei, add ignition.requiredConfirmations: 1" + ], + "result": "Hardhat project configured for Polkadot Hub TestNet; ready for contract development and deployment" + }, + { + "scenario": "Edge case: existing project uses Hardhat vars and breaks in CI or agent shell", + "user_says": "My Hardhat config uses vars.get and it fails with 'vars is not defined'", + "actions": [ + "Open hardhat.config.ts", + "Add 'import \"dotenv/config\";' as the first line", + "Replace all vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string", + "Remove the 'import { vars } from \"hardhat/config\";' line", + "Create .env with PRIVATE_KEY= and add .env to .gitignore" + ], + "result": "Project loads private key from .env; works in agent shells and CI without interactive prompts" + } + ] + }, + { + "id": "query-chain-data-sidecar-rest", + "title": "Query On-Chain Data with Sidecar REST API", + "description": "Queries Polkadot Hub on-chain data via curl against the Substrate API Sidecar REST service. Covers account balance-info, asset-balances (including USDT/USDC), asset metadata, and block endpoints using Parity's public Sidecar instance. Use when you need account balances, asset info, or block data without writing SDK code. Also covers running a local Sidecar instance via npm or Docker for production use. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required — read-only operations only.", + "version": "1.1.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/query-data/query-rest.md" + ], + "primary_page": "chain-interactions/query-data/query-rest.md", + "prerequisites": { + "runtime": [ + "curl (pre-installed on macOS and Linux; use WSL on Windows)", + "jq (optional, for formatted JSON output — install with: apt install jq or brew install jq)", + "Node.js v18+ and npm (only if running Sidecar locally)" + ], + "network": [ + "Parity public Sidecar endpoint: https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io (no setup required)", + "OR: WebSocket RPC endpoint for a Polkadot Hub node (if running Sidecar locally)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Choose your Sidecar endpoint", + "working_directory": ".", + "description": "You have two options:\n\n**Option A — Public endpoint (recommended for testing):** Use Parity's hosted Sidecar at `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io`. No setup required; skip to step 2.\n\n**Option B — Local Sidecar:** Run your own instance for production or high-frequency queries.\n\n```bash\n# Install globally\nnpm install -g @substrate/api-sidecar\n\n# Start with Polkadot Hub mainnet endpoint\nSAS_SUBSTRATE_URL=wss://polkadot-asset-hub-rpc.polkadot.io substrate-api-sidecar\n```\n\nWait for `SAS listening on http://127.0.0.1:8080/` before proceeding. If using the local instance, replace the base URL in all following steps with `http://127.0.0.1:8080`." + }, + { + "order": 2, + "action": "Query account balance", + "working_directory": ".", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/balance-info\" | jq ." + ], + "expected_output": "{\"at\":{\"hash\":\"0x...\",\"height\":\"12345\"},\"nonce\":\"0\",\"tokenSymbol\":\"DOT\",\"free\":\"...\",\"reserved\":\"...\",\"frozen\":\"...\",\"transferable\":\"...\"}", + "description": "Query the balance-info endpoint. Replace `INSERT_SS58_ADDRESS` with the target SS58 address. Response fields: `free` — spendable planck; `reserved` — locked/staked; `frozen` — frozen amounts; `transferable` — actual balance available to transfer (new field); `nonce` — transaction count. Divide raw planck values by 10^10 to convert to DOT/PAS. Omit `| jq .` if jq is not installed." + }, + { + "order": 3, + "action": "Query asset balances", + "working_directory": ".", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984\" | jq ." + ], + "expected_output": "{\"at\":{...},\"items\":[{\"assetId\":\"1984\",\"balance\":\"...\",\"isFrozen\":false,\"isSufficient\":false}]}", + "description": "Query asset balances for asset ID 1984 (USDT) held by the account. Replace `INSERT_SS58_ADDRESS` with the target SS58 address. Replace `1984` with any registered asset ID (e.g., `1337` for USDC). To query multiple assets in one request, append additional `assets[]=` parameters: `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984&assets[]=1337`. If the account holds no USDT, `items` will be an empty array." + }, + { + "order": 4, + "action": "Query asset metadata", + "working_directory": ".", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/pallets/assets/storage/Metadata?keys[]=1984\" | jq ." + ], + "expected_output": "{\"at\":{...},\"pallet\":\"assets\",\"storageItem\":\"Metadata\",\"keys\":[\"1984\"],\"value\":{\"deposit\":\"...\",\"name\":\"0x5465746865722055534454\",\"symbol\":\"0x55534474\",\"decimals\":\"6\",\"isFrozen\":false}}", + "description": "Query metadata for asset ID 1984 (USDT). The `name` and `symbol` fields are returned as hex-encoded strings: `0x5465746865722055534454` decodes to 'Tether USD', `0x55534474` decodes to 'USDt'. Replace `1984` with any asset ID. For asset details (owner, supply, isSufficient), use `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/pallets/assets/storage/Asset?keys[]=1984`." + }, + { + "order": 5, + "action": "Query block information", + "working_directory": ".", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/blocks/head\" | jq .", + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/blocks/INSERT_BLOCK_NUMBER\" | jq ." + ], + "expected_output": "{\"number\":\"12345\",\"hash\":\"0x...\",\"parentHash\":\"0x...\",\"extrinsics\":[...]}", + "description": "The first command fetches the latest finalized block. The second fetches a specific block by number — replace `INSERT_BLOCK_NUMBER` with any block number. The response includes block number, hash, parentHash, stateRoot, extrinsicsRoot, and extrinsics list." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "ECONNREFUSED 127.0.0.1:8080", + "cause": "Sidecar is not running (only applies when using the local instance).", + "resolution": "Start Sidecar in a separate terminal: SAS_SUBSTRATE_URL=wss://polkadot-asset-hub-rpc.polkadot.io substrate-api-sidecar. Wait for 'SAS listening' before sending curl requests. If using the public endpoint, this error does not apply." + }, + { + "pattern": "curl: (6) Could not resolve host / Connection reset / 502 Bad Gateway", + "cause": "The Sidecar endpoint is temporarily unavailable or the URL is incorrect.", + "resolution": "Verify the base URL. If using the public endpoint, check https://status.polkadot.network for outages and retry after a few minutes. If using a local instance, verify the WebSocket endpoint URL and that the Sidecar process is running." + }, + { + "pattern": "balance-info returns all-zero values for free/reserved/nonce", + "cause": "The queried account has no on-chain entry — it has never received any tokens.", + "resolution": "Fund the account at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation. Re-query the endpoint." + }, + { + "pattern": "asset-balances returns empty items array", + "cause": "The account does not hold the specified asset, or the asset ID is wrong.", + "resolution": "Verify the asset ID. USDT on Polkadot Hub is asset ID 1984. The account must hold the asset for a balance to appear." + } + ], + "supplementary_context": { + "description": "Load these pages when the user wants SDK-based querying as an alternative or needs to understand Sidecar response fields.", + "pages": [ + { + "slug": "chain-interactions-query-data-query-sdks", + "title": "Query On-Chain State with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", + "relevance": "SDK-based alternative to Sidecar — query the same on-chain data using PAPI or Polkadot.js TypeScript code." + }, + { + "slug": "reference-tools-sidecar", + "title": "Sidecar REST API", + "url": "https://docs.polkadot.com/reference/tools/sidecar.md", + "relevance": "Full Sidecar API reference listing all available endpoints, query parameters, and response schemas." + }, + { + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", + "relevance": "SDK approach to querying the same account balance data returned by the Sidecar balance-info endpoint." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: query account balance via REST without writing code", + "user_says": "Query the balance of a Polkadot Hub address using the REST API", + "actions": [ + "Use Parity's public Sidecar at https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io", + "Run curl against /accounts/{address}/balance-info", + "Divide the free field value by 10^10 to convert planck to DOT/PAS", + "Check the transferable field for the actual spendable balance" + ], + "result": "Account nonce and full balance breakdown (free, reserved, frozen, transferable) returned as JSON" + }, + { + "scenario": "Edge case: query USDT asset balance for an account", + "user_says": "How do I check if an account holds USDT on Polkadot Hub?", + "actions": [ + "Run curl against https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984", + "If items[] is non-empty, the account holds USDT (asset ID 1984)", + "Check the balance field in the response item (in smallest USDT unit, 6 decimals)" + ], + "result": "USDT balance for the account returned; empty items array if account holds no USDT" + } + ] + }, + { + "id": "calculate-transaction-fees-papi", + "title": "Estimate Transaction Fees with PAPI", + "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getEstimatedFees method. Use when you need to preview gas costs before submitting — for fee UIs, budget checks, or preflight validation. Returns estimated fee in planck and human-readable form, plus the total amount deducted (fee + transfer amount). Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getEstimatedFees PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required — fee estimation is read-only.", + "version": "1.1.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/send-transactions/calculate-transaction-fees.md" + ], + "primary_page": "chain-interactions/send-transactions/calculate-transaction-fees.md", + "prerequisites": { + "runtime": [ + "Node.js v18+ and npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://pas-rpc.stakeworld.io/assethub" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir fee-calculator && cd fee-calculator", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'fee-calculator' and initialize it as an ESM Node.js project. The type=module flag is required — polkadot-api uses ESM imports." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "fee-calculator", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling." + }, + { + "order": 3, + "action": "Generate PAPI type descriptors", + "working_directory": "fee-calculator", + "commands": [ + "npx papi add polkadotTestNet -w wss://pas-rpc.stakeworld.io/assethub" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. If the endpoint is unavailable, retry after a few minutes — the TestNet can be intermittently slow. Replace the -w URL if targeting a different network." + }, + { + "order": 4, + "action": "Fetch the fee estimation script", + "working_directory": "fee-calculator", + "reference_file": "papi-fee-calculator.ts", + "description": "Fetch the reference file and save it as 'papi-fee-calculator.ts'. Make these three substitutions: (1) Replace `INSERT_WS_ENDPOINT` with your WebSocket endpoint (e.g., `wss://pas-rpc.stakeworld.io/assethub`). (2) Replace `INSERT_ALICE_ADDRESS` with the sender's SS58 address. (3) Replace `INSERT_BOB_ADDRESS` with the recipient's SS58 address. Fee estimation does not sign or submit the transaction — any valid SS58 addresses work." + }, + { + "order": 5, + "action": "Run the fee estimation script", + "working_directory": "fee-calculator", + "commands": [ + "npx tsx papi-fee-calculator.ts" + ], + "expected_output": "Estimated fee: 0.001500 DOT\nTransaction amount: 1.000000 DOT\nTotal deducted: 1.001500 DOT", + "description": "Execute the script. It constructs an unsigned Balances.transfer_keep_alive transaction for 1 DOT (10^10 planck) and calls getEstimatedFees to retrieve the fee estimate without submitting. The output shows fee, transfer amount, and total in DOT units. Fees on Polkadot Hub TestNet are higher than Ethereum due to the ~1000 gwei base fee." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/send-transactions/calculate-transaction-fees", + "files": [ + { + "path": "papi-fee-calculator.ts", + "description": "Constructs a transfer_keep_alive tx and calls getEstimatedFees(); prints fee, transfer amount, and total in DOT units" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "cause": "PAPI type descriptors were not generated.", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://pas-rpc.stakeworld.io/assethub' in the fee-calculator directory." + }, + { + "pattern": "WebSocket connection failed / ECONNREFUSED on papi add or script start", + "cause": "Polkadot Hub TestNet endpoint is temporarily unavailable.", + "resolution": "Wait 2-5 minutes and retry. Try an alternative public endpoint if the issue persists." + }, + { + "pattern": "TypeError: tx.getEstimatedFees is not a function", + "cause": "Descriptor export name does not match the import, or polkadot-api version is below 1.0.0.", + "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api — version 1.0.0+ required." + }, + { + "pattern": "SyntaxError: Cannot use import statement in a module", + "cause": "Project is not configured as ESM.", + "resolution": "Run 'npm pkg set type=module' in the fee-calculator directory, then re-run." + } + ], + "supplementary_context": { + "description": "Load these pages when the user wants to submit the transaction after fee estimation or needs to understand fee mechanics.", + "pages": [ + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "How to sign and submit the same transaction whose fee was estimated — the natural next step." + }, + { + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md", + "relevance": "Full PAPI reference including getPaymentInfo signatures and other typed transaction methods." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: preview fee before sending a transfer", + "user_says": "How much will it cost to send 1 DOT on Polkadot Hub?", + "actions": [ + "Scaffold 'fee-calculator/' as an ESM Node.js project", + "Install polkadot-api and tsx", + "Generate descriptors with npx papi add polkadotTestNet", + "Fetch papi-fee-calculator.ts; substitute WS endpoint, sender address, recipient address", + "Run npx tsx papi-fee-calculator.ts" + ], + "result": "Estimated fee in DOT plus total deducted amount printed; user can budget before submitting the transfer" + }, + { + "scenario": "Edge case: fee appears unusually high compared to Ethereum", + "user_says": "The fee estimate seems high compared to what I expected", + "actions": [ + "Explain that Polkadot Hub TestNet has a base fee of ~1000 gwei — significantly higher than Ethereum mainnet", + "Note that PAS/DOT testnet fees do not reflect real economic cost", + "Get testnet tokens from https://faucet.polkadot.io/ if the sender balance is insufficient for actual submission" + ], + "result": "User understands TestNet fee mechanics and obtains testnet tokens if needed" + } + ], + "project_structure": "fee-calculator/\n├── .papi/\n│ └── descriptors/\n├── papi-fee-calculator.ts\n└── package.json" + }, + { + "id": "transfer-assets-parachains-paraspell", + "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK", + "description": "Transfers assets cross-chain between Polkadot parachains using the ParaSpell XCM SDK. Covers the full workflow: install the SDK, build an XCM transfer, dry-run for feasibility, verify existential deposit requirements, estimate fees, and submit against Paseo testnet. Use when you need to move tokens programmatically between parachains (e.g., Asset Hub to another system parachain). Requires a funded Paseo testnet mnemonic. Trigger phrases: 'XCM transfer ParaSpell', 'cross-chain transfer Polkadot', 'transfer assets between parachains', 'send tokens XCM', 'ParaSpell SDK'. Uses dotenv for mnemonic security.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" + ], + "primary_page": "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", + "prerequisites": { + "runtime": [ + "Node.js v18+ and npm" + ], + "network": [ + "WebSocket RPC for Paseo Asset Hub (source): wss://sys.ibp.network/asset-hub-paseo", + "Destination parachain WebSocket RPC (e.g., wss://sys.ibp.network/people-paseo for People Chain Paseo)" + ], + "tokens": [ + "Paseo testnet PAS in the sender account — get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." + ], + "wallet": [ + "SR25519 account mnemonic phrase for the sender account, funded with Paseo testnet PAS" + ] + }, + "env_vars": [ + { + "name": "MNEMONIC", + "description": "12-word BIP39 mnemonic for the sender on Paseo Asset Hub. Must be funded with testnet PAS. Never commit to version control.", + "required": true + }, + { + "name": "SOURCE_RPC", + "description": "WebSocket RPC URL for the source parachain (e.g., wss://sys.ibp.network/asset-hub-paseo).", + "required": true + }, + { + "name": "DEST_ADDRESS", + "description": "SS58-encoded recipient address on the destination parachain.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Initialize the project", + "working_directory": ".", + "commands": [ + "mkdir paraspell-xcm-transfer && cd paraspell-xcm-transfer", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'paraspell-xcm-transfer' and initialize it as an ESM Node.js project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "paraspell-xcm-transfer", + "commands": [ + "npm install @paraspell/sdk-pjs @polkadot/api @polkadot/keyring dotenv", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install the ParaSpell SDK (pjs variant), Polkadot.js API, keyring for signing, and dotenv for secure mnemonic loading." + }, + { + "order": 3, + "action": "Create .env and .gitignore for mnemonic", + "working_directory": "paraspell-xcm-transfer", + "commands": [ + "printf 'MNEMONIC=\\nSOURCE_RPC=wss://sys.ibp.network/asset-hub-paseo\\nDEST_ADDRESS=\\n' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create a .env file with three placeholders and a .gitignore to exclude it. Stop here and ask the user to edit .env directly: fill MNEMONIC with their 12-word Paseo testnet mnemonic, confirm SOURCE_RPC, and set DEST_ADDRESS to the recipient SS58 address on the destination chain. Do NOT ask for the mnemonic in chat. Wait for confirmation before continuing." + }, + { + "order": 4, + "action": "Create the XCM transfer script", + "working_directory": "paraspell-xcm-transfer", + "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains — chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." + }, + { + "order": 5, + "action": "Verify sender is funded and run the script", + "working_directory": "paraspell-xcm-transfer", + "commands": [ + "npx tsx xcm-transfer.ts" + ], + "expected_output": "Sender: ...\nDry-run: OK\nEstimated fee: ...\nStatus: InBlock\nFinalized: 0x...", + "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first — if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: Insufficient funds / Inability to pay some fees", + "cause": "Sender account has insufficient PAS to cover transfer amount plus XCM execution fees.", + "resolution": "Fund the sender on Paseo Asset Hub at https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Wait 2 minutes for confirmation." + }, + { + "pattern": "Dry-run failed: destination existential deposit not met", + "cause": "Transfer amount is below the existential deposit on the destination chain, or recipient has no balance.", + "resolution": "Increase the transfer AMOUNT above the destination chain's existential deposit, or ensure the recipient already has a balance on the destination chain." + }, + { + "pattern": "Error: chain 'AssetHubPaseo' not found in ParaSpell", + "cause": "Chain name does not match the ParaSpell supported chain list for the SDK version installed.", + "resolution": "Check the supported chains list for your @paraspell/sdk-pjs version. Chain names are case-sensitive and version-specific. Update SOURCE_CHAIN and DEST_CHAIN in the script accordingly." + }, + { + "pattern": "Transaction submitted but never finalizes", + "cause": "Paseo TestNet instability — the TestNet can drop transactions under load.", + "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script — nonce: -1 fetches the current nonce automatically." + } + ], + "supplementary_context": { + "description": "Load these pages for background on XCM mechanics, fee estimation, or to debug a failing transfer.", + "pages": [ + { + "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", + "title": "XCM Fee Estimation", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", + "relevance": "Detailed XCM fee estimation covering local, delivery, and remote execution components." + }, + { + "slug": "reference-tools-paraspell", + "title": "ParaSpell XCM SDK", + "url": "https://docs.polkadot.com/reference/tools/paraspell.md", + "relevance": "Full ParaSpell SDK reference including supported chains, asset IDs, and Builder API documentation." + }, + { + "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", + "title": "Replay and Dry Run XCMs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", + "relevance": "How to debug a failing XCM transfer by replaying it with Chopsticks before submitting to TestNet." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: transfer PAS from Asset Hub Paseo to People Chain Paseo", + "user_says": "Transfer 1 PAS from Asset Hub to People Chain on Paseo testnet", + "actions": [ + "Scaffold 'paraspell-xcm-transfer/' as an ESM project", + "Install @paraspell/sdk-pjs, @polkadot/api, keyring, dotenv", + "Create .env; ask user to fill MNEMONIC and DEST_ADDRESS without asking in chat", + "Create xcm-transfer.ts with the inline script", + "Verify sender has 2+ PAS at https://faucet.polkadot.io/", + "Run npx tsx xcm-transfer.ts" + ], + "result": "Dry-run passes, fee estimated, transaction finalized, PAS transferred cross-chain" + }, + { + "scenario": "Edge case: dry-run fails with existential deposit error", + "user_says": "The dry-run fails saying the destination existential deposit is not met", + "actions": [ + "Check the existential deposit for the destination chain using the ParaSpell SDK", + "Increase AMOUNT in the script above the existential deposit threshold", + "Re-run xcm-transfer.ts" + ], + "result": "Dry-run passes once the transfer amount covers the destination existential deposit" + } + ] + }, + { + "id": "pay-fees-alternative-token", + "title": "Pay Transaction Fees with an Alternative Token", + "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode — no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/send-transactions/pay-fees-with-different-tokens.md" + ], + "primary_page": "chain-interactions/send-transactions/pay-fees-with-different-tokens.md", + "prerequisites": { + "runtime": [ + "Node.js v18+ and npm", + "npx (bundled with npm v5.2+ — no separate install needed)" + ], + "network": [ + "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network — Chopsticks downloads state on first run" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Create project directory and install dependencies", + "working_directory": ".", + "commands": [ + "mkdir fee-proxy-demo && cd fee-proxy-demo", + "npm init -y && npm pkg set type=module", + "npm install @acala-network/chopsticks polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Create a new directory 'fee-proxy-demo', initialize as ESM, and install Chopsticks and PAPI. Chopsticks forks Polkadot Hub state locally; PAPI constructs and submits the fee-proxy transaction against the fork." + }, + { + "order": 2, + "action": "Create Chopsticks configuration", + "working_directory": "fee-proxy-demo", + "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key — this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." + }, + { + "order": 3, + "action": "Start the Chopsticks fork", + "working_directory": "fee-proxy-demo", + "commands": [ + "npx @acala-network/chopsticks --config=chopsticks.yml" + ], + "expected_output": "Listening on port 8000", + "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." + }, + { + "order": 4, + "action": "Generate PAPI type descriptors for the local fork", + "working_directory": "fee-proxy-demo", + "commands": [ + "npx papi add localFork -w ws://localhost:8000" + ], + "description": "Run this command in a second terminal while Chopsticks (step 3) is still running. Generates compile-time type descriptors named 'localFork' for the forked chain. Produces the 'localFork' export in @polkadot-api/descriptors. The descriptors reflect the exact runtime version of the forked state." + }, + { + "order": 5, + "action": "Create the fee-proxy script", + "working_directory": "fee-proxy-demo", + "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy — pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." + }, + { + "order": 6, + "action": "Run the fee-proxy script against the local fork", + "working_directory": "fee-proxy-demo", + "commands": [ + "npx tsx fee-proxy.ts" + ], + "expected_output": "Included in block: 0x...\nFees paid in USDT asset ID: 1984", + "description": "Execute the script while Chopsticks is still running. The script submits the fee-proxy transaction; Chopsticks validates it against the forked state and includes it in a mock block. If the pallet name 'AssetConversionTxPayment' does not exist in localFork, inspect api.tx in the TypeScript IDE and check the source page for the correct name." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "ECONNREFUSED ws://localhost:8000", + "cause": "Chopsticks is not running.", + "resolution": "Start Chopsticks first: npx @acala-network/chopsticks --config=chopsticks.yml. Wait for 'Listening on port 8000' before running the script or the papi add command." + }, + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or localFork is undefined", + "cause": "PAPI type descriptors were not generated against the Chopsticks fork endpoint.", + "resolution": "With Chopsticks running, execute: npx papi add localFork -w ws://localhost:8000. Then re-run the script." + }, + { + "pattern": "api.tx.AssetConversionTxPayment is undefined", + "cause": "The forked chain version uses a different pallet name for asset-based fee payment.", + "resolution": "Check available pallets by inspecting api.tx in a TypeScript IDE, or consult the source page for the correct pallet name. The pallet may be named 'AssetTxPayment' depending on the runtime version." + }, + { + "pattern": "Alice has insufficient USDT balance to pay fees", + "cause": "Alice's account in the forked state does not hold USDT (asset ID 1984).", + "resolution": "Use Chopsticks' dev_setStorage RPC method to seed Alice's USDT asset balance in the forked state, or identify a USDT-holding account in the forked state and use that address instead." + } + ], + "supplementary_context": { + "description": "Load these pages to understand fee mechanics, Chopsticks setup, or to compare with standard fee estimation.", + "pages": [ + { + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", + "relevance": "Full Chopsticks reference including all config options, RPC override methods, and mock-signature-host details." + }, + { + "slug": "chain-interactions-send-transactions-calculate-transaction-fees", + "title": "Calculate Transaction Fees", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", + "relevance": "How to estimate fees for a standard transaction — compare with the fee proxy approach to understand the difference." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: demonstrate paying fees in USDT on a local fork", + "user_says": "Show me how to pay Polkadot Hub transaction fees in USDT instead of PAS", + "actions": [ + "Create 'fee-proxy-demo/' with ESM Node.js init", + "Install Chopsticks and polkadot-api", + "Create chopsticks.yml pointing to wss://asset-hub-paseo.dotters.network with mock-signature-host: true", + "Start Chopsticks in a dedicated terminal; wait for 'Listening on port 8000'", + "Generate PAPI descriptors with npx papi add localFork -w ws://localhost:8000", + "Create fee-proxy.ts with inline script; run npx tsx fee-proxy.ts" + ], + "result": "Transaction included in a local mock block; fees deducted from Alice's USDT balance instead of PAS" + }, + { + "scenario": "Edge case: Alice has no USDT in the forked state", + "user_says": "The script fails saying Alice has no USDT balance", + "actions": [ + "Use Chopsticks' dev_setStorage RPC to set Alice's USDT asset balance in the forked state", + "Alternatively, query the forked state for an account with USDT balance and use that address as sender", + "Re-run fee-proxy.ts" + ], + "result": "Script succeeds once Alice's USDT balance is seeded or a funded account is substituted" + } + ] + }, + { + "id": "run-parachain-rpc-node", + "title": "Run a Parachain RPC Node", + "description": "Runs a Polkadot system parachain RPC node (using People Chain as example) in archive or pruned mode via Docker or systemd. Covers Docker image selection, binary installation, systemd service configuration, and sync verification. A substitution table adapts the steps for Collectives, BridgeHub, and other system parachains. Use when you need a self-hosted parachain RPC endpoint. Trigger phrases: 'run parachain node', 'People Chain RPC node', 'parachain RPC Docker', 'set up system parachain node', 'run parachain RPC systemd'. No tokens required.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "node-infrastructure/run-a-node/parachain-rpc.md" + ], + "primary_page": "node-infrastructure/run-a-node/parachain-rpc.md", + "prerequisites": { + "runtime": [ + "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", + "Disk: minimum 500 GB SSD for pruned node; 2+ TB SSD for archive node", + "RAM: minimum 8 GB; 16 GB recommended for archive nodes", + "CPU: 4+ cores recommended" + ], + "network": [ + "Open TCP ports: 30333 (P2P), 9944 (WebSocket RPC), 9615 (Prometheus metrics, optional)", + "Stable broadband connection for initial sync (several hundred GB download)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Choose deployment method and sync type", + "working_directory": ".", + "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table — replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." + }, + { + "order": 2, + "action": "Run the parachain node via Docker", + "working_directory": ".", + "commands": [ + "docker pull parity/polkadot-parachain:stable", + "mkdir -p /var/lib/people-chain", + "docker run -d --name people-chain-rpc --restart unless-stopped -p 30333:30333 -p 9944:9944 -p 9615:9615 -v /var/lib/people-chain:/data parity/polkadot-parachain:stable --chain=people-polkadot --base-path=/data --rpc-external --rpc-port=9944 --rpc-cors=all --prometheus-external --prometheus-port=9615 --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast" + ], + "description": "Pull the stable polkadot-parachain image and start the People Chain RPC node. Key arguments: --chain=people-polkadot selects the People Chain spec; --base-path=/data stores chain data in the mounted volume; --state-pruning=1000 --blocks-pruning=1000 keeps the last 1000 blocks (change both to 'archive' for a full archive node); --rpc-external exposes the WebSocket RPC; the -- separator and --chain=polkadot configure the embedded relay chain client. Replace /var/lib/people-chain with your data directory. For Paseo testnet People Chain, use --chain=people-paseo and --chain=paseo after the -- separator." + }, + { + "order": 3, + "action": "Run the parachain node via systemd (binary method alternative)", + "working_directory": ".", + "description": "For the systemd binary method (skip if using Docker in step 2): (1) Create system user: sudo useradd --system --no-create-home --shell /usr/sbin/nologin polkadot. (2) Create data dir: sudo mkdir -p /var/lib/people-chain && sudo chown polkadot:polkadot /var/lib/people-chain. (3) Download polkadot-parachain binary from https://github.com/paritytech/polkadot-sdk/releases (stable, amd64 or arm64) and install: sudo install -m 755 polkadot-parachain /usr/local/bin/. (4) Create /etc/systemd/system/people-chain-rpc.service:\n\n[Unit]\nDescription=People Chain RPC Node\nAfter=network.target\n\n[Service]\nUser=polkadot\nExecStart=/usr/local/bin/polkadot-parachain --chain=people-polkadot --base-path=/var/lib/people-chain --rpc-external --rpc-port=9944 --rpc-cors=all --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n\n(5) Enable and start: sudo systemctl daemon-reload && sudo systemctl enable people-chain-rpc && sudo systemctl start people-chain-rpc. Apply the same chain substitutions as in step 2." + }, + { + "order": 4, + "action": "Monitor sync progress", + "working_directory": ".", + "commands": [ + "docker logs -f people-chain-rpc 2>&1 | grep -E 'Syncing|Idle|Best|Peers'", + "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" + ], + "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", + "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." + }, + { + "order": 5, + "action": "Verify the RPC endpoint is operational", + "working_directory": ".", + "commands": [ + "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_chain\",\"params\":[]}' http://localhost:9944", + "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" + ], + "expected_output": "{\"result\":\"Polkadot People\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", + "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot People' for the mainnet People Chain. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete before pointing applications at this endpoint." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: No space left on device / database write error", + "cause": "The data volume has run out of disk space.", + "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=1000). Check usage with: du -sh /var/lib/people-chain" + }, + { + "pattern": "EADDRINUSE port 9944 or 30333", + "cause": "Another process is already using the required ports.", + "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." + }, + { + "pattern": "Peers count stays at 0 after several minutes", + "cause": "P2P port 30333 is blocked by firewall, or bootnodes are temporarily unavailable.", + "resolution": "Open TCP port 30333: sudo ufw allow 30333/tcp. For cloud VMs, update the security group to allow inbound TCP on 30333. If peers still do not connect after 10 minutes, check the Polkadot network status page." + }, + { + "pattern": "Sync stalls at the same block for more than 30 minutes", + "cause": "Node cannot find peers with required state, or the embedded relay chain client is not syncing.", + "resolution": "Restart the container or service: docker restart people-chain-rpc or sudo systemctl restart people-chain-rpc. If stalling persists, try switching sync mode: replace --sync=fast with --sync=warp." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to add TLS/WSS or run the Polkadot Hub RPC node specifically.", + "pages": [ + { + "slug": "node-infrastructure-run-a-node-polkadot-hub-rpc", + "title": "Run an RPC Node for Polkadot Hub", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/polkadot-hub-rpc.md", + "relevance": "How to run an RPC node for Polkadot Hub specifically — different chain spec and optional Ethereum RPC adapter." + }, + { + "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", + "title": "Set Up Secure WebSocket", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." + }, + { + "slug": "node-infrastructure-run-a-node-relay-chain-full-node", + "title": "Set Up a Node", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md", + "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: run a pruned People Chain RPC node via Docker", + "user_says": "Run a People Chain RPC node on Polkadot using Docker", + "actions": [ + "Pull parity/polkadot-parachain:stable", + "Create data directory /var/lib/people-chain", + "Run docker with --chain=people-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", + "Monitor sync with docker logs and system_syncState RPC", + "Verify with system_chain and system_health once currentBlock equals highestBlock" + ], + "result": "People Chain RPC node running, synced, serving requests on ws://localhost:9944" + }, + { + "scenario": "Edge case: node stalls at the same block for 30+ minutes", + "user_says": "The node has been stuck at block 12345 for 30 minutes", + "actions": [ + "Check both parachain and relay chain sync progress separately in the logs", + "Verify peers > 0 via system_health and that port 30333 is open", + "Restart: docker restart people-chain-rpc", + "If stalling persists after restart, switch to --sync=warp" + ], + "result": "Node resumes syncing after restart or sync mode change" + } + ] + }, + { + "id": "replay-dry-run-xcm-chopsticks", + "title": "Replay and Dry-Run XCMs Using Chopsticks", + "description": "Replays and dry-runs historical XCMs using Chopsticks multi-chain fork mode with PAPI TypeScript scripts. Covers building a local Wasm override for runtime logging, forking multiple chains via 'chopsticks xcm', extracting XCM call data from Subscan, submitting an XCM replay against the local fork with replay-xcm.ts, and simulating execution without state changes via dry-run-call.ts using DryRunApi.dry_run_call. Use when debugging a failing XCM, inspecting emitted events, or verifying fee coverage before resubmitting. Requires Rust build environment for Wasm compilation. Trigger phrases: 'replay XCM', 'dry-run XCM Chopsticks', 'debug XCM Polkadot', 'preview XCM execution', 'XCM dry run local fork'.", + "version": "1.1.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" + ], + "primary_page": "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", + "prerequisites": { + "runtime": [ + "Rust toolchain with cargo (to compile polkadot-fellows/runtimes for Wasm override)", + "Node.js v18+ and npm", + "npx (bundled with npm v5.2+)" + ], + "network": [ + "Internet access to fetch chain state for Chopsticks fork", + "Subscan or Polkadot.js Apps to locate and decode the XCM extrinsic call data" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Create project directory and install dependencies", + "working_directory": ".", + "commands": [ + "mkdir -p replay-xcm-tests && cd replay-xcm-tests", + "npm init -y && npm pkg set type=module", + "npm install -g @acala-network/chopsticks@latest", + "npm install --save-dev typescript @types/node tsx", + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers", + "npx tsc --init" + ], + "description": "Create the working directory, initialize an ESM Node.js project, and install all dependencies. Chopsticks is installed globally to avoid version conflicts. polkadot-api, @polkadot-labs/hdkd, and @polkadot-labs/hdkd-helpers are used by the replay and dry-run scripts." + }, + { + "order": 2, + "action": "Create .env with block numbers for the fork", + "working_directory": "replay-xcm-tests", + "description": "Create a file named '.env' in replay-xcm-tests with the following content:\n\n```text\nPOLKADOT_BLOCK_NUMBER=26481107\nPOLKADOT_HUB_BLOCK_NUMBER=9079591\nACALA_BLOCK_NUMBER=8826385\n```\n\nReplace these block numbers with the heights just *before* the XCM you want to replay was sent. The block numbers anchor the Chopsticks fork to the exact chain state at the time of the original XCM execution. Using the block immediately before the XCM block ensures the fork starts from the correct pre-execution state." + }, + { + "order": 3, + "action": "Build the Polkadot Hub Wasm override for runtime logging", + "working_directory": "replay-xcm-tests", + "commands": [ + "git clone git@github.com:polkadot-fellows/runtimes.git", + "cd runtimes && cargo build --release -p asset-hub-polkadot-runtime", + "mkdir -p ../wasms", + "cp target/release/wbuild/asset-hub-polkadot-runtime/asset_hub_polkadot_runtime.compact.compressed.wasm ../wasms/", + "cd .." + ], + "description": "Clone polkadot-fellows/runtimes and compile the Polkadot Hub runtime with the release profile. The compiled Wasm enables full runtime execution logs in Chopsticks, which are essential for tracing XCM execution flow. The cargo build may take 10-30 minutes on first run. Copy the resulting compressed Wasm to the wasms/ directory in replay-xcm-tests." + }, + { + "order": 4, + "action": "Download and configure the Chopsticks config for Polkadot Hub", + "working_directory": "replay-xcm-tests", + "commands": [ + "mkdir -p configs", + "wget https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot-asset-hub.yml -O configs/polkadot-asset-hub-override.yaml" + ], + "description": "Download the Polkadot Hub Chopsticks config and add the Wasm override and runtime log level. Edit 'configs/polkadot-asset-hub-override.yaml' to add these lines:\n\n```yaml\nruntime-log-level: 5\nwasm-override: wasms/asset_hub_polkadot_runtime.compact.compressed.wasm\n```\n\nThe `runtime-log-level: 5` enables verbose runtime execution logs needed to trace XCM flow. The `wasm-override` points to the locally built Wasm from step 3." + }, + { + "order": 5, + "action": "Start the multi-chain Chopsticks fork", + "working_directory": "replay-xcm-tests", + "interactive": true, + "commands": [ + "npx @acala-network/chopsticks xcm -r polkadot -p configs/polkadot-asset-hub-override.yaml -p acala" + ], + "expected_output": "Polkadot Hub RPC on http://localhost:8000\nAcala RPC on http://localhost:8001\nPolkadot RPC on http://localhost:8002", + "description": "Start Chopsticks in XCM mode in a dedicated terminal. It forks Polkadot relay chain, Polkadot Hub (with your custom Wasm override), and Acala locally. The first run downloads several MB of chain state — subsequent runs use the cached state. Wait for all three RPC endpoint announcements before proceeding to step 6. Keep this terminal running throughout the remaining steps." + }, + { + "order": 6, + "action": "Generate PAPI type descriptors for the local fork", + "working_directory": "replay-xcm-tests", + "commands": [ + "npx papi add polkadotHub -w ws://localhost:8000" + ], + "description": "Generate compile-time PAPI type descriptors from the local Chopsticks Polkadot Hub fork. This produces the 'polkadotHub' export in @polkadot-api/descriptors, used by both replay-xcm.ts and dry-run-call.ts. Chopsticks must be running (step 5) for this command to succeed." + }, + { + "order": 7, + "action": "Locate the XCM call data on Subscan", + "working_directory": ".", + "interactive": true, + "description": "This step is manual. Open Subscan for the source chain (e.g., https://assethub-polkadot.subscan.io). Navigate to the extrinsic that sent the XCM you want to replay. Decode the call data and copy the hex-encoded call payload (starts with '0x'). For the example XCM in the reference scripts, the call data is:\n\n```\n0x1f0803010100411f0300010100fc39fcf04a8071b7409823b7c82427ce67910c6ed80aa0e5093aff234624c8200304000002043205011f0092e81d790000000000\n```\n\nSave the hex string — you will substitute it in steps 8 and 9." + }, + { + "order": 8, + "action": "Fetch and run the XCM replay script", + "working_directory": "replay-xcm-tests", + "reference_file": "replay-xcm.ts", + "commands": [ + "npx tsx replay-xcm.ts" + ], + "expected_output": "👀 Executing XCM: {...}\n📦 Included in block #...: 0x...\n📣 Event: ...", + "description": "Fetch the reference file and save it as 'replay-xcm.ts'. In the file, replace the hardcoded call data hex string in `Binary.fromHex(...)` with the hex string you copied in step 7. The script uses Alice's dev account (DEV_PHRASE) as the signer — this works against the local Chopsticks fork because mock signatures are accepted. Run the script; it submits the XCM to the local fork and logs emitted events. Chopsticks must be running (step 5)." + }, + { + "order": 9, + "action": "Fetch and run the dry-run script", + "working_directory": "replay-xcm-tests", + "reference_file": "dry-run-call.ts", + "commands": [ + "npx tsx dry-run-call.ts" + ], + "expected_output": "{ executionResult: { type: 'Success', value: {...} }, ... }", + "description": "Fetch the reference file and save it as 'dry-run-call.ts'. Replace the hardcoded call data hex string in `Binary.fromHex(...)` with the same hex from step 7. The script calls DryRunApi.dry_run_call with XCM version 5 — this simulates execution without committing any state changes. Inspect the output for executionResult: a 'Success' type means the XCM would execute successfully; an 'Err' type reveals the failure reason (e.g., TooExpensive, Barrier blocked). Chopsticks must be running (step 5)." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms", + "files": [ + { + "path": "replay-xcm.ts", + "description": "Signs and submits an XCM transaction against a local Chopsticks fork using Alice's dev account; logs block inclusion and events" + }, + { + "path": "dry-run-call.ts", + "description": "Calls DryRunApi.dry_run_call to simulate XCM execution without state changes; uses XCM version 5 and logs the full dry-run result" + } + ] + }, + "error_patterns": [ + { + "pattern": "ECONNREFUSED ws://localhost:8000", + "cause": "Chopsticks is not running or not yet ready.", + "resolution": "Start Chopsticks in a dedicated terminal: npx @acala-network/chopsticks xcm -r polkadot -p configs/polkadot-asset-hub-override.yaml -p acala. Wait for all three RPC endpoint announcements before running scripts." + }, + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or 'polkadotHub' export", + "cause": "PAPI type descriptors were not generated from the local fork.", + "resolution": "Ensure Chopsticks is running, then run: npx papi add polkadotHub -w ws://localhost:8000. Regenerate descriptors if the fork was restarted since the last generation." + }, + { + "pattern": "executionResult: { type: 'Err', value: { type: 'TooExpensive' } }", + "cause": "The XCM message includes insufficient fees to cover execution on the destination chain.", + "resolution": "Increase the fee amount in the BuyExecution instruction of the XCM message before re-submitting. Check the estimate-xcm-fees-teleport skill for how to calculate required fees." + }, + { + "pattern": "cargo build fails or polkadot-fellows/runtimes won't compile", + "cause": "Rust toolchain version mismatch or missing build dependencies.", + "resolution": "Ensure the Rust toolchain matches the toolchain-version in polkadot-fellows/runtimes (typically specified in rust-toolchain.toml). Run 'rustup update' and retry. The first build may take 30+ minutes." + }, + { + "pattern": "Chopsticks fork diverges or shows 'Block not found'", + "cause": "The forked block is too old or the cached state (db.sqlite) is stale.", + "resolution": "Delete db.sqlite and restart Chopsticks to re-fork from the configured block numbers. Verify the POLKADOT_HUB_BLOCK_NUMBER in .env is correct and the block predates the XCM you want to replay." + } + ], + "supplementary_context": { + "description": "Load these pages for XCM fee estimation, the ParaSpell transfer skill, or Chopsticks configuration reference.", + "pages": [ + { + "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", + "title": "XCM Fee Estimation", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", + "relevance": "How to estimate XCM fees before submission — helps diagnose BuyExecution failures in a dry-run." + }, + { + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", + "relevance": "Full Chopsticks reference including all config options, block forking, and RPC override methods." + }, + { + "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", + "title": "Transfer Assets Between Parachains", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", + "relevance": "How to submit a real XCM transfer between parachains — use after debugging with Chopsticks." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: debug a failing XCM by dry-running it locally", + "user_says": "An XCM transfer I submitted failed. How can I debug it without spending more tokens?", + "actions": [ + "Create 'replay-xcm-tests/' and install all dependencies including Chopsticks", + "Create .env with block numbers from just before the failed XCM", + "Build Polkadot Hub Wasm from polkadot-fellows/runtimes (enables runtime logs)", + "Configure Chopsticks with wasm-override and runtime-log-level: 5", + "Start Chopsticks XCM fork in a dedicated terminal", + "Generate PAPI descriptors from local fork: npx papi add polkadotHub -w ws://localhost:8000", + "Locate the failed XCM on Subscan and copy the encoded call data hex", + "Fetch dry-run-call.ts; substitute the call data hex", + "Run npx tsx dry-run-call.ts; inspect executionResult for the failure reason" + ], + "result": "Dry-run reveals the failure reason (e.g., TooExpensive, Barrier blocked) and emitted events without spending tokens" + }, + { + "scenario": "Edge case: replaying the XCM against the local fork for full event tracing", + "user_says": "I need to see exactly which events fire when the XCM executes", + "actions": [ + "Complete steps 1-6 to set up the Chopsticks multi-chain fork", + "Fetch replay-xcm.ts; substitute the call data hex", + "Run npx tsx replay-xcm.ts", + "Inspect the logged events from signSubmitAndWatch to trace the full XCM execution path" + ], + "result": "All emitted events (including XCM execution events on destination chain) logged to console" + } + ], + "project_structure": "replay-xcm-tests/\n├── .env\n├── .papi/\n│ └── descriptors/\n├── configs/\n│ └── polkadot-asset-hub-override.yaml\n├── wasms/\n│ └── asset_hub_polkadot_runtime.compact.compressed.wasm\n├── dry-run-call.ts\n├── replay-xcm.ts\n├── tsconfig.json\n└── package.json" + }, + { + "id": "estimate-xcm-fees-teleport", + "title": "Estimate XCM Fees for Asset Teleport", + "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required — Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" + ], + "primary_page": "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", + "prerequisites": { + "runtime": [ + "Node.js v18+ and npm", + "npx (bundled with npm v5.2+)" + ], + "network": [ + "Internet connection to fork chain state from Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network", + "Internet connection to fork People Chain TestNet state" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Create project directory and install dependencies", + "working_directory": ".", + "commands": [ + "mkdir xcm-fee-estimate && cd xcm-fee-estimate", + "npm init -y && npm pkg set type=module", + "npm install @acala-network/chopsticks polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Create 'xcm-fee-estimate' as an ESM Node.js project and install Chopsticks (for local chain forks) and polkadot-api (PAPI, for fee estimation calls)." + }, + { + "order": 2, + "action": "Create Chopsticks configs for source and destination chains", + "working_directory": "xcm-fee-estimate", + "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 — 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 — 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." + }, + { + "order": 3, + "action": "Start both Chopsticks forks", + "working_directory": "xcm-fee-estimate", + "commands": [ + "npx @acala-network/chopsticks --config=chopsticks-hub.yml", + "npx @acala-network/chopsticks --config=chopsticks-people.yml" + ], + "expected_output": "Listening on port 8000\nListening on port 8001", + "description": "Start each Chopsticks instance in a separate terminal. The first forks Polkadot Hub TestNet on port 8000; the second forks People Chain on port 8001. Both must be running before proceeding to the next step. The first run of each downloads several MB of state and caches it to the db files." + }, + { + "order": 4, + "action": "Generate PAPI type descriptors for both forks", + "working_directory": "xcm-fee-estimate", + "commands": [ + "npx papi add hubFork -w ws://localhost:8000", + "npx papi add peopleFork -w ws://localhost:8001" + ], + "description": "Run these commands in a third terminal while both Chopsticks forks (step 3) are running. Generates compile-time PAPI descriptors named 'hubFork' and 'peopleFork'. Both descriptors must be generated before writing the estimation script." + }, + { + "order": 5, + "action": "Create the fee estimation script", + "working_directory": "xcm-fee-estimate", + "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples — consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." + }, + { + "order": 6, + "action": "Run the fee estimation script", + "working_directory": "xcm-fee-estimate", + "commands": [ + "npx tsx estimate-xcm-fees.ts" + ], + "expected_output": "Local execution fee (planck): 1500000000000000\nDelivery fee assets: [...]\nRemote execution fee (planck): 500000000000000", + "description": "Execute the script while both Chopsticks forks are running. The output shows all three fee components in planck. Divide by 10^18 to convert to PAS. The sum of all three components is the total fee budget to include in the XCM BuyExecution instruction. If any XcmPaymentApi method is not available, check the source page for the correct API method names for your chain runtime version." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "ECONNREFUSED ws://localhost:8000 or ws://localhost:8001", + "cause": "One or both Chopsticks forks are not running.", + "resolution": "Start each Chopsticks instance in a separate terminal. Run 'npx @acala-network/chopsticks --config=chopsticks-hub.yml' and 'npx @acala-network/chopsticks --config=chopsticks-people.yml'. Wait for 'Listening on port 8000/8001' before proceeding." + }, + { + "pattern": "XcmPaymentApi method not found / query_delivery_fees undefined", + "cause": "The chain runtime version does not implement the XcmPaymentApi runtime API at this method name.", + "resolution": "Check the source page for the correct API method names for your runtime version. The API surface changed between XCM V3 and V4. Verify the chain runtime version in the Chopsticks fork logs." + }, + { + "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or hubFork/peopleFork undefined", + "cause": "PAPI descriptors were not generated for one or both forks.", + "resolution": "With both Chopsticks forks running, execute: npx papi add hubFork -w ws://localhost:8000 and npx papi add peopleFork -w ws://localhost:8001. Then re-run the script." + }, + { + "pattern": "Fee estimate returns 0 or undefined for remote execution fee", + "cause": "The XCM message structure passed to query_xcm_weight does not match the expected format for the chain runtime.", + "resolution": "Inspect the source page for the exact XCM instruction sequence expected by the destination chain. Verify that the MultiLocation structures use the correct XCM version (V3 vs V4) matching the chain runtime." + } + ], + "supplementary_context": { + "description": "Load these pages for XCM transfer execution, Chopsticks configuration, or debug techniques.", + "pages": [ + { + "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", + "title": "Replay and Dry Run XCMs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", + "relevance": "How to dry-run a full XCM message after fee estimation to verify it executes correctly before submitting." + }, + { + "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", + "title": "Transfer Assets Between Parachains", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", + "relevance": "How to submit the actual XCM teleport transfer once fees are estimated and verified." + }, + { + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", + "relevance": "Full Chopsticks reference including multi-chain fork setup, config options, and mock-signature details." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: calculate the total fee budget needed for a PAS teleport to People Chain", + "user_says": "How much PAS do I need to include in the BuyExecution for a teleport from Polkadot Hub to People Chain?", + "actions": [ + "Create 'xcm-fee-estimate/' as ESM Node.js project and install Chopsticks and polkadot-api", + "Create chopsticks-hub.yml (port 8000) and chopsticks-people.yml (port 8001) with mock-signature-host: true", + "Start both Chopsticks forks in separate terminals", + "Generate PAPI descriptors: npx papi add hubFork and npx papi add peopleFork", + "Create estimate-xcm-fees.ts with the inline script; substitute sender and recipient addresses", + "Run npx tsx estimate-xcm-fees.ts and sum the three fee components" + ], + "result": "All three fee components printed in planck; sum used as BuyExecution fee budget for the teleport" + }, + { + "scenario": "Edge case: remote execution fee query fails with XcmPaymentApi not found", + "user_says": "The script errors with 'query_xcm_weight is not a function' on the People Chain fork", + "actions": [ + "Check the People Chain runtime version in Chopsticks logs", + "Consult the source page for the XcmPaymentApi method names for that runtime version", + "Update the API call in estimate-xcm-fees.ts to match the correct method signature" + ], + "result": "Remote execution fee estimated using the correct XcmPaymentApi method for the chain runtime" + } + ] + }, + { + "id": "set-up-parachain-template", "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Next step after SDK installation: clone and run the Polkadot SDK parachain template locally." - }, - { - "slug": "parachains-get-started", - "title": "Get Started with Parachain Development", - "url": "https://docs.polkadot.com/parachains/get-started.md", - "relevance": "Overview of parachain development paths and starting points available after SDK setup." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: fresh Ubuntu developer machine", - "user_says": "Install the Polkadot SDK on my Linux machine", - "actions": [ - "Run apt install for required packages (git, clang, libssl-dev, etc.)", - "Install Rust via rustup and source ~/.cargo/env", - "Run rustup default stable && rustup target add wasm32-unknown-unknown && rustup component add rust-src", - "Clone https://github.com/paritytech/polkadot-sdk.git and cd polkadot-sdk", - "Run cargo build --release --locked", - "Verify with ./target/release/polkadot --version" - ], - "result": "Polkadot binary prints version string — SDK toolchain ready for parachain development" - }, - { - "scenario": "Edge case: build runs out of memory on a machine with limited RAM", - "user_says": "The build keeps getting killed or runs out of memory", - "actions": [ - "Re-run with reduced parallelism: cargo build --release --locked -j 4", - "If still failing on Linux, add swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile, then retry" - ], - "result": "Build completes with reduced parallel jobs or additional swap space" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - } - }, - { - "id": "deploy-basic-contract-hardhat", - "title": "Deploy a Basic Smart Contract with Hardhat", - "description": "Scaffolds a Hardhat v2 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Must correspond to a testnet account funded with PAS tokens. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-deployment && cd hardhat-deployment", - "npm init -y" - ], - "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage — interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." - }, - { - "order": 2, - "action": "Install Hardhat and dependencies", - "working_directory": "hardhat-deployment", - "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", - "npm install dotenv" - ], - "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." - }, - { - "order": 3, - "action": "Create project directory structure", - "working_directory": "hardhat-deployment", - "commands": [ - "mkdir -p contracts ignition/modules" - ], - "description": "Create the contracts and ignition/modules directories that Hardhat expects. The test directory is optional and not needed for this deployment workflow." - }, - { - "order": 4, - "action": "Create .env file and .gitignore for private key", - "working_directory": "hardhat-deployment", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." - }, - { - "order": 5, - "action": "Fetch and configure hardhat.config.ts", - "working_directory": "hardhat-deployment", - "reference_file": "hardhat.config.ts", - "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors)." - }, - { - "order": 6, - "action": "Fetch Storage.sol contract", - "working_directory": "hardhat-deployment", - "reference_file": "Storage.sol", - "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed — this is a generic Storage contract with get/set functions." - }, - { - "order": 7, - "action": "Compile the contract", - "working_directory": "hardhat-deployment", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile Storage.sol. Successful output shows 'Compiled 1 Solidity file successfully' and creates artifacts in the artifacts/ directory. If compilation fails with 'invalid opcode: MCOPY', add 'evmVersion: \"cancun\"' inside the solidity.settings.evmVersion field of hardhat.config.ts and recompile." - }, - { - "order": 8, - "action": "Fetch the Ignition deployment module", - "working_directory": "hardhat-deployment", - "reference_file": "storage.ts", - "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed — the module deploys the Storage contract by name." - }, - { - "order": 9, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "hardhat-deployment", - "commands": [ - "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" - ], - "expected_output": "Hardhat Ignition 🚀 ... Storage deployed to: 0x...", - "interactive": true, - "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end — needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat", - "files": [ - { - "path": "hardhat.config.ts", - "description": "Hardhat config with polkadotTestnet network (RPC, chain ID, accounts via vars). Requires dotenv conversion and gasPrice addition per skill steps." - }, - { - "path": "Storage.sol", - "description": "Simple Storage contract with uint256 store/retrieve functions" - }, - { - "path": "storage.ts", - "description": "Hardhat Ignition module that deploys the Storage contract" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined (reading 'get')", - "cause": "The hardhat.config.ts still contains vars.get('PRIVATE_KEY') instead of the dotenv substitution.", - "resolution": "Open hardhat.config.ts, replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string, and add 'import \"dotenv/config\";' as the first line of the file." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "The deployer account has no testnet PAS tokens, or the gasPrice in hardhat.config.ts is below the network minimum (1000 gwei base fee).", - "resolution": "Get testnet tokens from https://faucet.polkadot.io/. Also verify the polkadotTestnet network block in hardhat.config.ts includes gasPrice: 5000000000000 (5000 gwei)." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported on retry", - "cause": "Ignition lost track of the deployment transaction. Often caused by requiredConfirmations: 0 or gasPrice below the base fee.", - "resolution": "First check if the contract was actually deployed: look in ignition/deployments/ for deployed_addresses.json. If deployed, import the existing deployment. If not deployed: (1) delete the ignition/deployments/ directory, (2) ensure hardhat.config.ts has gasPrice: 5000000000000 and ignition.requiredConfirmations: 1, (3) redeploy." - }, - { - "pattern": "CompilationError: Invalid opcode: MCOPY", - "cause": "The Solidity contract uses OpenZeppelin v5.4.0+ which requires the Cancun EVM version.", - "resolution": "In hardhat.config.ts, add evmVersion: 'cancun' inside the solidity compiler settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }" - } - ], - "supplementary_context": { - "description": "Load when the user wants to build on this deployment or needs to connect a frontend.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", - "title": "Deploy an ERC-20 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "Next step: deploy an ERC-20 token using the same Hardhat workflow." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Hardhat configuration reference for Polkadot Hub including verification setup." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a first smart contract to Polkadot Hub", - "user_says": "Deploy a Storage contract to Polkadot Hub TestNet using Hardhat", - "actions": [ - "Scaffold hardhat-deployment/ with npm init -y", - "Install hardhat, toolbox, dotenv", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Fetch and configure hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set requiredConfirmations: 1", - "Fetch contracts/Storage.sol", - "Run npx hardhat compile", - "Fetch ignition/modules/Storage.ts", - "Run npx hardhat ignition deploy --network polkadotTestnet; delegate confirmation prompt to user" - ], - "result": "Storage contract deployed; address printed to console" - }, - { - "scenario": "Edge case: deployment transaction gets stuck (IGN401)", - "user_says": "Ignition says the transaction was dropped and retrying gives 'Transaction Already Imported'", - "actions": [ - "Check ignition/deployments/ for deployed_addresses.json to determine if contract was actually deployed", - "If not deployed: delete ignition/deployments/, verify gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy" - ], - "result": "Fresh deployment succeeds with correct gas configuration" - } - ], - "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json" - }, - { - "id": "deploy-erc20-token-hardhat", - "title": "Deploy an ERC-20 Token Using Hardhat", - "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees and test transactions — get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer/test account. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Clone the ERC-20 template repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples/", - "cd revm-hardhat-examples/erc20-hardhat" - ], - "description": "Clone the revm-hardhat-examples repository and navigate into the erc20-hardhat subdirectory. This template contains a pre-configured Hardhat project with an OpenZeppelin ERC-20 contract, test suite, and Ignition deployment module." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npm i", - "npm install dotenv" - ], - "description": "Install all template dependencies from package.json, then additionally install dotenv. dotenv is needed to replace Hardhat's interactive vars system (which cannot be used in agent shells) with a .env-based private key approach." - }, - { - "order": 3, - "action": "Create .env file and .gitignore for private key", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." - }, - { - "order": 5, - "action": "Compile the contract", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile the ERC-20 contract. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in the solidity compiler settings and recompile." - }, - { - "order": 6, - "action": "Run the test suite", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npx hardhat test --network polkadotTestnet" - ], - "expected_output": "7 passing", - "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output — it confirms the contract logic is correct before deployment." - }, - { - "order": 7, - "action": "Deploy the ERC-20 contract", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet" - ], - "expected_output": "MyToken deployed to: 0x...", - "interactive": true, - "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success — needed for token interaction and verification." - } - ], - "error_patterns": [ - { - "pattern": "CompilationError: Invalid opcode: MCOPY", - "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode which requires Cancun EVM version. The hardhat.config.ts does not specify evmVersion: 'cancun'.", - "resolution": "In hardhat.config.ts, set evmVersion: 'cancun' inside solidity.settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }. Then recompile." - }, - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of deployment, commonly due to missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user wants to build a dApp on top of the deployed ERC-20 or interact with it.", - "pages": [ - { - "slug": "smart-contracts-cookbook-dapps-zero-to-hero", - "title": "Zero to Hero Smart Contract DApp", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract." - }, - { - "slug": "smart-contracts-libraries-ethers-js", + "description": "Clones the Polkadot SDK Parachain Template, compiles it in release mode, and runs a local development node to verify the build and environment. Use as the first step in the parachain launch workflow before registering on Paseo testnet or a local relay chain. Requires the Polkadot SDK build environment from the install-polkadot-sdk skill. The template provides a minimal parachain runtime with Aura consensus, FRAME pallets, and a pre-configured Cumulus integration. Trigger phrases: 'parachain template', 'set up parachain Polkadot SDK', 'clone parachain template', 'build parachain template', 'Polkadot SDK parachain starter'. First step in the three-page parachain launch series.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/launch-a-parachain/set-up-the-parachain-template.md" + ], + "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", + "prerequisites": { + "runtime": [ + "Rust toolchain with wasm32-unknown-unknown target — complete the install-polkadot-sdk skill first", + "Git", + "Disk: at least 5 GB free for the build artifacts", + "RAM: 8 GB minimum; 16 GB recommended for parallel compilation" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK Parachain Template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git", + "cd polkadot-sdk-parachain-template" + ], + "description": "Clone the official Polkadot SDK Parachain Template repository. The template contains a minimal parachain runtime with Aura consensus, standard FRAME pallets, and Cumulus integration for relay chain connectivity. It includes both a node binary and a runtime Wasm blob." + }, + { + "order": 2, + "action": "Review the template directory structure", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup — the default configuration targets a local development network." + }, + { + "order": 3, + "action": "Build the parachain node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "expected_output": "Compiling parachain-template-node v0.1.0\n...\nFinished release [optimized] target(s)", + "description": "Compile the parachain node and runtime in release mode. This step is time-consuming (20-60 minutes on first build) because it compiles the entire Polkadot SDK dependency tree. Subsequent builds use the Rust incremental cache and are significantly faster. If compilation fails with 'linker not found', install the build-essential package (Linux) or Xcode command line tools (macOS). On low-memory machines (less than 8 GB RAM), reduce parallel jobs: CARGO_BUILD_JOBS=2 cargo build --release." + }, + { + "order": 4, + "action": "Verify the binary was produced", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "./target/release/parachain-template-node --version" + ], + "expected_output": "parachain-template-node 0.1.0-...", + "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully — review the cargo build output for compilation errors." + }, + { + "order": 5, + "action": "Run the node in local development mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "./target/release/parachain-template-node --dev" + ], + "expected_output": "Running in --dev mode\n...\nIdle (0 peers)", + "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain — the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-cookbook", + "branch": "master", + "base_path": "polkadot-docs/parachains/parachain-template", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error[E0463]: can't find crate for 'std' / wasm32 target missing", + "cause": "The wasm32-unknown-unknown target is not installed in the Rust toolchain.", + "resolution": "Run: rustup target add wasm32-unknown-unknown. If using a specific toolchain version, prefix with rustup target add wasm32-unknown-unknown --toolchain stable." + }, + { + "pattern": "error: linking with 'cc' failed / linker not found", + "cause": "C/C++ build tools are not installed on the system.", + "resolution": "On Ubuntu/Debian: sudo apt install build-essential clang. On macOS: xcode-select --install. Then re-run cargo build --release." + }, + { + "pattern": "Killed / process killed during compilation / OOM", + "cause": "Insufficient RAM for parallel Rust compilation — cargo uses one thread per CPU core by default.", + "resolution": "Reduce parallel compilation jobs: CARGO_BUILD_JOBS=2 cargo build --release. Consider adding swap space on Linux: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile." + }, + { + "pattern": "error: package 'polkadot-sdk' not found / version mismatch in Cargo.lock", + "cause": "Cargo.lock is out of sync with the current Polkadot SDK release pinned in Cargo.toml.", + "resolution": "Run cargo update to refresh the lock file to the latest compatible versions, then retry cargo build --release." + } + ], + "supplementary_context": { + "description": "Load these pages for the prerequisite SDK install, the next steps in the parachain launch series, or runtime concepts.", + "pages": [ + { + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", + "relevance": "Prerequisite: install Rust, system dependencies, and the Polkadot SDK build environment before building the template." + }, + { + "slug": "parachains-launch-a-parachain-deploy-to-polkadot", + "title": "Deploy on Polkadot", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/deploy-to-polkadot.md", + "relevance": "Next step in the parachain launch series: acquire a para ID and slot on Paseo testnet after the template is built." + }, + { + "slug": "reference-parachains-networks", + "title": "Networks", + "url": "https://docs.polkadot.com/reference/parachains/networks.md", + "relevance": "Network endpoints and chain IDs for Paseo testnet and Polkadot mainnet parachain environments." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: first-time parachain developer setting up the template", + "user_says": "How do I get the Polkadot SDK Parachain Template running on my machine?", + "actions": [ + "Confirm Rust toolchain with wasm32 target is installed (install-polkadot-sdk skill if not)", + "Clone polkadot-sdk-parachain-template from GitHub", + "Run cargo build --release in the cloned directory (allow 20-60 minutes)", + "Verify with ./target/release/parachain-template-node --version", + "Start in dev mode with ./target/release/parachain-template-node --dev to confirm it works" + ], + "result": "Template node built and running in dev mode; environment confirmed ready for the next parachain launch step" + }, + { + "scenario": "Edge case: build is killed due to out-of-memory on a low-spec machine", + "user_says": "The cargo build process gets killed partway through on my 4 GB RAM machine", + "actions": [ + "Set CARGO_BUILD_JOBS=2 to limit parallel compilation threads", + "Add at least 4 GB of swap space: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile", + "Re-run CARGO_BUILD_JOBS=2 cargo build --release" + ], + "result": "Build completes successfully with reduced memory pressure from limited parallel jobs" + } + ] + }, + { + "id": "run-polkadot-hub-rpc-node", + "title": "Run an RPC Node for Polkadot Hub", + "description": "Sets up a Polkadot Hub (Asset Hub) RPC node in archive or pruned mode via Docker or systemd, with an optional Ethereum-compatible JSON-RPC adapter (eth-rpc) for EVM tooling. Covers Docker image selection, binary installation, snapshot download for faster sync, systemd service configuration, and sync verification. Use when you need a self-hosted Polkadot Hub RPC endpoint for Substrate or Ethereum-compatible tools. Trigger phrases: 'run Polkadot Hub node', 'Asset Hub RPC node', 'Polkadot Hub Docker node', 'eth-rpc Polkadot Hub', 'Polkadot Hub archive node'. No tokens required.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "node-infrastructure/run-a-node/polkadot-hub-rpc.md" + ], + "primary_page": "node-infrastructure/run-a-node/polkadot-hub-rpc.md", + "prerequisites": { + "runtime": [ + "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", + "Disk: minimum 500 GB SSD for pruned node; 2+ TB SSD for archive node", + "RAM: minimum 8 GB; 16 GB recommended for archive nodes", + "CPU: 4+ cores recommended" + ], + "network": [ + "Open TCP ports: 30333 (P2P), 9944 (WebSocket RPC), 9615 (Prometheus metrics, optional), 8545 (Ethereum RPC if using eth-rpc adapter)", + "Stable broadband connection for initial sync (several hundred GB download)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Choose deployment method and sync type", + "working_directory": ".", + "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync — check https://snapshots.polkadot.io or community providers for current snapshots." + }, + { + "order": 2, + "action": "Run the Polkadot Hub node via Docker", + "working_directory": ".", + "commands": [ + "docker pull parity/polkadot-parachain:stable", + "mkdir -p /var/lib/polkadot-hub", + "docker run -d --name polkadot-hub-rpc --restart unless-stopped -p 30333:30333 -p 9944:9944 -p 9615:9615 -v /var/lib/polkadot-hub:/data parity/polkadot-parachain:stable --chain=asset-hub-polkadot --base-path=/data --rpc-external --rpc-port=9944 --rpc-cors=all --prometheus-external --prometheus-port=9615 --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast" + ], + "description": "Pull the stable polkadot-parachain image and start the Polkadot Hub RPC node. Key arguments: --chain=asset-hub-polkadot selects the Polkadot Hub spec; --base-path=/data stores chain data in the mounted volume; --state-pruning=1000 --blocks-pruning=1000 keeps the last 1000 blocks (change both to 'archive' for a full archive node); --rpc-external exposes the WebSocket RPC on 9944; the -- separator and --chain=polkadot configure the embedded relay chain client. Replace /var/lib/polkadot-hub with your data directory. For Paseo TestNet, use --chain=asset-hub-paseo and --chain=paseo after the -- separator." + }, + { + "order": 3, + "action": "Run the Polkadot Hub node via systemd (binary method alternative)", + "working_directory": ".", + "description": "For the systemd binary method (skip if using Docker in step 2): (1) Create system user: sudo useradd --system --no-create-home --shell /usr/sbin/nologin polkadot. (2) Create data dir: sudo mkdir -p /var/lib/polkadot-hub && sudo chown polkadot:polkadot /var/lib/polkadot-hub. (3) Download polkadot-parachain binary from https://github.com/paritytech/polkadot-sdk/releases (stable, amd64 or arm64) and install: sudo install -m 755 polkadot-parachain /usr/local/bin/. (4) Create /etc/systemd/system/polkadot-hub-rpc.service:\n\n[Unit]\nDescription=Polkadot Hub RPC Node\nAfter=network.target\n\n[Service]\nUser=polkadot\nExecStart=/usr/local/bin/polkadot-parachain --chain=asset-hub-polkadot --base-path=/var/lib/polkadot-hub --rpc-external --rpc-port=9944 --rpc-cors=all --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n\n(5) Enable and start: sudo systemctl daemon-reload && sudo systemctl enable polkadot-hub-rpc && sudo systemctl start polkadot-hub-rpc. For archive mode, omit --state-pruning and --blocks-pruning flags." + }, + { + "order": 4, + "action": "Optionally install the Ethereum RPC adapter (eth-rpc)", + "working_directory": ".", + "commands": [ + "docker pull paritytech/frontier-node:latest", + "docker run -d --name polkadot-hub-eth-rpc --restart unless-stopped -p 8545:8545 --network host paritytech/frontier-node:latest --chain=asset-hub-polkadot --endpoint=ws://localhost:9944 --listen-addr=0.0.0.0:8545" + ], + "description": "This step is optional. The Ethereum RPC adapter (eth-rpc / Frontier) exposes an Ethereum-compatible JSON-RPC endpoint on port 8545, allowing Ethereum tooling (MetaMask, ethers.js, Hardhat) to connect to Polkadot Hub. The adapter proxies Ethereum RPC calls to the Substrate node. Run it after the main node (step 2 or 3) is fully synced and serving on ws://localhost:9944. For Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo. Skip this step if you only need the native Substrate WebSocket RPC endpoint." + }, + { + "order": 5, + "action": "Monitor sync progress", + "working_directory": ".", + "commands": [ + "docker logs -f polkadot-hub-rpc 2>&1 | grep -E 'Syncing|Idle|Best|Peers'", + "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" + ], + "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", + "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." + }, + { + "order": 6, + "action": "Verify the RPC endpoint is operational", + "working_directory": ".", + "commands": [ + "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_chain\",\"params\":[]}' http://localhost:9944", + "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" + ], + "expected_output": "{\"result\":\"Polkadot Asset Hub\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", + "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 — should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: No space left on device / database write error", + "cause": "The data volume has run out of disk space.", + "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=1000). Check usage with: du -sh /var/lib/polkadot-hub." + }, + { + "pattern": "EADDRINUSE port 9944 or 30333", + "cause": "Another process is already using the required ports.", + "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." + }, + { + "pattern": "Peers count stays at 0 after several minutes", + "cause": "P2P port 30333 is blocked by firewall or the bootnodes are temporarily unavailable.", + "resolution": "Open TCP port 30333: sudo ufw allow 30333/tcp. For cloud VMs, update the security group. If peers still do not connect after 10 minutes, check the Polkadot network status." + }, + { + "pattern": "eth_chainId returns unexpected value or eth-rpc adapter cannot connect to node", + "cause": "The eth-rpc adapter endpoint does not match the running node's WebSocket address, or the node is not yet fully synced.", + "resolution": "Verify the node is fully synced (step 5). Ensure the --endpoint flag for the eth-rpc adapter points to ws://localhost:9944 and the node's --rpc-external flag is active. Restart the eth-rpc adapter after the node is synced." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to add TLS/WSS, run a parachain RPC node, or understand the chain configuration.", + "pages": [ + { + "slug": "node-infrastructure-run-a-node-parachain-rpc", + "title": "Run a Parachain RPC Node", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/parachain-rpc.md", + "relevance": "How to run RPC nodes for other system parachains (People Chain, Collectives, BridgeHub) using the same polkadot-parachain binary." + }, + { + "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", + "title": "Set Up Secure WebSocket", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." + }, + { + "slug": "node-infrastructure-run-a-node-relay-chain-full-node", + "title": "Set Up a Node", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md", + "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: run a pruned Polkadot Hub RPC node via Docker", + "user_says": "Run a Polkadot Hub RPC node using Docker", + "actions": [ + "Pull parity/polkadot-parachain:stable", + "Create data directory /var/lib/polkadot-hub", + "Run docker with --chain=asset-hub-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", + "Monitor sync with docker logs and system_syncState RPC", + "Verify with system_chain and system_health once currentBlock equals highestBlock" + ], + "result": "Polkadot Hub RPC node running, synced, serving requests on ws://localhost:9944" + }, + { + "scenario": "Edge case: Ethereum tooling cannot connect — eth_chainId returns wrong value", + "user_says": "MetaMask connects to port 8545 but shows the wrong chain ID", + "actions": [ + "Verify the eth-rpc adapter is configured with the correct --chain flag matching the running node", + "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) — confirm eth_chainId returns this value", + "If chain ID is wrong, restart the eth-rpc adapter with --chain=asset-hub-paseo for TestNet or --chain=asset-hub-polkadot for mainnet", + "Update MetaMask network settings with the correct chain ID and RPC URL http://localhost:8545" + ], + "result": "MetaMask connects successfully with the correct Polkadot Hub chain ID and eth-rpc adapter" + } + ] + }, + { + "id": "set-up-polkadot-validator-node", + "title": "Set Up a Polkadot Validator Node", + "description": "Installs the three Polkadot validator binaries (polkadot, polkadot-prepare-worker, polkadot-execute-worker) needed to run a validator node, and prepares the server with NTP time synchronization and Landlock security. Covers four installation paths: GPG-verified curl download, APT package manager (Ubuntu/Debian), Docker image, or building from source. Use when provisioning a new validator server or upgrading binaries on an existing one. Trigger phrases: 'install Polkadot validator binaries', 'set up validator node', 'install polkadot-prepare-worker', 'configure validator server'. First step in the full validator onboarding flow before key management and bonding.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" + ], + "primary_page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", + "prerequisites": { + "runtime": [ + "Linux server with kernel 5.16 or later (Ubuntu 22.04 LTS or newer recommended)", + "64-bit x86-64 or ARM64 architecture", + "Root or sudo access" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Verify or install NTP time synchronization", + "working_directory": ".", + "commands": [ + "timedatectl" + ], + "expected_output": "System clock synchronized: yes", + "description": "Validators need accurate system time to avoid missing block authorship. Check NTP status with 'timedatectl'. If the output does not show 'System clock synchronized: yes', install and start NTP:\n\n sudo apt-get install ntp\n sudo ntpq -p\n\nThe 'ntpq -p' output should list one or more active peers with a '+' or '*' status prefix. Skipping NTP can cause the validator to miss blocks due to minor clock drift." + }, + { + "order": 2, + "action": "Verify Landlock security is activated", + "working_directory": ".", + "commands": [ + "dmesg | grep landlock || journalctl -kg landlock" + ], + "description": "Landlock (Linux kernel 5.13+) is required by the Polkadot validator for sandboxing. Run the command above as root. If Landlock is active, you will see kernel log entries referencing 'landlock'. If there is no output, your kernel does not have Landlock enabled. Most modern Ubuntu/Debian kernels (5.16+) include it by default. If Landlock is absent, upgrade to a supported kernel or rebuild with Landlock enabled (see https://docs.kernel.org/userspace-api/landlock.html#kernel-support). Polkadot validators can run without Landlock but will operate without the sandboxing protection." + }, + { + "order": 3, + "action": "Install the Polkadot binaries via curl with GPG verification (recommended method)", + "working_directory": ".", + "commands": [ + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot.asc", + "gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", + "gpg --verify polkadot.asc", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker.asc", + "gpg --verify polkadot-prepare-worker.asc", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker.asc", + "gpg --verify polkadot-execute-worker.asc", + "chmod +x polkadot polkadot-prepare-worker polkadot-execute-worker", + "sudo mv polkadot polkadot-prepare-worker polkadot-execute-worker /usr/local/bin/" + ], + "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 — check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE — APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE — Docker:\n docker pull parity/polkadot:stable2512-2" + }, + { + "order": 4, + "action": "Verify all three binaries are installed and versions match", + "working_directory": ".", + "commands": [ + "polkadot --version", + "polkadot-execute-worker --version", + "polkadot-prepare-worker --version" + ], + "expected_output": "All three commands print the same version string, e.g. 'polkadot stable2512-2-...'", + "description": "Run all three version commands and confirm the version strings are identical. If one binary is missing ('command not found'), ensure it was moved to /usr/local/bin/ in step 3 and that /usr/local/bin is in $PATH (echo $PATH). All three binaries must be in the same directory for the validator to function. If versions differ, re-download the mismatched binary from the same release tag used in step 3." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "gpg: BAD signature", + "cause": "The downloaded binary was corrupted or tampered with during download.", + "resolution": "Delete all downloaded files and repeat step 3 from a trusted network connection. Do not use any binary that fails GPG verification." + }, + { + "pattern": "polkadot: command not found after installation", + "cause": "The binary was not moved to a directory in $PATH, or the directory is not in $PATH.", + "resolution": "Run 'echo $PATH' and verify /usr/local/bin is listed. If not, run 'export PATH=$PATH:/usr/local/bin'. For persistence, add this export to /etc/environment or ~/.bashrc." + }, + { + "pattern": "version mismatch between polkadot and worker binaries", + "cause": "The three binaries were downloaded from different releases.", + "resolution": "Remove all three binaries and re-download all from the same release tag (e.g., polkadot-stable2512-2) from https://github.com/paritytech/polkadot-sdk/releases. All three must share the same version to interoperate." + }, + { + "pattern": "Landlock not in dmesg output", + "cause": "The running kernel is older than 5.13 or was compiled without Landlock.", + "resolution": "Upgrade the kernel: on Ubuntu, run 'sudo apt-get install linux-generic-hwe-22.04' then reboot. Verify with 'uname -r' that the new kernel is 5.16 or later. Alternatively, consult https://docs.kernel.org/userspace-api/landlock.html#kernel-support." + } + ], + "supplementary_context": { + "description": "Load these pages for key management, starting validation, or understanding validator requirements and operational tasks.", + "pages": [ + { + "slug": "node-infrastructure-run-a-validator-requirements", + "title": "Validator Requirements", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/requirements.md", + "relevance": "Minimum hardware and skill requirements before starting a validator — read before this skill." + }, + { + "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-key-management", + "title": "Validator Key Management", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md", + "relevance": "Next step after binary installation: generating and registering session keys." + }, + { + "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-start-validating", + "title": "Start Validating", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md", + "relevance": "Syncing the node, bonding DOT, and activating the validator after binary setup." + }, + { + "slug": "node-infrastructure-run-a-validator-operational-tasks-general-management", + "title": "General Management", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/operational-tasks/general-management.md", + "relevance": "Prometheus and Grafana monitoring stack setup and security best practices for running validators." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: install on a fresh Ubuntu 22.04 server", + "user_says": "Install the Polkadot validator binaries on a new Ubuntu server", + "actions": [ + "Run 'timedatectl' to check NTP sync; install ntp if not synced", + "Run 'dmesg | grep landlock' to verify Landlock is active", + "Download polkadot, polkadot-prepare-worker, and polkadot-execute-worker from release polkadot-stable2512-2", + "Verify each binary with GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", + "Move all three to /usr/local/bin and run --version on each to confirm matching versions" + ], + "result": "All three binaries installed at polkadot-stable2512-2, GPG verified, and accessible system-wide; ready for key management" + }, + { + "scenario": "Edge case: APT install on a Debian-based server without curl", + "user_says": "Install Polkadot on Ubuntu using apt instead of downloading manually", + "actions": [ + "Import the Parity GPG key into /usr/share/keyrings/parity.gpg", + "Add the Parity APT repository to /etc/apt/sources.list.d/parity.list", + "Run 'apt update && apt install parity-keyring polkadot'", + "Verify with 'polkadot --version', 'polkadot-execute-worker --version', 'polkadot-prepare-worker --version'" + ], + "result": "Polkadot and both worker binaries installed via APT with automatic future upgrades via apt upgrade" + } + ] + }, + { + "id": "set-up-chopsticks-fork", + "title": "Set Up and Use Chopsticks for Chain Forking", + "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/chopsticks.md" + ], + "primary_page": "reference/tools/chopsticks.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm (included with Node.js) or yarn or pnpm" + ], + "network": [ + "Access to a WebSocket RPC endpoint for the chain to fork (e.g., wss://polkadot-rpc.dwellir.com)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Install Chopsticks globally", + "working_directory": ".", + "commands": [ + "npm i -g @acala-network/chopsticks@1.2.7" + ], + "expected_output": "added N packages", + "description": "Install Chopsticks 1.2.7 globally. After installation, 'chopsticks' is available system-wide. For a project-local installation instead: mkdir my-chopsticks-project && cd my-chopsticks-project && npm init -y && npm i @acala-network/chopsticks@1.2.7. With local installation, run via 'npx @acala-network/chopsticks' instead of 'chopsticks' in subsequent steps." + }, + { + "order": 2, + "action": "Create a fork configuration file", + "working_directory": ".", + "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs — download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts — useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." + }, + { + "order": 3, + "action": "Start the chain fork", + "working_directory": ".", + "commands": [ + "npx @acala-network/chopsticks --config=polkadot.yml" + ], + "expected_output": "Running on port 8000", + "description": "Start the fork using the YAML config from step 2. Alternatively, fork without a config file using CLI flags:\n npx @acala-network/chopsticks --endpoint wss://polkadot-rpc.dwellir.com --block 100\n\nChoose the block number to fork from with --block (omit to use the chain head). Chopsticks caches state locally in an SQLite DB (set --db ./chopsticks.db to reuse between runs). When the fork is ready, you will see 'Running on port 8000'. The local fork is now accessible at ws://localhost:8000." + }, + { + "order": 4, + "action": "Interact with the fork", + "working_directory": ".", + "description": "Connect to the running fork at ws://localhost:8000.\n\n```typescript\nVia Polkadot.js API (TypeScript):\n import { ApiPromise, WsProvider } from '@polkadot/api';\n const api = await ApiPromise.create({ provider: new WsProvider('ws://localhost:8000') });\n const block = await api.rpc.chain.getBlock();\n console.log('Fork head:', block.block.header.number.toNumber());\n```\n\nVia Polkadot.js Apps UI: navigate to polkadot.js.org/apps, select network > Development > Custom, enter ws://localhost:8000, and click Switch.\n\nTo manipulate storage via the dev_setStorage WebSocket command (using wscat or a JS client):\n { \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"dev_setStorage\", \"params\": [{ \"System\": { \"Account\": [[\"0xALICE_KEY\", { \"data\": { \"free\": 1000000000000 } }]] } }] }" + }, + { + "order": 5, + "action": "Replay a historical block (optional)", + "working_directory": ".", + "commands": [ + "npx @acala-network/chopsticks run-block --endpoint wss://polkadot-rpc.dwellir.com --output-path ./block-1000-output.json --block 1000" + ], + "description": "Replay block 1000 from Polkadot and save the state diff to a JSON file. Replace the endpoint and block number as needed. The output includes detailed storage changes and runtime logs. Add --html to generate an HTML visualization of the storage diff. This is useful for debugging why a specific transaction produced unexpected state changes." + }, + { + "order": 6, + "action": "Test XCM between multiple chains (optional)", + "working_directory": ".", + "commands": [ + "npx @acala-network/chopsticks xcm --r polkadot --p moonbeam --p astar" + ], + "description": "Fork multiple chains simultaneously to test XCM message flow. The --r flag specifies the relay chain config (by name or file path) and --p specifies parachain configs. After startup, each chain runs on a different port (printed in the output). Send XCM messages between the forked chains using the Polkadot.js API or dev_* WebSocket commands. Note: XCM testing requires config files for each chain; use pre-built configs from https://github.com/AcalaNetwork/chopsticks/tree/master/configs." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Cannot connect to endpoint / WebSocket connection failed", + "cause": "The RPC endpoint URL is incorrect, the chain is temporarily unavailable, or the WSS endpoint requires authentication.", + "resolution": "Verify the endpoint URL by connecting with wscat: wscat -c wss://your-endpoint. Try a public endpoint such as wss://polkadot-rpc.dwellir.com for Polkadot. Check the chain's status page or Discord for outages." + }, + { + "pattern": "chopsticks: command not found", + "cause": "Global npm installation did not add the binary to PATH, or Chopsticks was installed locally without npx.", + "resolution": "For global installs, run 'npm root -g' to find the global bin directory and ensure it is in $PATH. For local installs, use 'npx @acala-network/chopsticks' instead of 'chopsticks'." + }, + { + "pattern": "MetaMask / Ethereum tooling cannot connect to the fork", + "cause": "Chopsticks uses the Smoldot light client which only supports the native Polkadot SDK API, not Ethereum JSON-RPC.", + "resolution": "This is a known limitation. Chopsticks forks cannot be used with MetaMask or ethers.js. For EVM-compatible local testing, use a local Polkadot Hub development node instead." + }, + { + "pattern": "Error replaying block: state root mismatch", + "cause": "The local fork has diverged from the live chain state, often because storage was manually modified before replay.", + "resolution": "Restart Chopsticks without --db to use a fresh state (or delete the SQLite db file). Avoid modifying storage with dev_setStorage before running run-block." + } + ], + "supplementary_context": { + "description": "Load these pages for parachain-specific fork tutorials, XCM debugging, or the fork-a-parachain cookbook tutorial.", + "pages": [ + { + "slug": "parachains-testing-fork-a-parachain", + "title": "Fork a Parachain Using Chopsticks", + "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", + "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks — same tool, focused on the parachain testing workflow with a CI-backed example." + }, + { + "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", + "title": "Replay and Dry Run XCMs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", + "relevance": "Using Chopsticks to replay and dry-run XCM messages — advanced XCM debugging workflow." + }, + { + "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", + "title": "XCM Fee Estimation", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", + "relevance": "Estimating XCM fees for teleports using Chopsticks local forks of Polkadot Hub and People Chain." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: fork Polkadot mainnet and inspect state", + "user_says": "Fork the Polkadot mainnet locally with Chopsticks so I can test transactions", + "actions": [ + "Install Chopsticks 1.2.7 globally: npm i -g @acala-network/chopsticks@1.2.7", + "Download the Polkadot config from the Chopsticks repo: curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml", + "Start the fork: npx @acala-network/chopsticks --config=polkadot.yml", + "Wait for 'Running on port 8000', then connect via Polkadot.js API at ws://localhost:8000" + ], + "result": "Local Polkadot fork running at ws://localhost:8000; ready for transaction testing without spending real tokens" + }, + { + "scenario": "Edge case: fork requires a funded test account", + "user_says": "I need Alice to have tokens on my forked chain for testing", + "actions": [ + "Add an import-storage section to the YAML config overriding Alice's System.Account free balance to a large value", + "Restart the fork with the updated config", + "Alternatively, send a dev_setStorage WebSocket command after the fork is running to set Alice's balance" + ], + "result": "Alice's account has the specified token balance on the local fork; transactions that require funds now succeed" + } + ] + }, + { + "id": "set-up-e2e-testing-moonwall", + "title": "Set Up End-to-End Testing with Moonwall", + "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain — local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/moonwall.md" + ], + "primary_page": "reference/tools/moonwall.md", + "prerequisites": { + "runtime": [ + "Node.js v20.10 or higher", + "npm, yarn, or pnpm" + ], + "network": [ + "A running Polkadot SDK node or Chopsticks fork for the test environment (e.g., ws://localhost:9944 or ws://localhost:8000)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Install Moonwall globally", + "working_directory": ".", + "commands": [ + "npm install -g @moonwall/cli@5.18.3" + ], + "expected_output": "added N packages", + "description": "Install Moonwall 5.18.3 globally so the 'moonwall' command is available system-wide. For a project-local installation: mkdir my-moonwall-project && cd my-moonwall-project && npm init -y && npm install @moonwall/cli@5.18.3. With a local install, run Moonwall via 'npx moonwall' or add it to package.json scripts." + }, + { + "order": 2, + "action": "Initialize the Moonwall configuration via the interactive wizard", + "working_directory": ".", + "commands": [ + "moonwall init" + ], + "interactive": true, + "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user — do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." + }, + { + "order": 3, + "action": "Review and customize moonwall.config.json", + "working_directory": ".", + "description": "Open the generated moonwall.config file and customize the environment settings. Key sections to update:\n\n - foundation.type: 'dev' (local binary), 'chopsticks' (fork), or 'read_only' (remote RPC)\n - For 'dev' foundation, set binPath to the path of your chain binary\n - connections[0].endpoint: the WebSocket URL of the node (e.g., ws://localhost:9944)\n - connections[0].type: the provider type (e.g., 'polkadotJs' for Polkadot.js API)\n\nExample configuration for testing a local dev node:\n {\n \"label\": \"my-chain-tests\",\n \"timeout\": 30000,\n \"environments\": [{\n \"name\": \"default_env\",\n \"foundation\": { \"type\": \"dev\", \"launchSpec\": [{ \"name\": \"node\", \"binPath\": \"./your-node-binary\", \"options\": [\"--dev\"] }] },\n \"testFileDir\": [\"./tests\"],\n \"connections\": [{ \"name\": \"api\", \"type\": \"polkadotJs\", \"endpoints\": [\"ws://localhost:9944\"] }]\n }]\n }" + }, + { + "order": 4, + "action": "Write a test suite", + "working_directory": ".", + "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run — do not create API connections manually." + }, + { + "order": 5, + "action": "Run the tests", + "working_directory": ".", + "commands": [ + "moonwall test default_env -c moonwall.config" + ], + "expected_output": "All test suites passed", + "description": "Run the tests against the 'default_env' environment defined in moonwall.config. Replace 'default_env' with the environment name you chose in step 2. The test runner outputs:\n - Test suite execution status (pass/fail per suite)\n - Individual test case results\n - Execution time per test\n - Detailed error logs for failed tests\n\nIf the foundation type is 'dev', Moonwall launches the node binary automatically and tears it down after tests complete. If using 'read_only' or 'chopsticks', ensure the endpoint is reachable before running tests." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "moonwall: command not found", + "cause": "Global npm installation did not add the binary to PATH.", + "resolution": "Run 'npm root -g' to find the global bin directory and verify it is in $PATH. Alternatively, use 'npx moonwall' for a local installation." + }, + { + "pattern": "Cannot find module '@moonwall/cli'", + "cause": "Moonwall was not installed or the local installation is missing.", + "resolution": "Run 'npm install @moonwall/cli@5.18.3' in the project directory. For global, run 'npm install -g @moonwall/cli@5.18.3'." + }, + { + "pattern": "Error: Cannot connect to endpoint ws://localhost:9944", + "cause": "The node specified in the moonwall.config foundation is not running, or the endpoint URL is incorrect.", + "resolution": "Start the node manually and verify it is listening on the configured port before running Moonwall. For 'dev' foundation, ensure the binPath points to an executable binary and the --dev flag is appropriate for your chain." + }, + { + "pattern": "Timeout exceeded for test suite", + "cause": "The test environment took too long to start or a test operation exceeded the global timeout.", + "resolution": "Increase the 'timeout' value in moonwall.config (e.g., from 30000 to 60000 ms). For slow chain operations like block finalization, add explicit wait-for-block assertions rather than fixed sleeps." + } + ], + "supplementary_context": { + "description": "Load these pages for Chopsticks fork setup (to use as Moonwall's test environment) or for a local development node to test against.", + "pages": [ + { + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", + "relevance": "Setting up a Chopsticks fork as the 'chopsticks' foundation type for Moonwall tests." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "How to run a local development node to use as the 'dev' foundation in Moonwall." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: run e2e tests against a local dev node", + "user_says": "Set up Moonwall to run e2e tests for my Polkadot SDK chain", + "actions": [ + "Install Moonwall 5.18.3 globally: npm install -g @moonwall/cli@5.18.3", + "Ask the user to run 'moonwall init' and complete the interactive wizard, choosing 'dev' foundation", + "Update moonwall.config with the correct node binPath and ws://localhost:9944 connection", + "Create a test file in ./tests using describeSuite with a foundationMethods of 'dev'", + "Run 'moonwall test default_env -c moonwall.config'" + ], + "result": "Moonwall launches the node binary, runs all test suites, and reports pass/fail results per test case" + }, + { + "scenario": "Edge case: test environment fails to start", + "user_says": "Moonwall says it cannot connect to the node endpoint", + "actions": [ + "Check the foundation type in moonwall.config — for 'dev', verify binPath is correct and the binary has execute permission", + "For 'read_only' or 'chopsticks', start the node or Chopsticks fork manually and confirm it is listening on the configured endpoint", + "Increase the connection timeout in moonwall.config if the node starts slowly" + ], + "result": "Node is reachable; Moonwall connects and runs tests" + } + ] + }, + { + "id": "build-dapp-viem-nextjs", + "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js", + "description": "Scaffolds a full-stack Polkadot Hub dApp: deploys a Solidity Storage contract via Hardhat Ignition to TestNet, then builds a Next.js 14 frontend with Viem wallet connect, read, and write components. Use when building a complete end-to-end smart contract application on Polkadot Hub from scratch. Trigger phrases: 'build a dapp polkadot', 'zero to hero tutorial', 'smart contract with frontend viem', 'deploy and connect next.js'. Covers dotenv private key security, 5000 gwei gas config, evmVersion cancun, Ignition recovery, and Next.js/Viem component wiring. Output: running dApp at localhost:3000.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/dapps/zero-to-hero.md" + ], + "primary_page": "smart-contracts/cookbook/dapps/zero-to-hero.md", + "prerequisites": { + "runtime": [ + "Node.js v22+", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for contract deployment gas" + ], + "wallet": [ + "MetaMask configured for Polkadot Hub TestNet (chainId 420420417)", + "0x-prefixed EVM private key for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Clone the reference repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git" + ], + "description": "Clone revm-hardhat-examples. This repo contains the complete zero-to-hero-dapp with both the Hardhat contract project and the Next.js frontend." + }, + { + "order": 2, + "action": "Install contract project dependencies", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install Hardhat and contract dependencies, then add dotenv. dotenv replaces Hardhat's interactive vars system, which cannot be used in agent shells." + }, + { + "order": 3, + "action": "Create .env file and protect it with .gitignore", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly, filling in PRIVATE_KEY=0x. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 4, + "action": "Update hardhat.config.ts for TestNet and security", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", + "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file." + }, + { + "order": 5, + "action": "Compile the Storage contract", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 1 Solidity file successfully", + "description": "Compile Storage.sol. If you see 'invalid opcode: MCOPY', verify evmVersion: 'cancun' is set in the Solidity compiler settings in hardhat.config.ts and recompile." + }, + { + "order": 6, + "action": "Deploy the contract to Polkadot Hub TestNet", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", + "commands": [ + "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" + ], + "expected_output": "Storage deployed to: 0x...", + "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." + }, + { + "order": 7, + "action": "Scaffold the Next.js frontend", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp", + "commands": [ + "echo 'n\\nn\\nn\\nn\\nn\\nn' | npx create-next-app@14 frontend --typescript --no-eslint --no-tailwind --no-src-dir --app" + ], + "description": "Scaffold Next.js 14 TypeScript app named 'frontend'. Piped 'n' answers handle any unexpected interactive prompts from create-next-app. If this fails due to prompt changes, run: npx create-next-app@14 frontend and answer No to all questions except TypeScript (Yes) and App Router (Yes)." + }, + { + "order": 8, + "action": "Install frontend dependencies", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", + "commands": [ + "npm install viem wagmi @tanstack/react-query" + ], + "description": "Install viem for contract interaction, wagmi for React hooks, and React Query (required by wagmi v2)." + }, + { + "order": 9, + "action": "Update contract address in contract config", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", + "description": "Open src/contracts/contract.ts (or the equivalent contract config file in the frontend). Replace the CONTRACT_ADDRESS placeholder or any hardcoded example address with the deployed contract address saved in step 6. Save the file." + }, + { + "order": 10, + "action": "Start the development server", + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", + "commands": [ + "npm run dev" + ], + "expected_output": "ready - started server on 0.0.0.0:3000", + "description": "Start the Next.js dev server. Open http://localhost:3000 in a browser. Click 'Connect Wallet' and approve the MetaMask popup. Read the current stored value, then write a new value and confirm the MetaMask transaction. Verify the displayed value updates after the transaction is mined." + } + ], + "reference_code": { + "repo": "polkadot-developers/revm-hardhat-examples", + "branch": "master", + "base_path": "zero-to-hero-dapp", + "files": [] + }, + "error_patterns": [ + { + "pattern": "priority is too low", + "cause": "Transaction gas price is below the 1000 gwei base fee on Polkadot Hub TestNet. Default Hardhat gas estimation produces values too low for this network.", + "resolution": "Set gasPrice: 5000000000000 in the polkadotTestnet network block in hardhat.config.ts. Delete ignition/deployments/ if a prior attempt was made, then redeploy." + }, + { + "pattern": "IGN401 / Transaction dropped", + "cause": "Ignition lost track of the transaction. The transaction may have succeeded or may be stuck in the mempool.", + "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded — use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." + }, + { + "pattern": "invalid opcode: MCOPY", + "cause": "OpenZeppelin v5.4.0+ uses the mcopy opcode (Cancun EVM upgrade). Solidity defaults to an older EVM version.", + "resolution": "Add evmVersion: 'cancun' to the Solidity compiler settings in hardhat.config.ts. Recompile with 'npx hardhat compile'." + }, + { + "pattern": "Error: could not detect network", + "cause": "Incorrect RPC URL or chain ID in hardhat.config.ts.", + "resolution": "Set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417 in the polkadotTestnet network block." + }, + { + "pattern": "Module not found: viem or wagmi", + "cause": "Frontend dependencies were not installed.", + "resolution": "Run 'npm install viem wagmi @tanstack/react-query' in the zero-to-hero-dapp/frontend directory." + } + ], + "supplementary_context": { + "description": "Load these pages for Hardhat configuration reference, Viem library details, or network connection parameters.", + "pages": [ + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Full Hardhat EVM configuration options for Polkadot Hub, including network settings and contract verification." + }, + { + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", + "relevance": "Viem library reference for chain config, wallet client, and contract interaction patterns on Polkadot Hub." + }, + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Network parameters (RPC URLs, chain IDs, WSS) for Polkadot Hub TestNet and Mainnet." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: full dApp from scratch", + "user_says": "Build a smart contract dApp on Polkadot Hub with Viem and Next.js", + "actions": [ + "Clone revm-hardhat-examples and navigate to zero-to-hero-dapp/storage-contract", + "Install dependencies and create .env with PRIVATE_KEY (user fills in)", + "Update hardhat.config.ts: dotenv, gasPrice 5000 gwei, evmVersion cancun, requiredConfirmations 1", + "Compile Storage.sol and deploy to polkadotTestnet via Ignition", + "Scaffold Next.js 14 frontend with create-next-app@14", + "Install viem, wagmi, react-query; update contract.ts with deployed address", + "Run npm run dev and test at localhost:3000" + ], + "result": "Storage contract deployed to TestNet; Next.js app at localhost:3000 with working wallet connect, read, and write contract interactions via MetaMask" + }, + { + "scenario": "Edge case: Ignition reports transaction dropped but contract may have deployed", + "user_says": "Ignition says IGN401 but I don't know if the contract deployed", + "actions": [ + "Check ignition/deployments//deployed_addresses.json for a Storage address", + "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", + "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" + ], + "result": "Contract address recovered from deployment state or clean redeployment completed" + } + ] + }, + { + "id": "deploy-erc20-token-remix", + "title": "Deploy an ERC-20 Token Using Remix IDE", + "description": "Deploys a Solidity ERC-20 token contract to Polkadot Hub TestNet entirely through the Remix browser IDE with MetaMask wallet injection. Use when you prefer a no-CLI workflow or are demonstrating contract deployment to non-developers. Trigger phrases: 'deploy ERC-20 remix', 'erc20 token remix polkadot', 'deploy token browser IDE'. Covers fetching contract code from revm-hardhat-examples, Remix compile settings (evmVersion cancun for OpenZeppelin v5), MetaMask TestNet configuration, and minting tokens via the Remix UI. No local environment required beyond a browser and MetaMask.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md" + ], + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md", + "prerequisites": { + "runtime": [ + "A modern web browser (Chrome or Firefox recommended)" + ], + "network": [ + "Polkadot Hub TestNet (chainId 420420417) — configured in MetaMask" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required to pay deployment gas" + ], + "wallet": [ + "MetaMask browser extension installed and unlocked", + "An account funded with testnet PAS on Polkadot Hub TestNet (chainId 420420417, RPC: https://services.polkadothub-rpc.com/testnet)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Fetch the ERC-20 contract source", + "working_directory": ".", + "description": "Fetch the OpenZeppelin ERC-20 contract source from the reference repository. The contract file is at: https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol\n\nRun: curl -sL https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol and display the contract source to the user. The user will paste this into Remix in the next step." + }, + { + "order": 2, + "action": "Open Remix IDE and create the contract file", + "working_directory": ".", + "description": "Navigate to https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), click the 'New File' icon and name it 'MyToken.sol'. Paste the full contract source fetched in step 1 into the editor. Save with Ctrl+S (or Cmd+S on Mac)." + }, + { + "order": 3, + "action": "Configure the Solidity compiler in Remix", + "working_directory": ".", + "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown — required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." + }, + { + "order": 4, + "action": "Add Polkadot Hub TestNet to MetaMask", + "working_directory": ".", + "description": "If MetaMask does not already have Polkadot Hub TestNet configured, add it manually:\n1. Open MetaMask and go to Settings > Networks > Add Network > Add a network manually.\n2. Enter: Network Name: Polkadot Hub TestNet; RPC URL: https://services.polkadothub-rpc.com/testnet; Chain ID: 420420417; Currency Symbol: PAS.\n3. Click Save and switch to the Polkadot Hub TestNet network.\n4. Confirm the account shown in MetaMask has PAS tokens. If not, visit https://faucet.polkadot.io/ to request testnet PAS." + }, + { + "order": 5, + "action": "Connect MetaMask to Remix and deploy", + "working_directory": ".", + "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect — click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation — review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10–30 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." + }, + { + "order": 6, + "action": "Verify the deployment and mint tokens", + "working_directory": ".", + "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' — should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." + } + ], + "reference_code": { + "repo": "polkadot-developers/revm-hardhat-examples", + "branch": "master", + "base_path": "erc20-hardhat", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Remix compilation fails with 'Unknown key evmVersion cancun'", + "cause": "Older Remix versions may not list 'cancun' in the EVM version dropdown.", + "resolution": "Update to the latest Remix version by refreshing the page. The cancun option should appear in Advanced Configuration. Alternatively, use a custom EVM version by typing 'cancun' in the input field if available." + }, + { + "pattern": "MetaMask shows wrong network after connecting to Remix", + "cause": "MetaMask is on a different network than Polkadot Hub TestNet.", + "resolution": "In MetaMask, switch to 'Polkadot Hub TestNet' (chainId 420420417) before deploying. Confirm the Remix environment section shows the correct chain ID." + }, + { + "pattern": "Transaction fails with insufficient funds", + "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", + "resolution": "Visit https://faucet.polkadot.io/, enter your MetaMask address, and request testnet PAS. Wait for the tokens to arrive (usually under 1 minute), then retry deployment." + }, + { + "pattern": "Remix shows 'Gas estimation failed'", + "cause": "Contract constructor arguments may be missing or invalid, or the MetaMask account has insufficient funds.", + "resolution": "Verify the initialOwner address is filled in correctly in the constructor arguments. Check PAS balance in MetaMask and top up via faucet if needed." + } + ], + "supplementary_context": { + "description": "Load these pages for ERC-20 Hardhat deployment (CLI alternative), network connection details, or wallet configuration.", + "pages": [ + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", + "title": "Deploy an ERC-20 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", + "relevance": "CLI-based ERC-20 deployment using Hardhat — use when the user prefers a terminal workflow over Remix." + }, + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Network parameters for manually adding Polkadot Hub to MetaMask." + }, + { + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask Injected Provider." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy ERC-20 via Remix", + "user_says": "Deploy an ERC-20 token on Polkadot Hub using Remix IDE", + "actions": [ + "Fetch MyToken.sol from revm-hardhat-examples and display to user", + "Open https://remix.ethereum.org, create MyToken.sol, paste contract", + "In Remix Solidity compiler, set version 0.8.20+ and EVM version 'cancun'", + "Add Polkadot Hub TestNet to MetaMask (chainId 420420417, RPC testnet URL)", + "Connect Injected Provider in Remix Deploy tab, deploy with initialOwner set to wallet address", + "Verify deployment and mint tokens via Remix UI" + ], + "result": "ERC-20 token deployed on Polkadot Hub TestNet; contract visible in Remix with working name, symbol, mint, and balanceOf functions" + }, + { + "scenario": "Edge case: user has no testnet PAS tokens", + "user_says": "Deployment failed — gas fee transaction rejected", + "actions": [ + "Direct user to https://faucet.polkadot.io/ and ask them to enter their MetaMask address", + "Wait for confirmation that PAS tokens have arrived (check MetaMask balance)", + "Retry deployment from the Remix Deploy tab" + ], + "result": "Account funded with testnet PAS; deployment proceeds successfully" + } + ] + }, + { + "id": "deploy-contracts-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "How to interact with the deployed ERC-20 contract using Ethers.js." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a new ERC-20 token to Polkadot Hub", - "user_says": "Deploy an ERC-20 token to Polkadot Hub TestNet", - "actions": [ - "Clone revm-hardhat-examples and cd erc20-hardhat", - "Run npm i && npm install dotenv", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Modify hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set evmVersion cancun, confirm requiredConfirmations: 1", - "Run npx hardhat compile", - "Run npx hardhat test --network polkadotTestnet to verify contract logic", - "Run npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet; delegate confirmation to user" - ], - "result": "ERC-20 token contract deployed; address printed to console" - }, - { - "scenario": "Edge case: compilation fails with MCOPY opcode error", - "user_says": "Compilation fails with 'Invalid opcode: MCOPY'", - "actions": [ - "Open hardhat.config.ts", - "Change the solidity field to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", - "Run npx hardhat compile again" - ], - "result": "Compilation succeeds with Cancun EVM version" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - } - }, - { - "id": "set-up-hardhat-pvm", - "title": "Set Up Hardhat with the Polkadot Plugin (PVM)", - "description": "Sets up a Hardhat project using the @parity/hardhat-polkadot plugin and the resolc compiler to compile Solidity to PolkaVM (PVM) bytecode. Use when targeting Polkadot Hub's PVM runtime rather than the standard EVM runtime. Covers project initialization, resolc installation, dotenv private key setup, TestNet config with gasPrice, and a compile-verify step. Trigger phrases: 'Hardhat PVM', 'hardhat-polkadot plugin', 'compile to PolkaVM', 'resolc Hardhat', 'PVM smart contract Polkadot'. Do NOT use this skill for standard EVM deployments; use set-up-hardhat-evm instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/hardhat-polkadot.md" - ], - "primary_page": "smart-contracts/dev-environments/hardhat-polkadot.md", - "prerequisites": { - "runtime": [ - "Node.js v22.5+ and npm v10.9.0+ (required for @parity/hardhat-polkadot compatibility)", - "npm or pnpm or yarn" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-pvm-example && cd hardhat-pvm-example", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project." - }, - { - "order": 2, - "action": "Install the Polkadot Hardhat plugin and resolc compiler", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npm install --save-dev @parity/hardhat-polkadot", - "npm install --save-dev @parity/resolc", - "npm install dotenv" - ], - "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them — use latest." - }, - { - "order": 3, - "action": "Initialize the Hardhat PVM project structure", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npx hardhat-polkadot init" - ], - "interactive": true, - "description": "Run the project creation wizard. This is an interactive command — delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." - }, - { - "order": 4, - "action": "Install all project dependencies after init", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npm install", - "echo '/ignition/deployments/' >> .gitignore" - ], - "description": "Install any additional dependencies added by the init wizard, then add ignition/deployments/ to .gitignore to avoid committing deployment state to version control." - }, - { - "order": 5, - "action": "Create .env file and .gitignore entry for private key", - "working_directory": "hardhat-pvm-example", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 6, - "action": "Fetch and configure hardhat.config.ts for TestNet", - "working_directory": "hardhat-pvm-example", - "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." - }, - { - "order": 7, - "action": "Compile the sample contract to verify setup", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled ... Solidity file(s) successfully", - "description": "Compile the sample contract generated by the init wizard. Successful compilation confirms resolc is working and the project structure is correct. The compiled artifacts appear in artifacts/contracts/. If compilation fails with resolc errors, verify @parity/resolc is installed and the version matches what the hardhat.config.ts specifies in the resolc field." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@parity/hardhat-polkadot' / resolc not found", - "cause": "The Polkadot plugin or resolc compiler was not installed.", - "resolution": "Run: npm install --save-dev @parity/hardhat-polkadot @parity/resolc in the project directory." - }, - { - "pattern": "Compilation fails with resolc errors / unsupported Solidity version", - "cause": "The resolc compiler version does not support the contract's Solidity pragma version.", - "resolution": "Check the @parity/resolc version installed and the Solidity pragma in the contract. Update the version field in hardhat.config.ts resolc settings to match what is installed, or downgrade the Solidity pragma in the contract to a supported version." - }, - { - "pattern": "Error: vars is not defined", - "cause": "hardhat.config.ts uses Hardhat's vars.get() system instead of dotenv.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - }, - { - "pattern": "Deployment hangs on local node / requiredConfirmations: 0", - "cause": "The ignition config has requiredConfirmations: 0, which causes Ignition to misinterpret pending transactions as dropped on local nodes that don't produce blocks instantly.", - "resolution": "Set ignition.requiredConfirmations: 1 in hardhat.config.ts." - }, - { - "pattern": "Binary permission issues on macOS (binary is quarantined)", - "cause": "macOS Gatekeeper quarantines downloaded binaries for dev-node and eth-rpc adapter.", - "resolution": "Run: xattr -d com.apple.quarantine /path/to/binary. Also ensure executable permission: chmod +x /path/to/binary." - } - ], - "supplementary_context": { - "description": "Load when the user wants to deploy a contract after setting up the PVM environment, or needs to understand PVM vs EVM differences.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Next step: deploy a basic contract once the Hardhat PVM environment is set up." - }, - { - "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", - "title": "EVM vs PVM", - "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md", - "relevance": "Understand the differences between EVM and PVM to choose the right compilation target." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: set up a PVM Hardhat project from scratch", - "user_says": "Set up Hardhat with the Polkadot PVM plugin", - "actions": [ - "Create hardhat-pvm-example/ and run npm init -y", - "Install @parity/hardhat-polkadot, @parity/resolc, dotenv", - "Run npx hardhat-polkadot init wizard (delegate to user)", - "Run npm install and add ignition/deployments/ to .gitignore", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Fetch and configure hardhat.config.ts: add dotenv, replace vars.get, add gasPrice 5000 gwei, requiredConfirmations: 1", - "Run npx hardhat compile to verify setup" - ], - "result": "PVM Hardhat project compiles successfully; ready to deploy contracts to Polkadot Hub" - }, - { - "scenario": "Edge case: user is on macOS and binary is quarantined", - "user_says": "The dev-node binary is blocked by macOS Gatekeeper", - "actions": [ - "Run: xattr -d com.apple.quarantine /path/to/dev-node", - "Run: chmod +x /path/to/dev-node", - "Restart the local node" - ], - "result": "Binary executes without quarantine errors" - } - ] - }, - { - "id": "set-up-hardhat-evm", - "title": "Set Up Hardhat for Polkadot Hub (EVM)", - "description": "Initializes a Hardhat v2 TypeScript project for the standard Polkadot Hub EVM runtime (not PVM). Configures the polkadotTestnet network with correct RPC URL, chain ID (420420417), gasPrice (5000 gwei), and dotenv-based private key management. Optionally configures Blockscout or Routescan contract verification. Use when setting up a reusable EVM development environment before deploying any contract. Trigger phrases: 'set up Hardhat for Polkadot EVM', 'configure Hardhat Polkadot Hub', 'Hardhat EVM Polkadot setup', 'Hardhat network config Polkadot'. Do NOT use for PVM; use set-up-hardhat-pvm instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/hardhat.md" - ], - "primary_page": "smart-contracts/dev-environments/hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js LTS (18.x, 20.x, or 22.x — even major version numbers only)", - "npm, pnpm, or yarn" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account — needed before deploying contracts" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key used in the polkadotTestnet accounts array. Fill before running any deployment. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-example && cd hardhat-example", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage — the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." - }, - { - "order": 2, - "action": "Install Hardhat and dependencies", - "working_directory": "hardhat-example", - "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", - "npm install dotenv" - ], - "description": "Install Hardhat v2, the Hardhat Toolbox (includes ethers.js, chai, and testing utilities), TypeScript support, and dotenv. The dotenv package replaces Hardhat's interactive 'npx hardhat vars set' mechanism, which cannot be used in agent shells." - }, - { - "order": 3, - "action": "Create project directory structure", - "working_directory": "hardhat-example", - "commands": [ - "mkdir -p contracts ignition/modules test", - "echo '/ignition/deployments/' >> .gitignore", - "echo '/artifacts/' >> .gitignore", - "echo '/cache/' >> .gitignore" - ], - "description": "Create the standard Hardhat directory structure and add common output directories to .gitignore." - }, - { - "order": 4, - "action": "Create .env file and .gitignore entry for private key", - "working_directory": "hardhat-example", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable — the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." - }, - { - "order": 5, - "action": "Fetch and configure hardhat.config.ts", - "working_directory": "hardhat-example", - "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: vars is not defined", - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - }, - { - "pattern": "HardhatError: HH8: There's one or more errors in your config / Cannot read properties of undefined (reading 'get')", - "cause": "The vars import is present but vars is undefined because the interactive vars store was never populated.", - "resolution": "Remove 'import { vars } from \"hardhat/config\";' and all vars.get() calls. Replace with process.env.PRIVATE_KEY loaded by dotenv." - }, - { - "pattern": "Error: insufficient funds for intrinsic transaction cost on deployment", - "cause": "gasPrice is missing or below the TestNet base fee of 1000 gwei.", - "resolution": "Add gasPrice: 5000000000000 to the polkadotTestnet network block in hardhat.config.ts. Also ensure the deployer account has testnet PAS from https://faucet.polkadot.io/." - }, - { - "pattern": "Cannot find module 'ts-node' / TypeScript compilation error on npx hardhat", - "cause": "TypeScript dependencies were not installed.", - "resolution": "Run: npm install --save-dev typescript ts-node @types/node in the project directory." - } - ], - "supplementary_context": { - "description": "Load when the user is ready to deploy a contract or wants to verify a deployed contract.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Next step: deploy a basic Storage contract using the configured Hardhat project." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", - "title": "Deploy an ERC-20 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "Deploy an ERC-20 token using the configured Hardhat environment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: configure a Hardhat project for Polkadot Hub from scratch", - "user_says": "Set up a Hardhat project for Polkadot Hub EVM", - "actions": [ - "Create hardhat-example/ and run npm init -y", - "Install hardhat@^2.27.0, hardhat-toolbox, typescript, ts-node, dotenv", - "Create contracts/, ignition/modules/, test/ directories and set .gitignore", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Fetch hardhat.config.ts from cookbook; add dotenv import, replace vars.get, add gasPrice 5000 gwei, add ignition.requiredConfirmations: 1" - ], - "result": "Hardhat project configured for Polkadot Hub TestNet; ready for contract development and deployment" - }, - { - "scenario": "Edge case: existing project uses Hardhat vars and breaks in CI or agent shell", - "user_says": "My Hardhat config uses vars.get and it fails with 'vars is not defined'", - "actions": [ - "Open hardhat.config.ts", - "Add 'import \"dotenv/config\";' as the first line", - "Replace all vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string", - "Remove the 'import { vars } from \"hardhat/config\";' line", - "Create .env with PRIVATE_KEY= and add .env to .gitignore" - ], - "result": "Project loads private key from .env; works in agent shells and CI without interactive prompts" - } - ] - }, - { - "id": "query-chain-data-sidecar-rest", - "title": "Query On-Chain Data with Sidecar REST API", - "description": "Runs the Substrate API Sidecar REST service connected to Polkadot Hub TestNet and queries on-chain data via curl. Use when you need account balances, asset info, or block data without writing SDK code. Covers Sidecar installation, startup with a TestNet endpoint, and the primary query endpoints: /accounts/{addr}/balance-info, /assets/{id}/asset-info, and /blocks/{id}. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required — read-only operations only.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/query-data/query-rest.md" - ], - "primary_page": "chain-interactions/query-data/query-rest.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm (to install and run @substrate/api-sidecar)", - "curl (pre-installed on macOS and Linux; use WSL on Windows)", - "jq (optional, for formatted JSON output — install with: apt install jq or brew install jq)" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Substrate API Sidecar", - "working_directory": ".", - "commands": [ - "npm install -g @substrate/api-sidecar" - ], - "description": "Install Sidecar globally. This makes the 'substrate-api-sidecar' command available in your shell. If you prefer not to install globally, invoke it with 'npx @substrate/api-sidecar' in step 2 instead." - }, - { - "order": 2, - "action": "Start Sidecar connected to Polkadot Hub TestNet", - "working_directory": ".", - "commands": [ - "SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network substrate-api-sidecar" - ], - "expected_output": "SAS listening on http://127.0.0.1:8080/", - "description": "Start Sidecar with SAS_SUBSTRATE_URL pointing to the Polkadot Hub TestNet WebSocket endpoint. The service listens on http://127.0.0.1:8080/ by default. Run this in a dedicated terminal and keep it running while executing the following steps. If using npx: SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network npx @substrate/api-sidecar. Polkadot Hub TestNet can be unstable — if the WebSocket connection drops, restart Sidecar." - }, - { - "order": 3, - "action": "Query account balance", - "working_directory": ".", - "commands": [ - "curl -s \"http://127.0.0.1:8080/accounts/INSERT_SS58_ADDRESS/balance-info\" | jq ." - ], - "expected_output": "{\"at\":{\"hash\":\"0x...\",\"height\":\"12345\"},\"nonce\":\"0\",\"tokenSymbol\":\"PAS\",\"free\":\"...\",\"reserved\":\"...\"}", - "description": "Query the balance-info endpoint. Replace 'INSERT_SS58_ADDRESS' with the target SS58 address. Response fields: free — spendable planck; reserved — locked/staked; miscFrozen / feeFrozen — frozen amounts; nonce — transaction count. Divide raw planck values by 10^10 to convert to PAS. Omit '| jq .' if jq is not installed." - }, - { - "order": 4, - "action": "Query block information", - "working_directory": ".", - "commands": [ - "curl -s \"http://127.0.0.1:8080/blocks/head\" | jq .", - "curl -s \"http://127.0.0.1:8080/blocks/1000\" | jq ." - ], - "expected_output": "{\"number\":\"12345\",\"hash\":\"0x...\",\"parentHash\":\"0x...\",\"extrinsics\":[...]}", - "description": "The first command fetches the latest finalized block; the second fetches block 1000 by number. Replace '1000' with any block number. The response includes block number, hash, parentHash, stateRoot, extrinsicsRoot, and extrinsics list." - }, - { - "order": 5, - "action": "Query asset metadata", - "working_directory": ".", - "commands": [ - "curl -s \"http://127.0.0.1:8080/assets/1984/asset-info\" | jq ." - ], - "expected_output": "{\"at\":{...},\"items\":[{\"id\":\"1984\",\"name\":\"USDT\",\"symbol\":\"USDT\",\"decimals\":\"6\"}]}", - "description": "Query metadata for asset ID 1984 (USDT on Polkadot Hub). Replace '1984' with any registered asset ID. If the asset does not exist on TestNet, items will be empty — this is expected since not all mainnet assets are bridged to TestNet." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED 127.0.0.1:8080", - "cause": "Sidecar is not running or not yet ready.", - "resolution": "Start Sidecar in a separate terminal: SAS_SUBSTRATE_URL=wss://asset-hub-paseo.dotters.network substrate-api-sidecar. Wait for the 'SAS listening' message before sending curl requests." - }, - { - "pattern": "Sidecar starts but shows repeated WebSocket error messages", - "cause": "Polkadot Hub TestNet WebSocket endpoint is temporarily unavailable. TestNet drops connections under load.", - "resolution": "Wait 2-5 minutes and restart Sidecar. Check the Polkadot Discord for TestNet outage notices." - }, - { - "pattern": "balance-info returns all-zero values for free/reserved/nonce", - "cause": "The queried account has no on-chain entry — it has never received any tokens.", - "resolution": "Fund the account at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation. Re-query the endpoint." - }, - { - "pattern": "curl: (6) Could not resolve host", - "cause": "Using the WebSocket URL (wss://) in curl instead of the Sidecar HTTP endpoint.", - "resolution": "Curl queries go to the Sidecar HTTP endpoint: http://127.0.0.1:8080. The wss:// URL is only used to start Sidecar, not to query it." - } - ], - "supplementary_context": { - "description": "Load these pages when the user wants SDK-based querying as an alternative or needs to understand Sidecar response fields.", - "pages": [ - { - "slug": "chain-interactions-query-data-query-sdks", - "title": "Query On-Chain State with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "SDK-based alternative to Sidecar — query the same on-chain data using PAPI or Polkadot.js TypeScript code." - }, - { - "slug": "reference-tools-sidecar", - "title": "Sidecar REST API", - "url": "https://docs.polkadot.com/reference/tools/sidecar.md", - "relevance": "Full Sidecar API reference listing all available endpoints, query parameters, and response schemas." - }, - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "SDK approach to querying the same account balance data returned by the Sidecar balance-info endpoint." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance via REST without writing code", - "user_says": "Query the balance of a Polkadot Hub address using the REST API", - "actions": [ - "Install @substrate/api-sidecar globally with npm install -g", - "Start Sidecar in a separate terminal with SAS_SUBSTRATE_URL pointing to wss://asset-hub-paseo.dotters.network", - "Run curl against http://127.0.0.1:8080/accounts/{address}/balance-info", - "Divide the free field value by 10^10 to convert planck to PAS" - ], - "result": "Account nonce and full balance breakdown (free, reserved, frozen) returned as JSON" - }, - { - "scenario": "Edge case: account shows zero balance across all fields", - "user_says": "The balance-info response returns zero for everything", - "actions": [ - "Confirm the address format is correct SS58 (not 0x hex)", - "Fund the address at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation", - "Re-query the balance-info endpoint" - ], - "result": "After funding, the free balance reflects the received testnet PAS" - } - ] - }, - { - "id": "calculate-transaction-fees-papi", - "title": "Estimate Transaction Fees with PAPI", - "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getPaymentInfo method. Use when you need to preview gas costs before submitting — for fee UIs, budget checks, or preflight validation. Returns partial_fee in planck, human-readable PAS, and weight breakdown. Also covers the Polkadot.js paymentInfo alternative described in the source page. Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getPaymentInfo PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required — fee estimation is read-only.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/calculate-transaction-fees.md" - ], - "primary_page": "chain-interactions/send-transactions/calculate-transaction-fees.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-fee-estimate && cd papi-fee-estimate", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-fee-estimate' and initialize it as an ESM Node.js project. The type=module flag is required — polkadot-api uses ESM imports." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-fee-estimate", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors", - "working_directory": "papi-fee-estimate", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. Polkadot Hub TestNet can be unstable — retry after a few minutes if the connection fails." - }, - { - "order": 4, - "action": "Create the fee estimation script", - "working_directory": "papi-fee-estimate", - "description": "Create a file named 'estimate-fees.ts' in papi-fee-estimate with this content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { polkadotTestNet } from \"@polkadot-api/descriptors\";\n\nconst WS_ENDPOINT = \"INSERT_WS_ENDPOINT\";\nconst SENDER_ADDRESS = \"INSERT_SENDER_ADDRESS\";\nconst DEST_ADDRESS = \"INSERT_DEST_ADDRESS\";\n\nasync function main() {\n const client = createClient(getWsProvider(WS_ENDPOINT));\n const api = client.getTypedApi(polkadotTestNet);\n const tx = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: DEST_ADDRESS },\n value: 10_000_000_000n,\n });\n const info = await tx.getPaymentInfo(SENDER_ADDRESS);\n console.log(\"Fee (planck):\", info.partial_fee.toString());\n console.log(\"Fee (PAS):\", (Number(info.partial_fee) / 1e10).toFixed(6));\n console.log(\"Weight ref_time:\", info.weight.ref_time.toString());\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nAfter creating the file, apply these substitutions: (1) Replace INSERT_WS_ENDPOINT with your WS endpoint (e.g. 'wss://asset-hub-paseo.dotters.network'). (2) Replace INSERT_SENDER_ADDRESS with any valid funded SS58 address — sender address affects fee because weight depends on account state. (3) Replace INSERT_DEST_ADDRESS with any valid SS58 recipient address. Save the file." - }, - { - "order": 5, - "action": "Run the fee estimation script", - "working_directory": "papi-fee-estimate", - "commands": [ - "npx tsx estimate-fees.ts" - ], - "expected_output": "Fee (planck): 15000000000\nFee (PAS): 0.001500\nWeight ref_time: 200000000", - "description": "Execute the script. It constructs an unsigned Balances.transfer_keep_alive transaction for 1 PAS and calls getPaymentInfo to retrieve the fee estimate without submitting. Fees on Polkadot Hub TestNet are higher than Ethereum due to the 1000 gwei base fee." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the papi-fee-estimate directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED on papi add or script start", - "cause": "Polkadot Hub TestNet endpoint is temporarily unavailable.", - "resolution": "Wait 2-5 minutes and retry. Check the Polkadot Discord for TestNet status." - }, - { - "pattern": "TypeError: tx.getPaymentInfo is not a function", - "cause": "Descriptor export name does not match the import, or polkadot-api version is below 1.0.0.", - "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api — version 1.0.0+ required." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "Project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the papi-fee-estimate directory, then re-run." - } - ], - "supplementary_context": { - "description": "Load these pages when the user wants to submit the transaction after fee estimation or needs to understand fee mechanics.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit the same transaction whose fee was estimated — the natural next step." - }, - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI reference including getPaymentInfo signatures and other typed transaction methods." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: preview fee before sending a transfer", - "user_says": "How much will it cost to send 1 PAS on Polkadot Hub?", - "actions": [ - "Scaffold 'papi-fee-estimate/' as an ESM Node.js project", - "Install polkadot-api and tsx", - "Generate descriptors with npx papi add polkadotTestNet", - "Create estimate-fees.ts with inline script; substitute WS endpoint and addresses", - "Run npx tsx estimate-fees.ts" - ], - "result": "Estimated fee in planck and PAS printed; user can budget before submitting the transfer" - }, - { - "scenario": "Edge case: fee appears unusually high compared to Ethereum", - "user_says": "The fee estimate shows 0.001 PAS — that seems expensive", - "actions": [ - "Explain that Polkadot Hub TestNet has a base fee of 1000 gwei — significantly higher than Ethereum mainnet", - "Note that PAS testnet fees do not reflect real economic cost", - "Get testnet tokens from https://faucet.polkadot.io/ if the sender balance is insufficient" - ], - "result": "User understands TestNet fee mechanics and obtains testnet PAS if needed" - } - ] - }, - { - "id": "transfer-assets-parachains-paraspell", - "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK", - "description": "Transfers assets cross-chain between Polkadot parachains using the ParaSpell XCM SDK. Covers the full workflow: install the SDK, build an XCM transfer, dry-run for feasibility, verify existential deposit requirements, estimate fees, and submit against Paseo testnet. Use when you need to move tokens programmatically between parachains (e.g., Asset Hub to another system parachain). Requires a funded Paseo testnet mnemonic. Trigger phrases: 'XCM transfer ParaSpell', 'cross-chain transfer Polkadot', 'transfer assets between parachains', 'send tokens XCM', 'ParaSpell SDK'. Uses dotenv for mnemonic security.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" - ], - "primary_page": "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm" - ], - "network": [ - "WebSocket RPC for Paseo Asset Hub (source): wss://sys.ibp.network/asset-hub-paseo", - "Destination parachain WebSocket RPC (e.g., wss://sys.ibp.network/people-paseo for People Chain Paseo)" - ], - "tokens": [ - "Paseo testnet PAS in the sender account — get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." - ], - "wallet": [ - "SR25519 account mnemonic phrase for the sender account, funded with Paseo testnet PAS" - ] - }, - "env_vars": [ - { - "name": "MNEMONIC", - "description": "12-word BIP39 mnemonic for the sender on Paseo Asset Hub. Must be funded with testnet PAS. Never commit to version control.", - "required": true - }, - { - "name": "SOURCE_RPC", - "description": "WebSocket RPC URL for the source parachain (e.g., wss://sys.ibp.network/asset-hub-paseo).", - "required": true - }, - { - "name": "DEST_ADDRESS", - "description": "SS58-encoded recipient address on the destination parachain.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir paraspell-xcm-transfer && cd paraspell-xcm-transfer", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'paraspell-xcm-transfer' and initialize it as an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "paraspell-xcm-transfer", - "commands": [ - "npm install @paraspell/sdk-pjs @polkadot/api @polkadot/keyring dotenv", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install the ParaSpell SDK (pjs variant), Polkadot.js API, keyring for signing, and dotenv for secure mnemonic loading." - }, - { - "order": 3, - "action": "Create .env and .gitignore for mnemonic", - "working_directory": "paraspell-xcm-transfer", - "commands": [ - "printf 'MNEMONIC=\\nSOURCE_RPC=wss://sys.ibp.network/asset-hub-paseo\\nDEST_ADDRESS=\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create a .env file with three placeholders and a .gitignore to exclude it. Stop here and ask the user to edit .env directly: fill MNEMONIC with their 12-word Paseo testnet mnemonic, confirm SOURCE_RPC, and set DEST_ADDRESS to the recipient SS58 address on the destination chain. Do NOT ask for the mnemonic in chat. Wait for confirmation before continuing." - }, - { - "order": 4, - "action": "Create the XCM transfer script", - "working_directory": "paraspell-xcm-transfer", - "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains — chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." - }, - { - "order": 5, - "action": "Verify sender is funded and run the script", - "working_directory": "paraspell-xcm-transfer", - "commands": [ - "npx tsx xcm-transfer.ts" - ], - "expected_output": "Sender: ...\nDry-run: OK\nEstimated fee: ...\nStatus: InBlock\nFinalized: 0x...", - "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first — if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: Insufficient funds / Inability to pay some fees", - "cause": "Sender account has insufficient PAS to cover transfer amount plus XCM execution fees.", - "resolution": "Fund the sender on Paseo Asset Hub at https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Wait 2 minutes for confirmation." - }, - { - "pattern": "Dry-run failed: destination existential deposit not met", - "cause": "Transfer amount is below the existential deposit on the destination chain, or recipient has no balance.", - "resolution": "Increase the transfer AMOUNT above the destination chain's existential deposit, or ensure the recipient already has a balance on the destination chain." - }, - { - "pattern": "Error: chain 'AssetHubPaseo' not found in ParaSpell", - "cause": "Chain name does not match the ParaSpell supported chain list for the SDK version installed.", - "resolution": "Check the supported chains list for your @paraspell/sdk-pjs version. Chain names are case-sensitive and version-specific. Update SOURCE_CHAIN and DEST_CHAIN in the script accordingly." - }, - { - "pattern": "Transaction submitted but never finalizes", - "cause": "Paseo TestNet instability — the TestNet can drop transactions under load.", - "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script — nonce: -1 fetches the current nonce automatically." - } - ], - "supplementary_context": { - "description": "Load these pages for background on XCM mechanics, fee estimation, or to debug a failing transfer.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", - "title": "XCM Fee Estimation", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "Detailed XCM fee estimation covering local, delivery, and remote execution components." - }, - { - "slug": "reference-tools-paraspell", - "title": "ParaSpell XCM SDK", - "url": "https://docs.polkadot.com/reference/tools/paraspell.md", - "relevance": "Full ParaSpell SDK reference including supported chains, asset IDs, and Builder API documentation." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", - "title": "Replay and Dry Run XCMs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "How to debug a failing XCM transfer by replaying it with Chopsticks before submitting to TestNet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: transfer PAS from Asset Hub Paseo to People Chain Paseo", - "user_says": "Transfer 1 PAS from Asset Hub to People Chain on Paseo testnet", - "actions": [ - "Scaffold 'paraspell-xcm-transfer/' as an ESM project", - "Install @paraspell/sdk-pjs, @polkadot/api, keyring, dotenv", - "Create .env; ask user to fill MNEMONIC and DEST_ADDRESS without asking in chat", - "Create xcm-transfer.ts with the inline script", - "Verify sender has 2+ PAS at https://faucet.polkadot.io/", - "Run npx tsx xcm-transfer.ts" - ], - "result": "Dry-run passes, fee estimated, transaction finalized, PAS transferred cross-chain" - }, - { - "scenario": "Edge case: dry-run fails with existential deposit error", - "user_says": "The dry-run fails saying the destination existential deposit is not met", - "actions": [ - "Check the existential deposit for the destination chain using the ParaSpell SDK", - "Increase AMOUNT in the script above the existential deposit threshold", - "Re-run xcm-transfer.ts" - ], - "result": "Dry-run passes once the transfer amount covers the destination existential deposit" - } - ] - }, - { - "id": "pay-fees-alternative-token", - "title": "Pay Transaction Fees with an Alternative Token", - "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode — no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/pay-fees-with-different-tokens.md" - ], - "primary_page": "chain-interactions/send-transactions/pay-fees-with-different-tokens.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm", - "npx (bundled with npm v5.2+ — no separate install needed)" - ], - "network": [ - "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network — Chopsticks downloads state on first run" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Create project directory and install dependencies", - "working_directory": ".", - "commands": [ - "mkdir fee-proxy-demo && cd fee-proxy-demo", - "npm init -y && npm pkg set type=module", - "npm install @acala-network/chopsticks polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Create a new directory 'fee-proxy-demo', initialize as ESM, and install Chopsticks and PAPI. Chopsticks forks Polkadot Hub state locally; PAPI constructs and submits the fee-proxy transaction against the fork." - }, - { - "order": 2, - "action": "Create Chopsticks configuration", - "working_directory": "fee-proxy-demo", - "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key — this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." - }, - { - "order": 3, - "action": "Start the Chopsticks fork", - "working_directory": "fee-proxy-demo", - "commands": [ - "npx @acala-network/chopsticks --config=chopsticks.yml" - ], - "expected_output": "Listening on port 8000", - "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." - }, - { - "order": 4, - "action": "Generate PAPI type descriptors for the local fork", - "working_directory": "fee-proxy-demo", - "commands": [ - "npx papi add localFork -w ws://localhost:8000" - ], - "description": "Run this command in a second terminal while Chopsticks (step 3) is still running. Generates compile-time type descriptors named 'localFork' for the forked chain. Produces the 'localFork' export in @polkadot-api/descriptors. The descriptors reflect the exact runtime version of the forked state." - }, - { - "order": 5, - "action": "Create the fee-proxy script", - "working_directory": "fee-proxy-demo", - "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy — pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." - }, - { - "order": 6, - "action": "Run the fee-proxy script against the local fork", - "working_directory": "fee-proxy-demo", - "commands": [ - "npx tsx fee-proxy.ts" - ], - "expected_output": "Included in block: 0x...\nFees paid in USDT asset ID: 1984", - "description": "Execute the script while Chopsticks is still running. The script submits the fee-proxy transaction; Chopsticks validates it against the forked state and includes it in a mock block. If the pallet name 'AssetConversionTxPayment' does not exist in localFork, inspect api.tx in the TypeScript IDE and check the source page for the correct name." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED ws://localhost:8000", - "cause": "Chopsticks is not running.", - "resolution": "Start Chopsticks first: npx @acala-network/chopsticks --config=chopsticks.yml. Wait for 'Listening on port 8000' before running the script or the papi add command." - }, - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or localFork is undefined", - "cause": "PAPI type descriptors were not generated against the Chopsticks fork endpoint.", - "resolution": "With Chopsticks running, execute: npx papi add localFork -w ws://localhost:8000. Then re-run the script." - }, - { - "pattern": "api.tx.AssetConversionTxPayment is undefined", - "cause": "The forked chain version uses a different pallet name for asset-based fee payment.", - "resolution": "Check available pallets by inspecting api.tx in a TypeScript IDE, or consult the source page for the correct pallet name. The pallet may be named 'AssetTxPayment' depending on the runtime version." - }, - { - "pattern": "Alice has insufficient USDT balance to pay fees", - "cause": "Alice's account in the forked state does not hold USDT (asset ID 1984).", - "resolution": "Use Chopsticks' dev_setStorage RPC method to seed Alice's USDT asset balance in the forked state, or identify a USDT-holding account in the forked state and use that address instead." - } - ], - "supplementary_context": { - "description": "Load these pages to understand fee mechanics, Chopsticks setup, or to compare with standard fee estimation.", - "pages": [ - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Full Chopsticks reference including all config options, RPC override methods, and mock-signature-host details." - }, - { - "slug": "chain-interactions-send-transactions-calculate-transaction-fees", - "title": "Calculate Transaction Fees", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", - "relevance": "How to estimate fees for a standard transaction — compare with the fee proxy approach to understand the difference." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: demonstrate paying fees in USDT on a local fork", - "user_says": "Show me how to pay Polkadot Hub transaction fees in USDT instead of PAS", - "actions": [ - "Create 'fee-proxy-demo/' with ESM Node.js init", - "Install Chopsticks and polkadot-api", - "Create chopsticks.yml pointing to wss://asset-hub-paseo.dotters.network with mock-signature-host: true", - "Start Chopsticks in a dedicated terminal; wait for 'Listening on port 8000'", - "Generate PAPI descriptors with npx papi add localFork -w ws://localhost:8000", - "Create fee-proxy.ts with inline script; run npx tsx fee-proxy.ts" - ], - "result": "Transaction included in a local mock block; fees deducted from Alice's USDT balance instead of PAS" - }, - { - "scenario": "Edge case: Alice has no USDT in the forked state", - "user_says": "The script fails saying Alice has no USDT balance", - "actions": [ - "Use Chopsticks' dev_setStorage RPC to set Alice's USDT asset balance in the forked state", - "Alternatively, query the forked state for an account with USDT balance and use that address as sender", - "Re-run fee-proxy.ts" - ], - "result": "Script succeeds once Alice's USDT balance is seeded or a funded account is substituted" - } - ] - }, - { - "id": "run-parachain-rpc-node", - "title": "Run a Parachain RPC Node", - "description": "Runs a Polkadot system parachain RPC node (using People Chain as example) in archive or pruned mode via Docker or systemd. Covers Docker image selection, binary installation, systemd service configuration, and sync verification. A substitution table adapts the steps for Collectives, BridgeHub, and other system parachains. Use when you need a self-hosted parachain RPC endpoint. Trigger phrases: 'run parachain node', 'People Chain RPC node', 'parachain RPC Docker', 'set up system parachain node', 'run parachain RPC systemd'. No tokens required.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "node-infrastructure/run-a-node/parachain-rpc.md" - ], - "primary_page": "node-infrastructure/run-a-node/parachain-rpc.md", - "prerequisites": { - "runtime": [ - "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", - "Disk: minimum 500 GB SSD for pruned node; 2+ TB SSD for archive node", - "RAM: minimum 8 GB; 16 GB recommended for archive nodes", - "CPU: 4+ cores recommended" - ], - "network": [ - "Open TCP ports: 30333 (P2P), 9944 (WebSocket RPC), 9615 (Prometheus metrics, optional)", - "Stable broadband connection for initial sync (several hundred GB download)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Choose deployment method and sync type", - "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table — replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." - }, - { - "order": 2, - "action": "Run the parachain node via Docker", - "working_directory": ".", - "commands": [ - "docker pull parity/polkadot-parachain:stable", - "mkdir -p /var/lib/people-chain", - "docker run -d --name people-chain-rpc --restart unless-stopped -p 30333:30333 -p 9944:9944 -p 9615:9615 -v /var/lib/people-chain:/data parity/polkadot-parachain:stable --chain=people-polkadot --base-path=/data --rpc-external --rpc-port=9944 --rpc-cors=all --prometheus-external --prometheus-port=9615 --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast" - ], - "description": "Pull the stable polkadot-parachain image and start the People Chain RPC node. Key arguments: --chain=people-polkadot selects the People Chain spec; --base-path=/data stores chain data in the mounted volume; --state-pruning=1000 --blocks-pruning=1000 keeps the last 1000 blocks (change both to 'archive' for a full archive node); --rpc-external exposes the WebSocket RPC; the -- separator and --chain=polkadot configure the embedded relay chain client. Replace /var/lib/people-chain with your data directory. For Paseo testnet People Chain, use --chain=people-paseo and --chain=paseo after the -- separator." - }, - { - "order": 3, - "action": "Run the parachain node via systemd (binary method alternative)", - "working_directory": ".", - "description": "For the systemd binary method (skip if using Docker in step 2): (1) Create system user: sudo useradd --system --no-create-home --shell /usr/sbin/nologin polkadot. (2) Create data dir: sudo mkdir -p /var/lib/people-chain && sudo chown polkadot:polkadot /var/lib/people-chain. (3) Download polkadot-parachain binary from https://github.com/paritytech/polkadot-sdk/releases (stable, amd64 or arm64) and install: sudo install -m 755 polkadot-parachain /usr/local/bin/. (4) Create /etc/systemd/system/people-chain-rpc.service:\n\n[Unit]\nDescription=People Chain RPC Node\nAfter=network.target\n\n[Service]\nUser=polkadot\nExecStart=/usr/local/bin/polkadot-parachain --chain=people-polkadot --base-path=/var/lib/people-chain --rpc-external --rpc-port=9944 --rpc-cors=all --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n\n(5) Enable and start: sudo systemctl daemon-reload && sudo systemctl enable people-chain-rpc && sudo systemctl start people-chain-rpc. Apply the same chain substitutions as in step 2." - }, - { - "order": 4, - "action": "Monitor sync progress", - "working_directory": ".", - "commands": [ - "docker logs -f people-chain-rpc 2>&1 | grep -E 'Syncing|Idle|Best|Peers'", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." - }, - { - "order": 5, - "action": "Verify the RPC endpoint is operational", - "working_directory": ".", - "commands": [ - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_chain\",\"params\":[]}' http://localhost:9944", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"result\":\"Polkadot People\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", - "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot People' for the mainnet People Chain. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete before pointing applications at this endpoint." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: No space left on device / database write error", - "cause": "The data volume has run out of disk space.", - "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=1000). Check usage with: du -sh /var/lib/people-chain" - }, - { - "pattern": "EADDRINUSE port 9944 or 30333", - "cause": "Another process is already using the required ports.", - "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." - }, - { - "pattern": "Peers count stays at 0 after several minutes", - "cause": "P2P port 30333 is blocked by firewall, or bootnodes are temporarily unavailable.", - "resolution": "Open TCP port 30333: sudo ufw allow 30333/tcp. For cloud VMs, update the security group to allow inbound TCP on 30333. If peers still do not connect after 10 minutes, check the Polkadot network status page." - }, - { - "pattern": "Sync stalls at the same block for more than 30 minutes", - "cause": "Node cannot find peers with required state, or the embedded relay chain client is not syncing.", - "resolution": "Restart the container or service: docker restart people-chain-rpc or sudo systemctl restart people-chain-rpc. If stalling persists, try switching sync mode: replace --sync=fast with --sync=warp." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to add TLS/WSS or run the Polkadot Hub RPC node specifically.", - "pages": [ - { - "slug": "node-infrastructure-run-a-node-polkadot-hub-rpc", - "title": "Run an RPC Node for Polkadot Hub", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/polkadot-hub-rpc.md", - "relevance": "How to run an RPC node for Polkadot Hub specifically — different chain spec and optional Ethereum RPC adapter." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", - "title": "Set Up Secure WebSocket", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-full-node", - "title": "Set Up a Node", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md", - "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run a pruned People Chain RPC node via Docker", - "user_says": "Run a People Chain RPC node on Polkadot using Docker", - "actions": [ - "Pull parity/polkadot-parachain:stable", - "Create data directory /var/lib/people-chain", - "Run docker with --chain=people-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", - "Monitor sync with docker logs and system_syncState RPC", - "Verify with system_chain and system_health once currentBlock equals highestBlock" - ], - "result": "People Chain RPC node running, synced, serving requests on ws://localhost:9944" - }, - { - "scenario": "Edge case: node stalls at the same block for 30+ minutes", - "user_says": "The node has been stuck at block 12345 for 30 minutes", - "actions": [ - "Check both parachain and relay chain sync progress separately in the logs", - "Verify peers > 0 via system_health and that port 30333 is open", - "Restart: docker restart people-chain-rpc", - "If stalling persists after restart, switch to --sync=warp" - ], - "result": "Node resumes syncing after restart or sync mode change" - } - ] - }, - { - "id": "replay-dry-run-xcm-chopsticks", - "title": "Replay and Dry-Run XCMs Using Chopsticks", - "description": "Forks a Polkadot chain locally with Chopsticks to replay and dry-run historical XCM messages using their on-chain call data. Covers building a Wasm binary, starting a Chopsticks fork, fetching encoded call data from Subscan, and invoking the xcmPallet.execute dry-run RPC method. Use when debugging a failing XCM, previewing execution outcomes, or verifying fee coverage before submitting to a live network. Requires Rust and the Polkadot SDK build environment. Manual step: locating the call data hex on Subscan. Trigger phrases: 'replay XCM', 'dry-run XCM Chopsticks', 'debug XCM Polkadot', 'preview XCM execution', 'XCM dry run local fork'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" - ], - "primary_page": "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "prerequisites": { - "runtime": [ - "Rust toolchain with wasm32-unknown-unknown target (from install-polkadot-sdk skill or rustup)", - "Node.js v18+ and npm", - "npx (bundled with npm v5.2+)" - ], - "network": [ - "Internet connection to fetch chain state for Chopsticks fork", - "Subscan access to locate the historical XCM call data hex" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Create project directory and install Chopsticks", - "working_directory": ".", - "commands": [ - "mkdir xcm-chopsticks-debug && cd xcm-chopsticks-debug", - "npm init -y", - "npm install @acala-network/chopsticks" - ], - "description": "Create a working directory 'xcm-chopsticks-debug' and install Chopsticks. Chopsticks will fork the chain state locally to allow replaying the XCM without interacting with the live network." - }, - { - "order": 2, - "action": "Create Chopsticks configuration for the source chain", - "working_directory": "xcm-chopsticks-debug", - "description": "Create a file named 'chopsticks.yml' with the following content:\n\nendpoint: INSERT_CHAIN_WS_ENDPOINT\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n\nReplace INSERT_CHAIN_WS_ENDPOINT with the WebSocket endpoint of the chain where the XCM originated (e.g., wss://asset-hub-paseo.dotters.network for Polkadot Hub TestNet or wss://rpc.polkadot.io for Polkadot relay chain). The 'mock-signature-host: true' allows submitting calls without real keys. The 'db' field caches state so subsequent runs start faster." - }, - { - "order": 3, - "action": "Start the Chopsticks fork", - "working_directory": "xcm-chopsticks-debug", - "commands": [ - "npx @acala-network/chopsticks --config=chopsticks.yml" - ], - "expected_output": "Listening on port 8000", - "description": "Start Chopsticks in a dedicated terminal. It downloads chain state from the configured endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite cache. Keep this terminal open during all following steps." - }, - { - "order": 4, - "action": "Locate the XCM call data on Subscan", - "working_directory": ".", - "interactive": true, - "description": "This step is manual. Open Subscan for the source chain (e.g., https://polkadot.subscan.io or https://assethub-polkadot.subscan.io). Navigate to the extrinsic or event containing the XCM you want to replay. Find the encoded call data hex string — it typically appears in the 'Call Data' or 'Params' section of the extrinsic detail page. Copy the full hex string (starts with '0x'). You will use this in step 5. Paste it into a local file named 'call-data.txt' for reference." - }, - { - "order": 5, - "action": "Replay the XCM call against the local fork", - "working_directory": "xcm-chopsticks-debug", - "commands": [ - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"xcmPallet_dryRunCall\",\"params\":[\"INSERT_SENDER_ADDRESS\", \"INSERT_CALL_DATA_HEX\"]}' http://localhost:8000" - ], - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"executionResult\":{\"Ok\":{}},\"emittedEvents\":[],...},\"id\":1}", - "description": "Replace INSERT_SENDER_ADDRESS with the SS58 address that submitted the original XCM and INSERT_CALL_DATA_HEX with the hex string from Subscan (step 4). The response's executionResult indicates success or failure along with the failure reason if any. If the RPC method is not available on the forked chain, use 'xcmPallet_dryRunXcm' as an alternative — check the source page for the correct method name and parameter signature for your chain version. Chopsticks must be running (step 3) for this to work." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED ws://localhost:8000", - "cause": "Chopsticks is not running or not yet ready.", - "resolution": "Start Chopsticks first in a dedicated terminal: npx @acala-network/chopsticks --config=chopsticks.yml. Wait for 'Listening on port 8000' before proceeding." - }, - { - "pattern": "xcmPallet_dryRunCall method not found / Method not implemented", - "cause": "The forked chain runtime does not expose this specific dry-run RPC method, or uses a different method name.", - "resolution": "Check the source page for the correct RPC method name for your chain version. Try 'xcmPallet_dryRunXcm' or 'dryRun' alternatives. The method availability depends on the chain runtime version." - }, - { - "pattern": "executionResult: Err with 'BuyExecution failed' or 'TooExpensive'", - "cause": "The XCM message includes insufficient fees to cover execution on the destination chain.", - "resolution": "Increase the fee amount in the BuyExecution instruction of the XCM message before re-submitting. Check the estimate-xcm-fees-teleport skill for how to calculate required fees." - }, - { - "pattern": "Chopsticks fork diverges from expected state / 'Block not found'", - "cause": "The forked block is too old and the cached state (db.sqlite) is stale or missing.", - "resolution": "Delete db.sqlite and restart Chopsticks to re-fork from the current chain head. For replaying a specific historical block, add 'block: BLOCK_NUMBER_OR_HASH' to chopsticks.yml." - } - ], - "supplementary_context": { - "description": "Load these pages for XCM fee estimation, the ParaSpell transfer skill, or Chopsticks configuration reference.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", - "title": "XCM Fee Estimation", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "How to estimate XCM fees before submission — helps diagnose BuyExecution failures in a dry-run." - }, - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Full Chopsticks reference including all config options, block forking, and RPC override methods." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", - "title": "Transfer Assets Between Parachains", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "relevance": "How to submit a real XCM transfer between parachains — use after debugging with Chopsticks." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: debug a failing XCM transfer by replaying it locally", - "user_says": "An XCM transfer I submitted failed. How can I debug it without spending more tokens?", - "actions": [ - "Create 'xcm-chopsticks-debug/' and install Chopsticks", - "Create chopsticks.yml pointing to the source chain endpoint with mock-signature-host: true", - "Start Chopsticks in a dedicated terminal; wait for 'Listening on port 8000'", - "Find the failed extrinsic on Subscan and copy the encoded call data hex", - "Call xcmPallet_dryRunCall via curl against localhost:8000 with the sender address and call data hex", - "Inspect executionResult for the failure reason and adjust the XCM message accordingly" - ], - "result": "Dry-run reveals the failure reason (e.g., TooExpensive, Barrier blocked) without spending testnet tokens" - }, - { - "scenario": "Edge case: the dry-run method is not available on the forked chain", - "user_says": "The curl call returns 'method not found' for xcmPallet_dryRunCall", - "actions": [ - "Check the source page for alternative RPC method names for the chain version", - "Try 'xcmPallet_dryRunXcm' or consult the chain's RPC documentation", - "If no dry-run RPC is available, use Chopsticks dev_newBlock to mine a block with the call and inspect the emitted events" - ], - "result": "XCM replay succeeds using the correct RPC method for the chain runtime version" - } - ] - }, - { - "id": "estimate-xcm-fees-teleport", - "title": "Estimate XCM Fees for Asset Teleport", - "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required — Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" - ], - "primary_page": "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm", - "npx (bundled with npm v5.2+)" - ], - "network": [ - "Internet connection to fork chain state from Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network", - "Internet connection to fork People Chain TestNet state" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Create project directory and install dependencies", - "working_directory": ".", - "commands": [ - "mkdir xcm-fee-estimate && cd xcm-fee-estimate", - "npm init -y && npm pkg set type=module", - "npm install @acala-network/chopsticks polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Create 'xcm-fee-estimate' as an ESM Node.js project and install Chopsticks (for local chain forks) and polkadot-api (PAPI, for fee estimation calls)." - }, - { - "order": 2, - "action": "Create Chopsticks configs for source and destination chains", - "working_directory": "xcm-fee-estimate", - "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 — 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 — 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." - }, - { - "order": 3, - "action": "Start both Chopsticks forks", - "working_directory": "xcm-fee-estimate", - "commands": [ - "npx @acala-network/chopsticks --config=chopsticks-hub.yml", - "npx @acala-network/chopsticks --config=chopsticks-people.yml" - ], - "expected_output": "Listening on port 8000\nListening on port 8001", - "description": "Start each Chopsticks instance in a separate terminal. The first forks Polkadot Hub TestNet on port 8000; the second forks People Chain on port 8001. Both must be running before proceeding to the next step. The first run of each downloads several MB of state and caches it to the db files." - }, - { - "order": 4, - "action": "Generate PAPI type descriptors for both forks", - "working_directory": "xcm-fee-estimate", - "commands": [ - "npx papi add hubFork -w ws://localhost:8000", - "npx papi add peopleFork -w ws://localhost:8001" - ], - "description": "Run these commands in a third terminal while both Chopsticks forks (step 3) are running. Generates compile-time PAPI descriptors named 'hubFork' and 'peopleFork'. Both descriptors must be generated before writing the estimation script." - }, - { - "order": 5, - "action": "Create the fee estimation script", - "working_directory": "xcm-fee-estimate", - "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples — consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." - }, - { - "order": 6, - "action": "Run the fee estimation script", - "working_directory": "xcm-fee-estimate", - "commands": [ - "npx tsx estimate-xcm-fees.ts" - ], - "expected_output": "Local execution fee (planck): 1500000000000000\nDelivery fee assets: [...]\nRemote execution fee (planck): 500000000000000", - "description": "Execute the script while both Chopsticks forks are running. The output shows all three fee components in planck. Divide by 10^18 to convert to PAS. The sum of all three components is the total fee budget to include in the XCM BuyExecution instruction. If any XcmPaymentApi method is not available, check the source page for the correct API method names for your chain runtime version." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED ws://localhost:8000 or ws://localhost:8001", - "cause": "One or both Chopsticks forks are not running.", - "resolution": "Start each Chopsticks instance in a separate terminal. Run 'npx @acala-network/chopsticks --config=chopsticks-hub.yml' and 'npx @acala-network/chopsticks --config=chopsticks-people.yml'. Wait for 'Listening on port 8000/8001' before proceeding." - }, - { - "pattern": "XcmPaymentApi method not found / query_delivery_fees undefined", - "cause": "The chain runtime version does not implement the XcmPaymentApi runtime API at this method name.", - "resolution": "Check the source page for the correct API method names for your runtime version. The API surface changed between XCM V3 and V4. Verify the chain runtime version in the Chopsticks fork logs." - }, - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or hubFork/peopleFork undefined", - "cause": "PAPI descriptors were not generated for one or both forks.", - "resolution": "With both Chopsticks forks running, execute: npx papi add hubFork -w ws://localhost:8000 and npx papi add peopleFork -w ws://localhost:8001. Then re-run the script." - }, - { - "pattern": "Fee estimate returns 0 or undefined for remote execution fee", - "cause": "The XCM message structure passed to query_xcm_weight does not match the expected format for the chain runtime.", - "resolution": "Inspect the source page for the exact XCM instruction sequence expected by the destination chain. Verify that the MultiLocation structures use the correct XCM version (V3 vs V4) matching the chain runtime." - } - ], - "supplementary_context": { - "description": "Load these pages for XCM transfer execution, Chopsticks configuration, or debug techniques.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", - "title": "Replay and Dry Run XCMs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "How to dry-run a full XCM message after fee estimation to verify it executes correctly before submitting." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", - "title": "Transfer Assets Between Parachains", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "relevance": "How to submit the actual XCM teleport transfer once fees are estimated and verified." - }, - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Full Chopsticks reference including multi-chain fork setup, config options, and mock-signature details." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: calculate the total fee budget needed for a PAS teleport to People Chain", - "user_says": "How much PAS do I need to include in the BuyExecution for a teleport from Polkadot Hub to People Chain?", - "actions": [ - "Create 'xcm-fee-estimate/' as ESM Node.js project and install Chopsticks and polkadot-api", - "Create chopsticks-hub.yml (port 8000) and chopsticks-people.yml (port 8001) with mock-signature-host: true", - "Start both Chopsticks forks in separate terminals", - "Generate PAPI descriptors: npx papi add hubFork and npx papi add peopleFork", - "Create estimate-xcm-fees.ts with the inline script; substitute sender and recipient addresses", - "Run npx tsx estimate-xcm-fees.ts and sum the three fee components" - ], - "result": "All three fee components printed in planck; sum used as BuyExecution fee budget for the teleport" - }, - { - "scenario": "Edge case: remote execution fee query fails with XcmPaymentApi not found", - "user_says": "The script errors with 'query_xcm_weight is not a function' on the People Chain fork", - "actions": [ - "Check the People Chain runtime version in Chopsticks logs", - "Consult the source page for the XcmPaymentApi method names for that runtime version", - "Update the API call in estimate-xcm-fees.ts to match the correct method signature" - ], - "result": "Remote execution fee estimated using the correct XcmPaymentApi method for the chain runtime" - } - ] - }, - { - "id": "set-up-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "description": "Clones the Polkadot SDK Parachain Template, compiles it in release mode, and runs a local development node to verify the build and environment. Use as the first step in the parachain launch workflow before registering on Paseo testnet or a local relay chain. Requires the Polkadot SDK build environment from the install-polkadot-sdk skill. The template provides a minimal parachain runtime with Aura consensus, FRAME pallets, and a pre-configured Cumulus integration. Trigger phrases: 'parachain template', 'set up parachain Polkadot SDK', 'clone parachain template', 'build parachain template', 'Polkadot SDK parachain starter'. First step in the three-page parachain launch series.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/launch-a-parachain/set-up-the-parachain-template.md" - ], - "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", - "prerequisites": { - "runtime": [ - "Rust toolchain with wasm32-unknown-unknown target — complete the install-polkadot-sdk skill first", - "Git", - "Disk: at least 5 GB free for the build artifacts", - "RAM: 8 GB minimum; 16 GB recommended for parallel compilation" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK Parachain Template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git", - "cd polkadot-sdk-parachain-template" - ], - "description": "Clone the official Polkadot SDK Parachain Template repository. The template contains a minimal parachain runtime with Aura consensus, standard FRAME pallets, and Cumulus integration for relay chain connectivity. It includes both a node binary and a runtime Wasm blob." - }, - { - "order": 2, - "action": "Review the template directory structure", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup — the default configuration targets a local development network." - }, - { - "order": 3, - "action": "Build the parachain node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "expected_output": "Compiling parachain-template-node v0.1.0\n...\nFinished release [optimized] target(s)", - "description": "Compile the parachain node and runtime in release mode. This step is time-consuming (20-60 minutes on first build) because it compiles the entire Polkadot SDK dependency tree. Subsequent builds use the Rust incremental cache and are significantly faster. If compilation fails with 'linker not found', install the build-essential package (Linux) or Xcode command line tools (macOS). On low-memory machines (less than 8 GB RAM), reduce parallel jobs: CARGO_BUILD_JOBS=2 cargo build --release." - }, - { - "order": 4, - "action": "Verify the binary was produced", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node --version" - ], - "expected_output": "parachain-template-node 0.1.0-...", - "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully — review the cargo build output for compilation errors." - }, - { - "order": 5, - "action": "Run the node in local development mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node --dev" - ], - "expected_output": "Running in --dev mode\n...\nIdle (0 peers)", - "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain — the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "polkadot-docs/parachains/parachain-template", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0463]: can't find crate for 'std' / wasm32 target missing", - "cause": "The wasm32-unknown-unknown target is not installed in the Rust toolchain.", - "resolution": "Run: rustup target add wasm32-unknown-unknown. If using a specific toolchain version, prefix with rustup target add wasm32-unknown-unknown --toolchain stable." - }, - { - "pattern": "error: linking with 'cc' failed / linker not found", - "cause": "C/C++ build tools are not installed on the system.", - "resolution": "On Ubuntu/Debian: sudo apt install build-essential clang. On macOS: xcode-select --install. Then re-run cargo build --release." - }, - { - "pattern": "Killed / process killed during compilation / OOM", - "cause": "Insufficient RAM for parallel Rust compilation — cargo uses one thread per CPU core by default.", - "resolution": "Reduce parallel compilation jobs: CARGO_BUILD_JOBS=2 cargo build --release. Consider adding swap space on Linux: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile." - }, - { - "pattern": "error: package 'polkadot-sdk' not found / version mismatch in Cargo.lock", - "cause": "Cargo.lock is out of sync with the current Polkadot SDK release pinned in Cargo.toml.", - "resolution": "Run cargo update to refresh the lock file to the latest compatible versions, then retry cargo build --release." - } - ], - "supplementary_context": { - "description": "Load these pages for the prerequisite SDK install, the next steps in the parachain launch series, or runtime concepts.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Prerequisite: install Rust, system dependencies, and the Polkadot SDK build environment before building the template." - }, - { - "slug": "parachains-launch-a-parachain-deploy-to-polkadot", - "title": "Deploy on Polkadot", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/deploy-to-polkadot.md", - "relevance": "Next step in the parachain launch series: acquire a para ID and slot on Paseo testnet after the template is built." - }, - { - "slug": "reference-parachains-networks", - "title": "Networks", - "url": "https://docs.polkadot.com/reference/parachains/networks.md", - "relevance": "Network endpoints and chain IDs for Paseo testnet and Polkadot mainnet parachain environments." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: first-time parachain developer setting up the template", - "user_says": "How do I get the Polkadot SDK Parachain Template running on my machine?", - "actions": [ - "Confirm Rust toolchain with wasm32 target is installed (install-polkadot-sdk skill if not)", - "Clone polkadot-sdk-parachain-template from GitHub", - "Run cargo build --release in the cloned directory (allow 20-60 minutes)", - "Verify with ./target/release/parachain-template-node --version", - "Start in dev mode with ./target/release/parachain-template-node --dev to confirm it works" - ], - "result": "Template node built and running in dev mode; environment confirmed ready for the next parachain launch step" - }, - { - "scenario": "Edge case: build is killed due to out-of-memory on a low-spec machine", - "user_says": "The cargo build process gets killed partway through on my 4 GB RAM machine", - "actions": [ - "Set CARGO_BUILD_JOBS=2 to limit parallel compilation threads", - "Add at least 4 GB of swap space: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile", - "Re-run CARGO_BUILD_JOBS=2 cargo build --release" - ], - "result": "Build completes successfully with reduced memory pressure from limited parallel jobs" - } - ] - }, - { - "id": "run-polkadot-hub-rpc-node", - "title": "Run an RPC Node for Polkadot Hub", - "description": "Sets up a Polkadot Hub (Asset Hub) RPC node in archive or pruned mode via Docker or systemd, with an optional Ethereum-compatible JSON-RPC adapter (eth-rpc) for EVM tooling. Covers Docker image selection, binary installation, snapshot download for faster sync, systemd service configuration, and sync verification. Use when you need a self-hosted Polkadot Hub RPC endpoint for Substrate or Ethereum-compatible tools. Trigger phrases: 'run Polkadot Hub node', 'Asset Hub RPC node', 'Polkadot Hub Docker node', 'eth-rpc Polkadot Hub', 'Polkadot Hub archive node'. No tokens required.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "node-infrastructure/run-a-node/polkadot-hub-rpc.md" - ], - "primary_page": "node-infrastructure/run-a-node/polkadot-hub-rpc.md", - "prerequisites": { - "runtime": [ - "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", - "Disk: minimum 500 GB SSD for pruned node; 2+ TB SSD for archive node", - "RAM: minimum 8 GB; 16 GB recommended for archive nodes", - "CPU: 4+ cores recommended" - ], - "network": [ - "Open TCP ports: 30333 (P2P), 9944 (WebSocket RPC), 9615 (Prometheus metrics, optional), 8545 (Ethereum RPC if using eth-rpc adapter)", - "Stable broadband connection for initial sync (several hundred GB download)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Choose deployment method and sync type", - "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync — check https://snapshots.polkadot.io or community providers for current snapshots." - }, - { - "order": 2, - "action": "Run the Polkadot Hub node via Docker", - "working_directory": ".", - "commands": [ - "docker pull parity/polkadot-parachain:stable", - "mkdir -p /var/lib/polkadot-hub", - "docker run -d --name polkadot-hub-rpc --restart unless-stopped -p 30333:30333 -p 9944:9944 -p 9615:9615 -v /var/lib/polkadot-hub:/data parity/polkadot-parachain:stable --chain=asset-hub-polkadot --base-path=/data --rpc-external --rpc-port=9944 --rpc-cors=all --prometheus-external --prometheus-port=9615 --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast" - ], - "description": "Pull the stable polkadot-parachain image and start the Polkadot Hub RPC node. Key arguments: --chain=asset-hub-polkadot selects the Polkadot Hub spec; --base-path=/data stores chain data in the mounted volume; --state-pruning=1000 --blocks-pruning=1000 keeps the last 1000 blocks (change both to 'archive' for a full archive node); --rpc-external exposes the WebSocket RPC on 9944; the -- separator and --chain=polkadot configure the embedded relay chain client. Replace /var/lib/polkadot-hub with your data directory. For Paseo TestNet, use --chain=asset-hub-paseo and --chain=paseo after the -- separator." - }, - { - "order": 3, - "action": "Run the Polkadot Hub node via systemd (binary method alternative)", - "working_directory": ".", - "description": "For the systemd binary method (skip if using Docker in step 2): (1) Create system user: sudo useradd --system --no-create-home --shell /usr/sbin/nologin polkadot. (2) Create data dir: sudo mkdir -p /var/lib/polkadot-hub && sudo chown polkadot:polkadot /var/lib/polkadot-hub. (3) Download polkadot-parachain binary from https://github.com/paritytech/polkadot-sdk/releases (stable, amd64 or arm64) and install: sudo install -m 755 polkadot-parachain /usr/local/bin/. (4) Create /etc/systemd/system/polkadot-hub-rpc.service:\n\n[Unit]\nDescription=Polkadot Hub RPC Node\nAfter=network.target\n\n[Service]\nUser=polkadot\nExecStart=/usr/local/bin/polkadot-parachain --chain=asset-hub-polkadot --base-path=/var/lib/polkadot-hub --rpc-external --rpc-port=9944 --rpc-cors=all --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n\n(5) Enable and start: sudo systemctl daemon-reload && sudo systemctl enable polkadot-hub-rpc && sudo systemctl start polkadot-hub-rpc. For archive mode, omit --state-pruning and --blocks-pruning flags." - }, - { - "order": 4, - "action": "Optionally install the Ethereum RPC adapter (eth-rpc)", - "working_directory": ".", - "commands": [ - "docker pull paritytech/frontier-node:latest", - "docker run -d --name polkadot-hub-eth-rpc --restart unless-stopped -p 8545:8545 --network host paritytech/frontier-node:latest --chain=asset-hub-polkadot --endpoint=ws://localhost:9944 --listen-addr=0.0.0.0:8545" - ], - "description": "This step is optional. The Ethereum RPC adapter (eth-rpc / Frontier) exposes an Ethereum-compatible JSON-RPC endpoint on port 8545, allowing Ethereum tooling (MetaMask, ethers.js, Hardhat) to connect to Polkadot Hub. The adapter proxies Ethereum RPC calls to the Substrate node. Run it after the main node (step 2 or 3) is fully synced and serving on ws://localhost:9944. For Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo. Skip this step if you only need the native Substrate WebSocket RPC endpoint." - }, - { - "order": 5, - "action": "Monitor sync progress", - "working_directory": ".", - "commands": [ - "docker logs -f polkadot-hub-rpc 2>&1 | grep -E 'Syncing|Idle|Best|Peers'", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." - }, - { - "order": 6, - "action": "Verify the RPC endpoint is operational", - "working_directory": ".", - "commands": [ - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_chain\",\"params\":[]}' http://localhost:9944", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"result\":\"Polkadot Asset Hub\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", - "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 — should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: No space left on device / database write error", - "cause": "The data volume has run out of disk space.", - "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=1000). Check usage with: du -sh /var/lib/polkadot-hub." - }, - { - "pattern": "EADDRINUSE port 9944 or 30333", - "cause": "Another process is already using the required ports.", - "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." - }, - { - "pattern": "Peers count stays at 0 after several minutes", - "cause": "P2P port 30333 is blocked by firewall or the bootnodes are temporarily unavailable.", - "resolution": "Open TCP port 30333: sudo ufw allow 30333/tcp. For cloud VMs, update the security group. If peers still do not connect after 10 minutes, check the Polkadot network status." - }, - { - "pattern": "eth_chainId returns unexpected value or eth-rpc adapter cannot connect to node", - "cause": "The eth-rpc adapter endpoint does not match the running node's WebSocket address, or the node is not yet fully synced.", - "resolution": "Verify the node is fully synced (step 5). Ensure the --endpoint flag for the eth-rpc adapter points to ws://localhost:9944 and the node's --rpc-external flag is active. Restart the eth-rpc adapter after the node is synced." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to add TLS/WSS, run a parachain RPC node, or understand the chain configuration.", - "pages": [ - { - "slug": "node-infrastructure-run-a-node-parachain-rpc", - "title": "Run a Parachain RPC Node", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/parachain-rpc.md", - "relevance": "How to run RPC nodes for other system parachains (People Chain, Collectives, BridgeHub) using the same polkadot-parachain binary." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", - "title": "Set Up Secure WebSocket", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-full-node", - "title": "Set Up a Node", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md", - "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run a pruned Polkadot Hub RPC node via Docker", - "user_says": "Run a Polkadot Hub RPC node using Docker", - "actions": [ - "Pull parity/polkadot-parachain:stable", - "Create data directory /var/lib/polkadot-hub", - "Run docker with --chain=asset-hub-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", - "Monitor sync with docker logs and system_syncState RPC", - "Verify with system_chain and system_health once currentBlock equals highestBlock" - ], - "result": "Polkadot Hub RPC node running, synced, serving requests on ws://localhost:9944" - }, - { - "scenario": "Edge case: Ethereum tooling cannot connect — eth_chainId returns wrong value", - "user_says": "MetaMask connects to port 8545 but shows the wrong chain ID", - "actions": [ - "Verify the eth-rpc adapter is configured with the correct --chain flag matching the running node", - "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) — confirm eth_chainId returns this value", - "If chain ID is wrong, restart the eth-rpc adapter with --chain=asset-hub-paseo for TestNet or --chain=asset-hub-polkadot for mainnet", - "Update MetaMask network settings with the correct chain ID and RPC URL http://localhost:8545" - ], - "result": "MetaMask connects successfully with the correct Polkadot Hub chain ID and eth-rpc adapter" - } - ] - }, - { - "id": "set-up-polkadot-validator-node", - "title": "Set Up a Polkadot Validator Node", - "description": "Installs the three Polkadot validator binaries (polkadot, polkadot-prepare-worker, polkadot-execute-worker) needed to run a validator node, and prepares the server with NTP time synchronization and Landlock security. Covers four installation paths: GPG-verified curl download, APT package manager (Ubuntu/Debian), Docker image, or building from source. Use when provisioning a new validator server or upgrading binaries on an existing one. Trigger phrases: 'install Polkadot validator binaries', 'set up validator node', 'install polkadot-prepare-worker', 'configure validator server'. First step in the full validator onboarding flow before key management and bonding.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" - ], - "primary_page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", - "prerequisites": { - "runtime": [ - "Linux server with kernel 5.16 or later (Ubuntu 22.04 LTS or newer recommended)", - "64-bit x86-64 or ARM64 architecture", - "Root or sudo access" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Verify or install NTP time synchronization", - "working_directory": ".", - "commands": [ - "timedatectl" - ], - "expected_output": "System clock synchronized: yes", - "description": "Validators need accurate system time to avoid missing block authorship. Check NTP status with 'timedatectl'. If the output does not show 'System clock synchronized: yes', install and start NTP:\n\n sudo apt-get install ntp\n sudo ntpq -p\n\nThe 'ntpq -p' output should list one or more active peers with a '+' or '*' status prefix. Skipping NTP can cause the validator to miss blocks due to minor clock drift." - }, - { - "order": 2, - "action": "Verify Landlock security is activated", - "working_directory": ".", - "commands": [ - "dmesg | grep landlock || journalctl -kg landlock" - ], - "description": "Landlock (Linux kernel 5.13+) is required by the Polkadot validator for sandboxing. Run the command above as root. If Landlock is active, you will see kernel log entries referencing 'landlock'. If there is no output, your kernel does not have Landlock enabled. Most modern Ubuntu/Debian kernels (5.16+) include it by default. If Landlock is absent, upgrade to a supported kernel or rebuild with Landlock enabled (see https://docs.kernel.org/userspace-api/landlock.html#kernel-support). Polkadot validators can run without Landlock but will operate without the sandboxing protection." - }, - { - "order": 3, - "action": "Install the Polkadot binaries via curl with GPG verification (recommended method)", - "working_directory": ".", - "commands": [ - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot.asc", - "gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", - "gpg --verify polkadot.asc", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker.asc", - "gpg --verify polkadot-prepare-worker.asc", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker.asc", - "gpg --verify polkadot-execute-worker.asc", - "chmod +x polkadot polkadot-prepare-worker polkadot-execute-worker", - "sudo mv polkadot polkadot-prepare-worker polkadot-execute-worker /usr/local/bin/" - ], - "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 — check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE — APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE — Docker:\n docker pull parity/polkadot:stable2512-2" - }, - { - "order": 4, - "action": "Verify all three binaries are installed and versions match", - "working_directory": ".", - "commands": [ - "polkadot --version", - "polkadot-execute-worker --version", - "polkadot-prepare-worker --version" - ], - "expected_output": "All three commands print the same version string, e.g. 'polkadot stable2512-2-...'", - "description": "Run all three version commands and confirm the version strings are identical. If one binary is missing ('command not found'), ensure it was moved to /usr/local/bin/ in step 3 and that /usr/local/bin is in $PATH (echo $PATH). All three binaries must be in the same directory for the validator to function. If versions differ, re-download the mismatched binary from the same release tag used in step 3." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "gpg: BAD signature", - "cause": "The downloaded binary was corrupted or tampered with during download.", - "resolution": "Delete all downloaded files and repeat step 3 from a trusted network connection. Do not use any binary that fails GPG verification." - }, - { - "pattern": "polkadot: command not found after installation", - "cause": "The binary was not moved to a directory in $PATH, or the directory is not in $PATH.", - "resolution": "Run 'echo $PATH' and verify /usr/local/bin is listed. If not, run 'export PATH=$PATH:/usr/local/bin'. For persistence, add this export to /etc/environment or ~/.bashrc." - }, - { - "pattern": "version mismatch between polkadot and worker binaries", - "cause": "The three binaries were downloaded from different releases.", - "resolution": "Remove all three binaries and re-download all from the same release tag (e.g., polkadot-stable2512-2) from https://github.com/paritytech/polkadot-sdk/releases. All three must share the same version to interoperate." - }, - { - "pattern": "Landlock not in dmesg output", - "cause": "The running kernel is older than 5.13 or was compiled without Landlock.", - "resolution": "Upgrade the kernel: on Ubuntu, run 'sudo apt-get install linux-generic-hwe-22.04' then reboot. Verify with 'uname -r' that the new kernel is 5.16 or later. Alternatively, consult https://docs.kernel.org/userspace-api/landlock.html#kernel-support." - } - ], - "supplementary_context": { - "description": "Load these pages for key management, starting validation, or understanding validator requirements and operational tasks.", - "pages": [ - { - "slug": "node-infrastructure-run-a-validator-requirements", - "title": "Validator Requirements", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/requirements.md", - "relevance": "Minimum hardware and skill requirements before starting a validator — read before this skill." - }, - { - "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-key-management", - "title": "Validator Key Management", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md", - "relevance": "Next step after binary installation: generating and registering session keys." - }, - { - "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-start-validating", - "title": "Start Validating", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md", - "relevance": "Syncing the node, bonding DOT, and activating the validator after binary setup." - }, - { - "slug": "node-infrastructure-run-a-validator-operational-tasks-general-management", - "title": "General Management", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/operational-tasks/general-management.md", - "relevance": "Prometheus and Grafana monitoring stack setup and security best practices for running validators." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: install on a fresh Ubuntu 22.04 server", - "user_says": "Install the Polkadot validator binaries on a new Ubuntu server", - "actions": [ - "Run 'timedatectl' to check NTP sync; install ntp if not synced", - "Run 'dmesg | grep landlock' to verify Landlock is active", - "Download polkadot, polkadot-prepare-worker, and polkadot-execute-worker from release polkadot-stable2512-2", - "Verify each binary with GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", - "Move all three to /usr/local/bin and run --version on each to confirm matching versions" - ], - "result": "All three binaries installed at polkadot-stable2512-2, GPG verified, and accessible system-wide; ready for key management" - }, - { - "scenario": "Edge case: APT install on a Debian-based server without curl", - "user_says": "Install Polkadot on Ubuntu using apt instead of downloading manually", - "actions": [ - "Import the Parity GPG key into /usr/share/keyrings/parity.gpg", - "Add the Parity APT repository to /etc/apt/sources.list.d/parity.list", - "Run 'apt update && apt install parity-keyring polkadot'", - "Verify with 'polkadot --version', 'polkadot-execute-worker --version', 'polkadot-prepare-worker --version'" - ], - "result": "Polkadot and both worker binaries installed via APT with automatic future upgrades via apt upgrade" - } - ] - }, - { - "id": "set-up-chopsticks-fork", - "title": "Set Up and Use Chopsticks for Chain Forking", - "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/chopsticks.md" - ], - "primary_page": "reference/tools/chopsticks.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm (included with Node.js) or yarn or pnpm" - ], - "network": [ - "Access to a WebSocket RPC endpoint for the chain to fork (e.g., wss://polkadot-rpc.dwellir.com)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Chopsticks globally", - "working_directory": ".", - "commands": [ - "npm i -g @acala-network/chopsticks@1.2.7" - ], - "expected_output": "added N packages", - "description": "Install Chopsticks 1.2.7 globally. After installation, 'chopsticks' is available system-wide. For a project-local installation instead: mkdir my-chopsticks-project && cd my-chopsticks-project && npm init -y && npm i @acala-network/chopsticks@1.2.7. With local installation, run via 'npx @acala-network/chopsticks' instead of 'chopsticks' in subsequent steps." - }, - { - "order": 2, - "action": "Create a fork configuration file", - "working_directory": ".", - "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs — download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts — useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." - }, - { - "order": 3, - "action": "Start the chain fork", - "working_directory": ".", - "commands": [ - "npx @acala-network/chopsticks --config=polkadot.yml" - ], - "expected_output": "Running on port 8000", - "description": "Start the fork using the YAML config from step 2. Alternatively, fork without a config file using CLI flags:\n npx @acala-network/chopsticks --endpoint wss://polkadot-rpc.dwellir.com --block 100\n\nChoose the block number to fork from with --block (omit to use the chain head). Chopsticks caches state locally in an SQLite DB (set --db ./chopsticks.db to reuse between runs). When the fork is ready, you will see 'Running on port 8000'. The local fork is now accessible at ws://localhost:8000." - }, - { - "order": 4, - "action": "Interact with the fork", - "working_directory": ".", - "description": "Connect to the running fork at ws://localhost:8000.\n\n```typescript\nVia Polkadot.js API (TypeScript):\n import { ApiPromise, WsProvider } from '@polkadot/api';\n const api = await ApiPromise.create({ provider: new WsProvider('ws://localhost:8000') });\n const block = await api.rpc.chain.getBlock();\n console.log('Fork head:', block.block.header.number.toNumber());\n```\n\nVia Polkadot.js Apps UI: navigate to polkadot.js.org/apps, select network > Development > Custom, enter ws://localhost:8000, and click Switch.\n\nTo manipulate storage via the dev_setStorage WebSocket command (using wscat or a JS client):\n { \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"dev_setStorage\", \"params\": [{ \"System\": { \"Account\": [[\"0xALICE_KEY\", { \"data\": { \"free\": 1000000000000 } }]] } }] }" - }, - { - "order": 5, - "action": "Replay a historical block (optional)", - "working_directory": ".", - "commands": [ - "npx @acala-network/chopsticks run-block --endpoint wss://polkadot-rpc.dwellir.com --output-path ./block-1000-output.json --block 1000" - ], - "description": "Replay block 1000 from Polkadot and save the state diff to a JSON file. Replace the endpoint and block number as needed. The output includes detailed storage changes and runtime logs. Add --html to generate an HTML visualization of the storage diff. This is useful for debugging why a specific transaction produced unexpected state changes." - }, - { - "order": 6, - "action": "Test XCM between multiple chains (optional)", - "working_directory": ".", - "commands": [ - "npx @acala-network/chopsticks xcm --r polkadot --p moonbeam --p astar" - ], - "description": "Fork multiple chains simultaneously to test XCM message flow. The --r flag specifies the relay chain config (by name or file path) and --p specifies parachain configs. After startup, each chain runs on a different port (printed in the output). Send XCM messages between the forked chains using the Polkadot.js API or dev_* WebSocket commands. Note: XCM testing requires config files for each chain; use pre-built configs from https://github.com/AcalaNetwork/chopsticks/tree/master/configs." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Cannot connect to endpoint / WebSocket connection failed", - "cause": "The RPC endpoint URL is incorrect, the chain is temporarily unavailable, or the WSS endpoint requires authentication.", - "resolution": "Verify the endpoint URL by connecting with wscat: wscat -c wss://your-endpoint. Try a public endpoint such as wss://polkadot-rpc.dwellir.com for Polkadot. Check the chain's status page or Discord for outages." - }, - { - "pattern": "chopsticks: command not found", - "cause": "Global npm installation did not add the binary to PATH, or Chopsticks was installed locally without npx.", - "resolution": "For global installs, run 'npm root -g' to find the global bin directory and ensure it is in $PATH. For local installs, use 'npx @acala-network/chopsticks' instead of 'chopsticks'." - }, - { - "pattern": "MetaMask / Ethereum tooling cannot connect to the fork", - "cause": "Chopsticks uses the Smoldot light client which only supports the native Polkadot SDK API, not Ethereum JSON-RPC.", - "resolution": "This is a known limitation. Chopsticks forks cannot be used with MetaMask or ethers.js. For EVM-compatible local testing, use a local Polkadot Hub development node instead." - }, - { - "pattern": "Error replaying block: state root mismatch", - "cause": "The local fork has diverged from the live chain state, often because storage was manually modified before replay.", - "resolution": "Restart Chopsticks without --db to use a fresh state (or delete the SQLite db file). Avoid modifying storage with dev_setStorage before running run-block." - } - ], - "supplementary_context": { - "description": "Load these pages for parachain-specific fork tutorials, XCM debugging, or the fork-a-parachain cookbook tutorial.", - "pages": [ - { - "slug": "parachains-testing-fork-a-parachain", - "title": "Fork a Parachain Using Chopsticks", - "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", - "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks — same tool, focused on the parachain testing workflow with a CI-backed example." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", - "title": "Replay and Dry Run XCMs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "Using Chopsticks to replay and dry-run XCM messages — advanced XCM debugging workflow." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", - "title": "XCM Fee Estimation", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "Estimating XCM fees for teleports using Chopsticks local forks of Polkadot Hub and People Chain." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: fork Polkadot mainnet and inspect state", - "user_says": "Fork the Polkadot mainnet locally with Chopsticks so I can test transactions", - "actions": [ - "Install Chopsticks 1.2.7 globally: npm i -g @acala-network/chopsticks@1.2.7", - "Download the Polkadot config from the Chopsticks repo: curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml", - "Start the fork: npx @acala-network/chopsticks --config=polkadot.yml", - "Wait for 'Running on port 8000', then connect via Polkadot.js API at ws://localhost:8000" - ], - "result": "Local Polkadot fork running at ws://localhost:8000; ready for transaction testing without spending real tokens" - }, - { - "scenario": "Edge case: fork requires a funded test account", - "user_says": "I need Alice to have tokens on my forked chain for testing", - "actions": [ - "Add an import-storage section to the YAML config overriding Alice's System.Account free balance to a large value", - "Restart the fork with the updated config", - "Alternatively, send a dev_setStorage WebSocket command after the fork is running to set Alice's balance" - ], - "result": "Alice's account has the specified token balance on the local fork; transactions that require funds now succeed" - } - ] - }, - { - "id": "set-up-e2e-testing-moonwall", - "title": "Set Up End-to-End Testing with Moonwall", - "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain — local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/moonwall.md" - ], - "primary_page": "reference/tools/moonwall.md", - "prerequisites": { - "runtime": [ - "Node.js v20.10 or higher", - "npm, yarn, or pnpm" - ], - "network": [ - "A running Polkadot SDK node or Chopsticks fork for the test environment (e.g., ws://localhost:9944 or ws://localhost:8000)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Moonwall globally", - "working_directory": ".", - "commands": [ - "npm install -g @moonwall/cli@5.18.3" - ], - "expected_output": "added N packages", - "description": "Install Moonwall 5.18.3 globally so the 'moonwall' command is available system-wide. For a project-local installation: mkdir my-moonwall-project && cd my-moonwall-project && npm init -y && npm install @moonwall/cli@5.18.3. With a local install, run Moonwall via 'npx moonwall' or add it to package.json scripts." - }, - { - "order": 2, - "action": "Initialize the Moonwall configuration via the interactive wizard", - "working_directory": ".", - "commands": [ - "moonwall init" - ], - "interactive": true, - "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user — do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." - }, - { - "order": 3, - "action": "Review and customize moonwall.config.json", - "working_directory": ".", - "description": "Open the generated moonwall.config file and customize the environment settings. Key sections to update:\n\n - foundation.type: 'dev' (local binary), 'chopsticks' (fork), or 'read_only' (remote RPC)\n - For 'dev' foundation, set binPath to the path of your chain binary\n - connections[0].endpoint: the WebSocket URL of the node (e.g., ws://localhost:9944)\n - connections[0].type: the provider type (e.g., 'polkadotJs' for Polkadot.js API)\n\nExample configuration for testing a local dev node:\n {\n \"label\": \"my-chain-tests\",\n \"timeout\": 30000,\n \"environments\": [{\n \"name\": \"default_env\",\n \"foundation\": { \"type\": \"dev\", \"launchSpec\": [{ \"name\": \"node\", \"binPath\": \"./your-node-binary\", \"options\": [\"--dev\"] }] },\n \"testFileDir\": [\"./tests\"],\n \"connections\": [{ \"name\": \"api\", \"type\": \"polkadotJs\", \"endpoints\": [\"ws://localhost:9944\"] }]\n }]\n }" - }, - { - "order": 4, - "action": "Write a test suite", - "working_directory": ".", - "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run — do not create API connections manually." - }, - { - "order": 5, - "action": "Run the tests", - "working_directory": ".", - "commands": [ - "moonwall test default_env -c moonwall.config" - ], - "expected_output": "All test suites passed", - "description": "Run the tests against the 'default_env' environment defined in moonwall.config. Replace 'default_env' with the environment name you chose in step 2. The test runner outputs:\n - Test suite execution status (pass/fail per suite)\n - Individual test case results\n - Execution time per test\n - Detailed error logs for failed tests\n\nIf the foundation type is 'dev', Moonwall launches the node binary automatically and tears it down after tests complete. If using 'read_only' or 'chopsticks', ensure the endpoint is reachable before running tests." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "moonwall: command not found", - "cause": "Global npm installation did not add the binary to PATH.", - "resolution": "Run 'npm root -g' to find the global bin directory and verify it is in $PATH. Alternatively, use 'npx moonwall' for a local installation." - }, - { - "pattern": "Cannot find module '@moonwall/cli'", - "cause": "Moonwall was not installed or the local installation is missing.", - "resolution": "Run 'npm install @moonwall/cli@5.18.3' in the project directory. For global, run 'npm install -g @moonwall/cli@5.18.3'." - }, - { - "pattern": "Error: Cannot connect to endpoint ws://localhost:9944", - "cause": "The node specified in the moonwall.config foundation is not running, or the endpoint URL is incorrect.", - "resolution": "Start the node manually and verify it is listening on the configured port before running Moonwall. For 'dev' foundation, ensure the binPath points to an executable binary and the --dev flag is appropriate for your chain." - }, - { - "pattern": "Timeout exceeded for test suite", - "cause": "The test environment took too long to start or a test operation exceeded the global timeout.", - "resolution": "Increase the 'timeout' value in moonwall.config (e.g., from 30000 to 60000 ms). For slow chain operations like block finalization, add explicit wait-for-block assertions rather than fixed sleeps." - } - ], - "supplementary_context": { - "description": "Load these pages for Chopsticks fork setup (to use as Moonwall's test environment) or for a local development node to test against.", - "pages": [ - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Setting up a Chopsticks fork as the 'chopsticks' foundation type for Moonwall tests." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "How to run a local development node to use as the 'dev' foundation in Moonwall." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run e2e tests against a local dev node", - "user_says": "Set up Moonwall to run e2e tests for my Polkadot SDK chain", - "actions": [ - "Install Moonwall 5.18.3 globally: npm install -g @moonwall/cli@5.18.3", - "Ask the user to run 'moonwall init' and complete the interactive wizard, choosing 'dev' foundation", - "Update moonwall.config with the correct node binPath and ws://localhost:9944 connection", - "Create a test file in ./tests using describeSuite with a foundationMethods of 'dev'", - "Run 'moonwall test default_env -c moonwall.config'" - ], - "result": "Moonwall launches the node binary, runs all test suites, and reports pass/fail results per test case" - }, - { - "scenario": "Edge case: test environment fails to start", - "user_says": "Moonwall says it cannot connect to the node endpoint", - "actions": [ - "Check the foundation type in moonwall.config — for 'dev', verify binPath is correct and the binary has execute permission", - "For 'read_only' or 'chopsticks', start the node or Chopsticks fork manually and confirm it is listening on the configured endpoint", - "Increase the connection timeout in moonwall.config if the node starts slowly" - ], - "result": "Node is reachable; Moonwall connects and runs tests" - } - ] - }, - { - "id": "build-dapp-viem-nextjs", - "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js", - "description": "Scaffolds a full-stack Polkadot Hub dApp: deploys a Solidity Storage contract via Hardhat Ignition to TestNet, then builds a Next.js 14 frontend with Viem wallet connect, read, and write components. Use when building a complete end-to-end smart contract application on Polkadot Hub from scratch. Trigger phrases: 'build a dapp polkadot', 'zero to hero tutorial', 'smart contract with frontend viem', 'deploy and connect next.js'. Covers dotenv private key security, 5000 gwei gas config, evmVersion cancun, Ignition recovery, and Next.js/Viem component wiring. Output: running dApp at localhost:3000.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/dapps/zero-to-hero.md" - ], - "primary_page": "smart-contracts/cookbook/dapps/zero-to-hero.md", - "prerequisites": { - "runtime": [ - "Node.js v22+", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for contract deployment gas" - ], - "wallet": [ - "MetaMask configured for Polkadot Hub TestNet (chainId 420420417)", - "0x-prefixed EVM private key for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Clone the reference repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git" - ], - "description": "Clone revm-hardhat-examples. This repo contains the complete zero-to-hero-dapp with both the Hardhat contract project and the Next.js frontend." - }, - { - "order": 2, - "action": "Install contract project dependencies", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install Hardhat and contract dependencies, then add dotenv. dotenv replaces Hardhat's interactive vars system, which cannot be used in agent shells." - }, - { - "order": 3, - "action": "Create .env file and protect it with .gitignore", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly, filling in PRIVATE_KEY=0x. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 4, - "action": "Update hardhat.config.ts for TestNet and security", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file." - }, - { - "order": 5, - "action": "Compile the Storage contract", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile Storage.sol. If you see 'invalid opcode: MCOPY', verify evmVersion: 'cancun' is set in the Solidity compiler settings in hardhat.config.ts and recompile." - }, - { - "order": 6, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" - ], - "expected_output": "Storage deployed to: 0x...", - "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." - }, - { - "order": 7, - "action": "Scaffold the Next.js frontend", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp", - "commands": [ - "echo 'n\\nn\\nn\\nn\\nn\\nn' | npx create-next-app@14 frontend --typescript --no-eslint --no-tailwind --no-src-dir --app" - ], - "description": "Scaffold Next.js 14 TypeScript app named 'frontend'. Piped 'n' answers handle any unexpected interactive prompts from create-next-app. If this fails due to prompt changes, run: npx create-next-app@14 frontend and answer No to all questions except TypeScript (Yes) and App Router (Yes)." - }, - { - "order": 8, - "action": "Install frontend dependencies", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", - "commands": [ - "npm install viem wagmi @tanstack/react-query" - ], - "description": "Install viem for contract interaction, wagmi for React hooks, and React Query (required by wagmi v2)." - }, - { - "order": 9, - "action": "Update contract address in contract config", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", - "description": "Open src/contracts/contract.ts (or the equivalent contract config file in the frontend). Replace the CONTRACT_ADDRESS placeholder or any hardcoded example address with the deployed contract address saved in step 6. Save the file." - }, - { - "order": 10, - "action": "Start the development server", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", - "commands": [ - "npm run dev" - ], - "expected_output": "ready - started server on 0.0.0.0:3000", - "description": "Start the Next.js dev server. Open http://localhost:3000 in a browser. Click 'Connect Wallet' and approve the MetaMask popup. Read the current stored value, then write a new value and confirm the MetaMask transaction. Verify the displayed value updates after the transaction is mined." - } - ], - "reference_code": { - "repo": "polkadot-developers/revm-hardhat-examples", - "branch": "master", - "base_path": "zero-to-hero-dapp", - "files": [] - }, - "error_patterns": [ - { - "pattern": "priority is too low", - "cause": "Transaction gas price is below the 1000 gwei base fee on Polkadot Hub TestNet. Default Hardhat gas estimation produces values too low for this network.", - "resolution": "Set gasPrice: 5000000000000 in the polkadotTestnet network block in hardhat.config.ts. Delete ignition/deployments/ if a prior attempt was made, then redeploy." - }, - { - "pattern": "IGN401 / Transaction dropped", - "cause": "Ignition lost track of the transaction. The transaction may have succeeded or may be stuck in the mempool.", - "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded — use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." - }, - { - "pattern": "invalid opcode: MCOPY", - "cause": "OpenZeppelin v5.4.0+ uses the mcopy opcode (Cancun EVM upgrade). Solidity defaults to an older EVM version.", - "resolution": "Add evmVersion: 'cancun' to the Solidity compiler settings in hardhat.config.ts. Recompile with 'npx hardhat compile'." - }, - { - "pattern": "Error: could not detect network", - "cause": "Incorrect RPC URL or chain ID in hardhat.config.ts.", - "resolution": "Set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417 in the polkadotTestnet network block." - }, - { - "pattern": "Module not found: viem or wagmi", - "cause": "Frontend dependencies were not installed.", - "resolution": "Run 'npm install viem wagmi @tanstack/react-query' in the zero-to-hero-dapp/frontend directory." - } - ], - "supplementary_context": { - "description": "Load these pages for Hardhat configuration reference, Viem library details, or network connection parameters.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Full Hardhat EVM configuration options for Polkadot Hub, including network settings and contract verification." - }, - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Viem library reference for chain config, wallet client, and contract interaction patterns on Polkadot Hub." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters (RPC URLs, chain IDs, WSS) for Polkadot Hub TestNet and Mainnet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: full dApp from scratch", - "user_says": "Build a smart contract dApp on Polkadot Hub with Viem and Next.js", - "actions": [ - "Clone revm-hardhat-examples and navigate to zero-to-hero-dapp/storage-contract", - "Install dependencies and create .env with PRIVATE_KEY (user fills in)", - "Update hardhat.config.ts: dotenv, gasPrice 5000 gwei, evmVersion cancun, requiredConfirmations 1", - "Compile Storage.sol and deploy to polkadotTestnet via Ignition", - "Scaffold Next.js 14 frontend with create-next-app@14", - "Install viem, wagmi, react-query; update contract.ts with deployed address", - "Run npm run dev and test at localhost:3000" - ], - "result": "Storage contract deployed to TestNet; Next.js app at localhost:3000 with working wallet connect, read, and write contract interactions via MetaMask" - }, - { - "scenario": "Edge case: Ignition reports transaction dropped but contract may have deployed", - "user_says": "Ignition says IGN401 but I don't know if the contract deployed", - "actions": [ - "Check ignition/deployments//deployed_addresses.json for a Storage address", - "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", - "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" - ], - "result": "Contract address recovered from deployment state or clean redeployment completed" - } - ] - }, - { - "id": "deploy-erc20-token-remix", - "title": "Deploy an ERC-20 Token Using Remix IDE", - "description": "Deploys a Solidity ERC-20 token contract to Polkadot Hub TestNet entirely through the Remix browser IDE with MetaMask wallet injection. Use when you prefer a no-CLI workflow or are demonstrating contract deployment to non-developers. Trigger phrases: 'deploy ERC-20 remix', 'erc20 token remix polkadot', 'deploy token browser IDE'. Covers fetching contract code from revm-hardhat-examples, Remix compile settings (evmVersion cancun for OpenZeppelin v5), MetaMask TestNet configuration, and minting tokens via the Remix UI. No local environment required beyond a browser and MetaMask.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md", - "prerequisites": { - "runtime": [ - "A modern web browser (Chrome or Firefox recommended)" - ], - "network": [ - "Polkadot Hub TestNet (chainId 420420417) — configured in MetaMask" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required to pay deployment gas" - ], - "wallet": [ - "MetaMask browser extension installed and unlocked", - "An account funded with testnet PAS on Polkadot Hub TestNet (chainId 420420417, RPC: https://services.polkadothub-rpc.com/testnet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Fetch the ERC-20 contract source", - "working_directory": ".", - "description": "Fetch the OpenZeppelin ERC-20 contract source from the reference repository. The contract file is at: https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol\n\nRun: curl -sL https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol and display the contract source to the user. The user will paste this into Remix in the next step." - }, - { - "order": 2, - "action": "Open Remix IDE and create the contract file", - "working_directory": ".", - "description": "Navigate to https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), click the 'New File' icon and name it 'MyToken.sol'. Paste the full contract source fetched in step 1 into the editor. Save with Ctrl+S (or Cmd+S on Mac)." - }, - { - "order": 3, - "action": "Configure the Solidity compiler in Remix", - "working_directory": ".", - "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown — required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." - }, - { - "order": 4, - "action": "Add Polkadot Hub TestNet to MetaMask", - "working_directory": ".", - "description": "If MetaMask does not already have Polkadot Hub TestNet configured, add it manually:\n1. Open MetaMask and go to Settings > Networks > Add Network > Add a network manually.\n2. Enter: Network Name: Polkadot Hub TestNet; RPC URL: https://services.polkadothub-rpc.com/testnet; Chain ID: 420420417; Currency Symbol: PAS.\n3. Click Save and switch to the Polkadot Hub TestNet network.\n4. Confirm the account shown in MetaMask has PAS tokens. If not, visit https://faucet.polkadot.io/ to request testnet PAS." - }, - { - "order": 5, - "action": "Connect MetaMask to Remix and deploy", - "working_directory": ".", - "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect — click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation — review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10–30 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." - }, - { - "order": 6, - "action": "Verify the deployment and mint tokens", - "working_directory": ".", - "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' — should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." - } - ], - "reference_code": { - "repo": "polkadot-developers/revm-hardhat-examples", - "branch": "master", - "base_path": "erc20-hardhat", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix compilation fails with 'Unknown key evmVersion cancun'", - "cause": "Older Remix versions may not list 'cancun' in the EVM version dropdown.", - "resolution": "Update to the latest Remix version by refreshing the page. The cancun option should appear in Advanced Configuration. Alternatively, use a custom EVM version by typing 'cancun' in the input field if available." - }, - { - "pattern": "MetaMask shows wrong network after connecting to Remix", - "cause": "MetaMask is on a different network than Polkadot Hub TestNet.", - "resolution": "In MetaMask, switch to 'Polkadot Hub TestNet' (chainId 420420417) before deploying. Confirm the Remix environment section shows the correct chain ID." - }, - { - "pattern": "Transaction fails with insufficient funds", - "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", - "resolution": "Visit https://faucet.polkadot.io/, enter your MetaMask address, and request testnet PAS. Wait for the tokens to arrive (usually under 1 minute), then retry deployment." - }, - { - "pattern": "Remix shows 'Gas estimation failed'", - "cause": "Contract constructor arguments may be missing or invalid, or the MetaMask account has insufficient funds.", - "resolution": "Verify the initialOwner address is filled in correctly in the constructor arguments. Check PAS balance in MetaMask and top up via faucet if needed." - } - ], - "supplementary_context": { - "description": "Load these pages for ERC-20 Hardhat deployment (CLI alternative), network connection details, or wallet configuration.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", - "title": "Deploy an ERC-20 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "CLI-based ERC-20 deployment using Hardhat — use when the user prefers a terminal workflow over Remix." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters for manually adding Polkadot Hub to MetaMask." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask Injected Provider." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy ERC-20 via Remix", - "user_says": "Deploy an ERC-20 token on Polkadot Hub using Remix IDE", - "actions": [ - "Fetch MyToken.sol from revm-hardhat-examples and display to user", - "Open https://remix.ethereum.org, create MyToken.sol, paste contract", - "In Remix Solidity compiler, set version 0.8.20+ and EVM version 'cancun'", - "Add Polkadot Hub TestNet to MetaMask (chainId 420420417, RPC testnet URL)", - "Connect Injected Provider in Remix Deploy tab, deploy with initialOwner set to wallet address", - "Verify deployment and mint tokens via Remix UI" - ], - "result": "ERC-20 token deployed on Polkadot Hub TestNet; contract visible in Remix with working name, symbol, mint, and balanceOf functions" - }, - { - "scenario": "Edge case: user has no testnet PAS tokens", - "user_says": "Deployment failed — gas fee transaction rejected", - "actions": [ - "Direct user to https://faucet.polkadot.io/ and ask them to enter their MetaMask address", - "Wait for confirmation that PAS tokens have arrived (check MetaMask balance)", - "Retry deployment from the Remix Deploy tab" - ], - "result": "Account funded with testnet PAS; deployment proceeds successfully" - } - ] - }, - { - "id": "deploy-contracts-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "description": "Sets up a Node.js project, compiles a Solidity contract with solc, deploys to Polkadot Hub TestNet using Ethers.js v6, and interacts with the deployed contract via read and write script calls. Use when integrating Ethers.js into a new or existing Node.js project targeting Polkadot Hub. Trigger phrases: 'deploy with ethers.js polkadot', 'ethers.js contract deployment', 'use ethers polkadot hub', 'ethersjs testnet'. Covers dotenv credential setup, custom Polkadot Hub provider configuration, and the compile-deploy-interact workflow. Produces a deployed contract with verified read/write interaction.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/ethers-js.md" - ], - "primary_page": "smart-contracts/libraries/ethers-js.md", - "prerequisites": { - "runtime": [ - "Node.js v22+", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" - ], - "wallet": [ - "0x-prefixed EVM private key for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "required": true - }, - { - "name": "RPC_URL", - "description": "HTTP RPC endpoint for Polkadot Hub TestNet. Set to https://services.polkadothub-rpc.com/testnet.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create the project directory and initialize", - "working_directory": ".", - "commands": [ - "mkdir ethers-deploy && cd ethers-deploy", - "npm init -y", - "npm pkg set type=module" - ], - "description": "Create and initialize an ESM Node.js project. ESM is required for Ethers.js v6 imports." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "ethers-deploy", - "commands": [ - "npm install ethers dotenv", - "npm install --save-dev solc typescript tsx @types/node" - ], - "description": "Install ethers (v6), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling." - }, - { - "order": 3, - "action": "Create .env file and .gitignore", - "working_directory": "ethers-deploy", - "commands": [ - "printf 'PRIVATE_KEY=\\nRPC_URL=https://services.polkadothub-rpc.com/testnet\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create .env with RPC_URL pre-filled and empty PRIVATE_KEY. Stop and ask the user to edit .env directly by adding their 0x-prefixed private key as PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing." - }, - { - "order": 4, - "action": "Fetch the provider connection helper", - "working_directory": "ethers-deploy", - "reference_file": "connectToProvider.js", - "description": "Fetch the reference file `connectToProvider.js` and save it to the project root. Verify it points at the Polkadot Hub TestNet RPC URL `https://services.polkadothub-rpc.com/testnet`; if it uses a placeholder, replace `INSERT_RPC_URL` with the testnet URL." - }, - { - "order": 5, - "action": "Fetch the Storage.sol contract", - "working_directory": "ethers-deploy", - "reference_file": "Storage.sol", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." - }, - { - "order": 6, - "action": "Fetch the compilation script", - "working_directory": "ethers-deploy", - "reference_file": "compile.js", - "description": "Fetch the reference file `compile.js` and save it to the project root. The script invokes `solc` against `Storage.sol` and writes the ABI and bytecode to `artifacts/`." - }, - { - "order": 7, - "action": "Compile the Solidity contract", - "working_directory": "ethers-deploy", - "commands": [ - "node compile.js" - ], - "description": "Run the compile script to generate the contract ABI and bytecode. Confirm an `artifacts/` directory was created with `Storage.json` (or similar) before proceeding to deploy." - }, - { - "order": 8, - "action": "Fetch the deployment script", - "working_directory": "ethers-deploy", - "reference_file": "deploy.js", - "description": "Fetch the reference file `deploy.js` and save it to the project root. Then apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line (or `require(\"dotenv\").config()` for CJS).\n(2) Replace `INSERT_RPC_URL` with `process.env.RPC_URL` (or hardcode the testnet URL).\n(3) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`.\n(4) Replace any hardcoded chain ID with `420420417`." - }, - { - "order": 9, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "ethers-deploy", - "commands": [ - "node deploy.js" - ], - "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." - }, - { - "order": 10, - "action": "Fetch the interaction script and interact with the deployed contract", - "working_directory": "ethers-deploy", - "reference_file": "checkStorage.js", - "description": "Fetch the reference file `checkStorage.js` and save it as `checkStorage.js`. Then substitute `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step. Run it with `node checkStorage.js`. Verify the output shows successful read and write operations against the deployed contract." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/smart-contracts/libraries/ethers-js", - "files": [ - { - "path": "Storage.sol", - "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." - }, - { - "path": "compile.js", - "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts for Storage.sol" - }, - { - "path": "deploy.js", - "description": "Deploys the compiled Storage contract to Polkadot Hub via ethers.js Wallet + ContractFactory" - }, - { - "path": "checkStorage.js", - "description": "Reads + writes the deployed Storage contract via ethers.js Contract.connect; calls the `storedNumber` getter and `setNumber(value)` setter." - }, - { - "path": "connectToProvider.js", - "description": "Initializes ethers.js JsonRpcProvider for Polkadot Hub TestNet" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds for intrinsic transaction cost", - "cause": "The deployer account does not have enough PAS to pay for deployment gas.", - "resolution": "Visit https://faucet.polkadot.io/, enter the deployer address, and request testnet PAS. Confirm balance in MetaMask or via eth_getBalance RPC call, then retry." - }, - { - "pattern": "Error: could not detect network", - "cause": "The RPC_URL in .env is incorrect or the endpoint is unreachable.", - "resolution": "Verify RPC_URL=https://services.polkadothub-rpc.com/testnet in .env. Test connectivity: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'" - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "Project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the ethers-deploy directory to enable ES modules." - }, - { - "pattern": "Error: transaction underpriced", - "cause": "The gas price in the transaction is below the 1000 gwei TestNet base fee.", - "resolution": "Add an explicit gasPrice to the transaction options: { gasPrice: ethers.parseUnits('5000', 'gwei') }. Polkadot Hub TestNet has a base fee of 1000 gwei." - } - ], - "supplementary_context": { - "description": "Load these pages for network details, wallet setup, or other library alternatives.", - "pages": [ - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters (RPC URLs, chain IDs, WebSocket endpoints) for all Polkadot Hub environments." - }, - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Alternative to Ethers.js: Viem deployment pattern for Polkadot Hub (more type-safe, recommended for new projects)." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Hardhat-based deployment workflow — use when the project needs a full Hardhat setup rather than lightweight scripts." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy and interact with a Storage contract", - "user_says": "Deploy a contract to Polkadot Hub using Ethers.js", - "actions": [ - "Create ethers-deploy ESM project and install ethers, dotenv, solc, tsx", - "Create .env with RPC_URL and empty PRIVATE_KEY (user fills in)", - "Fetch compile.ts, deploy.ts, interact.ts from polkadot-cookbook", - "Apply substitutions: dotenv import, process.env.RPC_URL, process.env.PRIVATE_KEY, chainId 420420417", - "Run npx tsx compile.ts to build ABI/bytecode", - "Run npx tsx deploy.ts and save deployed contract address", - "Run npx tsx interact.ts to verify read/write contract operations" - ], - "result": "Solidity contract compiled, deployed to Polkadot Hub TestNet, and verified with read/write operations via Ethers.js scripts" - }, - { - "scenario": "Edge case: transaction underpriced error during deployment", - "user_says": "Deploy failed with transaction underpriced", - "actions": [ - "Open deploy.ts and add gasPrice override to the deployment transaction options", - "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", - "Retry with npx tsx deploy.ts" - ], - "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet" - } - ], - "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json" - }, - { - "id": "deploy-contracts-viem", - "title": "Deploy Contracts to Polkadot Hub with Viem", - "description": "Sets up a TypeScript project, compiles a Solidity contract, deploys to Polkadot Hub TestNet using Viem v2 walletClient.deployContract, and interacts with the deployed contract via publicClient and walletClient. Use when building a TypeScript project that needs direct Viem integration with Polkadot Hub without a Hardhat setup. Trigger phrases: 'deploy with viem polkadot', 'viem contract deployment', 'use viem polkadot hub', 'deploy viem typescript'. Covers custom Polkadot Hub chain definition, dotenv credential setup, and the compile-deploy-interact pattern. Companion to deploy-contracts-ethers-js.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/viem.md" - ], - "primary_page": "smart-contracts/libraries/viem.md", - "prerequisites": { - "runtime": [ - "Node.js v22+", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" - ], - "wallet": [ - "0x-prefixed EVM private key for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create the project directory and initialize", - "working_directory": ".", - "commands": [ - "mkdir viem-deploy && cd viem-deploy", - "npm init -y && npm pkg set type=module", - "mkdir -p src contracts abis artifacts" - ], - "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) — the reference scripts assume this layout for relative paths." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "viem-deploy", - "commands": [ - "npm install viem dotenv", - "npm install --save-dev solc typescript tsx @types/node" - ], - "description": "Install viem (v2), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling." - }, - { - "order": 3, - "action": "Create .env file and .gitignore", - "working_directory": "viem-deploy", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create .env with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly by adding PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing." - }, - { - "order": 4, - "action": "Fetch the chain configuration", - "working_directory": "viem-deploy", - "reference_file": "chainConfig.ts", - "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) — apply the same substitutions consistently in all three files." - }, - { - "order": 5, - "action": "Fetch the Storage.sol contract", - "working_directory": "viem-deploy", - "reference_file": "Storage.sol", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." - }, - { - "order": 6, - "action": "Fetch the public client setup", - "working_directory": "viem-deploy", - "reference_file": "createClient.ts", - "description": "Fetch the reference file `createClient.ts` and save it as `src/createClient.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (the file declares the chain locally and has the same 8 placeholders). Constructs a viem `PublicClient` against the Polkadot Hub TestNet chain definition; used for reads and tx receipts." - }, - { - "order": 7, - "action": "Fetch the wallet client setup", - "working_directory": "viem-deploy", - "reference_file": "createWallet.ts", - "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders — including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." - }, - { - "order": 8, - "action": "Fetch the compilation script", - "working_directory": "viem-deploy", - "reference_file": "compile.ts", - "description": "Fetch the reference file `compile.ts` and save it as `src/compile.ts`. The script invokes `solc` against `contracts/Storage.sol` and writes ABI + bytecode artifacts to `abis/` and `artifacts/`." - }, - { - "order": 9, - "action": "Compile the Solidity contract", - "working_directory": "viem-deploy", - "commands": [ - "npx tsx src/compile.ts" - ], - "description": "Run the compile script. Verify that `abis/Storage.json` and `artifacts/Storage.bin` (or equivalent) exist before deploying." - }, - { - "order": 10, - "action": "Fetch the deployment script", - "working_directory": "viem-deploy", - "reference_file": "deploy.ts", - "description": "Fetch the reference file `deploy.ts` and save it as `src/deploy.ts`. Apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line.\n(2) Replace `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``.\n(3) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`.\n(4) Replace `INSERT_CHAIN_ID` with `420420417n` (BigInt).\n\nThe script reads from `../abis` and `../artifacts` relative to the script location, which resolves correctly when the script is in `src/`." - }, - { - "order": 11, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "viem-deploy", - "commands": [ - "npx tsx src/deploy.ts" - ], - "description": "Run the deployment script. Save the deployed contract address from the output.\n\nIf the transaction fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction fails with `fee cap too low` or similar gas errors: add a `gasPrice` override in `deploy.ts` (e.g. `gasPrice: parseGwei('5000')`)." - }, - { - "order": 12, - "action": "Fetch the interaction script and interact with the deployed contract", - "working_directory": "viem-deploy", - "reference_file": "interact.ts", - "description": "Fetch the reference file `interact.ts` and save it as `src/interact.ts`. Replace `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step and `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``. Run it with `npx tsx src/interact.ts`. Verify the value returned by reading `storedNumber` changes after calling `setNumber(value)`." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/smart-contracts/libraries/viem", - "files": [ - { - "path": "Storage.sol", - "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." - }, - { - "path": "chainConfig.ts", - "description": "Custom Polkadot Hub TestNet chain definition for viem (chain ID, RPC, native currency)" - }, - { - "path": "createClient.ts", - "description": "viem PublicClient construction against Polkadot Hub TestNet" - }, - { - "path": "createWallet.ts", - "description": "viem WalletClient construction with PRIVATE_KEY from env" - }, - { - "path": "compile.ts", - "description": "Solidity compiler wrapper that produces ABI + bytecode artifacts" - }, - { - "path": "deploy.ts", - "description": "Deploys the compiled Storage contract via viem WalletClient.deployContract" - }, - { - "path": "interact.ts", - "description": "Reads + writes the deployed Storage contract via viem readContract/writeContract" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: fee cap too low / transaction underpriced", - "cause": "Viem's default gas estimation produces a gas price below the 1000 gwei TestNet base fee.", - "resolution": "Add an explicit gasPrice override to the deployContract call: { gasPrice: parseGwei('5000') }. Import parseGwei from 'viem'. Polkadot Hub TestNet requires at least 1000 gwei; use 5000 gwei (5x) as a safe margin." - }, - { - "pattern": "Error: Chain ID mismatch", - "cause": "The Viem chain definition's id does not match the connected network's chain ID.", - "resolution": "Verify the chain object has id: 420420417. Check with: const chainId = await publicClient.getChainId(); and confirm it returns 420420417." - }, - { - "pattern": "Cannot find module 'viem'", - "cause": "Viem package not installed.", - "resolution": "Run 'npm install viem' in the viem-deploy directory." - }, - { - "pattern": "Error: insufficient funds for intrinsic transaction cost", - "cause": "The deployer account does not have enough PAS for gas.", - "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS for the deployer address. Verify balance with: await publicClient.getBalance({ address: '0x...' })" - } - ], - "supplementary_context": { - "description": "Load these pages for network details, Ethers.js comparison, or a full Hardhat-based workflow.", - "pages": [ - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters for Polkadot Hub including chain IDs, RPC URLs, and WebSocket endpoints." - }, - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js equivalent workflow — use when the user prefers Ethers.js v6 over Viem." - }, - { - "slug": "smart-contracts-cookbook-dapps-zero-to-hero", - "title": "Zero to Hero Smart Contract DApp", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend — the natural next step after deploying a contract with Viem." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy and interact with a contract", - "user_says": "Deploy a smart contract to Polkadot Hub with Viem", - "actions": [ - "Create viem-deploy ESM project, install viem, dotenv, solc, tsx", - "Create .env with empty PRIVATE_KEY (user fills in)", - "Fetch chain.ts from polkadot-cookbook, verify chain ID 420420417 and TestNet RPC", - "Fetch deploy.ts, read.ts, write.ts; apply dotenv import and process.env.PRIVATE_KEY substitutions", - "Compile Solidity contract to ABI/bytecode artifacts", - "Run npx tsx deploy.ts and save deployed contract address", - "Update read.ts/write.ts with deployed address; run both to verify contract interaction" - ], - "result": "Contract deployed to Polkadot Hub TestNet; read and write operations confirmed via Viem scripts" - }, - { - "scenario": "Edge case: gas fee cap too low during deployment", - "user_says": "Deployment fails with fee cap too low error", - "actions": [ - "Open deploy.ts and locate the walletClient.deployContract call", - "Add gasPrice: parseGwei('5000') to the transaction options object", - "Import parseGwei from 'viem' at the top of the file", - "Retry with npx tsx deploy.ts" - ], - "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee" - } - ], - "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json" - }, - { - "id": "set-up-pallet-mock-runtime", - "title": "Set Up a Mock Runtime for Pallet Unit Testing", - "description": "Creates a mock runtime module for FRAME pallet unit testing using construct_runtime! and derive_impl. Use when you need a self-contained test environment for a custom FRAME pallet before writing unit tests. Configures frame_system with test defaults, satisfies pallet Config traits, and provides genesis storage helpers for different initial states. Trigger phrases: 'mock runtime pallet', 'set up pallet testing', 'construct_runtime test', 'pallet test environment', 'derive_impl TestDefaultConfig'. Requires a completed FRAME pallet in pallets/pallet-custom. Do NOT use for full parachain runtime integration testing.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/mock-runtime.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/mock-runtime.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain (stable, via rustup)", - "Cargo", - "Completed the Make a Custom Pallet guide — the counter pallet must exist in pallets/pallet-custom" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create mock.rs and add module declaration to lib.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch mock.rs" - ], - "description": "Create the empty mock.rs file. Then open src/lib.rs and insert the following two lines immediately after the 'pub use pallet::*;' line:\n\n```rust\n#[cfg(test)]\nmod mock;\n```\n\nThe #[cfg(test)] attribute ensures this module is only compiled when running tests, keeping it out of production builds." - }, - { - "order": 4, - "action": "Fetch and save the complete mock.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "reference_file": "mock.rs", - "description": "Fetch the reference file and save it as 'mock.rs', replacing the empty placeholder. The file must contain: (1) imports including construct_runtime!, frame_system, and IdentityLookup; (2) Test runtime type and Block type alias via construct_runtime!; (3) #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] for System with AccountId = u64, Lookup = IdentityLookup, Block = Block; (4) impl pallet_custom::Config for Test with type RuntimeEvent = RuntimeEvent and type CounterMaxValue = ConstU32<1000>; (5) new_test_ext() genesis helper; (6) new_test_ext_with_counter(val: u32) helper; (7) new_test_ext_with_interactions() helper accepting counter value and Vec of (AccountId, u32) interactions." - }, - { - "order": 5, - "action": "Verify mock runtime compiles", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "commands": [ - "cargo test --package pallet-custom --lib" - ], - "expected_output": "test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok\ntest mock::test_genesis_config_builds ... ok", - "description": "Compile the test code including the new mock module. Two auto-generated tests will run: the construct_runtime integrity test and the genesis config build test. Both must pass. If compilation fails with 'missing field' errors, add the missing associated type to the impl pallet_custom::Config for Test block in mock.rs using a sensible default (e.g., type WeightInfo = ();)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/mock-runtime", - "files": [ - { - "path": "mock.rs", - "description": "Complete mock runtime: construct_runtime! with System + CustomPallet, frame_system TestDefaultConfig, pallet Config impl, and three genesis storage helpers (default, counter init, interactions init)" - } - ] - }, - "error_patterns": [ - { - "pattern": "error[E0277]: the trait bound is not satisfied / missing associated type in Config impl", - "cause": "The mock.rs Config implementation is missing an associated type that the pallet's Config trait declares (e.g., WeightInfo).", - "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder — for WeightInfo: type WeightInfo = ();" - }, - { - "pattern": "error[E0412]: cannot find type in scope / unresolved import", - "cause": "An import path in mock.rs does not match the current crate layout.", - "resolution": "Verify that import paths in mock.rs match your actual pallet crate name and module structure. Check Cargo.toml for the crate name." - }, - { - "pattern": "error: proc macro `construct_runtime` panicked", - "cause": "A pallet in construct_runtime! has a misconfigured index or incompatible Config.", - "resolution": "Verify the pallet name and module path match the actual module. Ensure all required Config associated types are implemented in the mock." - } - ], - "supplementary_context": { - "description": "Load when continuing pallet development after mock runtime setup.", - "pages": [ - { - "slug": "parachains-customize-runtime-pallet-development-pallet-testing", - "title": "Unit Test Pallets", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/pallet-testing.md", - "relevance": "Next step: write comprehensive unit tests using the mock runtime." - }, - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Prerequisite: the custom counter pallet that this mock runtime is written for." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: create a test environment for the custom counter pallet", - "user_says": "Set up a mock runtime so I can write unit tests for my counter pallet", - "actions": [ - "Create pallets/pallet-custom/src/mock.rs", - "Add #[cfg(test)] mod mock; to lib.rs after pub use pallet::*;", - "Fetch the complete mock.rs from the polkadot-cookbook reference and save it", - "Run cargo test --package pallet-custom --lib to confirm auto-generated tests pass" - ], - "result": "Two auto-generated tests pass: construct_runtime integrity test and genesis config build test. The mock runtime is ready for unit test authoring." - }, - { - "scenario": "Edge case: pallet Config has additional associated types not in the reference mock.rs", - "user_says": "Compilation fails with 'missing associated type WeightInfo in impl pallet_custom::Config for Test'", - "actions": [ - "Open src/mock.rs", - "Add type WeightInfo = (); inside the impl pallet_custom::Config for Test block", - "Re-run cargo test --package pallet-custom --lib" - ], - "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments." - } - ], - "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs" - }, - { - "id": "unit-test-frame-pallet", - "title": "Write Unit Tests for a FRAME Pallet", - "description": "Writes a complete tests.rs module for a FRAME pallet using assert_ok!, assert_noop!, and System::assert_last_event macros. Use after completing the mock runtime guide to verify pallet logic in complete isolation. Covers all critical test categories: dispatchable success paths, arithmetic guard errors, root-only origin checks, event data correctness, user-interaction tracking, and genesis state loading. Trigger phrases: 'unit test FRAME pallet', 'test pallet logic', 'assert_noop assert_ok', 'pallet test suite', 'test dispatchable'. Requires completed create-a-pallet and set-up-pallet-mock-runtime guides. Do NOT use for integration or end-to-end testing of a full parachain.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/pallet-testing.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/pallet-testing.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain (stable, via rustup)", - "Cargo", - "Completed the Make a Custom Pallet guide — counter pallet in pallets/pallet-custom", - "Completed the Mock Your Runtime guide — mock.rs must exist in pallets/pallet-custom/src" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create tests.rs and add module declaration to lib.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch tests.rs" - ], - "description": "Create the empty tests.rs file. Then open src/lib.rs and add the following two lines immediately after the mock module declaration:\n\n```rust\n#[cfg(test)]\nmod tests;\n```\n\nThe module declarations in lib.rs should now read:\n#[cfg(test)] mod mock;\n#[cfg(test)] mod tests;" - }, - { - "order": 4, - "action": "Write test module imports", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Open src/tests.rs and write these imports at the top:\n\n```rust\nuse crate::{mock::*, Error, Event};\nuse frame::deps::frame_support::{assert_noop, assert_ok};\nuse frame::deps::sp_runtime::DispatchError;\n```\n\nThese bring in: all mock runtime items (Test, new_test_ext*, RuntimeOrigin, System, CustomPallet, CounterValue, UserInteractions), the pallet Error and Event enums, FRAME assertion macros, and DispatchError for origin checks." - }, - { - "order": 5, - "action": "Write basic operation and event emission tests", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion — events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() — uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() — uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() — uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" - }, - { - "order": 6, - "action": "Write error condition and access control tests", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() — uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() — uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() — uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() — uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" - }, - { - "order": 7, - "action": "Write genesis configuration and interaction tracking tests", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() — uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() — calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() — account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" - }, - { - "order": 8, - "action": "Run the complete test suite", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "commands": [ - "cargo test --package pallet-custom" - ], - "expected_output": "test result: ok. 15 passed; 0 failed; 0 ignored", - "description": "Run all tests. Expected output shows 15 passing tests: 13 pallet tests plus 2 auto-generated mock tests. If fewer appear, verify every function has #[test]. If an event test fails with 'left: []', confirm System::set_block_number(1) is the first call inside the execute_with closure." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "polkadot-docs/parachains/customize-runtime/pallet-development/pallet-testing", - "files": [] - }, - "error_patterns": [ - { - "pattern": "left: [], right: [EventRecord { ... }] / assertion failed on event", - "cause": "System::set_block_number(1) was not called before the dispatchable. Events are suppressed on block 0.", - "resolution": "Add System::set_block_number(1); as the first line inside the execute_with closure for any test that checks events." - }, - { - "pattern": "error: assert_noop! failed — storage was modified", - "cause": "The dispatchable modified storage before returning an error, violating the no-op contract.", - "resolution": "This is a pallet bug: all preconditions must be checked before any storage mutations. Fix the pallet extrinsic, not the test." - }, - { - "pattern": "thread 'tests::...' panicked at 'called Result::unwrap() on Err'", - "cause": "A dispatchable wrapped in assert_ok! returned an error. Usually wrong origin or incorrect genesis state.", - "resolution": "Check that the genesis helper (new_test_ext vs new_test_ext_with_counter) matches expected initial state, and the origin type matches what the extrinsic requires." - } - ], - "supplementary_context": { - "description": "Load when extending pallet testing or proceeding to benchmarking.", - "pages": [ - { - "slug": "parachains-customize-runtime-pallet-development-benchmark-pallet", - "title": "Benchmark Your Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/benchmark-pallet.md", - "relevance": "Next step: benchmark extrinsics and generate production weight files." - }, - { - "slug": "parachains-customize-runtime-pallet-development-mock-runtime", - "title": "Mock Your Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md", - "relevance": "Prerequisite: the mock runtime providing new_test_ext helpers and the Test runtime type." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: write a complete test suite for the counter pallet", - "user_says": "Write unit tests for my custom counter pallet covering increment, decrement, errors, and genesis config", - "actions": [ - "Create pallets/pallet-custom/src/tests.rs", - "Add #[cfg(test)] mod tests; to lib.rs", - "Write imports: use crate::{mock::*, Error, Event}; plus FRAME macros", - "Write increment_works, set_counter_value_works, decrement_works with System::set_block_number(1) for event checks", - "Write error tests: overflow, underflow, root-only access control", - "Write genesis_config_works and interaction tracking tests", - "Run cargo test --package pallet-custom — expect 15 passing tests" - ], - "result": "All 15 tests pass including 2 auto-generated mock tests." - }, - { - "scenario": "Edge case: event assertion fails with empty event list", - "user_says": "My event test fails: 'left: [], right: [EventRecord {...}]'", - "actions": [ - "Open the failing test in tests.rs", - "Add System::set_block_number(1); as the first line inside the execute_with closure", - "Re-run cargo test --package pallet-custom" - ], - "result": "The event test passes because events are recorded starting from block 1." - } - ] - }, - { - "id": "benchmark-frame-pallet", - "title": "Benchmark a FRAME Pallet and Generate Weight Files", - "description": "Implements full FRAME pallet benchmarking: adds benchmarking.rs with #[benchmark] functions, defines WeightInfo trait, wires it into pallet Config, configures Cargo.toml features, builds release WASM, installs frame-omni-bencher, downloads the weight template, and generates weights.rs. Use when a custom pallet needs accurate extrinsic weights before production deployment. Trigger phrases: 'benchmark pallet', 'frame-omni-bencher', 'generate weight file', 'WeightInfo', 'pallet weights production'. Requires a working parachain template with the custom pallet integrated. Do NOT use for estimating gas on EVM contracts.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/benchmark-pallet.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/benchmark-pallet.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with wasm32-unknown-unknown target", - "Cargo", - "The custom counter pallet in pallets/pallet-custom with mock runtime set up (from mock-runtime guide)" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create benchmarking.rs with benchmark functions", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch benchmarking.rs" - ], - "description": "Create pallets/pallet-custom/src/benchmarking.rs with:\n\n```rust\n#![cfg(feature = \"runtime-benchmarks\")]\nuse super::*;\nuse frame::deps::frame_benchmarking::v2::*;\nuse frame::benchmarking::prelude::RawOrigin;\n\n#[benchmarks]\nmod benchmarks {\n use super::*;\n\n #[benchmark]\n fn set_counter_value() {\n let new_value: u32 = 100;\n #[extrinsic_call]\n _(RawOrigin::Root, new_value);\n assert_eq!(CounterValue::::get(), new_value);\n }\n\n #[benchmark]\n fn increment() {\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 50;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), amount);\n assert_eq!(UserInteractions::::get(caller), 1);\n }\n\n #[benchmark]\n fn decrement() {\n CounterValue::::put(100);\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 30;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), 70);\n }\n```\n\n impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);\n}\n\nFor a different pallet, update function names and assertions accordingly." - }, - { - "order": 4, - "action": "Define WeightInfo trait in weights.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch weights.rs" - ], - "description": "Create pallets/pallet-custom/src/weights.rs with a WeightInfo trait and () placeholder implementation:\n\n```rust\npub trait WeightInfo {\n fn set_counter_value() -> frame::prelude::Weight;\n fn increment() -> frame::prelude::Weight;\n fn decrement() -> frame::prelude::Weight;\n}\n\nimpl WeightInfo for () {\n fn set_counter_value() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(10_000, 0) }\n fn increment() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n fn decrement() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n}\n```\n\nThe () impl is used in tests; benchmark-generated values will replace this file in the final step." - }, - { - "order": 5, - "action": "Wire WeightInfo into pallet Config and extrinsic annotations", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Edit src/lib.rs with four changes:\n1. At the top add: pub mod weights; (expose weights module)\n2. After 'pub use pallet::*;' add: #[cfg(feature = \"runtime-benchmarks\")] mod benchmarking;\n3. In #[pallet::config] trait add: type WeightInfo: weights::WeightInfo;\n4. Replace hardcoded weight attributes on each extrinsic:\n - #[pallet::weight(T::WeightInfo::set_counter_value())]\n - #[pallet::weight(T::WeightInfo::increment())]\n - #[pallet::weight(T::WeightInfo::decrement())]" - }, - { - "order": 6, - "action": "Update pallet Cargo.toml with runtime-benchmarks feature", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "description": "Open pallets/pallet-custom/Cargo.toml and add the runtime-benchmarks feature under [features]:\n\n```toml\n[features]\ndefault = [\"std\"]\nruntime-benchmarks = [\n \"frame/runtime-benchmarks\",\n]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame/std\",\n]\n```" - }, - { - "order": 7, - "action": "Update mock.rs, runtime Cargo.toml, runtime config, and benchmarks.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Four integration edits required:\n1. src/mock.rs: add type WeightInfo = (); to impl pallet_custom::Config for Test.\n2. runtime/Cargo.toml: add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks feature list.\n3. runtime/src/configs/mod.rs: add type WeightInfo = (); to impl pallet_custom::Config for Runtime (placeholder).\n4. runtime/src/benchmarks.rs: add [pallet_custom, CustomPallet] inside the define_benchmarks! macro." - }, - { - "order": 8, - "action": "Test benchmark compilation", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "commands": [ - "cargo test -p pallet-custom --features runtime-benchmarks" - ], - "expected_output": "test benchmarking::benchmarks::bench_set_counter_value ... ok", - "description": "Run benchmark unit tests generated by impl_benchmark_test_suite!. All three benchmark functions must pass before building the full runtime. These tests verify the benchmark code compiles and the assertions hold." - }, - { - "order": 9, - "action": "Build the release runtime with benchmarks enabled", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release --features runtime-benchmarks" - ], - "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking — do not use for production deployment." - }, - { - "order": 10, - "action": "Install frame-omni-bencher", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo install frame-omni-bencher --locked" - ], - "expected_output": "Installed package `frame-omni-bencher`", - "description": "Install via cargo, or download a pre-built binary from the Polkadot SDK releases page. Replace VERSION with the SDK version tag used by the parachain template (e.g., polkadot-stable2412):\nmacOS: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher-aarch64-apple-darwin && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/\nUbuntu: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/" - }, - { - "order": 11, - "action": "Create the weight Handlebars template file", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content — needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions — you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." - }, - { - "order": 12, - "action": "Execute benchmarks and generate weights.rs", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "frame-omni-bencher v1 benchmark pallet --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm --pallet pallet_custom --extrinsic \"\" --template ./pallets/pallet-custom/frame-weight-template.hbs --output ./pallets/pallet-custom/src/weights.rs" - ], - "expected_output": "Wrote weight file to ./pallets/pallet-custom/src/weights.rs", - "description": "Run benchmarks against the WASM runtime and generate production weights.rs. This overwrites the placeholder weights.rs from step 2. The output contains a SubstrateWeight struct implementing WeightInfo with actual measured values. For more precise measurements, add: --steps 50 --repeat 20" - }, - { - "order": 13, - "action": "Update runtime config to use generated weights", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/configs/mod.rs and update the pallet Config to use measured weights instead of ():\n\n```rust\nimpl pallet_custom::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type CounterMaxValue = ConstU32<1000>;\n type WeightInfo = pallet_custom::weights::SubstrateWeight;\n}\n```\n\nThen rebuild for production without the benchmarks flag:\ncargo build --release" - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/benchmark-pallet", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0412]: cannot find type Weight / use of undeclared crate", - "cause": "The weights.rs uses frame_support types with the wrong import path for the SDK version.", - "resolution": "Use frame::prelude::Weight rather than frame_support::weights::Weight for SDK versions that use the unified frame crate." - }, - { - "pattern": "Error: No benchmarks found for pallet_custom", - "cause": "The pallet was not registered in define_benchmarks! in runtime/src/benchmarks.rs, or the pallet name is misspelled.", - "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! — use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." - }, - { - "pattern": "could not compile parachain-template-runtime / runtime-benchmarks feature error", - "cause": "The pallet's runtime-benchmarks feature was not added to the runtime's feature cascade in runtime/Cargo.toml.", - "resolution": "Add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks list in runtime/Cargo.toml." - } - ], - "supplementary_context": { - "description": "Load for context on pallet development prerequisites or parachain template.", - "pages": [ - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "The custom counter pallet being benchmarked." - }, - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "The parachain template runtime structure required for benchmark integration." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: benchmark the counter pallet and generate a production weight file", - "user_says": "Benchmark my counter pallet and generate weights.rs for production use", - "actions": [ - "Create benchmarking.rs with #[benchmark] functions for all three extrinsics", - "Create placeholder weights.rs with WeightInfo trait and () impl", - "Add WeightInfo to pallet Config and update #[pallet::weight] attributes in lib.rs", - "Update pallet Cargo.toml with runtime-benchmarks feature", - "Wire into runtime: mock.rs WeightInfo=(), runtime Cargo.toml, runtime Config, define_benchmarks!", - "Run cargo test -p pallet-custom --features runtime-benchmarks to verify benchmark compilation", - "cargo build --release --features runtime-benchmarks to build benchmark WASM", - "Install frame-omni-bencher, download frame-weight-template.hbs", - "Run frame-omni-bencher to generate weights.rs", - "Update runtime Config to use pallet_custom::weights::SubstrateWeight" - ], - "result": "A measured weights.rs replaces the placeholder; runtime uses accurate production weights." - }, - { - "scenario": "Edge case: frame-omni-bencher reports 'No benchmarks found'", - "user_says": "frame-omni-bencher says No benchmarks found for pallet_custom", - "actions": [ - "Open runtime/src/benchmarks.rs", - "Verify define_benchmarks! includes [pallet_custom, CustomPallet]", - "If missing, add the entry with underscore crate name and correct runtime type alias", - "Rebuild with --features runtime-benchmarks and re-run frame-omni-bencher" - ], - "result": "Benchmarks are discovered and weights.rs is generated successfully." - } - ] - }, - { - "id": "set-up-local-dev-node", - "title": "Set Up a Local Development Node", - "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/local-dev-node.md" - ], - "primary_page": "smart-contracts/dev-environments/local-dev-node.md", - "prerequisites": { - "runtime": [ - "Rust toolchain (stable + nightly via rustup) with wasm32-unknown-unknown target", - "Polkadot SDK build dependencies — complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", - "Git", - "At least 20 GB free disk space for the release build" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the polkadot-sdk repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk.git", - "cd polkadot-sdk" - ], - "description": "Clone the official polkadot-sdk repository. The clone downloads several GB of source code. The repository contains both the revive-dev-node implementation and the ETH-RPC adapter." - }, - { - "order": 2, - "action": "Build the revive-dev-node binary", - "working_directory": "polkadot-sdk", - "commands": [ - "cargo build -p revive-dev-node --bin revive-dev-node --release" - ], - "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the Revive Dev node in release mode. Release builds are optimized but take significantly longer (up to 30 minutes). The binary will be at polkadot-sdk/target/release/revive-dev-node. If out of memory, limit parallelism by appending: -- -j2" - }, - { - "order": 3, - "action": "Build the ETH-RPC adapter binary", - "working_directory": "polkadot-sdk", - "commands": [ - "cargo build -p pallet-revive-eth-rpc --bin eth-rpc --release" - ], - "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the ETH-RPC adapter. This translates Ethereum JSON-RPC calls into Substrate requests. Binary at polkadot-sdk/target/release/eth-rpc." - }, - { - "order": 4, - "action": "Start the Revive Dev node", - "working_directory": "polkadot-sdk", - "commands": [ - "./target/release/revive-dev-node --dev" - ], - "interactive": true, - "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running — open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" - }, - { - "order": 5, - "action": "Start the ETH-RPC adapter in a new terminal", - "working_directory": "polkadot-sdk", - "commands": [ - "./target/release/eth-rpc --dev" - ], - "interactive": true, - "description": "Open a new terminal, navigate to the polkadot-sdk directory, and start the adapter with --dev. It connects to the local node and exposes Ethereum JSON-RPC at http://localhost:8545. Success is indicated by log lines showing the adapter is ready. For debug logging, prepend: RUST_LOG=\"info,eth-rpc=debug\"\n\nVerify the environment is ready by connecting Hardhat, Remix, or MetaMask to http://localhost:8545." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "polkadot-docs/smart-contracts/local-dev-node", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error: linker not found / error while loading shared libraries / could not compile", - "cause": "Missing system build dependencies such as clang, cmake, or protobuf-compiler.", - "resolution": "Complete the Install Polkadot SDK Dependencies guide. On Ubuntu: sudo apt install -y cmake clang libclang-dev protobuf-compiler. On macOS: brew install cmake protobuf." - }, - { - "pattern": "Connection refused at localhost:8545 / eth-rpc exits immediately", - "cause": "The revive-dev-node is not running when eth-rpc starts, or it crashed before the adapter connected.", - "resolution": "Ensure revive-dev-node --dev is running in a separate terminal and producing blocks before starting eth-rpc. The adapter connects via WebSocket to the node." - }, - { - "pattern": "Method not found / eth_method not supported", - "cause": "An Ethereum tool is calling a JSON-RPC method not implemented by the ETH-RPC adapter (e.g., anvil_* or debug_*).", - "resolution": "The local node supports a subset of the Ethereum JSON-RPC API. Use Polkadot Hub TestNet for full API compatibility. Check the Polkadot EVM differences documentation for supported methods." - } - ], - "supplementary_context": { - "description": "Load for guidance on connecting Ethereum tools or deploying contracts.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Prerequisite: installing Rust and Polkadot SDK build dependencies." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Configure Hardhat to connect to http://localhost:8545 for local testing." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: start a local dev environment for smart contract development", - "user_says": "Set up a local Polkadot node for testing my smart contracts", - "actions": [ - "Clone polkadot-sdk from GitHub", - "Build revive-dev-node with cargo build --release in polkadot-sdk/", - "Build eth-rpc adapter with cargo build --release", - "Start revive-dev-node --dev in terminal 1", - "Start eth-rpc --dev in terminal 2", - "Confirm ETH-RPC is listening at http://localhost:8545" - ], - "result": "Local node produces blocks; ETH-RPC adapter accepts standard Ethereum JSON-RPC at localhost:8545." - }, - { - "scenario": "Edge case: cargo build takes excessively long or runs out of memory", - "user_says": "The build has been running for over an hour and my machine is unresponsive", - "actions": [ - "Cancel the build with Ctrl+C", - "Check free disk space (df -h) — at least 20 GB required", - "Check free RAM — at least 8 GB recommended", - "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" - ], - "result": "Build completes more slowly with reduced CPU and memory usage." - } - ] - }, - { - "id": "interact-with-chain-dedot", - "title": "Interact with Polkadot Chains Using Dedot", - "description": "Sets up a lightweight Dedot TypeScript client to read chain state and send transactions on any Polkadot SDK blockchain. Use when you need a modern, tree-shakable alternative to Polkadot.js for querying storage, subscribing to events, calling runtime APIs, or submitting signed extrinsics. Trigger phrases: 'use Dedot', 'query chain with Dedot', 'Dedot client', 'interact with chain TypeScript'. Output: account balance, runtime constants, and submitted transaction hash. No testnet tokens required for read-only steps.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/dedot.md" - ], - "primary_page": "reference/tools/dedot.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.polkadot.io for Polkadot mainnet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize an ESM Node.js project", - "working_directory": ".", - "commands": [ - "mkdir dedot-example && cd dedot-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory named 'dedot-example' and initialize it as an ESM Node.js project. The 'type=module' setting is required because Dedot uses ES module imports." - }, - { - "order": 2, - "action": "Install Dedot and TypeScript tooling", - "working_directory": "dedot-example", - "commands": [ - "npm install dedot", - "npm install -D @dedot/chaintypes tsx typescript" - ], - "description": "Install the 'dedot' runtime package, '@dedot/chaintypes' for per-chain type definitions and IntelliSense, and 'tsx' plus 'typescript' as dev tooling. The '@dedot/chaintypes' package enables auto-completion for chain-specific pallets and storage entries." - }, - { - "order": 3, - "action": "Fetch the DedotClient initialization script", - "working_directory": "dedot-example", - "reference_file": "client-initialization-via-ws.ts", - "description": "Fetch the reference file `client-initialization-via-ws.ts` and save it as `main.ts`. Then make these substitutions:\n(1) Replace `INSERT_ADDRESS` with the SS58 address you'll query.\n(2) Replace `wss://rpc.polkadot.io` with the target chain's WebSocket endpoint if needed.\n\nThe script connects via `WsProvider` and uses `PolkadotApi` (from `@dedot/chaintypes`) as the ChainApi interface for type safety. If you target a different chain, replace `PolkadotApi` with the appropriate ChainApi (e.g. `KusamaApi`) or omit it to fall back to `SubstrateApi`." - }, - { - "order": 4, - "action": "Append the balance query and disconnect", - "working_directory": "dedot-example", - "description": "Open `main.ts` and append the following block immediately before the end of `main()` (or wherever the connection is established) so the script queries account balance and cleanly disconnects:\n\n```typescript\nconst balance = await client.query.system.account('INSERT_ADDRESS');\nconsole.log('Free balance:', balance.data.free);\nawait client.disconnect();\n```\n\nReplace `INSERT_ADDRESS` with the same SS58 address you used in the previous step." - }, - { - "order": 5, - "action": "Run the query script", - "working_directory": "dedot-example", - "commands": [ - "npx tsx main.ts" - ], - "expected_output": "Free balance: ", - "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts — it will not throw an error." - }, - { - "order": 6, - "action": "Sign and send a transaction", - "working_directory": "dedot-example", - "description": "Fetch the reference file `sign-and-send-tx-with-keyring.ts` and save it as `send-tx.ts`. The pattern uses `@polkadot/keyring` for signing. Make these substitutions:\n(1) Replace `INSERT_DEST_ADDRESS` with the recipient SS58 address.\n(2) Replace `2_000_000_000_000n` with the desired amount in planck (1 DOT = 10^10 planck).\n\nThen install the signer dependency:\n\n```bash\nnpm install @polkadot/keyring @polkadot/util-crypto\n```\n\nRun with `npx tsx send-tx.ts`. The script will print the transaction hash on inclusion.", - "reference_file": "sign-and-send-tx-with-keyring.ts" - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/reference/tools/dedot", - "files": [ - { - "path": "client-initialization-via-ws.ts", - "description": "DedotClient initialization via WsProvider against a Polkadot Hub WebSocket endpoint, typed against PolkadotApi from @dedot/chaintypes" - }, - { - "path": "sign-and-send-tx-with-keyring.ts", - "description": "Signs and submits a Balances.transfer extrinsic using a @polkadot/keyring keypair; demonstrates the full signTx flow" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find package 'dedot'", - "cause": "Dependencies not installed or node_modules missing.", - "resolution": "Run 'npm install dedot' in the dedot-example directory." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "Project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the project directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED", - "cause": "Wrong WebSocket endpoint URL or the node is unreachable.", - "resolution": "Verify the WebSocket URL is correct and the node is online. For Polkadot mainnet use wss://rpc.polkadot.io. For Polkadot Hub TestNet use wss://asset-hub-paseo.dotters.network." - }, - { - "pattern": "TypeError: Cannot read properties of undefined (reading 'free')", - "cause": "The queried account does not exist on chain (never had a balance).", - "resolution": "An account with no on-chain history returns a default AccountInfo with all balances at 0. Check that the ADDRESS constant is a valid SS58 address for the target chain. Use a funded account or a known validator address for initial testing." - } - ], - "supplementary_context": { - "description": "Load these pages when the user asks about connecting to specific chains or using alternative clients.", - "pages": [ - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "Multi-SDK tutorial showing how to query account balance using PAPI, Polkadot.js, Dedot, and other clients side-by-side." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to construct and submit signed transactions using multiple SDKs including Dedot." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance", - "user_says": "Use Dedot to check the balance of a Polkadot account", - "actions": [ - "Scaffold 'dedot-example/' as an ESM Node.js project", - "Install dedot, @dedot/chaintypes, and tsx", - "Create main.ts with DedotClient connected to wss://rpc.polkadot.io", - "Query client.query.system.account for the target address", - "Run npx tsx main.ts and read the printed free balance" - ], - "result": "Free balance of the queried address printed to console in planck units." - }, - { - "scenario": "Edge case: targeting a custom Polkadot SDK chain", - "user_says": "Connect Dedot to my parachain and query a custom pallet", - "actions": [ - "Generate a ChainApi interface for the custom chain: npx dedot chaintypes -w wss://your-node.example.com", - "Import the generated ChainApi and pass it as the type parameter to DedotClient.new()", - "Access custom pallet storage via client.query..()" - ], - "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs." - } - ], - "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json" - }, - { - "id": "run-parachain-node-omni-node", - "title": "Run a Parachain Node with Polkadot Omni Node", - "description": "Installs the polkadot-omni-node pre-built binary and runs a full node for any compatible Polkadot parachain using an external chain spec file. Use when you need to run a parachain node without maintaining a custom node codebase, or when testing a runtime against a live network. Trigger phrases: 'run a parachain node', 'polkadot-omni-node', 'spin up a parachain full node', 'start omni node'. Output: syncing parachain node with WebSocket RPC at ws://localhost:9944. Requires a chain spec JSON file for the target parachain.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/omninode.md" - ], - "primary_page": "reference/tools/omninode.md", - "prerequisites": { - "runtime": [ - "macOS (Apple Silicon or Intel) or Linux (Ubuntu/Debian) for the pre-built binary path", - "Rust and Cargo (via rustup) if building from source — see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" - ], - "network": [ - "Internet access to download the binary and to sync the parachain" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install polkadot-omni-node", - "working_directory": ".", - "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust — see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" - }, - { - "order": 2, - "action": "Verify the installation", - "working_directory": ".", - "commands": [ - "polkadot-omni-node --version" - ], - "expected_output": "polkadot-omni-node ", - "description": "Confirm the binary is installed and accessible from PATH. You should see the installed version number. If the command is not found, ensure /usr/local/bin is in your PATH, or use the full path to the binary." - }, - { - "order": 3, - "action": "Obtain a chain specification", - "working_directory": ".", - "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path — you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." - }, - { - "order": 4, - "action": "Run a parachain full node", - "working_directory": ".", - "description": "Launch the node with warp sync enabled. Replace './chain_spec.json' with the actual path to your saved chain spec file:\n\nTo see all available flags before running:\n```\npolkadot-omni-node --help\n```\n\nTo start the node:\n```\npolkadot-omni-node --chain ./chain_spec.json --sync warp\n```\n\nThe --chain flag points to the chain spec; --sync warp enables fast catch-up to the latest finalized state (historical blocks sync in the background).\n\nYou will see log lines like 'Syncing' and 'Imported #NNNN' as the node connects to peers and downloads state. The node exposes a WebSocket RPC endpoint at ws://localhost:9944 by default.\n\nTo stop the node, press Ctrl+C. For long-running production use, configure a systemd service or a process manager." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "polkadot-omni-node: command not found", - "cause": "Binary not on PATH or installation failed.", - "resolution": "Verify the binary was moved to /usr/local/bin/ and that directory is in $PATH. Run 'which polkadot-omni-node' to check. If missing, repeat the installation step." - }, - { - "pattern": "Error: The chain spec is not compatible / missing required runtime API", - "cause": "The parachain runtime does not implement all APIs required by polkadot-omni-node (e.g., GetParachainInfo, AuraApi, or required pallets).", - "resolution": "Check the runtime compatibility requirements at docs.polkadot.com/reference/tools/omninode/. The runtime must implement GetParachainInfo, AuraApi, and include the System, ParachainSystem, Aura, and ParachainInfo pallets. See the parachain template at github.com/paritytech/polkadot-sdk-parachain-template for a reference implementation." - }, - { - "pattern": "No peers found / node stuck at block 0", - "cause": "Network connectivity issue, firewall blocking P2P ports, or incorrect chain spec.", - "resolution": "Ensure outbound TCP on ports 30333/30334 is not blocked. Verify the chain spec corresponds to an active network. Check the Polkadot Discord for any network outages." - } - ], - "supplementary_context": { - "description": "Load these pages for context on parachain runtimes, chain specs, or building from source.", - "pages": [ - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "How to build a parachain template runtime that is compatible with polkadot-omni-node." - }, - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Install Rust and system dependencies needed if building polkadot-omni-node from source." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run an Asset Hub full node", - "user_says": "Use polkadot-omni-node to run an Asset Hub node", - "actions": [ - "Download the latest polkadot-omni-node binary from the Polkadot SDK releases page", - "Verify installation with polkadot-omni-node --version", - "Download the Asset Hub chain spec JSON from paritytech.github.io/chainspecs/", - "Save as chain_spec.json and run: polkadot-omni-node --chain ./chain_spec.json --sync warp" - ], - "result": "Syncing Asset Hub node with WebSocket RPC accessible at ws://localhost:9944." - }, - { - "scenario": "Edge case: build from source when no pre-built binary is available for the platform", - "user_says": "I'm on a non-standard Linux architecture — install polkadot-omni-node from source", - "actions": [ - "Install Rust via rustup and system dependencies per parachains/install-polkadot-sdk/", - "Check crates.io for the latest polkadot-omni-node version", - "Run: cargo install --locked polkadot-omni-node@INSERT_VERSION", - "Verify: polkadot-omni-node --version" - ], - "result": "polkadot-omni-node installed from source and verified on a non-standard platform." - } - ] - }, - { - "id": "interact-polkadot-node-py-substrate", - "title": "Interact with a Polkadot Node Using Python Substrate Interface", - "description": "Sets up the Python Substrate Interface library to connect to any Polkadot SDK blockchain, query on-chain storage (account balance, nonce), and submit signed extrinsics. Use when building Python scripts, data pipelines, or backend services that need to read or write chain state without a JavaScript runtime. Trigger phrases: 'Python Substrate Interface', 'py-substrate-interface', 'query Polkadot with Python', 'Substrate Python', 'substrate-interface pip'. Output: printed account balance details and transaction receipt with extrinsic and block hash.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/py-substrate-interface.md" - ], - "primary_page": "reference/tools/py-substrate-interface.md", - "prerequisites": { - "runtime": [ - "Python 3.7+", - "pip" - ], - "network": [ - "WebSocket RPC endpoint for the target Polkadot SDK node (e.g., wss://rpc.polkadot.io for Polkadot mainnet, or ws://127.0.0.1:9944 for a local node)" - ] - }, - "env_vars": [ - { - "name": "MNEMONIC", - "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file — do not ask for it in chat.", - "required": false - } - ], - "steps": [ - { - "order": 1, - "action": "Create a virtual environment and install the library", - "working_directory": ".", - "commands": [ - "python3 -m venv py-substrate-example && cd py-substrate-example", - "source bin/activate", - "pip install substrate-interface" - ], - "description": "Create an isolated virtual environment named 'py-substrate-example' and install the substrate-interface library. On Windows, replace 'source bin/activate' with '.\\Scripts\\activate'. After activation, the prompt shows '(py-substrate-example)'. All subsequent commands must be run in this activated environment." - }, - { - "order": 2, - "action": "Create the connection script", - "working_directory": "py-substrate-example", - "description": "Create 'connect.py' with the following content. Replace 'INSERT_WS_URL' with the WebSocket endpoint for your target node (e.g., 'wss://rpc.polkadot.io' for Polkadot mainnet or 'ws://127.0.0.1:9944' for a local dev node).\n\nFetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/substrate_interface.py -o connect.py\n\nThen replace INSERT_WS_URL in connect.py with the target endpoint. Run to verify the connection:\npython3 connect.py\n\nExpected output: 'Connected to chain: Polkadot' (or your target chain name). If the chain name is wrong or absent, the URL may be incorrect." - }, - { - "order": 3, - "action": "Query account balance", - "working_directory": "py-substrate-example", - "description": "Create 'read_state.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/read_state.py -o read_state.py\n\nSubstitutions required:\n- At the top of the file, add the SubstrateInterface connection (same as connect.py, replacing INSERT_WS_URL with the endpoint).\n- Replace 'INSERT_ADDRESS' with the SS58 address to query.\n\nRun:\npython3 read_state.py\n\nExpected output:\n Account Details:\n - Free Balance: \n - Reserved: \n - Nonce: \n\nThe values are in planck (smallest unit). Divide by 10^10 for DOT values on Polkadot mainnet." - }, - { - "order": 4, - "action": "Submit a balance transfer", - "working_directory": "py-substrate-example", - "description": "Create '.env' file:\nMNEMONIC=\n\nAdd '.env' to .gitignore:\necho '.env' >> .gitignore\n\nStop and ask the user to fill in their mnemonic phrase in the .env file. Do NOT ask for the mnemonic phrase in chat. Wait for confirmation before continuing.\n\nInstall dotenv:\npip install python-dotenv\n\nCreate 'send_tx.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/send_tx.py -o send_tx.py\n\nAt the top of send_tx.py, add:\n```python\nimport os\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom substrateinterface import SubstrateInterface, Keypair\n\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\nkeypair = Keypair.create_from_mnemonic(os.environ['MNEMONIC'])\n```\n\nThen add the template content below. Substitutions in send_tx.py:\n- Replace INSERT_WS_URL with the WebSocket endpoint.\n- Replace 'INSERT_ADDRESS' (in call_params 'dest') with the recipient SS58 address.\n- Replace 'INSERT_VALUE' with the transfer amount in planck (e.g., 1000000000000 for 0.1 DOT).\n\nRun:\npython3 send_tx.py\n\nExpected output:\n Transaction successful:\n - Extrinsic Hash: 0x...\n - Block Hash: 0x..." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ModuleNotFoundError: No module named 'substrateinterface'", - "cause": "Library not installed or virtual environment not activated.", - "resolution": "Ensure the virtual environment is activated (run 'source bin/activate' on macOS/Linux or '.\\Scripts\\activate' on Windows), then run 'pip install substrate-interface'." - }, - { - "pattern": "ConnectionError / WebSocket connection refused", - "cause": "Wrong WebSocket URL or the node is not running.", - "resolution": "Verify the URL in SubstrateInterface(url=...). For a local node ensure it's running with --rpc-external or uses the default port 9944. For public endpoints, test with wss://rpc.polkadot.io." - }, - { - "pattern": "ValueError: Invalid address format", - "cause": "The address passed to the storage query is not a valid SS58-encoded address.", - "resolution": "Verify the account address is in SS58 format and matches the network's prefix. For Polkadot mainnet, addresses start with '1'; for testnet (Paseo) addresses start with a different prefix. Use polkadot.js.org/apps to validate the address." - }, - { - "pattern": "ExtrinsicFailed: Token.FundsUnavailable", - "cause": "The sender account does not have enough balance to pay the transfer amount plus transaction fees.", - "resolution": "Check the account balance with read_state.py before submitting. Ensure the 'value' parameter in call_params covers both the transfer amount and transaction fees." - } - ], - "supplementary_context": { - "description": "Load these pages for context on chain interaction patterns or the Python SDK documentation.", - "pages": [ - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "Multi-SDK account query tutorial including a Python Substrate Interface example alongside PAPI and Polkadot.js." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to construct and submit transactions using multiple SDK clients including Python Substrate Interface." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance from Python", - "user_says": "Use Python to check the balance of a Polkadot account", - "actions": [ - "Create virtual environment and install substrate-interface", - "Create connect.py with SubstrateInterface(url='wss://rpc.polkadot.io') and verify connection", - "Create read_state.py with substrate.query(module='System', storage_function='Account', params=[ADDRESS])", - "Print free_balance, reserved, and nonce from account_info.value" - ], - "result": "Account balance details printed with free balance in planck." - }, - { - "scenario": "Edge case: transaction fails with ExtrinsicFailed", - "user_says": "My Python transfer script runs but the extrinsic failed", - "actions": [ - "Check receipt.is_success — if False, print receipt.error_message for the specific error", - "If FundsUnavailable: verify sender balance covers transfer amount plus fees using read_state.py", - "If BadOrigin: verify the keypair matches the sender address in the call_params 'dest' field" - ], - "result": "Root cause identified from receipt.error_message; transaction corrected and resubmitted." - } - ] - }, - { - "id": "interact-polkadot-node-subxt", - "title": "Interact with a Polkadot Node Using Subxt (Rust)", - "description": "Scaffolds a Rust project with the Subxt library to interact with any Polkadot SDK blockchain using compile-time type safety. Use when building Rust services, CLI tools, or backend applications that need to query chain state, call runtime APIs, or submit transactions without an external RPC framework. Trigger phrases: 'Subxt', 'Rust Polkadot client', 'interact with Polkadot in Rust', 'subxt-cli metadata', 'type-safe Polkadot Rust'. Output: existential deposit constant, account info, and confirmed balance transfer event.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/subxt.md" - ], - "primary_page": "reference/tools/subxt.md", - "prerequisites": { - "runtime": [ - "Rust and Cargo installed via rustup (https://rustup.rs/)", - "A Rust project directory (cargo new my_project)" - ] - }, - "env_vars": [ - { - "name": "SECRET_PHRASE", - "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file — do not ask for it in chat.", - "required": false - } - ], - "steps": [ - { - "order": 1, - "action": "Create a Rust project", - "working_directory": ".", - "commands": [ - "cargo new subxt-example && cd subxt-example" - ], - "description": "Create a new Rust binary project named 'subxt-example'. All subsequent steps run from within this directory." - }, - { - "order": 2, - "action": "Install the subxt-cli tool", - "working_directory": "subxt-example", - "description": "Install the subxt-cli command-line tool used to download chain metadata. Check https://crates.io/crates/subxt-cli for the latest version and replace INSERT_SUBXT_CLI_VERSION below:\n\ncargo install subxt-cli@INSERT_SUBXT_CLI_VERSION\n\nAs of the current reference files, the version is 0.50.0, so the command would be:\ncargo install subxt-cli@0.50.0\n\nThis installs the 'subxt' binary globally. Installation may take several minutes as it compiles from source." - }, - { - "order": 3, - "action": "Add Subxt dependencies to Cargo.toml", - "working_directory": "subxt-example", - "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." - }, - { - "order": 4, - "action": "Download chain metadata", - "working_directory": "subxt-example", - "description": "Use subxt-cli to fetch the chain's runtime metadata. Replace INSERT_NODE_URL with the WebSocket URL of the node (e.g., wss://rpc.polkadot.io for Polkadot mainnet):\n\nsubxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale\n\nThis saves the SCALE-encoded metadata to 'polkadot_metadata.scale' in the project root. The #[subxt::subxt] macro references this file at compile time. If the command hangs, verify the node URL is reachable and the node is running." - }, - { - "order": 5, - "action": "Create the main Rust source file", - "working_directory": "subxt-example", - "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." - }, - { - "order": 6, - "action": "Build and run", - "working_directory": "subxt-example", - "commands": [ - "cargo run" - ], - "expected_output": "Existential deposit: \nAccount info: \nBalance transfer successful: ", - "description": "Build and run the project. The first build will take several minutes as dependencies compile. Subsequent builds are faster. The program: (1) reads the existential deposit constant, (2) fetches account info for INSERT_ADDRESS, (3) submits a balance transfer and waits for finalization. If the transfer step fails with an error, check that the account has sufficient balance (at least INSERT_AMOUNT + gas fees) and that INSERT_SECRET_PHRASE belongs to the sender account." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0425]: cannot find value / type in scope — after changing metadata", - "cause": "The generated types from #[subxt::subxt] changed after re-downloading metadata from a different chain or after a runtime upgrade.", - "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ — check the Subxt docs for the new paths." - }, - { - "pattern": "Error: Rpc error: target url is not valid", - "cause": "INSERT_NODE_URL was not replaced or the URL format is incorrect.", - "resolution": "Ensure NODE_URL in subxt.rs is a full WebSocket URL starting with ws:// or wss:// (e.g., wss://rpc.polkadot.io). HTTP URLs are not supported." - }, - { - "pattern": "invalid phrase: mnemonic / BIP39 error", - "cause": "INSERT_SECRET_PHRASE was not replaced or is malformed.", - "resolution": "Verify the mnemonic is 12 or 24 BIP39 words separated by spaces. Load it from the SECRET_PHRASE env variable rather than hardcoding. Ensure there are no leading/trailing spaces in the .env value." - }, - { - "pattern": "Module 'Balances' / ExtrinsicFailed", - "cause": "Insufficient balance, wrong destination address, or node RPC error.", - "resolution": "Check that the sender account has enough balance to cover the transfer amount plus transaction fees. Verify INSERT_DEST_ADDRESS is a valid SS58 address for the target network." - } - ], - "supplementary_context": { - "description": "Load these pages for context on chain interaction patterns or the Subxt API surface.", - "pages": [ - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "Multi-SDK account query tutorial including Subxt alongside PAPI, Polkadot.js, and Python Substrate Interface." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit transactions with multiple SDK clients — context for the Subxt transaction pattern." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query chain constants and account balance", - "user_says": "Use Subxt in Rust to read the existential deposit and an account's balance", - "actions": [ - "Create Rust project and install subxt-cli", - "Add subxt, subxt-signer, tokio dependencies to Cargo.toml", - "Fetch chain metadata: subxt metadata --url wss://rpc.polkadot.io > polkadot_metadata.scale", - "Create subxt.rs with #[subxt::subxt] macro pointing to polkadot_metadata.scale", - "Replace INSERT_NODE_URL and INSERT_ADDRESS, then run cargo run" - ], - "result": "Existential deposit constant and account info struct printed to stdout." - }, - { - "scenario": "Edge case: runtime upgrade changes generated types", - "user_says": "My Subxt code broke after the chain did a runtime upgrade", - "actions": [ - "Re-fetch metadata: subxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale", - "Run cargo build and review compile errors for changed type paths", - "Update pallet names or storage entry paths in subxt.rs to match the new metadata" - ], - "result": "Code updated to match post-upgrade runtime types and successfully compiles and runs." - } - ] - }, - { - "id": "deploy-erc721-nft-hardhat", - "title": "Deploy an ERC-721 NFT Using Hardhat", - "description": "Scaffolds a Hardhat project manually, installs OpenZeppelin contracts and dotenv, creates a MyNFT.sol ERC-721 contract and Ignition deployment module, compiles with Cancun EVM version, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when deploying an ERC-721 NFT to Polkadot Hub. Requires testnet PAS tokens and a funded EVM wallet. Trigger phrases: 'deploy NFT Polkadot', 'ERC-721 Hardhat', 'deploy non-fungible token', 'mint NFT Polkadot Hub'. Uses OpenZeppelin Contracts v5.4.0+ requiring evmVersion cancun. Do NOT use for ERC-20 tokens; use deploy-erc20-token-hardhat instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create the project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-nft-deployment", - "cd hardhat-nft-deployment", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead." - }, - { - "order": 2, - "action": "Install Hardhat, OpenZeppelin, and dotenv", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox", - "npm install @openzeppelin/contracts dotenv" - ], - "description": "Install Hardhat v2 toolchain, OpenZeppelin contracts (v5.4.0+), and dotenv for secure private key handling." - }, - { - "order": 3, - "action": "Create the Hardhat configuration file", - "working_directory": "hardhat-nft-deployment", - "description": "Create a file named 'hardhat.config.ts' with the following content:\n\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\nimport '@nomicfoundation/hardhat-toolbox';\nimport 'dotenv/config';\n\nconst config: HardhatUserConfig = {\n solidity: {\n version: '0.8.28',\n settings: { evmVersion: 'cancun' }\n },\n networks: {\n polkadotTestnet: {\n url: 'https://services.polkadothub-rpc.com/testnet',\n chainId: 420420417,\n accounts: [process.env.PRIVATE_KEY as string],\n gasPrice: 5000000000000\n }\n },\n ignition: { requiredConfirmations: 1 }\n};\n\nexport default config;\n```\n\nKey points: (1) 'dotenv/config' must be the first functional import. (2) evmVersion: 'cancun' is required for OpenZeppelin v5.4.0+ (mcopy opcode). (3) gasPrice: 5000000000000 (5000 gwei) prevents 'priority is too low' errors on TestNet. (4) requiredConfirmations: 1 prevents Ignition from misreading pending transactions as dropped." - }, - { - "order": 4, - "action": "Create .env file and .gitignore entry", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 5, - "action": "Create project directories", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "mkdir -p contracts ignition/modules" - ], - "description": "Create the contracts and ignition/modules directories that Hardhat expects." - }, - { - "order": 6, - "action": "Create the MyNFT.sol contract", - "working_directory": "hardhat-nft-deployment", - "description": "Create 'contracts/MyNFT.sol' with the following OpenZeppelin ERC-721 contract.Use the inline content below:\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```" - }, - { - "order": 7, - "action": "Create the Ignition deployment module", - "working_directory": "hardhat-nft-deployment", - "description": "Create 'ignition/modules/MyNFT.ts'. Replace INSERT_OWNER_ADDRESS with the Ethereum address that should own the NFT contract (typically the address derived from your PRIVATE_KEY):\n\n```typescript\nimport { buildModule } from '@nomicfoundation/hardhat-ignition/modules';\n\nconst MyNFTModule = buildModule('MyNFTModule', (m) => {\n const myNFT = m.contract('MyNFT', ['INSERT_OWNER_ADDRESS']);\n return { myNFT };\n});\n\nexport default MyNFTModule;\n```" - }, - { - "order": 8, - "action": "Compile the contract", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile MyNFT.sol. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in hardhat.config.ts and recompile." - }, - { - "order": 9, - "action": "Deploy the NFT contract to Polkadot Hub TestNet", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet" - ], - "expected_output": "MyNFTModule#MyNFT - 0x...", - "interactive": true, - "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "CompilationError: Invalid opcode: MCOPY", - "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode requiring Cancun EVM version.", - "resolution": "In hardhat.config.ts set evmVersion: 'cancun' inside solidity.settings, then run 'npx hardhat compile' again." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of deployment due to missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." - }, - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - } - ], - "supplementary_context": { - "description": "Load when the user asks about minting NFTs, building a dApp on the deployed contract, or ERC-721 concepts.", - "pages": [ - { - "slug": "smart-contracts-cookbook-dapps-zero-to-hero", - "title": "Zero to Hero Smart Contract DApp", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract on Polkadot Hub." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Reference for full Hardhat EVM environment setup and network configuration for Polkadot Hub." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a new ERC-721 NFT contract", - "user_says": "Deploy an ERC-721 NFT contract to Polkadot Hub TestNet", - "actions": [ - "Create project directory 'hardhat-nft-deployment' and run npm init -y", - "Install hardhat, OpenZeppelin contracts, and dotenv", - "Create hardhat.config.ts with dotenv, evmVersion: cancun, gasPrice 5000 gwei, requiredConfirmations: 1", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Create contracts/MyNFT.sol and ignition/modules/MyNFT.ts (replacing INSERT_OWNER_ADDRESS)", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet; delegate confirmation to user" - ], - "result": "ERC-721 NFT contract deployed; contract address printed to console" - }, - { - "scenario": "Edge case: compilation fails with MCOPY opcode error", - "user_says": "Compilation fails with 'Invalid opcode: MCOPY'", - "actions": [ - "Open hardhat.config.ts", - "Set solidity to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", - "Run npx hardhat compile again" - ], - "result": "Compilation succeeds with Cancun EVM version" - } - ] - }, - { - "id": "deploy-erc721-nft-remix", - "title": "Deploy an ERC-721 NFT Using Remix IDE", - "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md", - "prerequisites": { - "network": [ - "Polkadot Hub TestNet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "MetaMask browser extension installed and configured for Polkadot Hub TestNet", - "Testnet account funded with PAS tokens" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Open Remix IDE and create the contract file", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Navigate to https://remix.ethereum.org in your web browser. (2) Under the 'contracts' folder, click 'Create new file' and name it 'MyNFT.sol'. (3) Paste the following ERC-721 contract code:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```\n\nWait for the user to confirm the file has been created and the code pasted." - }, - { - "order": 2, - "action": "Compile the contract in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Click the 'Solidity Compiler' icon in the left panel. (2) Click the 'Compile MyNFT.sol' button. Success: the compiler icon shows a green checkmark. If errors appear, check that the contract code was pasted correctly and the Solidity version is 0.8.22+." - }, - { - "order": 3, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Click 'Deploy & Run Transactions' in the left panel. (2) In the 'Environment' dropdown, select 'Injected Provider - MetaMask'. (3) Approve the MetaMask connection popup. (4) Confirm MetaMask is on Polkadot Hub TestNet (chain ID 420420417). If not, add it via MetaMask Settings > Networks with RPC https://services.polkadothub-rpc.com/testnet. Confirm the funded account is visible." - }, - { - "order": 4, - "action": "Deploy the NFT contract", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Ensure 'MyNFT' is selected in the Contract dropdown. (2) Expand the Deploy section and enter the constructor argument: the owner address (typically your MetaMask wallet address). (3) Click 'Deploy'. (4) Confirm the transaction in MetaMask. Once deployed, the contract appears in 'Deployed Contracts'. Record the contract address." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask: Wrong network / chain ID mismatch", - "cause": "MetaMask is connected to a different network than Polkadot Hub TestNet.", - "resolution": "Switch to or add Polkadot Hub TestNet in MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet." - }, - { - "pattern": "insufficient funds for gas", - "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", - "resolution": "Get testnet PAS tokens from https://faucet.polkadot.io/ for the connected account, then retry." - }, - { - "pattern": "Compilation errors in Remix", - "cause": "Contract code syntax error or Solidity version mismatch.", - "resolution": "Verify the contract code was pasted correctly. In the Solidity Compiler tab, ensure the compiler version is 0.8.22 or later." - } - ], - "supplementary_context": { - "description": "Load when the user asks about connecting Remix to Polkadot Hub or wants CLI-based NFT deployment.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub and configure MetaMask for the TestNet." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-nft-nft-hardhat", - "title": "Deploy an ERC-721 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", - "relevance": "Alternative CLI-based NFT deployment using Hardhat — better for production or CI/CD workflows." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy ERC-721 NFT via Remix IDE", - "user_says": "Deploy an ERC-721 NFT using Remix", - "actions": [ - "Open https://remix.ethereum.org and create contracts/MyNFT.sol with the ERC-721 code", - "Compile via the Solidity Compiler plugin", - "Connect MetaMask with Injected Provider to Polkadot Hub TestNet", - "Enter owner address as constructor argument and click Deploy", - "Confirm transaction in MetaMask" - ], - "result": "ERC-721 NFT contract deployed; contract address shown in Remix Deployed Contracts panel" - }, - { - "scenario": "Edge case: user wants a custom NFT with additional features", - "user_says": "I want to customize my NFT with a supply cap or royalties", - "actions": [ - "Direct user to https://wizard.openzeppelin.com/polkadot to generate a custom ERC-721 contract", - "Paste the generated code into Remix as contracts/MyNFT.sol", - "Proceed with the standard compile and deploy steps" - ], - "result": "Custom ERC-721 contract with user-specified extensions compiled and deployed via Remix" - } - ] - }, - { - "id": "set-up-foundry-polkadot-hub", - "title": "Use Foundry with Polkadot Hub", - "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/foundry.md" - ], - "primary_page": "smart-contracts/dev-environments/foundry.md", - "prerequisites": { - "runtime": [ - "Unix-based OS (Linux or macOS) or Windows WSL", - "Git installed", - "curl installed" - ], - "network": [ - "Polkadot Hub TestNet (chain ID 420420417) — supported natively by Foundry nightly as --chain polkadot-testnet" - ], - "tokens": [ - "Testnet PAS tokens for deployment — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Loaded via 'source .env' into the shell session. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Install Foundry nightly build", - "working_directory": ".", - "commands": [ - "curl -L https://foundry.paradigm.xyz | bash", - "foundryup --version nightly" - ], - "expected_output": "forge Version: (nightly build version string)", - "description": "Install foundryup then the Foundry nightly build. Nightly is required — stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." - }, - { - "order": 2, - "action": "Initialize a new Foundry project", - "working_directory": ".", - "commands": [ - "forge init my-foundry-project", - "cd my-foundry-project" - ], - "description": "Create a new Foundry project with src/ (contracts), script/ (deployment scripts), test/ (Solidity tests), lib/ (dependencies), and foundry.toml. A sample Counter.sol is placed in src/." - }, - { - "order": 3, - "action": "Configure foundry.toml for Polkadot Hub TestNet", - "working_directory": "my-foundry-project", - "description": "Replace the contents of 'foundry.toml' with the following. Choose Blockscout (no API key) or Routescan:\n\nBlockscout:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"\", url = \"https://blockscout-testnet.polkadot.io/api?\" }\n```\n\nRoutescan:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"verifyContract\", url = \"https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan\" }\n```\n\nWith this config, use --chain polkadot-testnet in commands without specifying the RPC URL explicitly." - }, - { - "order": 4, - "action": "Create .env file and load private key", - "working_directory": "my-foundry-project", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env with their 0x-prefixed private key for a testnet-funded account. Do NOT ask for the private key in chat. After confirmation, instruct the user to run 'source .env' in their terminal to load the variable into their shell session." - }, - { - "order": 5, - "action": "Compile contracts", - "working_directory": "my-foundry-project", - "commands": [ - "forge build" - ], - "expected_output": "Compiler run successful!", - "description": "Compile all Solidity contracts in src/. Artifacts output to out/. The sample Counter.sol should compile without errors." - }, - { - "order": 6, - "action": "Deploy a contract to Polkadot Hub TestNet", - "working_directory": "my-foundry-project", - "commands": [ - "forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url https://services.polkadothub-rpc.com/testnet --private-key $PRIVATE_KEY --broadcast" - ], - "expected_output": "Deployed to: 0x...", - "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address — needed for verification and interaction." - }, - { - "order": 7, - "action": "Verify the contract on the block explorer", - "working_directory": "my-foundry-project", - "description": "Replace INSERT_CONTRACT_ADDRESS with the address from step 6.\n\nBlockscout:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet\n```\n\nRoutescan:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --verifier-url 'https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan' --etherscan-api-key 'verifyContract' --chain polkadot-testnet\n```\n\nExpected output: 'Response: OK' and a URL to the verified contract." - }, - { - "order": 8, - "action": "Interact with the contract using Cast", - "working_directory": "my-foundry-project", - "description": "Replace INSERT_CONTRACT_ADDRESS and INSERT_ACCOUNT_ADDRESS with actual values.\n\nRead from contract:\n```bash\ncast call INSERT_CONTRACT_ADDRESS 'number()(uint256)' --chain polkadot-testnet\n```\n\nWrite to contract:\n```bash\ncast send INSERT_CONTRACT_ADDRESS 'setNumber(uint256)' 42 --chain polkadot-testnet --private-key $PRIVATE_KEY\n```\n\nCheck balance:\n```bash\ncast balance INSERT_ACCOUNT_ADDRESS --chain polkadot-testnet\n```" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "foundryup: command not found / forge: command not found", - "cause": "Foundry was installed but PATH was not updated for the current shell session.", - "resolution": "Run 'source ~/.bashrc' (or '~/.zshrc' on macOS) or start a new terminal." - }, - { - "pattern": "Error: Chain 'polkadot-testnet' not found / unknown chain", - "cause": "Using the Foundry stable release instead of nightly. Stable does not include Polkadot chain definitions.", - "resolution": "Install the nightly build: run 'foundryup --version nightly'." - }, - { - "pattern": "Error: Priority is too low / insufficient funds", - "cause": "Account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet PAS from https://faucet.polkadot.io/. The Foundry nightly build handles gas pricing automatically for Polkadot chains." - }, - { - "pattern": "forge test: tests pass locally but fail on TestNet", - "cause": "forge test runs against Anvil (Ethereum semantics), not Polkadot Hub. Existential deposit and gas model differ.", - "resolution": "Use forge test only for unit testing contract logic. For Polkadot-specific behavior, test against a local dev node or TestNet." - } - ], - "supplementary_context": { - "description": "Load when the user wants to understand Polkadot Hub EVM differences or compare Foundry with Hardhat.", - "pages": [ - { - "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", - "title": "EVM vs PVM", - "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md", - "relevance": "Explains differences between Polkadot Hub EVM and standard Ethereum including gas model and existential deposit." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network details for Polkadot Hub TestNet and MainNet RPC endpoints and chain IDs." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: install Foundry and deploy a contract", - "user_says": "Set up Foundry and deploy a contract to Polkadot Hub TestNet", - "actions": [ - "Install Foundry nightly: curl -L https://foundry.paradigm.xyz | bash && foundryup --version nightly", - "Run forge init my-foundry-project", - "Configure foundry.toml with Polkadot TestNet verifier settings", - "Create .env with PRIVATE_KEY; ask user to fill in and run source .env", - "Run forge build to compile", - "Run forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url ... --private-key $PRIVATE_KEY --broadcast", - "Verify: forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet" - ], - "result": "Contract compiled, deployed, and verified on Polkadot Hub TestNet" - }, - { - "scenario": "Edge case: stable Foundry installed instead of nightly", - "user_says": "I get 'unknown chain polkadot-testnet' when running forge create", - "actions": [ - "Run 'forge --version' to check build type", - "If not nightly, run 'foundryup --version nightly' to upgrade", - "Retry the forge create command" - ], - "result": "Foundry upgraded to nightly; polkadot-testnet chain recognized and deployment succeeds" - } - ] - }, - { - "id": "spawn-test-network-zombienet", - "title": "Spawn and Test a Parachain Network with Zombienet", - "description": "Installs Zombienet (executable, Nix, or Docker), downloads polkadot and polkadot-parachain binaries, writes a TOML network config defining relay chain validators and parachain collators, spawns the network via the native provider, writes .zndsl test files using Zombienet's DSL, and runs the test suite. Use when spawning and testing ephemeral Polkadot SDK networks locally. Trigger phrases: 'Zombienet', 'spawn local parachain', 'test parachain network', 'local relay chain Zombienet', 'zndsl test'. Requires polkadot and polkadot-parachain binaries.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/zombienet.md" - ], - "primary_page": "reference/tools/zombienet.md", - "prerequisites": { - "runtime": [ - "Unix-based OS (Linux or macOS) or Windows WSL", - "polkadot and polkadot-parachain binaries — downloaded via: zombienet setup polkadot polkadot-parachain" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Zombienet", - "working_directory": ".", - "commands": [ - "curl -L https://github.com/paritytech/zombienet/releases/latest/download/zombienet-linux-x64 -o zombienet", - "chmod +x zombienet", - "mv zombienet /usr/local/bin/zombienet", - "zombienet version" - ], - "description": "Download the Zombienet executable for Linux x64. For macOS replace 'zombienet-linux-x64' with 'zombienet-macos'. For macOS Gatekeeper issues run: xattr -d com.apple.quarantine zombienet. The 'zombienet version' command confirms successful installation. Docker alternative: docker run -it --rm -v $(pwd):/home/nonroot/zombie-net/host-current-files paritytech/zombienet" - }, - { - "order": 2, - "action": "Download required node binaries", - "working_directory": ".", - "commands": [ - "zombienet setup polkadot polkadot-parachain" - ], - "description": "Download the polkadot and polkadot-parachain binaries for the native provider. Zombienet downloads these to a local path and adds them to PATH for the session. Verify availability: 'polkadot --version' and 'polkadot-parachain --version'." - }, - { - "order": 3, - "action": "Create a network configuration file", - "working_directory": ".", - "description": "Create 'network.toml' with a basic network configuration. This defines a relay chain with two validators and a parachain with one collator. If using the Polkadot Hub TestNet chain spec first download it:\n```bash\nwget https://paseo-r2.zondax.ch/chain-specs/paseo-asset-hub.json\n```\n\nThen create network.toml:\n```toml\n[settings]\ntimeout = 1000\n\n[relaychain]\nchain = \"paseo\"\ndefault_command = \"polkadot\"\n\n [[relaychain.nodes]]\n name = \"alice\"\n validator = true\n\n [[relaychain.nodes]]\n name = \"bob\"\n validator = true\n\n[[parachains]]\nid = 1000\nchain_spec_path = \"./paseo-asset-hub.json\"\n\n [parachains.collator]\n name = \"collator-01\"\n command = \"polkadot-parachain\"\n```\n\nAdjust id, chain_spec_path, and command fields to match your target network. For relay-only tests, omit the [[parachains]] section." - }, - { - "order": 4, - "action": "Spawn the network", - "working_directory": ".", - "commands": [ - "zombienet spawn network.toml --provider native" - ], - "expected_output": "Network launched\nRPC endpoints:", - "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal — the network persists until you stop it with Ctrl+C." - }, - { - "order": 5, - "action": "Write a test file", - "working_directory": ".", - "description": "Create 'network-test.zndsl' to test the spawned network:\n\n```\nDescription: Basic network health test\nNetwork: ./network.toml\nCreds: config\n\nalice: is up\nbob: is up\nalice: parachain 1000 is registered within 200 seconds\nalice: parachain 1000 block height is at least 10 within 300 seconds\n```\n\nCommon assertion types: node health ('alice: is up'), parachain registration, block height, metrics ('alice: reports node_roles is 4'), and log patterns ('alice: log line matches glob \"Imported #1\" within 10 seconds'). Remove parachain assertions if your network has no parachain." - }, - { - "order": 6, - "action": "Run the test suite", - "working_directory": ".", - "commands": [ - "zombienet test network-test.zndsl --provider native" - ], - "expected_output": "PASS", - "description": "Execute the test suite against the running network. Each assertion is evaluated in order; Zombienet reports PASS or FAIL. Exit code 0 = all pass, 1 = any failure. Ensure the network from step 4 is still running before executing tests." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "polkadot: binary not found / command not found", - "cause": "polkadot or polkadot-parachain binaries are not in PATH.", - "resolution": "Run 'zombienet setup polkadot polkadot-parachain' to download the binaries, then add them to PATH: export PATH=$PATH:." - }, - { - "pattern": "macOS: cannot be opened because it is from an unidentified developer", - "cause": "macOS Gatekeeper is blocking the Zombienet executable.", - "resolution": "Run: xattr -d com.apple.quarantine zombienet" - }, - { - "pattern": "Timeout: network did not start within timeout seconds", - "cause": "Binary download was slow or the chain spec file is missing or invalid.", - "resolution": "Increase the timeout value in the [settings] section of network.toml. Verify chain_spec_path exists and is valid." - }, - { - "pattern": "Test FAIL: parachain X is not registered within N seconds", - "cause": "Parachain onboarding takes time or the collator failed to start.", - "resolution": "Increase the 'within N seconds' timeout in the .zndsl assertion. Review Zombienet logs for collator startup errors." - } - ], - "supplementary_context": { - "description": "Load when the user wants more Zombienet configuration options or wants to build and deploy the parachain template first.", - "pages": [ - { - "slug": "parachains-testing-run-a-parachain-network", - "title": "Run a Parachain Network", - "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md", - "relevance": "Full tutorial for spawning a local parachain test network including building the parachain template binary." - }, - { - "slug": "parachains-testing-fork-a-parachain", - "title": "Fork a Parachain Using Chopsticks", - "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", - "relevance": "Alternative testing approach using Chopsticks to fork a live parachain for isolated state testing." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: spawn and test a relay+parachain network", - "user_says": "Spawn a local Zombienet network and run tests", - "actions": [ - "Install Zombienet executable and run zombienet setup polkadot polkadot-parachain", - "Download paseo-asset-hub.json chain spec if using Polkadot Hub parachain", - "Create network.toml with relay chain (alice, bob) and parachain (id=1000)", - "Run zombienet spawn network.toml --provider native in a separate terminal", - "Create network-test.zndsl with health and parachain assertions", - "Run zombienet test network-test.zndsl --provider native" - ], - "result": "Local network spawns successfully; all test assertions PASS" - }, - { - "scenario": "Edge case: test relay chain only without a parachain", - "user_says": "Spawn just a relay chain network for testing consensus behavior", - "actions": [ - "Remove the [[parachains]] section from network.toml", - "Remove parachain assertions from network-test.zndsl", - "Run zombienet spawn network.toml --provider native", - "Run zombienet test network-test.zndsl --provider native" - ], - "result": "Relay chain with alice and bob validators spawns; health assertions pass" - } - ] - }, - { - "id": "deploy-interact-contracts-web3js", - "title": "Deploy and Interact with Smart Contracts Using Web3.js", - "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/web3-js.md" - ], - "primary_page": "smart-contracts/libraries/web3-js.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm v6.13.4 or later" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for deployment gas — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Used in deploy.js and updateStorage.js. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir web3js-project", - "cd web3js-project", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project." - }, - { - "order": 2, - "action": "Install Web3.js and solc", - "working_directory": "web3js-project", - "commands": [ - "npm install web3", - "npm install --save-dev solc" - ], - "description": "Install the Web3.js library for blockchain interaction and solc for compiling Solidity contracts to EVM bytecode." - }, - { - "order": 3, - "action": "Create project directories and the Storage contract", - "working_directory": "web3js-project", - "commands": [ - "mkdir -p contracts scripts abis artifacts" - ], - "description": "Create the expected directory structure (contracts/, scripts/, abis/, artifacts/), then fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter:\n\n```solidity\n// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.9;\n\ncontract Storage {\n uint256 public storedNumber;\n function setNumber(uint256 _newNumber) public { storedNumber = _newNumber; }\n}\n```", - "reference_file": "Storage.sol" - }, - { - "order": 4, - "action": "Create the provider connection script and verify connectivity", - "working_directory": "web3js-project", - "description": "Fetch the reference file `connectToProvider.js` and save it as `scripts/connectToProvider.js`. Then make these substitutions:\n(1) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`\n(2) Replace `INSERT_CHAIN_ID` with `420420417`\n(3) Replace `INSERT_CHAIN_NAME` with `polkadot-hub-testnet`\n\nThen verify: `node scripts/connectToProvider.js`\nExpected output: `Connected to chain ID: 420420417`", - "reference_file": "connectToProvider.js" - }, - { - "order": 5, - "action": "Create the compile script and compile the contract", - "working_directory": "web3js-project", - "commands": [ - "node scripts/compile.js" - ], - "expected_output": "ABI and bytecode saved", - "description": "Fetch the reference file `compile.js` and save it as `scripts/compile.js`. The script reads `contracts/Storage.sol`, compiles it with solc, saves ABI to `abis/Storage.json` and bytecode to `artifacts/Storage.bin`. Then run it to compile.", - "reference_file": "compile.js" - }, - { - "order": 6, - "action": "Create the deployment script and deploy the contract", - "working_directory": "web3js-project", - "description": "Fetch the reference file `deploy.js` and save it as `scripts/deploy.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY` (do NOT hardcode the key)\n(2) Ensure the RPC URL points to `https://services.polkadothub-rpc.com/testnet`\n\nSet `PRIVATE_KEY` in your environment: `export PRIVATE_KEY=0x...` (never commit this). Then deploy:\n\n```bash\nnode scripts/deploy.js\n```\n\nExpected: contract address saved in `contract-address.json`. Record this address.", - "reference_file": "deploy.js" - }, - { - "order": 7, - "action": "Create the interaction script and interact with the contract", - "working_directory": "web3js-project", - "description": "Fetch the reference file `updateStorage.js` and save it as `scripts/updateStorage.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`\n(2) Replace `INSERT_CONTRACT_ADDRESS` with the address from step 6, or ensure `contract-address.json` is read automatically\n(3) Verify the ABI references `abis/Storage.json`\n\nThen run: `node scripts/updateStorage.js`\nExpected: current stored value displayed, then updated to 1.", - "reference_file": "updateStorage.js" - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "base_path": ".snippets/code/smart-contracts/libraries/web3-js", - "files": [ - { - "path": "Storage.sol", - "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." - }, - { - "path": "connectToProvider.js", - "description": "Web3.js provider connection to Polkadot Hub TestNet" - }, - { - "path": "compile.js", - "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts" - }, - { - "path": "deploy.js", - "description": "Deploys the compiled Storage contract via Web3.js Contract.deploy().send()" - }, - { - "path": "updateStorage.js", - "description": "Calls `setNumber(value)` on the deployed Storage contract via Web3.js, then reads `storedNumber` to confirm." - } - ], - "branch": "master" - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet PAS from https://faucet.polkadot.io/ and verify the funded account is used in deploy.js." - }, - { - "pattern": "Error: Cannot find module 'web3'", - "cause": "Web3.js dependency was not installed.", - "resolution": "Run 'npm install web3' in the web3js-project directory." - }, - { - "pattern": "Error reading ABI or bytecode file / file not found", - "cause": "Compile step was skipped or output files are not in expected locations.", - "resolution": "Run 'node scripts/compile.js' first to generate abis/Storage.json and artifacts/Storage.bin." - }, - { - "pattern": "Error: cannot connect to provider / network timeout", - "cause": "Wrong RPC URL or TestNet is temporarily unavailable.", - "resolution": "Verify the RPC URL is 'https://services.polkadothub-rpc.com/testnet'. Check Polkadot status pages for TestNet outages." - } - ], - "supplementary_context": { - "description": "Load when the user wants a modern alternative to Web3.js or asks about contract interaction options.", - "pages": [ - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Recommended modern alternative to Web3.js for deploying and interacting with contracts on Polkadot Hub." - }, - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "TypeScript-native alternative library for Polkadot Hub contract interactions." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a Storage contract and interact with it", - "user_says": "Deploy a smart contract to Polkadot Hub using Web3.js", - "actions": [ - "Create web3js-project, npm init -y, install web3 and solc", - "Create contracts/Storage.sol with the storage contract", - "Create and run scripts/connectToProvider.js (replace INSERT_RPC_URL, INSERT_CHAIN_ID, INSERT_CHAIN_NAME)", - "Create and run scripts/compile.js to generate ABI and bytecode", - "Create scripts/deploy.js, replace INSERT_PRIVATE_KEY with process.env.PRIVATE_KEY, run it", - "Create scripts/updateStorage.js, replace INSERT_PRIVATE_KEY and INSERT_CONTRACT_ADDRESS, run it" - ], - "result": "Storage contract deployed; read and write interactions confirmed" - }, - { - "scenario": "Edge case: user wants a modern library instead of Web3.js", - "user_says": "Web3.js is sunset, what should I use instead?", - "actions": [ - "Note that Web3.js has been sunset", - "Recommend Ethers.js (deploy-contracts-ethers-js) or viem (deploy-contracts-viem) as actively maintained alternatives" - ], - "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction" - } - ], - "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json" - }, - { - "id": "add-existing-pallet-to-runtime", - "title": "Add an Existing Pallet to a Parachain Runtime", - "description": "Integrates an existing Polkadot SDK pallet into a local parachain runtime. Covers adding the pallet crate to Cargo.toml, implementing the pallet's Config trait in runtime/src/lib.rs, registering the pallet in the construct_runtime! macro, compiling the runtime, generating a chain spec, and running the parachain locally to verify the pallet is active. Use when extending a Polkadot SDK parachain template with standard SDK pallets (pallet-utility, pallet-multisig, pallet-proxy, etc.). Trigger phrases: 'add pallet to runtime', 'integrate pallet parachain', 'pallet-utility runtime', 'extend parachain runtime', 'construct_runtime pallet'. Prerequisite: working parachain template dev environment (see set-up-the-parachain-template). Final GUI verification uses Polkadot.js Apps.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/add-existing-pallets.md" - ], - "primary_page": "parachains/customize-runtime/add-existing-pallets.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with the wasm32-unknown-unknown target (`rustup target add wasm32-unknown-unknown`)", - "Polkadot SDK system dependencies installed (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" - ], - "network": [ - "No external network required — all compilation and testing is local" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Identify the pallet version to use", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "grep 'polkadot-sdk' runtime/Cargo.toml | head -5" - ], - "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version — you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." - }, - { - "order": 4, - "action": "Add the pallet dependency to runtime/Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 — the output should not show dependency resolution errors." - }, - { - "order": 5, - "action": "Implement the pallet's Config trait in runtime/src/lib.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified — the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." - }, - { - "order": 6, - "action": "Register the pallet in construct_runtime!", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes — choose carefully as it cannot be changed without a migration. Save the file." - }, - { - "order": 7, - "action": "Compile the runtime", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release 2>&1 | tail -20" - ], - "description": "Build the full parachain node (includes the runtime WASM blob). This typically takes 5-15 minutes on first build. A successful build ends with: Compiling node-template vX.X.X ... Finished release [optimized] target(s). If compilation fails with 'the trait `Config` for `pallet_utility` is not satisfied', check that all required associated types are implemented in step 3." - }, - { - "order": 8, - "action": "Generate a fresh chain spec and start the node locally", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/local-spec.json", - "./target/release/parachain-template-node --dev --tmp --chain /tmp/local-spec.json" - ], - "description": "Generate a development chain spec and start the node in --dev mode. The --tmp flag stores chain data in a temporary directory. Once the node is running and producing blocks (look for: Imported #1 in logs), open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics. Verify that 'utility' appears in the pallet dropdown as a callable extrinsic module." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0277]: the trait `pallet_utility::Config` is not implemented for `Runtime`", - "cause": "The impl pallet_utility::Config for Runtime block is missing or has a typo in runtime/src/lib.rs.", - "resolution": "Ensure the impl block is present and all required associated types are specified. Run cargo check -p runtime to see the full list of missing types. Each missing type will be listed as 'required by this bound in pallet_utility::Config'." - }, - { - "pattern": "error[E0432]: unresolved import `pallet_utility`", - "cause": "pallet-utility was not added to runtime/Cargo.toml, or it was added but Cargo has not re-resolved dependencies.", - "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again — Cargo will fetch and compile the new dependency." - }, - { - "pattern": "Utility pallet does not appear in Polkadot.js Apps extrinsics", - "cause": "The pallet was registered in construct_runtime! but with the wrong name, or the node is running an old binary without the updated runtime.", - "resolution": "Stop the node, rebuild with cargo build --release, regenerate the chain spec, and restart. Verify the construct_runtime! entry name exactly matches what you expect to see in the UI." - } - ], - "supplementary_context": { - "description": "Load these pages for parachain template setup or for adding multiple pallet instances.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Rust toolchain and system dependencies required before building any parachain runtime." - }, - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Prerequisite: setting up the parachain template development environment." - }, - { - "slug": "parachains-customize-runtime-add-pallet-instances", - "title": "Add Multiple Pallet Instances", - "url": "https://docs.polkadot.com/parachains/customize-runtime/add-pallet-instances.md", - "relevance": "Next step: adding multiple instances of the same pallet (e.g., two pallet-collective instances)." - }, - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Building a custom FRAME pallet from scratch and integrating it into the runtime." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: add pallet-utility to a parachain runtime", - "user_says": "Add pallet-utility to my parachain runtime", - "actions": [ - "Check the existing Polkadot SDK version in runtime/Cargo.toml", - "Add pallet-utility dependency with matching version and default-features = false", - "Add pallet-utility/std to the std feature list", - "Implement pallet_utility::Config for Runtime in runtime/src/lib.rs with all required associated types", - "Add Utility: pallet_utility entry to construct_runtime!", - "Run cargo build --release", - "Generate chain spec and start node with --dev --tmp", - "Verify 'utility' appears in Polkadot.js Apps extrinsics" - ], - "result": "pallet-utility integrated into the runtime; utility.batch, utility.batchAll, and utility.dispatchAs are callable on-chain" - }, - { - "scenario": "Edge case: compilation fails with missing associated type in Config", - "user_says": "I get a compiler error about missing associated types in my Config impl", - "actions": [ - "Read the full compiler error — each missing type is listed with 'required by this bound'", - "Add each missing associated type to the impl block", - "Refer to the pallet's source (src/lib.rs) or docs for the expected type definitions", - "Re-run cargo build --release" - ], - "result": "All required Config associated types satisfied; compilation succeeds" - } - ] - }, - { - "id": "configure-multiple-pallet-instances", - "title": "Configure Multiple Instances of a Pallet in a Runtime", - "description": "Configures two independent instances of an instantiable Polkadot SDK pallet within a single parachain runtime. Uses pallet-collective as the example to demonstrate adding TechnicalCommittee (Instance1) and Council (Instance2) with separate membership and voting parameters. Covers identifying instantiable pallets, adding per-instance Cargo dependencies, implementing Config(Instance1) and Config(Instance2) traits, registering both instances in construct_runtime!, compiling, and verifying instance independence. Use when a runtime needs two separate governance bodies, two token pools, or any other scenario requiring two isolated instances of the same pallet logic. Trigger phrases: 'multiple pallet instances', 'two collective instances', 'pallet-collective council technical committee', 'instantiable pallet runtime'. Prerequisite: working parachain template (see add-existing-pallet-to-runtime).", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/add-pallet-instances.md" - ], - "primary_page": "parachains/customize-runtime/add-pallet-instances.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with wasm32-unknown-unknown target", - "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)", - "Familiarity with adding a single pallet to a runtime (see add-existing-pallet-to-runtime)" - ], - "network": [ - "No external network required — local development only" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Verify pallet supports instantiation", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Not all pallets support multiple instances. An instantiable pallet uses a second generic type parameter (the Instance) in its Config trait definition, e.g.: `pub trait Config: frame_system::Config`. Check the pallet's source or documentation for this pattern. pallet-collective and pallet-membership are standard examples. If the pallet does not have the Instance generic, it cannot be instantiated multiple times." - }, - { - "order": 4, - "action": "Add pallet dependencies to runtime/Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances — add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" - }, - { - "order": 5, - "action": "Implement Config for TechnicalCommittee", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the first instance's Config implementation. For pallet-collective as TechnicalCommittee:\n\n```rust\ntype TechnicalCommitteeInstance = pallet_collective::Instance1;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<5>;\n type MaxProposals = ConstU32<100>;\n type MaxMembers = ConstU32<100>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nSubstitute `ConstU32` values with the desired governance parameters." - }, - { - "order": 6, - "action": "Implement Config for Council", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage — `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." - }, - { - "order": 7, - "action": "Register both instances in construct_runtime!", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics — choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." - }, - { - "order": 8, - "action": "Compile the runtime", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release 2>&1 | tail -20" - ], - "description": "Build the parachain node. Compilation succeeds when both instance Config impls are complete and construct_runtime! entries are correct. Expected output: Finished release [optimized] target(s). If you see 'conflicting implementations of trait Config', check that each instance type alias (TechnicalCommitteeInstance, CouncilInstance) is distinct." - }, - { - "order": 9, - "action": "Verify instance independence", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/instances-spec.json", - "./target/release/parachain-template-node --dev --tmp --chain /tmp/instances-spec.json" - ], - "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics — verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0119]: conflicting implementations of trait `pallet_collective::Config`", - "cause": "Two impl blocks are targeting the same instance type — usually because both use the default instance () instead of Instance1/Instance2.", - "resolution": "Ensure each impl block explicitly names a different instance: impl pallet_collective::Config and impl pallet_collective::Config. Type aliases (type TechnicalCommitteeInstance = pallet_collective::Instance1;) help clarify intent." - }, - { - "pattern": "error: the pallet does not implement the Instance trait", - "cause": "The pallet does not support instantiation — it lacks the I: 'static = () generic in its Config trait.", - "resolution": "Only instantiable pallets (those with a second generic parameter) can be used this way. Check the pallet source or use a different pallet that supports multiple instances." - }, - { - "pattern": "Only one instance visible in Polkadot.js Apps", - "cause": "One construct_runtime! entry is missing or has a typo.", - "resolution": "Verify both entries are present in construct_runtime! with distinct names and correct instance specifiers: TechnicalCommittee: pallet_collective:: and Council: pallet_collective::. Rebuild and restart the node." - } - ], - "supplementary_context": { - "description": "Load these pages for single pallet integration or custom pallet development.", - "pages": [ - { - "slug": "parachains-customize-runtime-add-existing-pallets", - "title": "Add an Existing Pallet to the Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Prerequisite: adding a single pallet to a runtime — covers the Cargo, Config, and construct_runtime! basics." - }, - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Building a custom pallet from scratch — next step after mastering pallet integration." - }, - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Rust toolchain and build dependency installation." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: add TechnicalCommittee and Council as two pallet-collective instances", - "user_says": "Add a TechnicalCommittee and a Council to my parachain runtime using pallet-collective", - "actions": [ - "Verify pallet-collective has the I: 'static = () generic — confirm it is instantiable", - "Add pallet-collective to runtime/Cargo.toml with default-features = false and add to std features", - "Implement pallet_collective::Config as TechnicalCommitteeInstance with committee-appropriate parameters", - "Implement pallet_collective::Config as CouncilInstance with council-appropriate parameters", - "Add TechnicalCommittee: pallet_collective:: and Council: pallet_collective:: to construct_runtime!", - "Run cargo build --release", - "Start node in --dev mode and verify both 'technicalCommittee' and 'council' appear in Polkadot.js Apps" - ], - "result": "Two independent pallet-collective instances registered — TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" - }, - { - "scenario": "Edge case: runtime compiles but only one instance shows in the UI", - "user_says": "My runtime compiled but I only see one collective in Polkadot.js Apps", - "actions": [ - "Check construct_runtime! for both TechnicalCommittee and Council entries", - "Verify each entry uses the correct instance specifier: :: and ::", - "Rebuild with cargo build --release", - "Regenerate chain spec and restart node" - ], - "result": "Both collective instances visible and callable in Polkadot.js Apps" - } - ] - }, - { - "id": "create-frame-pallet", - "title": "Create a Custom FRAME Pallet", - "description": "Guides through building a custom FRAME pallet from scratch using the Polkadot SDK. Uses a counter pallet (increment and decrement extrinsics with bounded storage) as the example to demonstrate all core FRAME patterns: pallet directory layout, Cargo.toml configuration, #[pallet::config], #[pallet::storage], #[pallet::event], #[pallet::error], #[pallet::call], and genesis config. After implementing the pallet, integrates it into the parachain runtime (Cargo dependency, Config impl, construct_runtime!), compiles, and runs locally. Use when building any new runtime module with on-chain state and extrinsics. Trigger phrases: 'create FRAME pallet', 'custom pallet from scratch', 'write a pallet', 'FRAME storage events extrinsics', 'build counter pallet'. Prerequisites: Polkadot SDK installed, parachain template set up. Verification step uses Polkadot.js Apps GUI.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/create-a-pallet.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/create-a-pallet.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown)", - "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" - ], - "network": [ - "No external network required — all work is local" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create the pallet directory and Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "mkdir -p pallets/counter/src", - "touch pallets/counter/src/lib.rs" - ], - "description": "Create the pallet directory structure inside the parachain template workspace. The pallet name (counter) becomes the crate name. Create pallets/counter/Cargo.toml with the following content, replacing with the version matching your other workspace pallets:\n\n```toml\n[package]\nname = \"pallet-counter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ncodec = { package = \"parity-scale-codec\", version = \"3\", default-features = false, features = [\"derive\"] }\nscale-info = { version = \"2\", default-features = false, features = [\"derive\"] }\nframe-support = { version = \"\", default-features = false }\nframe-system = { version = \"\", default-features = false }\nframe-benchmarking = { version = \"\", default-features = false, optional = true }\n\n[features]\ndefault = [\"std\"]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame-support/std\",\n \"frame-system/std\",\n]\nruntime-benchmarks = [\"frame-benchmarking/runtime-benchmarks\"]\n```\n\nAlso add pallet-counter to the workspace's root Cargo.toml members list:\nmembers = [\n ...\n \"pallets/counter\",\n]" - }, - { - "order": 4, - "action": "Implement the pallet in pallets/counter/src/lib.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Write the pallet implementation in pallets/counter/src/lib.rs. A minimal counter pallet contains:\n\n#![cfg_attr(not(feature = \"std\"), no_std)]\n\npub use pallet::*;\n\n```rust\n#[frame_support::pallet]\npub mod pallet {\n use frame_support::pallet_prelude::*;\n use frame_system::pallet_prelude::*;\n\n #[pallet::pallet]\n pub struct Pallet(_);\n\n #[pallet::config]\n pub trait Config: frame_system::Config {\n type RuntimeEvent: From> + IsType<::RuntimeEvent>;\n #[pallet::constant]\n type MaxValue: Get;\n }\n```\n\n #[pallet::storage]\n pub type CounterValue = StorageValue<_, u32, ValueQuery>;\n\n```rust\n #[pallet::event]\n #[pallet::generate_deposit(pub(super) fn deposit_event)]\n pub enum Event {\n CounterIncremented { new_value: u32 },\n CounterDecremented { new_value: u32 },\n }\n\n #[pallet::error]\n pub enum Error {\n CounterOverflow,\n CounterUnderflow,\n }\n\n #[pallet::call]\n impl Pallet {\n #[pallet::call_index(0)]\n #[pallet::weight(10_000)]\n pub fn increment(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_add(1).ok_or(Error::::CounterOverflow)?;\n ensure!(new_val <= T::MaxValue::get(), Error::::CounterOverflow);\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterIncremented { new_value: new_val });\n Ok(())\n }\n\n #[pallet::call_index(1)]\n #[pallet::weight(10_000)]\n pub fn decrement(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_sub(1).ok_or(Error::::CounterUnderflow)?;\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterDecremented { new_value: new_val });\n Ok(())\n }\n }\n}\n```\n\nThe MaxValue constant bounds the counter to prevent overflow. checked_add/checked_sub replace panicking arithmetic." - }, - { - "order": 5, - "action": "Add pallet-counter to runtime/Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml and add pallet-counter as a workspace dependency:\n\n```toml\n[dependencies]\n...\npallet-counter = { path = \"../pallets/counter\", default-features = false }\n\nAdd to the std feature list:\n[features]\nstd = [\n ...\n \"pallet-counter/std\",\n]\n```\n\nNote: path = \"../pallets/counter\" is a relative path from the runtime/ directory to the pallet directory." - }, - { - "order": 6, - "action": "Implement pallet_counter::Config in runtime/src/lib.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 — adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." - }, - { - "order": 7, - "action": "Register the pallet in construct_runtime!", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Find construct_runtime! in runtime/src/lib.rs and add the counter pallet:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n Counter: pallet_counter,\n }\n);\n```\n\nThe name Counter is the on-chain pallet identifier. Save the file." - }, - { - "order": 8, - "action": "Compile and run locally", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release 2>&1 | tail -20", - "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/counter-spec.json", - "./target/release/parachain-template-node --dev --tmp --chain /tmp/counter-spec.json" - ], - "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics — 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0412]: cannot find type `ConstU32` in this scope", - "cause": "ConstU32 is not imported in runtime/src/lib.rs.", - "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it — search for existing ConstU32 usages to confirm." - }, - { - "pattern": "error[E0277]: the trait `pallet_counter::Config` is not implemented", - "cause": "The impl pallet_counter::Config for Runtime block is missing or has a missing associated type.", - "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type — add them one by one." - }, - { - "pattern": "error: failed to select a version for `pallet-counter` / could not find Cargo.toml", - "cause": "The path in runtime/Cargo.toml does not match the actual pallet directory location.", - "resolution": "Verify the relative path: from runtime/, pallets/counter/ should be at ../pallets/counter. Run ls ../pallets/counter/Cargo.toml from inside the runtime/ directory to confirm the path exists." - }, - { - "pattern": "CounterOverflow error when submitting increment", - "cause": "The counter has reached MaxValue (100 by default).", - "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design — the error confirms the overflow protection is working." - } - ], - "supplementary_context": { - "description": "Load these pages for runtime setup prerequisites or next steps in pallet development.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Rust and system dependencies required before building any FRAME pallet." - }, - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Setting up the parachain template workspace where the custom pallet lives." - }, - { - "slug": "parachains-customize-runtime-add-existing-pallets", - "title": "Add an Existing Pallet to the Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Adding an existing SDK pallet to a runtime — prerequisite concepts for runtime integration." - }, - { - "slug": "parachains-customize-runtime-pallet-development-mock-runtime", - "title": "Mock Your Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md", - "relevance": "Next step: setting up a mock runtime for unit testing the custom pallet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: build a counter pallet with increment and decrement extrinsics", - "user_says": "Create a custom FRAME pallet from scratch", - "actions": [ - "Create pallets/counter/src/ directory and lib.rs file", - "Write Cargo.toml with frame-support, frame-system, parity-scale-codec, and scale-info dependencies", - "Add pallet-counter to workspace root Cargo.toml members", - "Implement the pallet in lib.rs: Config trait, StorageValue, Event enum, Error enum, and two call extrinsics (increment/decrement) with overflow/underflow protection", - "Add pallet-counter path dependency to runtime/Cargo.toml", - "Implement pallet_counter::Config for Runtime with MaxValue = ConstU32<100>", - "Add Counter: pallet_counter to construct_runtime!", - "Run cargo build --release", - "Start node in --dev mode; submit increment extrinsic via Polkadot.js Apps; verify CounterValue storage" - ], - "result": "Custom counter pallet deployed locally; increment and decrement extrinsics callable on-chain; CounterValue storage updates correctly with overflow protection" - }, - { - "scenario": "Edge case: adding a genesis config to set an initial counter value", - "user_says": "How do I set the initial counter value to 10 at genesis?", - "actions": [ - "Add a #[pallet::genesis_config] struct and #[pallet::genesis_build] impl to the pallet's lib.rs to accept an initial_value", - "In the runtime's chain_spec.rs (or equivalent), include Counter: CounterConfig { initial_value: 10 } in the genesis state", - "Rebuild and regenerate the chain spec to include the genesis config" - ], - "result": "Node starts with CounterValue pre-set to 10; visible in Developer > Chain State > counter.counterValue immediately after genesis" - } - ] - }, - { - "id": "retrieve-runtime-metadata", - "title": "Retrieve Polkadot Runtime Metadata", - "description": "Provides three methods for fetching Polkadot SDK runtime metadata: curl JSON-RPC (state_getMetadata, returns raw hex bytes), subxt CLI (human-readable JSON, ideal for inspection), and Polkadot.js Apps RPC UI (interactive). Use when building client libraries, inspecting pallet structure, or verifying runtime composition after an upgrade. Trigger phrases: 'get runtime metadata', 'state_getMetadata', 'inspect pallet calls', 'fetch chain metadata', 'subxt metadata', 'what pallets are on chain'. No private key or wallet required. The metadata output describes all pallets, storage items, calls, events, errors, and constants in the current runtime.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/parachains/chain-data.md" - ], - "primary_page": "reference/parachains/chain-data.md", - "prerequisites": { - "runtime": [ - "curl (Method A — available by default on macOS and most Linux distros)", - "subxt CLI (Method B — install with: cargo install subxt-cli)" - ], - "network": [ - "An accessible RPC endpoint for the target Polkadot SDK node (e.g. https://rpc.polkadot.io for mainnet, or http://127.0.0.1:9944 for a local dev node)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "(Method A) Fetch metadata via curl JSON-RPC", - "working_directory": ".", - "commands": [ - "curl -H \"Content-Type: application/json\" -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\"}' https://rpc.polkadot.io" - ], - "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string — not human-readable. Use Method B or C if you need human-readable JSON.", - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":\"0x6d657461...\",\"id\":1}" - }, - { - "order": 2, - "action": "(Method B) Fetch metadata as human-readable JSON using subxt CLI", - "working_directory": ".", - "commands": [ - "subxt metadata --url wss://rpc.polkadot.io --format json > spec.json" - ], - "description": "Install the subxt CLI if not already installed: cargo install subxt-cli. Run subxt metadata to fetch and decode the metadata to JSON. Replace wss://rpc.polkadot.io with your node's WebSocket endpoint (wss:// for remote, ws:// for local). For a local dev node: subxt metadata --url ws://127.0.0.1:9944 --format json > spec.json. You can also explore metadata interactively at https://paritytech.github.io/subxt-explorer/.", - "expected_output": "spec.json created containing the runtime metadata in human-readable JSON format" - }, - { - "order": 3, - "action": "(Method C) Retrieve metadata via Polkadot.js Apps RPC UI", - "working_directory": ".", - "description": "Navigate to https://polkadot.js.org/apps/#/rpc and connect to your target node. Click the Developer dropdown and select RPC Calls. 1. Select state as the endpoint. 2. Select getMetadata(at) as the method. 3. Click Submit RPC Call. The metadata is returned in JSON format in the UI. This is the most interactive option but is not scriptable." - }, - { - "order": 4, - "action": "Interpret the metadata output", - "working_directory": ".", - "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type → find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions — do not hardcode them." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "curl returns connection refused or timeout", - "cause": "The RPC endpoint URL is incorrect, the node is not running, or the endpoint does not accept HTTP connections.", - "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections — use subxt (Method B) with ws:// or wss:// instead of curl." - }, - { - "pattern": "subxt metadata fails with 'failed to fetch metadata'", - "cause": "The WebSocket URL is incorrect, the node is not running, or the subxt-cli version is incompatible with the node's metadata version.", - "resolution": "Verify the WebSocket URL (wss:// for TLS, ws:// for local). Confirm the node is running. Update subxt-cli: cargo install subxt-cli --force." - }, - { - "pattern": "Metadata output shows unexpected pallets or an empty pallets array", - "cause": "The node connected to is a different chain than expected, or the genesis has not been customized.", - "resolution": "Verify the RPC endpoint points to the intended chain. For local dev nodes ensure the chain spec matches the expected runtime." - } - ], - "supplementary_context": { - "description": "Load these pages for context on how metadata is used in client applications or SDK-level interactions.", - "pages": [ - { - "slug": "reference-tools-subxt", - "title": "Subxt Rust API", - "url": "https://docs.polkadot.com/reference/tools/subxt.md", - "relevance": "Subxt library usage for generating typed client code from runtime metadata — the primary consumer of metadata in Rust applications." - }, - { - "slug": "reference-parachains-data-encoding", - "title": "Data Encoding", - "url": "https://docs.polkadot.com/reference/parachains/data-encoding.md", - "relevance": "SCALE encoding details that explain why the raw curl metadata output is hex-encoded bytes rather than readable JSON." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: inspect pallet list and call signatures of Polkadot mainnet", - "user_says": "Get the runtime metadata from the Polkadot mainnet node", - "actions": [ - "Run: subxt metadata --url wss://rpc.polkadot.io --format json > spec.json", - "Open spec.json and locate the pallets array", - "Find a specific pallet by name (e.g. Balances) and follow its calls type index to read available calls" - ], - "result": "spec.json contains the complete runtime metadata in human-readable JSON; pallets, calls, storage items, events, and types are all inspectable." - }, - { - "scenario": "Edge case: inspect metadata from a local dev node", - "user_says": "How do I get the metadata from my local dev node at ws://127.0.0.1:9944?", - "actions": [ - "Verify the local node is running (polkadot-omni-node --dev or similar)", - "Run: subxt metadata --url ws://127.0.0.1:9944 --format json > local-spec.json", - "Or use curl: curl -H \"Content-Type: application/json\" -d '{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"state_getMetadata\"}' http://127.0.0.1:9944" - ], - "result": "Metadata from the local dev node retrieved; reflects the exact runtime version currently running locally." - } - ] - }, - { - "id": "use-polkadot-js-api", - "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", - "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode — new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/polkadot-js-api.md" - ], - "primary_page": "reference/tools/polkadot-js-api.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.ibp.network/polkadot for Polkadot mainnet)" - ] - }, - "env_vars": [ - { - "name": "WS_ENDPOINT", - "description": "WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet).", - "required": true - }, - { - "name": "MNEMONIC", - "description": "12 or 24-word seed phrase for the signing account. Required only for the transaction-sending step.", - "required": false - } - ], - "steps": [ - { - "order": 1, - "action": "Initialize ESM Node.js project and install @polkadot/api", - "working_directory": ".", - "commands": [ - "mkdir polkadot-api-demo && cd polkadot-api-demo", - "npm init -y && npm pkg set type=module", - "npm install @polkadot/api @polkadot/keyring dotenv" - ], - "description": "Create a new directory named polkadot-api-demo and initialize it as an ESM Node.js project. The type=module flag is required because @polkadot packages use ESM-only imports. Install dotenv for secure credential loading." - }, - { - "order": 2, - "action": "Create .env file and add to .gitignore", - "working_directory": "polkadot-api-demo", - "commands": [ - "printf 'WS_ENDPOINT=\\nMNEMONIC=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly — do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." - }, - { - "order": 3, - "action": "Create the chain-query script", - "working_directory": "polkadot-api-demo", - "description": "Create a file named query.ts with the following content. Replace INSERT_ADDRESS with the SS58-encoded address to query (e.g., an address from the create-polkadot-account skill output):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const chain = await api.rpc.system.chain();\n const existentialDeposit = api.consts.balances.existentialDeposit;\n console.log(`Connected to: ${chain}`);\n console.log(`Existential deposit: ${existentialDeposit.toHuman()}`);\n const account = await api.query.system.account('INSERT_ADDRESS');\n console.log('Account data:', account.toHuman());\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);" - }, - { - "order": 4, - "action": "Run the query script", - "working_directory": "polkadot-api-demo", - "commands": [ - "npx tsx query.ts" - ], - "expected_output": "Connected to: Polkadot (or your target chain name)\nExistential deposit: 10 mDOT (or equivalent)\nAccount data: { nonce: ..., data: { free: ..., reserved: ... } }" - }, - { - "order": 5, - "action": "Create and run the transaction script", - "working_directory": "polkadot-api-demo", - "description": "Create a file named transfer.ts. Only proceed if MNEMONIC is set in .env. Replace INSERT_RECIPIENT with the destination SS58 address and INSERT_AMOUNT with the transfer amount in planck units (e.g., 1000000000000 = 1 DOT with 10 decimals, or 1000000000000000000 = 1 PAS with 18 decimals):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider, Keyring } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const keyring = new Keyring({ type: 'sr25519' });\n const sender = keyring.addFromUri(process.env.MNEMONIC!);\n console.log(`Sending from: ${sender.address}`);\n const txHash = await api.tx.balances\n .transferKeepAlive('INSERT_RECIPIENT', INSERT_AMOUNT)\n .signAndSend(sender);\n console.log(`Transaction hash: ${txHash}`);\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);\n\nThen run: npx tsx transfer.ts" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: WebSocket is not open / ECONNREFUSED", - "cause": "The WS_ENDPOINT URL is unreachable or the protocol is wrong.", - "resolution": "Verify WS_ENDPOINT in .env starts with wss:// (not https://). Check the node is online. For Polkadot Hub TestNet use wss://services.polkadothub-rpc.com/testnet." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "The project is not configured as an ESM module.", - "resolution": "Run 'npm pkg set type=module' in the polkadot-api-demo directory, then retry." - }, - { - "pattern": "Error: Cannot find module '@polkadot/api'", - "cause": "Dependencies not installed.", - "resolution": "Run 'npm install @polkadot/api @polkadot/keyring dotenv' in the polkadot-api-demo directory." - }, - { - "pattern": "RpcError: 1010: Invalid Transaction: Inability to pay some fees", - "cause": "The sender account has insufficient funds to pay transaction fees.", - "resolution": "Fund the sender account with tokens. For Polkadot Hub TestNet use the faucet at https://faucet.polkadot.io/." - } - ], - "supplementary_context": { - "description": "Load these pages when the user asks about API alternatives, migration paths, or needs deeper context on chain interactions.", - "pages": [ - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Polkadot API (PAPI) — the recommended replacement for @polkadot/api in new projects." - }, - { - "slug": "reference-tools-dedot", - "title": "Dedot", - "url": "https://docs.polkadot.com/reference/tools/dedot.md", - "relevance": "Dedot — modern lightweight alternative to @polkadot/api with better TypeScript inference." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "Multi-SDK transaction guide covering PAPI, Polkadot.js, Dedot, and Subxt variants." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance", - "user_says": "Use @polkadot/api to check an account balance on Polkadot Hub", - "actions": [ - "Initialize ESM Node.js project and install @polkadot/api", - "Create .env with WS_ENDPOINT set to the Polkadot Hub TestNet WebSocket URL", - "Create query.ts with ApiPromise.create() and api.query.system.account()", - "Run npx tsx query.ts and read account balance output" - ], - "result": "Account free/reserved/frozen balances printed to console; chain name and existential deposit shown" - }, - { - "scenario": "Edge case: user is starting a new project", - "user_says": "I want to build a new Polkadot app with polkadot.js api", - "actions": [ - "Surface maintenance-mode warning: @polkadot/api is in maintenance mode", - "Recommend PAPI (reference-tools-papi) or Dedot (reference-tools-dedot) for new projects", - "If user confirms they want @polkadot/api anyway, proceed with installation steps" - ], - "result": "User informed of deprecation status; skill proceeds with @polkadot/api if that is the deliberate choice" - } - ] - }, - { - "id": "deploy-basic-contract-remix", - "title": "Deploy a Basic Smart Contract with Remix IDE", - "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", - "Chain ID: 420420417" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ (rate limit: check faucet page)" - ], - "wallet": [ - "MetaMask browser extension installed and configured for Polkadot Hub TestNet", - "Wallet funded with testnet PAS tokens" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Verify MetaMask is connected to Polkadot Hub TestNet", - "working_directory": ".", - "description": "Instruct the user: Open MetaMask and confirm the selected network is 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add the network. Also confirm the account balance shows testnet PAS tokens. If the balance is zero, visit https://faucet.polkadot.io/ to request tokens. Wait for the user to confirm both conditions before proceeding." - }, - { - "order": 2, - "action": "Open Remix IDE and locate Storage.sol", - "working_directory": ".", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed." - }, - { - "order": 3, - "action": "Compile Storage.sol in Remix", - "working_directory": ".", - "description": "Instruct the user: Click the Solidity Compiler icon (second icon in the left sidebar). Confirm the compiler version is 0.8.x or higher. Click the blue 'Compile 1_Storage.sol' button. Wait for the green checkmark indicating successful compilation. If there are errors, verify the compiler version matches the pragma in the file.", - "expected_output": "Green checkmark on the Solidity Compiler sidebar icon; no errors shown." - }, - { - "order": 4, - "action": "Deploy to Polkadot Hub TestNet via MetaMask", - "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", - "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel." - }, - { - "order": 5, - "action": "Interact with the deployed Storage contract", - "working_directory": ".", - "description": "Instruct the user: In the 'Deployed Contracts' panel, expand the deployed Storage contract. To write a value: enter a number in the field next to the 'store' button and click 'store'. Confirm the MetaMask transaction. To read the stored value: click the 'retrieve' button (no transaction needed). The return value shows in the Remix console below.", - "expected_output": "retrieve() returns the number stored in the previous store() call." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask: User denied account access / MetaMask not connected", - "cause": "MetaMask connection was rejected or MetaMask is not installed.", - "resolution": "Click the MetaMask extension icon and ensure it is unlocked. Re-select 'Injected Provider - MetaMask' in the Remix Environment dropdown and approve the connection prompt." - }, - { - "pattern": "Transaction failed: insufficient funds for gas", - "cause": "The connected MetaMask account has no testnet PAS tokens.", - "resolution": "Visit https://faucet.polkadot.io/ to obtain testnet PAS tokens. Ensure the faucet is sending to the correct address shown in MetaMask." - }, - { - "pattern": "Wrong network in MetaMask / chain ID mismatch", - "cause": "MetaMask is connected to a different network instead of Polkadot Hub TestNet.", - "resolution": "Open MetaMask and switch to 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add it." - }, - { - "pattern": "Solidity compile error: pragma mismatch", - "cause": "The Remix compiler version does not match the pragma in Storage.sol.", - "resolution": "In the Solidity Compiler tab, select a compiler version compatible with the pragma at the top of the contract file." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to set up MetaMask, obtain tokens, or move to a CLI-based deployment workflow.", - "pages": [ - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to configure MetaMask or Talisman for Polkadot Hub TestNet." - }, - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "How to obtain testnet PAS tokens for deployment transactions." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "CLI-based alternative: deploy the same Storage contract using Hardhat and a local toolchain." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: first contract deployment", - "user_says": "Deploy a smart contract on Polkadot Hub using Remix IDE", - "actions": [ - "Verify MetaMask is on Polkadot Hub TestNet and account has PAS tokens", - "Open remix.ethereum.org and locate contracts/1_Storage.sol", - "Compile with Solidity Compiler tab", - "Select Injected Provider - MetaMask in Deploy tab and click Deploy", - "Confirm MetaMask transaction and wait for mining" - ], - "result": "Storage contract deployed; appears in Remix Deployed Contracts panel with callable store/retrieve functions" - }, - { - "scenario": "Edge case: no testnet tokens", - "user_says": "I try to deploy but MetaMask says insufficient funds", - "actions": [ - "Direct user to https://faucet.polkadot.io/ to request testnet PAS tokens", - "Ask user to confirm the faucet sent tokens to the correct address", - "Once confirmed, retry the Deploy step in Remix" - ], - "result": "User obtains testnet PAS tokens and deployment proceeds successfully" - } - ] - }, - { - "id": "connect-remix-polkadot", - "title": "Connect Remix IDE to Polkadot Hub", - "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/remix.md" - ], - "primary_page": "smart-contracts/dev-environments/remix.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed and unlocked with Polkadot Hub TestNet configured (chain ID 420420417)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Open Remix IDE", - "working_directory": ".", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. Wait for the IDE to fully load (the file explorer appears on the left). Ensure MetaMask is unlocked and set to Polkadot Hub TestNet before proceeding." - }, - { - "order": 2, - "action": "Navigate to the Deploy and Run Transactions tab", - "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left." - }, - { - "order": 3, - "action": "Select Injected Provider - MetaMask", - "working_directory": ".", - "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." - }, - { - "order": 4, - "action": "Verify the connection", - "working_directory": ".", - "description": "Instruct the user: Confirm the following in the Deploy panel: 1) The Account field shows your MetaMask wallet address. 2) The network indicator shows chain ID 420420417 (Polkadot Hub TestNet). If the chain ID shows a different value, switch networks in MetaMask to Polkadot Hub TestNet and the Remix panel will update automatically.", - "expected_output": "Remix Deploy panel shows MetaMask account address and Polkadot Hub TestNet (chain ID 420420417)." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask not detected / No injected Web3 provider", - "cause": "MetaMask extension is not installed or is disabled in the browser.", - "resolution": "Install the MetaMask browser extension from https://metamask.io and reload Remix. Ensure the extension is enabled for the remix.ethereum.org domain." - }, - { - "pattern": "Wrong network shown in Remix (chain ID is not 420420417)", - "cause": "MetaMask is connected to a different chain.", - "resolution": "Open MetaMask and manually switch to Polkadot Hub TestNet. If Polkadot Hub TestNet is not in the list, follow the connect-wallet-polkadot-hub skill to add it." - }, - { - "pattern": "Remix Deploy panel shows 'JavaScript VM' after trying to switch", - "cause": "The injected provider selection was not saved or MetaMask was not connected.", - "resolution": "Click the Environment dropdown again and re-select 'Injected Provider - MetaMask'. Approve the MetaMask connection popup if it appears." - } - ], - "supplementary_context": { - "description": "Load these pages for wallet setup or when the user wants to deploy a contract after connecting.", - "pages": [ - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask before using Remix." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-remix", - "title": "Deploy a Basic Contract with Remix IDE", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", - "relevance": "Next step after connecting Remix: deploy the default Storage.sol contract." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: connect Remix before deployment", - "user_says": "Connect Remix IDE to Polkadot Hub so I can deploy a contract", - "actions": [ - "Open https://remix.ethereum.org", - "Click Deploy and Run Transactions tab", - "Select Injected Provider - MetaMask in the Environment dropdown", - "Approve the MetaMask connection popup", - "Verify account address and chain ID 420420417 appear in the Deploy panel" - ], - "result": "Remix IDE is connected to Polkadot Hub TestNet via MetaMask and ready for contract deployment" - }, - { - "scenario": "Edge case: MetaMask on wrong network", - "user_says": "Remix shows the wrong chain ID after connecting", - "actions": [ - "Ask user to open MetaMask and switch to Polkadot Hub TestNet", - "If network not present, direct to connect-wallet-polkadot-hub skill", - "Confirm Remix updates to show chain ID 420420417 automatically" - ], - "result": "Remix shows correct network after MetaMask network switch" - } - ] - }, - { - "id": "connect-wallet-polkadot-hub", - "title": "Connect a Wallet to Polkadot Hub", - "description": "Guides the user through adding Polkadot Hub TestNet (RPC, chain ID 420420417, PAS token) to MetaMask or Talisman and switching the active network. All steps are browser extension GUI interactions. Use before deploying smart contracts, using Remix IDE with Polkadot Hub, or obtaining testnet tokens. Trigger phrases: 'connect metamask polkadot hub', 'add polkadot network metamask', 'set up wallet polkadot testnet', 'configure talisman polkadot hub'. Outcome: wallet shows Polkadot Hub TestNet as active network and is ready for on-chain interactions.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/integrations/wallets.md" - ], - "primary_page": "smart-contracts/integrations/wallets.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed (https://metamask.io) OR Talisman installed (https://talisman.xyz)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Open MetaMask network settings", - "working_directory": ".", - "description": "Instruct the user: Click the MetaMask extension icon in the browser toolbar to open it. Click the network selector at the top of the MetaMask popup (shows 'Ethereum Mainnet' by default). Click 'Add network' at the bottom of the network list. On the Add Network page, click 'Add a network manually'." - }, - { - "order": 2, - "action": "Enter Polkadot Hub TestNet network details", - "working_directory": ".", - "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." - }, - { - "order": 3, - "action": "Switch to Polkadot Hub TestNet", - "working_directory": ".", - "description": "Instruct the user: In MetaMask, click the network selector again. Select 'Polkadot Hub TestNet' from the list. The header now shows 'Polkadot Hub TestNet' and the balance shows PAS.", - "expected_output": "MetaMask header displays 'Polkadot Hub TestNet'; account balance shows PAS balance." - }, - { - "order": 4, - "action": "Obtain testnet PAS tokens if needed", - "working_directory": ".", - "description": "If the PAS balance is 0, instruct the user: Visit https://faucet.polkadot.io/ in the browser. Paste the MetaMask account address (click the address in MetaMask to copy it). Select 'Polkadot Hub TestNet' in the faucet network dropdown. Click 'Get tokens'. Tokens typically arrive within 30 seconds.", - "expected_output": "MetaMask PAS balance shows a non-zero amount." - }, - { - "order": 5, - "action": "Alternative: Add Polkadot Hub TestNet to Talisman", - "working_directory": ".", - "description": "If the user prefers Talisman instead of MetaMask, instruct them: Open the Talisman extension and click the settings icon. Go to Networks and Tokens, then Add Network. Enter the same network details as step 2: RPC URL: https://services.polkadothub-rpc.com/testnet, Chain ID: 420420417, Symbol: PAS. Save and switch to the Polkadot Hub TestNet network in Talisman." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Could not fetch chain ID / Invalid RPC URL", - "cause": "The RPC URL was entered incorrectly or the TestNet endpoint is temporarily unavailable.", - "resolution": "Verify the RPC URL is exactly: https://services.polkadothub-rpc.com/testnet (no trailing slash). Check the Polkadot Hub TestNet status page or Discord for outages." - }, - { - "pattern": "Chain ID mismatch: expected 420420417", - "cause": "A different endpoint was used that returns a different chain ID.", - "resolution": "Delete the incorrectly configured network from MetaMask and re-add it using the exact RPC URL and chain ID 420420417." - }, - { - "pattern": "Network added but balance shows 0 PAS", - "cause": "Account not funded with testnet tokens.", - "resolution": "Visit https://faucet.polkadot.io/, paste your address, select Polkadot Hub TestNet, and request tokens." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs network reference data or wants to proceed to contract deployment after wallet setup.", - "pages": [ - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Full network connection reference with RPC URLs, chain IDs, and WSS endpoints for all Polkadot Hub environments." - }, - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "Step-by-step guide to obtaining testnet PAS tokens from the Polkadot faucet." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub using the configured MetaMask wallet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: add Polkadot Hub TestNet to MetaMask", - "user_says": "Add Polkadot Hub TestNet to MetaMask and get some test tokens", - "actions": [ - "Open MetaMask, click network selector, click Add network, then Add a network manually", - "Enter network name, RPC URL (https://services.polkadothub-rpc.com/testnet), chain ID 420420417, symbol PAS", - "Save and switch to Polkadot Hub TestNet", - "Visit https://faucet.polkadot.io/ and request testnet PAS tokens" - ], - "result": "MetaMask shows Polkadot Hub TestNet as active network with a non-zero PAS balance" - }, - { - "scenario": "Edge case: RPC URL rejected by MetaMask", - "user_says": "MetaMask says it could not fetch the chain ID when I enter the RPC URL", - "actions": [ - "Verify the RPC URL is exactly https://services.polkadothub-rpc.com/testnet with no trailing slash", - "Check whether the Polkadot Hub TestNet is experiencing downtime", - "Retry adding the network once the endpoint is confirmed reachable" - ], - "result": "Network added successfully once the correct RPC URL is verified and the endpoint is reachable" - } - ] - }, - { - "id": "use-wagmi-polkadot-hub", - "title": "Build a Wagmi dApp Connected to Polkadot Hub", - "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo — all code is inlined in the steps.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/wagmi.md" - ], - "primary_page": "smart-contracts/libraries/wagmi.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "wallet": [ - "MetaMask or any EIP-1193 wallet installed in the browser" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Scaffold the Next.js project non-interactively", - "working_directory": ".", - "commands": [ - "npx create-next-app@15 wagmi-dapp --ts --no-eslint --no-tailwind --no-src-dir --app --use-npm --skip-install", - "cd wagmi-dapp", - "npm install" - ], - "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input — `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm — skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." - }, - { - "order": 2, - "action": "Install Wagmi v3 and peer dependencies", - "working_directory": "wagmi-dapp", - "commands": [ - "npm install wagmi viem@2.x @tanstack/react-query" - ], - "description": "Install wagmi (React hooks for Ethereum), viem v2 (low-level EVM transport), and @tanstack/react-query (required peer dependency for Wagmi v3 data-fetching hooks)." - }, - { - "order": 3, - "action": "Create the Wagmi config file with Polkadot Hub chain", - "working_directory": "wagmi-dapp", - "description": "Create a new file at src/wagmi.ts (or wagmi.ts in the project root if using the src/ layout). Paste the following content exactly:\n\n```typescript\nimport { createConfig, http } from 'wagmi';\nimport { defineChain } from 'viem';\nimport { injected } from 'wagmi/connectors';\n\nexport const polkadotHubTestnet = defineChain({\n id: 420420417,\n name: 'Polkadot Hub TestNet',\n nativeCurrency: { name: 'PAS', symbol: 'PAS', decimals: 18 },\n rpcUrls: {\n default: { http: ['https://services.polkadothub-rpc.com/testnet'] },\n },\n});\n\nexport const config = createConfig({\n chains: [polkadotHubTestnet],\n connectors: [injected()],\n transports: {\n [polkadotHubTestnet.id]: http(),\n },\n});\n```\n\nThis configures Wagmi to use Polkadot Hub TestNet as the only chain with the injected (MetaMask) connector." - }, - { - "order": 4, - "action": "Wrap the app with WagmiProvider and QueryClientProvider", - "working_directory": "wagmi-dapp", - "description": "Open src/app/layout.tsx (or pages/_app.tsx if using pages router). Add a client-side providers wrapper. Create src/app/providers.tsx with:\n\n```typescript\n'use client';\nimport { WagmiProvider } from 'wagmi';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { config } from '../wagmi';\n\nconst queryClient = new QueryClient();\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n return (\n \n \n {children}\n \n \n );\n}\n```\n\nThen wrap the body in src/app/layout.tsx with: import { Providers } from './providers'; and wrap {children} with Providers." - }, - { - "order": 5, - "action": "Add wallet connection component", - "working_directory": "wagmi-dapp", - "description": "Create src/components/ConnectWallet.tsx with the following content:\n\n```typescript\n'use client';\nimport { useAccount, useConnect, useDisconnect } from 'wagmi';\n\nexport function ConnectWallet() {\n const { address, isConnected } = useAccount();\n const { connect, connectors } = useConnect();\n const { disconnect } = useDisconnect();\n\n if (isConnected) {\n return (\n
\n

Connected: {address}

\n \n
\n );\n }\n\n return (\n
\n {connectors.map((connector) => (\n \n ))}\n
\n );\n}\n```\n\nThis renders a connect/disconnect button using the injected (MetaMask) connector." - }, - { - "order": 6, - "action": "Add block number query using useBlockNumber", - "working_directory": "wagmi-dapp", - "description": "Create src/components/BlockNumber.tsx with:\n\n```typescript\n'use client';\nimport { useBlockNumber } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nexport function BlockNumber() {\n const { data: blockNumber } = useBlockNumber({\n chainId: polkadotHubTestnet.id,\n watch: true,\n });\n return

Current block: {blockNumber?.toString() ?? 'Loading...'}

;\n}\n```\n\nThe watch: true option polls for new blocks and updates the component automatically." - }, - { - "order": 7, - "action": "Add Storage contract read interaction using useReadContract", - "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type — without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." - }, - { - "order": 8, - "action": "Add Storage contract write interaction using useWriteContract", - "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from ." - }, - { - "order": 9, - "action": "Assemble components in the main page and start the dev server", - "working_directory": "wagmi-dapp", - "commands": [ - "npm run dev" - ], - "description": "Open src/app/page.tsx and replace its content with:\n\n```typescript\nimport { ConnectWallet } from '../components/ConnectWallet';\nimport { BlockNumber } from '../components/BlockNumber';\nimport { StorageRead } from '../components/StorageRead';\nimport { StorageWrite } from '../components/StorageWrite';\n\nexport default function Home() {\n return (\n
\n

Wagmi Polkadot Hub dApp

\n \n \n \n \n
\n );\n}\n```\n\nThen run npm run dev to start the Next.js dev server on http://localhost:3000.", - "expected_output": "Next.js dev server running at http://localhost:3000" - }, - { - "order": 10, - "action": "Connect wallet and test contract interactions in the browser", - "working_directory": "wagmi-dapp", - "interactive": true, - "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation — approve it. After confirmation, the stored value should update to the new number.", - "expected_output": "Wallet connected, block number displayed, Storage contract read/write working" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: No connectors found / useConnect: No connector available", - "cause": "WagmiProvider not wrapping the component tree, or connectors not configured.", - "resolution": "Ensure the Providers wrapper (WagmiProvider + QueryClientProvider) wraps the entire app in layout.tsx and that the config in wagmi.ts includes the injected() connector." - }, - { - "pattern": "ChainMismatchError: Chain ID 420420417 not supported", - "cause": "The wallet is connected to a different chain than Polkadot Hub TestNet.", - "resolution": "In MetaMask, switch to the Polkadot Hub TestNet network (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill to add the network if it is not present." - }, - { - "pattern": "TypeError: Cannot read properties of undefined (reading 'toString') on blockNumber", - "cause": "useBlockNumber returned undefined before the first block is fetched.", - "resolution": "The component handles this with the null-coalescing operator (blockNumber?.toString() ?? 'Loading...'). This is expected on initial render and resolves automatically." - }, - { - "pattern": "Error: wagmi requires react-query v5 or higher", - "cause": "Incompatible @tanstack/react-query version installed.", - "resolution": "Run: npm install @tanstack/react-query@latest to ensure v5+ is installed." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs MetaMask setup, faucet tokens, or wants to use viem directly instead of Wagmi.", - "pages": [ - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Lower-level viem library for direct contract calls without React hooks — useful when Wagmi is not needed." - }, - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required before the wallet connection step works." - }, - { - "slug": "smart-contracts-libraries-wagmi", - "title": "Wagmi for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/wagmi.md", - "relevance": "Full Wagmi source page with the pre-deployed Storage contract address and complete code snippets." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: scaffold a Wagmi dApp and connect to Polkadot Hub TestNet", - "user_says": "Help me build a Next.js app with Wagmi that connects to Polkadot Hub", - "actions": [ - "Scaffold wagmi-dapp with create-next-app@15 non-interactively", - "Install wagmi, viem@2.x, and @tanstack/react-query", - "Create wagmi.ts with polkadotHubTestnet chain definition and createConfig", - "Wrap the app with WagmiProvider and QueryClientProvider in providers.tsx", - "Add ConnectWallet, BlockNumber, StorageRead, StorageWrite components", - "Start dev server and delegate browser testing to the user" - ], - "result": "Next.js app at localhost:3000 with wallet connection and live block number display" - }, - { - "scenario": "Edge case: user wants to call their own deployed contract instead of the pre-deployed Storage", - "user_says": "I deployed my own ERC-20 token — how do I call it with Wagmi useReadContract?", - "actions": [ - "Replace CONTRACT_ADDRESS in StorageRead.tsx with the user's deployed contract address", - "Replace STORAGE_ABI with the ERC-20 ABI fragment for the desired function (e.g. balanceOf)", - "Update functionName and args accordingly", - "Keep chainId: polkadotHubTestnet.id to ensure the call targets the correct network" - ], - "result": "useReadContract returns the result from the user's deployed contract on Polkadot Hub TestNet" - } - ] - }, - { - "id": "deploy-interact-contracts-web3py", - "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", - "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/web3-py.md" - ], - "primary_page": "smart-contracts/libraries/web3-py.md", - "prerequisites": { - "runtime": [ - "Python 3.9+ with pip", - "Solidity compiler (installed automatically by py-solc-x)" - ], - "network": [ - "Polkadot Hub TestNet — RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" - ], - "tokens": [ - "Small PAS balance for deployment gas — obtain from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (PRIVATE_KEY stored in .env — never enter in chat)" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and Python virtual environment", - "working_directory": ".", - "commands": [ - "mkdir web3py-contracts && cd web3py-contracts", - "python3 -m venv venv", - "source venv/bin/activate" - ], - "description": "Create a new directory named 'web3py-contracts', create a Python virtual environment inside it, and activate it. On Windows use 'venv\\Scripts\\activate' instead." - }, - { - "order": 2, - "action": "Install Python dependencies", - "working_directory": "web3py-contracts", - "commands": [ - "pip install web3 py-solc-x python-dotenv" - ], - "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." - }, - { - "order": 3, - "action": "Create the .env file with PRIVATE_KEY", - "working_directory": "web3py-contracts", - "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" - }, - { - "order": 4, - "action": "Create the Storage.sol Solidity contract", - "working_directory": "web3py-contracts", - "description": "Create a file named 'Storage.sol' with the following content:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ncontract Storage {\n uint256 private storedNumber;\n\n function store(uint256 num) public {\n storedNumber = num;\n }\n\n function retrieve() public view returns (uint256) {\n return storedNumber;\n }\n}\n```\n\nThis is a simple Storage contract with a store(uint256) write function and a retrieve() read function." - }, - { - "order": 5, - "action": "Create compile.py to compile the Solidity contract", - "working_directory": "web3py-contracts", - "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", - "commands": [ - "python compile.py" - ], - "expected_output": "Compilation successful — compiled.json written." - }, - { - "order": 6, - "action": "Create deploy.py to deploy the contract", - "working_directory": "web3py-contracts", - "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", - "commands": [ - "python deploy.py" - ], - "expected_output": "Deploying from account: 0x...\nContract deployed at: 0x...\nAddress saved to contract_address.txt" - }, - { - "order": 7, - "action": "Create interact.py to call store and retrieve", - "working_directory": "web3py-contracts", - "description": "Create a file named 'interact.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\naccount = w3.eth.account.from_key(PRIVATE_KEY)\n\nwith open('contract_address.txt') as f:\n contract_address = f.read().strip()\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\nabi = compiled['contracts']['Storage.sol']['Storage']['abi']\n\ncontract = w3.eth.contract(address=contract_address, abi=abi)\n\n# Read current value\nvalue = contract.functions.retrieve().call()\nprint(f'Current stored value: {value}')\n\n# Write new value\nnonce = w3.eth.get_transaction_count(account.address)\ntx = contract.functions.store(42).build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\nprint(f'store(42) tx: {receipt.transactionHash.hex()}')\n\n# Verify\nvalue = contract.functions.retrieve().call()\nprint(f'Updated stored value: {value}')\n```", - "commands": [ - "python interact.py" - ], - "expected_output": "Current stored value: 0\nstore(42) tx: 0x...\nUpdated stored value: 42" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "AssertionError: Failed to connect to RPC endpoint", - "cause": "The RPC URL is unreachable or the TestNet is down.", - "resolution": "Verify the URL https://services.polkadothub-rpc.com/testnet is correct and reachable. Check Polkadot Hub TestNet status." - }, - { - "pattern": "ValueError: insufficient funds for gas * price + value", - "cause": "The deployer account has no PAS tokens.", - "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS tokens for your account address." - }, - { - "pattern": "KeyError: 'PRIVATE_KEY'", - "cause": "The .env file is missing or PRIVATE_KEY is not defined in it.", - "resolution": "Create a .env file in the web3py-contracts directory with the line: PRIVATE_KEY=0xYOUR_KEY. Ensure python-dotenv is installed and load_dotenv() is called before accessing os.environ['PRIVATE_KEY']." - }, - { - "pattern": "SolcError: Source file requires different compiler version", - "cause": "Solidity pragma version mismatch.", - "resolution": "Ensure install_solc('0.8.20') is called in compile.py before compile_standard. The py-solc-x library downloads the compiler if not already cached." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to understand the Polkadot Hub network params or wants to use JavaScript/TypeScript instead of Python.", - "pages": [ - { - "slug": "smart-contracts-libraries-web3-py", - "title": "Web3.py", - "url": "https://docs.polkadot.com/smart-contracts/libraries/web3-py.md", - "relevance": "Full Web3.py source page with complete code snippets and additional contract examples." - }, - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js library — recommended JavaScript alternative to the sunset Web3.js library." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Full network connection reference with RPC URL, chain ID, and WSS endpoints for Polkadot Hub." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a Storage contract to Polkadot Hub TestNet using Python", - "user_says": "Deploy a smart contract to Polkadot Hub using Python and Web3.py", - "actions": [ - "Create web3py-contracts/ directory and Python virtualenv", - "Install web3, py-solc-x, python-dotenv via pip", - "Instruct user to add PRIVATE_KEY to .env file directly (not in chat)", - "Create Storage.sol, compile.py, deploy.py, interact.py from inline code", - "Run python compile.py then python deploy.py", - "Run python interact.py to verify store/retrieve" - ], - "result": "Contract deployed and verified on Polkadot Hub TestNet; stored value updated to 42" - }, - { - "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", - "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?", - "actions": [ - "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", - "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", - "Update deploy.py: change compiled['contracts']['YourFile.sol']['YourContract'] to match", - "Update interact.py: change the ABI extraction path and function calls to match the user's contract ABI" - ], - "result": "Custom contract compiled and deployed; interact.py calls the contract's specific functions" - } - ] - }, - { - "id": "interact-erc20-precompile-polkadot-hub", - "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", - "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/erc20.md" - ], - "primary_page": "smart-contracts/precompiles/erc20.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "PAS balance in MetaMask for transfer/approve operations — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the ERC-20 precompile address from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to the Polkadot Hub TestNet network (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet). If the network is not added yet, use the add-polkadot-hub-testnet-metamask skill to add it. Verify your PAS balance is non-zero before proceeding." - }, - { - "order": 3, - "action": "Open Remix IDE and create the ERC-20 interface file", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), create a new file named 'IERC20.sol'. Paste the standard ERC-20 interface into the file:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function balanceOf(address account) external view returns (uint256);\n function transfer(address to, uint256 amount) external returns (bool);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n```\n\nClick the Solidity Compiler tab (shield icon), set the compiler version to 0.8.20, and click 'Compile IERC20.sol'." - }, - { - "order": 4, - "action": "Connect Remix to MetaMask and load the precompile using At Address", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." - }, - { - "order": 5, - "action": "Call balanceOf to check PAS token balance", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", - "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18" - }, - { - "order": 6, - "action": "Call approve and transfer functions", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", - "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: 'At Address' button disabled or no output", - "cause": "The IERC20.sol file was not compiled successfully before clicking At Address.", - "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." - }, - { - "pattern": "MetaMask: Wrong network — expected Custom (420420417)", - "cause": "MetaMask is still on Ethereum Mainnet or another network.", - "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." - }, - { - "pattern": "Transaction reverted: insufficient balance", - "cause": "Attempting to transfer more PAS than the account holds.", - "resolution": "Call balanceOf first to confirm the available balance, then use an amount in wei that does not exceed it." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to add MetaMask, or wants to call the precompile from a contract.", - "pages": [ - { - "slug": "smart-contracts-precompiles-erc20", - "title": "Interact with the ERC20 Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", - "relevance": "Full ERC-20 precompile source page with the exact precompile address, complete ABI, and additional usage examples." - }, - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite for this skill." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: check PAS balance via ERC-20 precompile in Remix", - "user_says": "How do I call balanceOf on the ERC-20 precompile in Remix?", - "actions": [ - "Load source page to find the ERC-20 precompile address", - "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create IERC20.sol in Remix with the standard ERC-20 interface and compile it", - "Set Environment to Injected Provider - MetaMask in Deploy and Run tab", - "Click At Address with the precompile address", - "Expand balanceOf, enter the MetaMask address, and click call" - ], - "result": "uint256 PAS balance in wei displayed in Remix console" - }, - { - "scenario": "Edge case: user wants to call the precompile from a Solidity contract", - "user_says": "How do I use the ERC-20 precompile from inside my Solidity contract?", - "actions": [ - "Load source page to get the ERC-20 precompile address", - "In the Solidity contract, import or define the IERC20 interface", - "Store the precompile address as a constant: IERC20 constant PAS_ERC20 = IERC20(PRECOMPILE_ADDRESS)", - "Call PAS_ERC20.balanceOf(msg.sender) or PAS_ERC20.transfer(to, amount) as needed", - "Deploy the wrapper contract via Remix with MetaMask connected to Polkadot Hub TestNet" - ], - "result": "Solidity contract calls the ERC-20 precompile to read balances or transfer native PAS tokens" - } - ] - }, - { - "id": "interact-storage-precompile-remix", - "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", - "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/storage.md" - ], - "primary_page": "smart-contracts/precompiles/storage.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the Storage precompile address from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance." - }, - { - "order": 3, - "action": "Create the Storage precompile interface in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'IStorage.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IStorage {\n function setBytes(bytes32 key, bytes memory value) external;\n function getBytes(bytes32 key) external view returns (bytes memory);\n}\n```\n\nGo to the Solidity Compiler tab, set the version to 0.8.20, and click 'Compile IStorage.sol'. Confirm there are no errors." - }, - { - "order": 4, - "action": "Connect Remix to MetaMask and load the precompile using At Address", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection popup. Verify the network displays 'Custom (420420417) network'. Select 'IStorage' in the Contract dropdown. Paste the Storage precompile address (from step 1) into the 'At Address' input. Click 'At Address'. An IStorage instance should appear in the Deployed Contracts section." - }, - { - "order": 5, - "action": "Call setBytes to store a value", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", - "expected_output": "Transaction confirmed; tx hash shown in Remix console" - }, - { - "order": 6, - "action": "Call getBytes to retrieve the stored value", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the IStorage instance, expand 'getBytes'. Enter the same bytes32 key used in setBytes. Click 'call' (blue button). The output displays the stored bytes value in hex. To decode: the hex 0x68656c6c6f decodes to 'hello' in UTF-8.", - "expected_output": "bytes output matching the hex value stored in setBytes" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: At Address button grayed out", - "cause": "IStorage.sol was not compiled, or the wrong contract is selected in the dropdown.", - "resolution": "Compile IStorage.sol first via the Solidity Compiler tab, then return to Deploy and Run. Ensure 'IStorage' is selected in the Contract dropdown before clicking At Address." - }, - { - "pattern": "getBytes returns 0x (empty bytes)", - "cause": "The key used in getBytes does not match the key used in setBytes, or the setBytes transaction did not confirm.", - "resolution": "Ensure the bytes32 key in getBytes is identical to the one used in setBytes. Check Remix console for the setBytes transaction hash to confirm it was mined." - }, - { - "pattern": "MetaMask: Transaction failed with out of gas", - "cause": "Gas estimate too low for setBytes with a large value.", - "resolution": "In MetaMask, increase the gas limit manually before confirming the setBytes transaction." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to call the precompile from Solidity, or needs Remix connection help.", - "pages": [ - { - "slug": "smart-contracts-precompiles-storage", - "title": "Interact with the Storage Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/storage.md", - "relevance": "Full Storage precompile source page with the exact precompile address, full ABI, and key encoding examples." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." - }, - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: store and retrieve bytes using the Storage precompile", - "user_says": "How do I use the Storage precompile in Remix to store a value?", - "actions": [ - "Load source page to get the Storage precompile address", - "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create IStorage.sol in Remix with setBytes/getBytes interface and compile it", - "Set Environment to Injected Provider - MetaMask, select IStorage, click At Address with the precompile address", - "Call setBytes with a bytes32 key (hex-padded) and hex-encoded bytes value", - "Call getBytes with the same key to verify the stored value" - ], - "result": "Value stored on-chain via setBytes; getBytes returns the same hex-encoded bytes" - }, - { - "scenario": "Edge case: user wants to use the Storage precompile from within a Solidity contract", - "user_says": "Can I call the Storage precompile from inside my own contract?", - "actions": [ - "Load source page to get the precompile address", - "In the user's contract, import or define the IStorage interface", - "Store the precompile address as a constant: IStorage constant STORE = IStorage(PRECOMPILE_ADDRESS)", - "Call STORE.setBytes(key, value) and STORE.getBytes(key) in contract functions", - "Deploy the wrapper contract in Remix targeting Polkadot Hub TestNet" - ], - "result": "User's contract reads and writes key-value data through the Storage precompile" - } - ] - }, - { - "id": "interact-system-precompile-remix", - "title": "Interact with the System Precompile on Polkadot Hub via Remix", - "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/system.md" - ], - "primary_page": "smart-contracts/precompiles/system.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the System precompile address from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance." - }, - { - "order": 3, - "action": "Create the System precompile interface in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." - }, - { - "order": 4, - "action": "Connect Remix to MetaMask and load the precompile using At Address", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection. Verify the network shows 'Custom (420420417) network'. Select 'ISystem' in the Contract dropdown. Paste the System precompile address (from step 1) into 'At Address'. Click 'At Address'. An ISystem instance should appear in the Deployed Contracts section." - }, - { - "order": 5, - "action": "Call blake2b to hash data", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", - "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data" - }, - { - "order": 6, - "action": "Call verifySignature for sr25519 verification", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'verifySignature' function. To use this function you need three values:\n\n- INSERT_SIGNATURE: Replace with a real sr25519 signature as hex bytes (64 bytes, 128 hex chars). Generate a signature using a Polkadot.js signer or subkey tool: subkey sign --scheme sr25519 --suri '//Alice' --message 'your_message'\n\n- INSERT_PUBLIC_KEY: Replace with the sr25519 public key (32 bytes, 64 hex chars) corresponding to the signer.\n\n- message: The message bytes that were signed (hex-encoded).\n\nEnter the three values in the 'sig', 'pubKey', and 'message' fields respectively, then click 'call'. The function returns true if the signature is valid, false otherwise.", - "expected_output": "bool: true if the sr25519 signature is valid for the given pubKey and message" - }, - { - "order": 7, - "action": "Call accountExists to check account presence", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'accountExists' function. Enter an Ethereum-format address (0x...) in the 'account' field. Click 'call'. The function returns true if the account exists on-chain (i.e. has been funded or deployed), false if it has never received any funds or activity.", - "expected_output": "bool: true if the account exists on Polkadot Hub TestNet, false otherwise" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: call reverts on verifySignature with invalid inputs", - "cause": "The sig or pubKey bytes are not the correct length or format for sr25519.", - "resolution": "sr25519 signatures are 64 bytes (128 hex chars with 0x prefix = 130 chars total). Public keys are 32 bytes (64 hex chars). Use the subkey tool to generate a valid signature: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'. Ensure all inputs are hex-encoded with 0x prefix." - }, - { - "pattern": "At Address: no contract at address", - "cause": "Incorrect precompile address entered, or MetaMask connected to the wrong network.", - "resolution": "Re-load the source page to get the correct System precompile address. Verify MetaMask shows chain ID 420420417." - }, - { - "pattern": "ISystem.sol compilation error: function not found in interface", - "cause": "The interface in ISystem.sol may not match the exact ABI exposed by the precompile.", - "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/system.md for the authoritative ABI and update the interface accordingly." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to use sr25519 signing tools, or needs Remix setup help.", - "pages": [ - { - "slug": "smart-contracts-precompiles-system", - "title": "Interact with the System Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/system.md", - "relevance": "Full System precompile source page with the exact precompile address, authoritative ABI, and all function signatures." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." - }, - { - "slug": "smart-contracts-precompiles-erc20", - "title": "Interact with the ERC20 Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", - "relevance": "ERC-20 precompile — often used alongside the System precompile in contract interactions." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: compute a BLAKE2b hash using the System precompile in Remix", - "user_says": "How do I use the System precompile to hash data with BLAKE2b in Remix?", - "actions": [ - "Load source page to get the System precompile address", - "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create ISystem.sol in Remix with the BLAKE2 and other function signatures, then compile", - "Set Environment to Injected Provider - MetaMask, select ISystem, click At Address with the precompile address", - "Expand blake2b, enter hex-encoded input data (e.g. 0x68656c6c6f for 'hello'), click call", - "Record the BLAKE2b hash output" - ], - "result": "BLAKE2b hash of the input bytes returned as hex in the Remix console" - }, - { - "scenario": "Edge case: user wants to verify an sr25519 signature from a Polkadot account", - "user_says": "Can I verify a Polkadot sr25519 signature on-chain using the System precompile?", - "actions": [ - "Generate a test signature using subkey: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'", - "Note the 64-byte signature hex and the 32-byte Alice public key hex", - "In Remix, expand verifySignature on the ISystem instance", - "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", - "Click call and confirm the result is true" - ], - "result": "bool true returned — sr25519 signature verified on-chain via the System precompile" - } - ] - }, - { - "id": "interact-xcm-precompile-remix", - "title": "Interact with the XCM Precompile via Remix IDE", - "description": "Guides you through creating an IXcm Solidity interface in Remix, loading the XCM precompile, and calling weighMessage, execute, and send functions. Use when you need to test XCM message dispatch from a smart contract, estimate XCM execution weight, or send cross-chain messages from Polkadot Hub via Solidity. All interaction is browser-based using Remix IDE with MetaMask. Trigger phrases: 'XCM precompile', 'send XCM from contract', 'weighMessage', 'xcm execute', 'cross-chain from Solidity', 'IXcm interface'. Output: XCM weight estimates and on-chain transaction receipts.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/xcm.md" - ], - "primary_page": "smart-contracts/precompiles/xcm.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the XCM precompile address and IXcm ABI from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance for gas fees." - }, - { - "order": 3, - "action": "Create the IXcm interface in Remix and compile it", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." - }, - { - "order": 4, - "action": "Load the XCM precompile using At Address in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." - }, - { - "order": 5, - "action": "Call weighMessage to estimate XCM execution cost", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", - "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost" - }, - { - "order": 6, - "action": "Call execute to run an XCM message locally", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", - "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs" - }, - { - "order": 7, - "action": "Call send to dispatch an XCM to another chain", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", - "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: transaction reverted on execute or send", - "cause": "SCALE-encoded XCM bytes are malformed or the weight limit is too low.", - "resolution": "Verify the encoding against the example on the source page. Use weighMessage first to obtain the correct maxWeight. Ensure the correct precompile address is used for TestNet." - }, - { - "pattern": "At Address: no contract at address", - "cause": "Incorrect precompile address entered, or MetaMask is connected to the wrong network.", - "resolution": "Reload the source page to get the authoritative XCM precompile address. Verify MetaMask shows chain ID 420420417." - }, - { - "pattern": "IXcm.sol compilation error: function not found", - "cause": "The interface definition does not match the ABI the precompile exposes.", - "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md for the authoritative ABI and update IXcm.sol accordingly." - }, - { - "pattern": "MetaMask: insufficient funds for gas", - "cause": "PAS balance too low to cover gas on Polkadot Hub TestNet (base fee: 1000 gwei).", - "resolution": "Obtain PAS tokens from https://faucet.polkadot.io/. Ensure the account has at least 0.01 PAS before submitting state-changing calls." - } - ], - "supplementary_context": { - "description": "Load these pages for XCM encoding details, precompile addresses, and cross-chain messaging concepts.", - "pages": [ - { - "slug": "smart-contracts-precompiles-xcm", - "title": "Interact with the XCM Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/xcm.md", - "relevance": "Authoritative XCM precompile address, IXcm interface definition, and SCALE-encoded message examples." - }, - { - "slug": "reference-tools-xcm-tools", - "title": "XCM Tools", - "url": "https://docs.polkadot.com/reference/tools/xcm-tools.md", - "relevance": "XCM tooling for encoding, debugging, and monitoring message delivery." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Polkadot Hub TestNet network details and RPC endpoints." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: estimate XCM execution fee before sending", - "user_says": "How much will it cost to send an XCM message from my contract on Polkadot Hub?", - "actions": [ - "Load XCM precompile address and IXcm ABI from source page", - "Create and compile IXcm.sol in Remix", - "Load precompile via At Address", - "Call weighMessage with encoded destination and message bytes", - "Read the returned weight estimate" - ], - "result": "Weight estimate (refTime and proofSize) indicating the execution cost on the destination chain" - }, - { - "scenario": "Edge case: execute call reverts due to malformed SCALE encoding", - "user_says": "My XCM execute call keeps reverting in Remix", - "actions": [ - "Compare SCALE-encoded message bytes against the source page example", - "Call weighMessage first to confirm the message decodes correctly", - "Check that maxWeight is at least the refTime returned by weighMessage", - "If revert persists, reload IXcm.sol ABI from source page to confirm interface matches precompile" - ], - "result": "Root cause identified (malformed encoding or wrong interface); corrected message executes successfully" - } - ] - }, - { - "id": "deploy-parachain-to-polkadot-testnet", - "title": "Deploy a Parachain to the Polkadot TestNet (Paseo)", - "description": "Covers the end-to-end parachain launch workflow on Paseo TestNet: obtain PAS tokens, reserve a para ID via Polkadot.js Apps, generate collator keys with subkey (Docker), build plain and raw chain specs with chain-spec-builder, export genesis wasm and state with polkadot-omni-node, register a parathread on-chain, generate a node key, start the collator, and insert session keys via RPC. Use this skill after completing the set-up-parachain-template skill. Trigger phrases: 'deploy parachain', 'register parachain Paseo', 'launch parachain TestNet', 'reserve para ID', 'parachain registration'. Part of the three-page launch series: set-up-the-parachain-template to deploy-to-polkadot to obtain-coretime.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/launch-a-parachain/deploy-to-polkadot.md" - ], - "primary_page": "parachains/launch-a-parachain/deploy-to-polkadot.md", - "prerequisites": { - "runtime": [ - "Polkadot SDK parachain template built in release mode (see set-up-parachain-template skill)", - "Rust and Cargo installed", - "Docker installed (for subkey key generation)", - "polkadot-omni-node binary: cargo install polkadot-omni-node", - "chain-spec-builder binary: cargo install chain-spec-builder" - ], - "network": [ - "Paseo TestNet — wss://rpc.ibp.network/paseo", - "Server with TCP port 30333 open for P2P collator connections" - ], - "tokens": [ - "PAS tokens from https://faucet.polkadot.io/ — required for para ID reservation and parathread registration fees" - ], - "wallet": [ - "Polkadot.js browser extension (https://polkadot.js.org/extension/) with a PAS-funded account" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Obtain PAS tokens from the faucet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://faucet.polkadot.io/, select the 'Paseo' network, paste your SS58 account address, and submit. The faucet delivers 500 PAS per request. Verify the balance in Polkadot.js Apps before proceeding." - }, - { - "order": 2, - "action": "Reserve a para ID on Paseo TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID — record it as PARA_ID (needed in all subsequent steps).", - "expected_output": "Para ID assigned and visible in the Parathreads tab" - }, - { - "order": 3, - "action": "Generate collator session keys using subkey (Docker)", - "working_directory": ".", - "commands": [ - "docker run --rm -it parity/subkey generate --scheme sr25519", - "docker run --rm -it parity/subkey generate --scheme ed25519" - ], - "description": "Run both commands. The first produces an SR25519 key (used for Aura block production); the second produces an ED25519 key (used for GRANDPA finality). Record both seed phrases and Account IDs securely. Never share the seed phrases. Save SR25519 Account ID as AURA_KEY and ED25519 Account ID as GRANDPA_KEY." - }, - { - "order": 4, - "action": "Build the plain chain spec", - "working_directory": "parachain-template", - "commands": [ - "chain-spec-builder create --relay-chain paseo --para-id INSERT_PARA_ID --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm named-preset development" - ], - "description": "Replace INSERT_PARA_ID with the value of PARA_ID from step 2 (e.g., 2000). This produces 'chain_spec.json'. Then open 'chain_spec.json' and make these edits: (1) Set the 'id' field to a unique string (e.g., 'my-parachain-paseo'). (2) Insert AURA_KEY and GRANDPA_KEY (from step 3) into the 'aura' and 'grandpa' genesis sections. (3) Confirm 'para_id' equals PARA_ID. Save the file." - }, - { - "order": 5, - "action": "Build the raw chain spec", - "working_directory": "parachain-template", - "commands": [ - "polkadot-omni-node build-spec --chain chain_spec.json --raw > chain_spec_raw.json" - ], - "description": "Converts the human-readable chain spec to SCALE-encoded raw format. Verify 'chain_spec_raw.json' was created and is non-empty.", - "expected_output": "chain_spec_raw.json created (non-empty JSON file)" - }, - { - "order": 6, - "action": "Export genesis wasm and state", - "working_directory": "parachain-template", - "commands": [ - "polkadot-omni-node export-genesis-wasm --chain chain_spec_raw.json genesis_wasm", - "polkadot-omni-node export-genesis-state --chain chain_spec_raw.json genesis_state" - ], - "description": "Produces 'genesis_wasm' (runtime Wasm bytecode) and 'genesis_state' (initial chain state). Both files are required for the on-chain registration in step 7." - }, - { - "order": 7, - "action": "Register the parathread on-chain", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In Polkadot.js Apps (connected to Paseo), navigate to Network > Parachains > Parathreads. Find your reserved PARA_ID and click 'Register'. In the modal, upload 'genesis_wasm' for 'Genesis WASM' and 'genesis_state' for 'Initial state'. Submit and sign. After finalization, the status changes from 'Reserved' to 'Onboarding'. Registration takes effect at the next epoch (up to 600 blocks on Paseo).", - "expected_output": "Parathread status changes to 'Onboarding' in the Parathreads tab" - }, - { - "order": 8, - "action": "Generate a P2P node key for the collator", - "working_directory": "parachain-template", - "commands": [ - "polkadot-omni-node generate-node-key --file node-key.dat" - ], - "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure — losing it changes the peer ID on next restart." - }, - { - "order": 9, - "action": "Start the collator node", - "working_directory": "parachain-template", - "commands": [ - "./target/release/parachain-template-node --collator --chain chain_spec_raw.json --node-key-file node-key.dat --port 30333 --rpc-port 9944 --rpc-methods=Unsafe -- --chain paseo --sync warp" - ], - "description": "The '--' separator splits collator arguments from the embedded relay chain (Paseo) arguments. '--sync warp' enables fast warp sync for Paseo. The collator first syncs the relay chain (several minutes). Wait for log messages containing 'Parachain synced' before inserting session keys in step 10." - }, - { - "order": 10, - "action": "Insert session keys into the collator keystore", - "working_directory": ".", - "commands": [ - "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"aura\",\"INSERT_AURA_SEED_PHRASE\",\"INSERT_AURA_PUBLIC_KEY_HEX\"],\"id\":1}'", - "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"gran\",\"INSERT_GRANDPA_SEED_PHRASE\",\"INSERT_GRANDPA_PUBLIC_KEY_HEX\"],\"id\":1}'" - ], - "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history — they contain seed phrases. Run them directly in the server terminal.", - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1} for each key insertion" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "chain-spec-builder: cannot find runtime Wasm file", - "cause": "The parachain was not built in release mode or the Wasm path is incorrect.", - "resolution": "Run 'cargo build --release' in the parachain-template directory. The Wasm artifact is at: target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm" - }, - { - "pattern": "Parathread registration fails: 'ParaAlreadyExists' or 'NotRegistrar'", - "cause": "The para ID was not properly reserved, or the registration account differs from the manager.", - "resolution": "Verify that PARA_ID was successfully reserved in step 2 using the correct account. Use the same account for the registration in step 7." - }, - { - "pattern": "Collator log: 'Relay chain does not contain our parachain'", - "cause": "Parathread registration has not finalized on-chain, or chain_spec_raw.json has the wrong para_id.", - "resolution": "Verify the Parathreads tab shows 'Onboarding' for PARA_ID. Confirm chain_spec_raw.json contains the correct para_id. Registration takes up to one epoch (600 blocks on Paseo)." - }, - { - "pattern": "author_insertKey: 'Method not found'", - "cause": "The collator was started without '--rpc-methods=Unsafe'.", - "resolution": "Stop the collator, add '--rpc-methods=Unsafe' to the startup command, and restart. Never expose unsafe RPC methods to external network interfaces." - } - ], - "supplementary_context": { - "description": "Load these pages for template setup, coretime, and local test network guidance.", - "pages": [ - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Required prerequisite: build the parachain template locally before deploying to TestNet." - }, - { - "slug": "parachains-launch-a-parachain-obtain-coretime", - "title": "Obtain Coretime", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/obtain-coretime.md", - "relevance": "Next step: obtain on-demand or bulk coretime to activate parachain block production." - }, - { - "slug": "parachains-testing-run-a-parachain-network", - "title": "Run a Parachain Network", - "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md", - "relevance": "Test the parachain locally with Zombienet before deploying to TestNet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: first deployment to Paseo after building the template", - "user_says": "I built the parachain template — how do I deploy it to Paseo TestNet?", - "actions": [ - "Get PAS tokens from faucet", - "Reserve a para ID via Polkadot.js Apps", - "Generate Aura (SR25519) and GRANDPA (ED25519) collator keys with subkey Docker", - "Build plain chain spec, embed keys, build raw chain spec, export genesis data", - "Register parathread on-chain via Polkadot.js Apps", - "Generate node key, start collator, insert session keys via RPC" - ], - "result": "Parachain registered on Paseo TestNet with status 'Onboarding'; collator is running and syncing" - }, - { - "scenario": "Edge case: collator shows 0 peers after launch", - "user_says": "My collator started but shows 0 peers and is not syncing the relay chain", - "actions": [ - "Verify port 30333 is open on the server firewall", - "Confirm the startup command includes the '-- --chain paseo' relay chain section", - "Add a known Paseo bootnode with '--bootnodes /dns/...' to the startup flags", - "Wait 10 minutes for peer discovery — initial warp sync connection can be slow" - ], - "result": "Collator finds relay chain peers and begins syncing; block production starts after parathread onboards" - } - ] - }, - { - "id": "connect-polkadot-hub-testnet", - "title": "Connect to Polkadot Hub and Get Test Tokens", - "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/connect.md" - ], - "primary_page": "smart-contracts/connect.md", - "prerequisites": { - "wallet": [ - "MetaMask browser extension installed (https://metamask.io/download/)" - ], - "network": [ - "Internet access to Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", - "Internet access to the faucet: https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Add Polkadot Hub TestNet to MetaMask", - "working_directory": ".", - "description": "In MetaMask, open Settings > Networks > Add a network > Add a network manually. Enter these parameters: Network name: 'Polkadot Hub TestNet'; New RPC URL: 'https://services.polkadothub-rpc.com/testnet'; Chain ID: '420420417'; Currency symbol: 'PAS'. For the Block Explorer URL, check https://docs.polkadot.com/smart-contracts/connect/ for the current explorer URL, as it may change. Click Save and switch MetaMask to this network. This step must be delegated to the user." - }, - { - "order": 2, - "action": "Request PAS test tokens from the faucet", - "working_directory": ".", - "description": "Navigate to https://faucet.polkadot.io/. Select 'Polkadot Hub TestNet', paste your 0x-prefixed EVM address, and click the request button. The faucet credits PAS tokens within seconds. Rate limit: approximately 500 PAS per 24 hours per address. This step must be delegated to the user." - }, - { - "order": 3, - "action": "Verify MetaMask balance", - "working_directory": ".", - "description": "In MetaMask, confirm the PAS balance is non-zero on the Polkadot Hub TestNet network. To verify programmatically: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"YOUR_ADDRESS\",\"latest\"],\"id\":1}'. Replace YOUR_ADDRESS with the 0x-prefixed address. A non-zero hex result confirms the account is funded." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask: 'Invalid chain ID' or cannot connect to network", - "cause": "Incorrect chain ID or RPC URL entered in MetaMask network settings.", - "resolution": "Verify the chain ID is exactly 420420417 (decimal) and the RPC URL is https://services.polkadothub-rpc.com/testnet. Remove and re-add the network if the error persists." - }, - { - "pattern": "Faucet rate limit: 'Already requested recently'", - "cause": "The address already received tokens within the 24-hour rate limit window.", - "resolution": "Wait 24 hours, or use a different development wallet address." - }, - { - "pattern": "RPC endpoint returns errors or is unreachable", - "cause": "TestNet may be under maintenance, or the public RPC endpoint URL has changed.", - "resolution": "Check https://docs.polkadot.com/smart-contracts/connect/ for the latest endpoint URLs. The WSS alternative is wss://services.polkadothub-rpc.com/testnet." - } - ], - "supplementary_context": { - "description": "Load these pages for development framework configuration that uses the TestNet endpoints established here.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Hardhat network configuration using the TestNet RPC URL and chain ID provided by this skill." - }, - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "Detailed faucet workflow for obtaining PAS test tokens." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: set up development environment from scratch", - "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?", - "actions": [ - "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", - "Request PAS tokens from https://faucet.polkadot.io/", - "Verify funded balance in MetaMask" - ], - "result": "MetaMask connected to Polkadot Hub TestNet with funded PAS balance, ready for smart contract deployment" - }, - { - "scenario": "Edge case: looking up MainNet connection parameters", - "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?", - "actions": [ - "Reference the MainNet network parameters from the source page", - "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" - ], - "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage" - } - ] - }, - { - "id": "store-retrieve-data-bulletin-chain", - "title": "Store and Retrieve Data on the Bulletin Chain", - "description": "Stores a file on the Polkadot Bulletin Chain using PAPI TypeScript and retrieves it by CID via the IPFS gateway. Use when you need decentralized IPFS-compatible file storage for dApp assets, NFT metadata, or files under 8 MiB. Requires a Polkadot account authorized via the Bulletin Chain Console UI faucet (no programmatic self-authorization). Trigger phrases: 'store file Bulletin Chain', 'PAPI TransactionStorage store', 'Polkadot decentralized storage', 'Bulletin Chain IPFS'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/store-data/bulletin-chain.md" - ], - "primary_page": "chain-interactions/store-data/bulletin-chain.md", - "prerequisites": { - "runtime": [ - "Node.js v18 or later", - "npm" - ], - "network": [ - "Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" - ], - "tokens": [ - "Bulletin Chain storage authorization (not DOT/PAS) — obtained from Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/. The authorize_account extrinsic requires Root origin; self-authorization is not available programmatically." - ], - "wallet": [ - "A Polkadot account (SS58) with active Bulletin Chain authorization, plus its 12-word mnemonic for signing" - ] - }, - "env_vars": [ - { - "name": "MNEMONIC", - "description": "12-word mnemonic of the authorized Polkadot account. Do NOT ask user to paste mnemonic in chat — they must edit .env directly.", - "required": true - } - ], - "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── (generated Bulletin Chain descriptors)\n├── store-data.ts\n├── .env\n├── .gitignore\n├── node_modules/\n└── package.json", - "steps": [ - { - "order": 1, - "action": "Authorize account via Bulletin Chain Console UI", - "working_directory": ".", - "description": "This step requires manual browser interaction — delegate to the user.\n\n1. Open https://paritytech.github.io/polkadot-bulletin-chain/ and click Connect to link a wallet (Polkadot.js, Talisman, SubWallet, or Fearless).\n2. Go to Faucet > Storage Faucet tab.\n3. Under Authorize Account, enter the number of Transactions and Bytes needed, click Authorize Account, and approve in the wallet extension.\n4. Verify authorization on the Accounts tab — note the expiration block number.\n\nNote: Authorization expires at a block. Once expired, data cannot be renewed without re-authorizing." - }, - { - "order": 2, - "action": "Create and initialize the project", - "working_directory": ".", - "commands": [ - "mkdir bulletin-store-example", - "cd bulletin-store-example", - "npm init -y", - "npm pkg set type=module" - ], - "description": "Create a new Node.js ESM project. The ESM module type is required for polkadot-api and hdkd packages." - }, - { - "order": 3, - "action": "Install dependencies", - "working_directory": "bulletin-store-example", - "commands": [ - "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv tsx" - ], - "description": "Install polkadot-api (typed chain client), hdkd and hdkd-helpers (key derivation), multiformats (CID decoding), dotenv (secure mnemonic loading), and tsx (TypeScript runner)." - }, - { - "order": 4, - "action": "Add Bulletin Chain metadata", - "working_directory": "bulletin-store-example", - "commands": [ - "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" - ], - "expected_output": "Descriptors generated successfully", - "description": "Connect to the Bulletin Chain RPC, download chain metadata, and generate typed descriptors in .papi/. These descriptors provide compile-time type safety for all pallet interactions." - }, - { - "order": 5, - "action": "Create .env with mnemonic placeholder", - "working_directory": "bulletin-store-example", - "commands": [ - "printf 'MNEMONIC=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty MNEMONIC placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in MNEMONIC with the 12-word mnemonic of the authorized account. Do NOT ask for the mnemonic in chat. Wait for user confirmation before proceeding." - }, - { - "order": 6, - "action": "Create store-data.ts", - "working_directory": "bulletin-store-example", - "reference_file": "store-data.ts", - "description": "Fetch the reference file and save as store-data.ts. Then apply these modifications:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Remove `DEV_PHRASE` from the `@polkadot-labs/hdkd-helpers` import (keep `entropyToMiniSecret` and `mnemonicToEntropy`).\n3. Change `mnemonicToEntropy(DEV_PHRASE)` to `mnemonicToEntropy(process.env.MNEMONIC as string)`.\n4. Replace `'INSERT_IMAGE_PATH'` with the path to the file the user wants to store (ask the user for this path before running).\nSave the file." - }, - { - "order": 7, - "action": "Run the store script", - "working_directory": "bulletin-store-example", - "commands": [ - "npx tsx store-data.ts" - ], - "expected_output": "Image stored successfully! CID: bafk2bzace...", - "description": "Run the script to submit the file to the Bulletin Chain. On success it prints the block hash, transaction index, CID, and an IPFS gateway URL. Save the CID and (block, index) pair — needed to retrieve or renew the stored data." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", - "files": [ - { - "path": "store-data.ts", - "description": "PAPI TypeScript script that connects to Bulletin Chain, reads a local file, and submits it via TransactionStorage.store. Uses DEV_PHRASE by default — replace with process.env.MNEMONIC as instructed in step 6." - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: account has no authorization", - "cause": "The signing account has no active authorization on the Bulletin Chain, or it has expired.", - "resolution": "Re-authorize via the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ before retrying." - }, - { - "pattern": "file too large / data exceeds per-transaction limit", - "cause": "File exceeds the ~8 MiB per-transaction size limit.", - "resolution": "Split into chunks under 8 MiB and submit multiple store transactions, recording (block, index) per chunk." - }, - { - "pattern": "MNEMONIC is undefined / Cannot derive key from undefined", - "cause": ".env not populated or dotenv not loaded.", - "resolution": "Verify .env contains MNEMONIC= and that `import 'dotenv/config';` is the first line of store-data.ts." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED wss://paseo-bulletin-rpc.polkadot.io", - "cause": "Network issue or RPC temporarily unavailable.", - "resolution": "Check internet connectivity and retry after a few minutes. Monitor the Polkadot Discord for outage announcements." - } - ], - "supplementary_context": { - "description": "Load when the user asks about Bulletin Chain architecture, data retention, retrieval methods (P2P vs IPFS gateway), or the renewal workflow.", - "pages": [ - { - "slug": "reference-polkadot-hub-data-storage", - "title": "Data Storage", - "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage.md", - "relevance": "Technical reference for TransactionStorage pallet extrinsics, storage size limits, retention period, retrieval methods (IPFS gateway, P2P Helia), and renewal mechanics." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: store an image for an NFT collection", - "user_says": "Store my NFT artwork on the Polkadot Bulletin Chain and get its IPFS CID", - "actions": [ - "Authorize Polkadot account via Console UI faucet", - "Set up Node.js ESM project and install polkadot-api + hdkd + dotenv", - "Run npx papi add bulletin to generate chain descriptors", - "Create .env with MNEMONIC, modify store-data.ts to use process.env.MNEMONIC", - "Set FILE_PATH to the image path, run npx tsx store-data.ts", - "Receive CID for use in NFT metadata" - ], - "result": "File stored on-chain; CID like bafk2bzacea6wlxy... retrievable at https://paseo-ipfs.polkadot.io/ipfs/" - }, - { - "scenario": "Edge case: stored data approaches expiry", - "user_says": "My stored data is about to expire — how do I renew it?", - "actions": [ - "Retrieve the (block, index) pair from when the data was stored", - "Confirm expiration block has not yet passed (check Bulletin Chain Explorer)", - "Call TransactionStorage.renew({block, index}) using PAPI with the same authorized signer", - "Record new (block, index) from the Renewed event for future renewals" - ], - "result": "Retention timer reset; original CID remains valid for another full retention period" - } - ] - }, - { - "id": "deploy-uniswap-v2-core-pvm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", - "description": "Clones the polkavm-hardhat-examples repo, compiles Uniswap V2 Factory and Pair contracts to the Polkadot Virtual Machine (PVM) using the Hardhat Polkadot plugin and resolc compiler, and deploys to Polkadot Hub TestNet. Use when deploying a DEX AMM factory on Polkadot Hub with PVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 PVM', 'UniswapV2Factory PVM Polkadot', 'deploy AMM Polkadot Hub PVM', 'polkavm-hardhat-examples uniswap'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", - "prerequisites": { - "runtime": [ - "Node.js v16 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account (AH_PRIV_KEY)" - ] - }, - "env_vars": [ - { - "name": "AH_PRIV_KEY", - "description": "0x-prefixed EVM private key for the account deploying to Polkadot Hub TestNet (passetHub network). Must be funded with testnet PAS. Do NOT ask user to paste key in chat — they must edit .env directly.", - "required": true - }, - { - "name": "LOCAL_PRIV_KEY", - "description": "0x-prefixed EVM private key for local testing. For local-only use, Alice's dev key (0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133) is the default in the config. Not required for TestNet deployment.", - "required": false - } - ], - "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the polkavm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", - "cd polkavm-hardhat-examples/uniswap-v2-polkadot" - ], - "description": "Clone the repository and navigate to the Uniswap V2 project. The hardhat.config.js already uses dotenv (require('dotenv').config()) and process.env variables — no Hardhat vars conversion needed." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npm install" - ], - "description": "Install all project dependencies including @parity/hardhat-polkadot and the resolc PVM compiler." - }, - { - "order": 3, - "action": "Create .env with private key placeholders", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env" - ], - "description": "Create .env with empty placeholders. The .gitignore already includes .env. Stop here and ask the user to edit .env directly — fill in AH_PRIV_KEY with the 0x-prefixed private key of the account that will deploy to Polkadot Hub TestNet. Do NOT ask for the key in chat. For LOCAL_PRIV_KEY, Alice's dev key is the default in hardhat.config.js for local testing. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Compile the contracts", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile the Uniswap V2 contracts to PVM bytecode using the Hardhat Polkadot plugin and the resolc compiler. Compiled artifacts (ABI and PVM bytecode) appear in the artifacts/ directory." - }, - { - "order": 5, - "action": "Deploy to Polkadot Hub TestNet", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npx hardhat run scripts/deploy.js --network passetHub" - ], - "expected_output": "Factory deployed to : 0x...", - "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet via the passetHub network config. The script outputs the deployed addresses — save them, especially the Factory address, which is needed for creating liquidity pairs.\n\nNote: The documentation shows `--network polkadotHubTestNet` but the actual network name in hardhat.config.js is `passetHub`." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account not funded with testnet PAS, or network base fee (1000 gwei on Polkadot Hub TestNet) exceeds the transaction gas price.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. If gas issues persist, add gasPrice: 5000000000000 (5000 gwei) to the passetHub network block in hardhat.config.js." - }, - { - "pattern": "Error: AH_PRIV_KEY is undefined / invalid private key", - "cause": ".env file not populated or not loaded by dotenv.", - "resolution": "Verify .env exists in uniswap-v2-polkadot/ and contains AH_PRIV_KEY=0x. The config uses require('dotenv').config() automatically." - }, - { - "pattern": "Error: network passetHub not found / unknown network", - "cause": "Using wrong --network flag; documentation shows polkadotHubTestNet but the config defines passetHub.", - "resolution": "Use --network passetHub instead of --network polkadotHubTestNet." - }, - { - "pattern": "Compilation error: resolc not found / PVM compiler unavailable", - "cause": "The resolc compiler is not installed or not accessible.", - "resolution": "Run npm install to reinstall @parity/hardhat-polkadot and its bundled resolc compiler. Verify package.json includes the plugin." - } - ], - "supplementary_context": { - "description": "Load when the user asks about PVM vs EVM differences, local development setup, or testing the deployed contracts.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local Polkadot development node and ETH-RPC adapter to test the Uniswap V2 PVM contracts locally before deploying to TestNet." - }, - { - "slug": "smart-contracts-dev-environments-hardhat-polkadot", - "title": "Set Up Hardhat with the Polkadot Plugin", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot.md", - "relevance": "Configure the @parity/hardhat-polkadot plugin and resolc compiler for PVM smart contract development." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using PVM", - "user_says": "Deploy Uniswap V2 on Polkadot Hub using PVM", - "actions": [ - "Clone polkavm-hardhat-examples and cd uniswap-v2-polkadot", - "Run npm install", - "Create .env with AH_PRIV_KEY for testnet deployment", - "Run npx hardhat compile to compile to PVM bytecode", - "Run npx hardhat run scripts/deploy.js --network passetHub", - "Save deployed Factory and Pair addresses" - ], - "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet with PVM bytecode" - }, - { - "scenario": "Edge case: deployment fails with insufficient funds", - "user_says": "My Uniswap V2 deployment is failing with insufficient funds", - "actions": [ - "Confirm account has testnet PAS at https://faucet.polkadot.io/", - "If gas price issue, add gasPrice: 5000000000000 to passetHub config in hardhat.config.js", - "Retry deployment with npx hardhat run scripts/deploy.js --network passetHub" - ], - "result": "Deployment succeeds after funding the account and optionally raising gas price" - } - ] - }, - { - "id": "deploy-uniswap-v2-core-evm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Core contracts (Factory and Pair) with standard Hardhat and TypeScript, converts Hardhat vars to dotenv for agent-compatible key management, and deploys to Polkadot Hub TestNet via the EVM execution path. Use when deploying a DEX AMM factory on Polkadot Hub using standard EVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 EVM Polkadot', 'UniswapV2Factory EVM Polkadot Hub', 'deploy AMM Polkadot REVM'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout b0a8627059a9d9cb759682310219557550186bc4", - "cd uniswap-v2-core-hardhat" - ], - "description": "Clone the repository, check out the pinned commit (tested configuration), and navigate to the Uniswap V2 Core project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies, then additionally install dotenv. dotenv is required to replace Hardhat's interactive vars system with a .env-based private key approach usable in agent shells." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key for the testnet deployer account. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee) to prevent 'Priority is too low' errors.\n5. Confirm `ignition.requiredConfirmations` is 1 (not 0) — zero-confirmation only works on local dev nodes with instant finality.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 3 Solidity files successfully", - "description": "Compile UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair (Solidity 0.5.16). Artifacts (ABI and EVM bytecode) appear in the artifacts/ directory." - }, - { - "order": 6, - "action": "Deploy to Polkadot Hub TestNet", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npx hardhat run scripts/deploy.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV2Factory deployed to: 0x...", - "description": "Deploy UniswapV2Factory, two test ERC-20 tokens, and create a trading pair. The script outputs all deployed addresses. Save the Factory address — needed for creating additional pairs and for the Periphery (Router) deployment." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." - }, - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses vars.get() instead of dotenv.", - "resolution": "Ensure `import 'dotenv/config';` is the first line of hardhat.config.ts and replace vars.get('TESTNET_PRIVATE_KEY') with process.env.TESTNET_PRIVATE_KEY." - }, - { - "pattern": "TESTNET_PRIVATE_KEY is undefined", - "cause": ".env not populated or not in the correct directory.", - "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ and contains TESTNET_PRIVATE_KEY=0x." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of deployment — missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user asks about the V2 Periphery Router deployment, local testing, or Hardhat configuration for Polkadot.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", - "title": "Uniswap V2 Periphery with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", - "relevance": "Next step: deploy the Uniswap V2 Router (WETH9, Router02) on top of the deployed V2 Core." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local Polkadot development node to run the test suite against a local node before TestNet deployment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using EVM", - "user_says": "Deploy Uniswap V2 on Polkadot Hub using EVM Hardhat", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY", - "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", - "Run npx hardhat compile", - "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", - "Save Factory, TokenA, TokenB, and Pair addresses" - ], - "result": "UniswapV2Factory, two test ERC-20 tokens, and a trading pair deployed to Polkadot Hub TestNet via EVM" - }, - { - "scenario": "Edge case: deployment stuck with IGN401 Transaction dropped", - "user_says": "Ignition says my V2 Core deployment transaction was dropped", - "actions": [ - "Verify gasPrice: 5000000000000 is set in polkadotTestnet network config", - "Delete ignition/deployments/ directory", - "Confirm TESTNET_PRIVATE_KEY account still has PAS balance", - "Redeploy with npx hardhat run scripts/deploy.ts --network polkadotTestnet" - ], - "result": "Clean deployment after resolving gas configuration and clearing stale Ignition state" - } - ] - }, - { - "id": "deploy-uniswap-v2-periphery-evm", - "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Periphery contracts (WETH9, Router02) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when adding the Router layer (liquidity, swaps, slippage protection) on top of Uniswap V2 Core. V2 Core Solidity dependency is a local file reference — no pre-deployed core required. Trigger phrases: 'deploy Uniswap V2 Router EVM Polkadot', 'UniswapV2Router02 Polkadot Hub', 'Uniswap periphery Polkadot REVM'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", - "cd uniswap-v2-periphery-hardhat" - ], - "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V2 Periphery project. The sibling directory uniswap-v2-core-hardhat/ (at the same commit) is automatically used for the local V2 Core Solidity dependency." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies including the local V2 Core reference (resolved automatically from ../uniswap-v2-core-hardhat/). Then install dotenv to replace Hardhat's interactive vars system." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee).\n5. Confirm `ignition.requiredConfirmations` is 1.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 5 Solidity files successfully", - "description": "Compile the Uniswap V2 Periphery contracts using a multi-compiler setup (Solidity 0.5.16 for V2 Core dependency and 0.6.6 for Router contracts). Artifacts appear in the artifacts/ directory." - }, - { - "order": 6, - "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", - "interactive": true, - "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 using Hardhat Ignition. Ignition will prompt to confirm the target network name and chain ID — delegate this confirmation to the user. After confirmation, contracts are deployed in two batches (Factory+WETH9 in parallel, then Router02). Save all three deployed addresses." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." - }, - { - "pattern": "Error: Cannot find module '@uniswap/v2-core' / ENOENT uniswap-v2-core-hardhat", - "cause": "The local V2 Core Solidity dependency (../../uniswap-v2-core-hardhat) is not accessible. Likely cloning only the periphery subdirectory or checking out different commits for each.", - "resolution": "Ensure the full revm-hardhat-examples repo is cloned and both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ are at the same commit. Run npm install from uniswap-v2-periphery-hardhat/." - }, - { - "pattern": "vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat vars.get() instead of dotenv.", - "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost transaction state — missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1, then redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user asks about the V2 Core prerequisite, testing the Router, or V3 upgrades.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", - "title": "Uniswap V2 Core with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", - "relevance": "V2 Core tutorial covering Factory and Pair deployment — the Periphery Router builds on top of the same Core contracts included as a local dependency." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local development node to run the test suite against localNode before TestNet deployment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V2 Periphery Router to Polkadot Hub TestNet", - "user_says": "Deploy the Uniswap V2 Router on Polkadot Hub EVM", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY", - "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy with --network polkadotTestnet, confirm when prompted", - "Save WETH9, Factory, and Router02 addresses" - ], - "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet" - }, - { - "scenario": "Edge case: npm install fails with missing V2 Core module", - "user_says": "npm install is failing because it cannot find @uniswap/v2-core", - "actions": [ - "Verify the full repo is cloned (both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ must exist)", - "Ensure git checkout used the same commit for the whole repo (a871364...)", - "Re-run npm install from uniswap-v2-periphery-hardhat/ — the local file reference resolves to ../uniswap-v2-core-hardhat/" - ], - "result": "npm install succeeds and @uniswap/v2-core is resolved from the sibling directory" - } - ] - }, - { - "id": "deploy-uniswap-v3-core-evm", - "title": "Deploy Uniswap V3 Core on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Core contracts (UniswapV3Factory) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. V3 config requires bytecodeHash set to none to keep Factory under the 24KB EIP-170 contract size limit. Use when deploying a concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 EVM Polkadot', 'UniswapV3Factory Polkadot Hub', 'concentrated liquidity Polkadot REVM'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── NoDelegateCall.sol\n │ ├── UniswapV3Factory.sol\n │ ├── UniswapV3Pool.sol\n │ └── UniswapV3PoolDeployer.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", - "cd uniswap-v3-core-hardhat" - ], - "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Core project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies then add dotenv to replace Hardhat's interactive vars system with .env-based private key management." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei).\n5. Confirm `ignition.requiredConfirmations` is 1.\n6. Preserve the existing `bytecodeHash: 'none'` in the Solidity compiler settings — this is required to keep UniswapV3Factory under the EIP-170 24KB contract size limit.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 5 Solidity files successfully", - "description": "Compile UniswapV3Factory, UniswapV3Pool, UniswapV3PoolDeployer, NoDelegateCall, and math libraries (Solidity 0.7.6). The bytecodeHash: 'none' setting in the Solidity config is critical — without it, UniswapV3Factory exceeds the EIP-170 24KB contract size limit." - }, - { - "order": 6, - "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", - "interactive": true, - "description": "Deploy UniswapV3Factory using Hardhat Ignition. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save the deployed Factory address — it is the entry point for creating V3 pools and is needed for the Periphery (SwapRouter, NonfungiblePositionManager) deployment." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: contract code too large / Contract creation code storage out of gas", - "cause": "bytecodeHash is not set to 'none', causing UniswapV3Factory to exceed the EIP-170 24KB limit.", - "resolution": "In hardhat.config.ts under the Solidity compiler settings, ensure metadata: { bytecodeHash: 'none' } is present. Recompile after the fix." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." - }, - { - "pattern": "vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat vars.get().", - "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost transaction state.", - "resolution": "Delete ignition/deployments/, verify gas config (gasPrice: 5000000000000, requiredConfirmations: 1), and redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user asks about V3 Periphery (SwapRouter, NFPM) deployment, local testing, or concentrated liquidity mechanics.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", - "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", - "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager on top of the deployed V3 Factory." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local development node to run the 187-test V3 suite before TestNet deployment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V3 Core to Polkadot Hub TestNet", - "user_says": "Deploy Uniswap V3 on Polkadot Hub EVM", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY", - "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice, preserve bytecodeHash: 'none'", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy UniswapV3Factory.ts --network polkadotTestnet, confirm when prompted", - "Save deployed UniswapV3Factory address" - ], - "result": "UniswapV3Factory deployed to Polkadot Hub TestNet — entry point for creating V3 concentrated-liquidity pools" - }, - { - "scenario": "Edge case: compilation fails with contract too large", - "user_says": "Hardhat compilation fails saying UniswapV3Factory is too large", - "actions": [ - "Check hardhat.config.ts Solidity compiler settings for the existence of metadata: { bytecodeHash: 'none' }", - "If missing, add it under settings: { optimizer: {...}, metadata: { bytecodeHash: 'none' } }", - "Recompile with npx hardhat compile" - ], - "result": "Compilation succeeds after setting bytecodeHash: 'none' to exclude metadata hash and keep contract under the 24KB EIP-170 limit" - } - ] - }, - { - "id": "deploy-uniswap-v3-periphery-evm", - "title": "Deploy Uniswap V3 Periphery on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Periphery contracts (SwapRouter, NonfungiblePositionManager) with Hardhat, converts Hardhat vars to dotenv, and deploys all four contracts (UniswapV3Factory, WETH9, SwapRouter, NonfungiblePositionManager) to Polkadot Hub TestNet in a single Hardhat Ignition run. The V3 Core contracts are resolved automatically from a local sibling package reference. Use when building a full-stack concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Periphery Polkadot', 'SwapRouter NonfungiblePositionManager Polkadot Hub', 'Uniswap V3 full deployment EVM Polkadot'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-periphery-hardhat/\n ├── contracts/\n │ ├── SwapRouter.sol\n │ ├── NonfungiblePositionManager.sol\n │ ├── NonfungibleTokenPositionDescriptor.sol\n │ ├── base/\n │ ├── interfaces/\n │ ├── lens/\n │ ├── libraries/\n │ └── test/\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Periphery.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n ├── package.json\n └── tsconfig.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout 96696ad15c3cf01b9168a71ad5114f27c34a8726", - "cd uniswap-v3-periphery-hardhat" - ], - "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Periphery project. The monorepo layout is critical: the Periphery project depends on V3 Core contracts through a local file reference (`\"@uniswap/v3-core\": \"file:../uniswap-v3-core-hardhat\"`), so both sibling directories must be present." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies. `npm install` resolves the local `@uniswap/v3-core` sibling package automatically — no separate step is needed. Then add `dotenv` to replace Hardhat's interactive vars system with .env-based private key management." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from \"hardhat/config\";` to `import { HardhatUserConfig } from \"hardhat/config\";`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet base fee).\n5. Confirm `ignition.requiredConfirmations` is 1 (already set).\n6. Preserve `bytecodeHash: \"none\"` in the Solidity compiler settings — required so the compiled UniswapV3Pool bytecode hash matches the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol, enabling correct CREATE2 pool address derivation.\n7. Preserve `allowUnlimitedContractSize: true` for the hardhat network — several Periphery contracts exceed the 24KB EIP-170 limit and require this for local testing.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 39 Solidity files successfully", - "description": "Compile SwapRouter, NonfungiblePositionManager, and all supporting periphery contracts (Solidity 0.7.6). The `bytecodeHash: 'none'` setting ensures the Pool bytecode hash matches PoolAddress.sol's hardcoded constant, which is required for CREATE2 pool address computation to work correctly during swaps and LP operations." - }, - { - "order": 6, - "action": "Deploy all contracts to Polkadot Hub TestNet via Hardhat Ignition", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV3PeripheryModule#UniswapV3Factory deployed at 0x...\nUniswapV3PeripheryModule#WETH9 deployed at 0x...\nUniswapV3PeripheryModule#SwapRouter deployed at 0x...\nUniswapV3PeripheryModule#NonfungiblePositionManager deployed at 0x...", - "interactive": true, - "description": "Deploy all four contracts using Hardhat Ignition. The module deploys UniswapV3Factory and WETH9 in the first batch (in parallel), then SwapRouter and NonfungiblePositionManager once their dependencies are ready. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save all four deployed addresses: they are needed for any downstream swap or LP interaction." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." - }, - { - "pattern": "vars is not defined / Cannot read properties of undefined (reading 'has')", - "cause": "hardhat.config.ts still uses Hardhat vars.get() or vars.has().", - "resolution": "Add `import 'dotenv/config';` as the first line and replace `vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of a pending transaction due to TestNet latency or gas underpricing.", - "resolution": "Delete `ignition/deployments/`, verify gasPrice is 5000000000000 and requiredConfirmations is 1, then redeploy. If the deployment receipt exists in deployed_addresses.json but code is missing at the address, wait and verify via eth_getCode before retrying." - }, - { - "pattern": "POOL_INIT_CODE_HASH mismatch / pool address computation incorrect", - "cause": "bytecodeHash is not set to 'none', causing the compiled Pool bytecode to differ from the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol.", - "resolution": "In hardhat.config.ts under Solidity compiler settings, ensure `metadata: { bytecodeHash: 'none' }` is present. Recompile after the fix." - }, - { - "pattern": "Error: cannot estimate gas / contract deployment failed during testing", - "cause": "The hardhat network is missing allowUnlimitedContractSize: true, causing Periphery contracts that exceed 24KB to fail during local testing.", - "resolution": "In hardhat.config.ts, ensure `networks.hardhat.allowUnlimitedContractSize = true` is set. This flag only applies to the in-process Hardhat network used for testing; it is not needed for TestNet deployment." - } - ], - "supplementary_context": { - "description": "Load when the user asks about V3 Core as a prerequisite, testing against a local development node, or the Hardhat environment setup.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", - "title": "Uniswap V3 Core with EVM on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", - "relevance": "The V3 Core contracts (UniswapV3Factory, UniswapV3Pool) are resolved automatically via local npm reference — no separate Core tutorial step required, but this page explains the Factory/Pool architecture the Periphery builds on." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Required for running the 39-test suite (SwapRouter and NonfungiblePositionManager tests) against a local Polkadot node via `npx hardhat test --network localNode` before TestNet deployment." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Reference for Hardhat network configuration, Ignition deployment options, and gas settings specific to Polkadot Hub." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy all Uniswap V3 Periphery contracts to Polkadot Hub TestNet", - "user_says": "Deploy Uniswap V3 SwapRouter and NonfungiblePositionManager to Polkadot Hub", - "actions": [ - "Clone revm-hardhat-examples, check out pinned commit, cd uniswap-v3-periphery-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY placeholder, ask user to fill it in", - "Convert hardhat.config.ts: add dotenv import, replace vars.get with process.env, add gasPrice: 5000000000000", - "Run npx hardhat compile (expects 39 Solidity files compiled successfully)", - "Run npx hardhat ignition deploy UniswapV3Periphery.ts --network polkadotTestnet, confirm when prompted", - "Save deployed addresses for UniswapV3Factory, WETH9, SwapRouter, and NonfungiblePositionManager" - ], - "result": "All four Uniswap V3 Periphery contracts deployed to Polkadot Hub TestNet — SwapRouter for token swaps and NonfungiblePositionManager for concentrated liquidity LP positions" - }, - { - "scenario": "Edge case: deployment fails with IGN401 or Transaction Already Imported", - "user_says": "Ignition reports the transaction was dropped and retrying gives 'Transaction Already Imported'", - "actions": [ - "Do not retry at the same gas price — that will fail again with the same error", - "Check ignition/deployments/ and deployed_addresses.json for any partial state", - "Verify the contract is not already deployed: run eth_getCode at any addresses in deployed_addresses.json", - "If not deployed, delete the ignition/deployments/ directory", - "Confirm gasPrice: 5000000000000 and requiredConfirmations: 1 are set in hardhat.config.ts", - "Re-run: npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" - ], - "result": "Deployment completes cleanly after removing stale Ignition state and confirming gas configuration" - } - ] + "description": "Sets up a Node.js project, compiles a Solidity contract with solc, deploys to Polkadot Hub TestNet using Ethers.js v6, and interacts with the deployed contract via read and write script calls. Use when integrating Ethers.js into a new or existing Node.js project targeting Polkadot Hub. Trigger phrases: 'deploy with ethers.js polkadot', 'ethers.js contract deployment', 'use ethers polkadot hub', 'ethersjs testnet'. Covers dotenv credential setup, custom Polkadot Hub provider configuration, and the compile-deploy-interact workflow. Produces a deployed contract with verified read/write interaction.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/libraries/ethers-js.md" + ], + "primary_page": "smart-contracts/libraries/ethers-js.md", + "prerequisites": { + "runtime": [ + "Node.js v22+", + "npm" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" + ], + "wallet": [ + "0x-prefixed EVM private key for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "required": true + }, + { + "name": "RPC_URL", + "description": "HTTP RPC endpoint for Polkadot Hub TestNet. Set to https://services.polkadothub-rpc.com/testnet.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create the project directory and initialize", + "working_directory": ".", + "commands": [ + "mkdir ethers-deploy && cd ethers-deploy", + "npm init -y", + "npm pkg set type=module" + ], + "description": "Create and initialize an ESM Node.js project. ESM is required for Ethers.js v6 imports." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "ethers-deploy", + "commands": [ + "npm install ethers dotenv", + "npm install --save-dev solc typescript tsx @types/node" + ], + "description": "Install ethers (v6), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling." + }, + { + "order": 3, + "action": "Create .env file and .gitignore", + "working_directory": "ethers-deploy", + "commands": [ + "printf 'PRIVATE_KEY=\\nRPC_URL=https://services.polkadothub-rpc.com/testnet\\n' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create .env with RPC_URL pre-filled and empty PRIVATE_KEY. Stop and ask the user to edit .env directly by adding their 0x-prefixed private key as PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing." + }, + { + "order": 4, + "action": "Fetch the provider connection helper", + "working_directory": "ethers-deploy", + "reference_file": "connectToProvider.js", + "description": "Fetch the reference file `connectToProvider.js` and save it to the project root. Verify it points at the Polkadot Hub TestNet RPC URL `https://services.polkadothub-rpc.com/testnet`; if it uses a placeholder, replace `INSERT_RPC_URL` with the testnet URL." + }, + { + "order": 5, + "action": "Fetch the Storage.sol contract", + "working_directory": "ethers-deploy", + "reference_file": "Storage.sol", + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." + }, + { + "order": 6, + "action": "Fetch the compilation script", + "working_directory": "ethers-deploy", + "reference_file": "compile.js", + "description": "Fetch the reference file `compile.js` and save it to the project root. The script invokes `solc` against `Storage.sol` and writes the ABI and bytecode to `artifacts/`." + }, + { + "order": 7, + "action": "Compile the Solidity contract", + "working_directory": "ethers-deploy", + "commands": [ + "node compile.js" + ], + "description": "Run the compile script to generate the contract ABI and bytecode. Confirm an `artifacts/` directory was created with `Storage.json` (or similar) before proceeding to deploy." + }, + { + "order": 8, + "action": "Fetch the deployment script", + "working_directory": "ethers-deploy", + "reference_file": "deploy.js", + "description": "Fetch the reference file `deploy.js` and save it to the project root. Then apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line (or `require(\"dotenv\").config()` for CJS).\n(2) Replace `INSERT_RPC_URL` with `process.env.RPC_URL` (or hardcode the testnet URL).\n(3) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`.\n(4) Replace any hardcoded chain ID with `420420417`." + }, + { + "order": 9, + "action": "Deploy the contract to Polkadot Hub TestNet", + "working_directory": "ethers-deploy", + "commands": [ + "node deploy.js" + ], + "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." + }, + { + "order": 10, + "action": "Fetch the interaction script and interact with the deployed contract", + "working_directory": "ethers-deploy", + "reference_file": "checkStorage.js", + "description": "Fetch the reference file `checkStorage.js` and save it as `checkStorage.js`. Then substitute `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step. Run it with `node checkStorage.js`. Verify the output shows successful read and write operations against the deployed contract." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/smart-contracts/libraries/ethers-js", + "files": [ + { + "path": "Storage.sol", + "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." + }, + { + "path": "compile.js", + "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts for Storage.sol" + }, + { + "path": "deploy.js", + "description": "Deploys the compiled Storage contract to Polkadot Hub via ethers.js Wallet + ContractFactory" + }, + { + "path": "checkStorage.js", + "description": "Reads + writes the deployed Storage contract via ethers.js Contract.connect; calls the `storedNumber` getter and `setNumber(value)` setter." + }, + { + "path": "connectToProvider.js", + "description": "Initializes ethers.js JsonRpcProvider for Polkadot Hub TestNet" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds for intrinsic transaction cost", + "cause": "The deployer account does not have enough PAS to pay for deployment gas.", + "resolution": "Visit https://faucet.polkadot.io/, enter the deployer address, and request testnet PAS. Confirm balance in MetaMask or via eth_getBalance RPC call, then retry." + }, + { + "pattern": "Error: could not detect network", + "cause": "The RPC_URL in .env is incorrect or the endpoint is unreachable.", + "resolution": "Verify RPC_URL=https://services.polkadothub-rpc.com/testnet in .env. Test connectivity: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'" + }, + { + "pattern": "SyntaxError: Cannot use import statement in a module", + "cause": "Project is not configured as ESM.", + "resolution": "Run 'npm pkg set type=module' in the ethers-deploy directory to enable ES modules." + }, + { + "pattern": "Error: transaction underpriced", + "cause": "The gas price in the transaction is below the 1000 gwei TestNet base fee.", + "resolution": "Add an explicit gasPrice to the transaction options: { gasPrice: ethers.parseUnits('5000', 'gwei') }. Polkadot Hub TestNet has a base fee of 1000 gwei." + } + ], + "supplementary_context": { + "description": "Load these pages for network details, wallet setup, or other library alternatives.", + "pages": [ + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Network parameters (RPC URLs, chain IDs, WebSocket endpoints) for all Polkadot Hub environments." + }, + { + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", + "relevance": "Alternative to Ethers.js: Viem deployment pattern for Polkadot Hub (more type-safe, recommended for new projects)." + }, + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", + "relevance": "Hardhat-based deployment workflow — use when the project needs a full Hardhat setup rather than lightweight scripts." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy and interact with a Storage contract", + "user_says": "Deploy a contract to Polkadot Hub using Ethers.js", + "actions": [ + "Create ethers-deploy ESM project and install ethers, dotenv, solc, tsx", + "Create .env with RPC_URL and empty PRIVATE_KEY (user fills in)", + "Fetch compile.ts, deploy.ts, interact.ts from polkadot-cookbook", + "Apply substitutions: dotenv import, process.env.RPC_URL, process.env.PRIVATE_KEY, chainId 420420417", + "Run npx tsx compile.ts to build ABI/bytecode", + "Run npx tsx deploy.ts and save deployed contract address", + "Run npx tsx interact.ts to verify read/write contract operations" + ], + "result": "Solidity contract compiled, deployed to Polkadot Hub TestNet, and verified with read/write operations via Ethers.js scripts" + }, + { + "scenario": "Edge case: transaction underpriced error during deployment", + "user_says": "Deploy failed with transaction underpriced", + "actions": [ + "Open deploy.ts and add gasPrice override to the deployment transaction options", + "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", + "Retry with npx tsx deploy.ts" + ], + "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet" + } + ], + "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json" + }, + { + "id": "deploy-contracts-viem", + "title": "Deploy Contracts to Polkadot Hub with Viem", + "description": "Sets up a TypeScript project, compiles a Solidity contract, deploys to Polkadot Hub TestNet using Viem v2 walletClient.deployContract, and interacts with the deployed contract via publicClient and walletClient. Use when building a TypeScript project that needs direct Viem integration with Polkadot Hub without a Hardhat setup. Trigger phrases: 'deploy with viem polkadot', 'viem contract deployment', 'use viem polkadot hub', 'deploy viem typescript'. Covers custom Polkadot Hub chain definition, dotenv credential setup, and the compile-deploy-interact pattern. Companion to deploy-contracts-ethers-js.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/libraries/viem.md" + ], + "primary_page": "smart-contracts/libraries/viem.md", + "prerequisites": { + "runtime": [ + "Node.js v22+", + "npm" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" + ], + "wallet": [ + "0x-prefixed EVM private key for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create the project directory and initialize", + "working_directory": ".", + "commands": [ + "mkdir viem-deploy && cd viem-deploy", + "npm init -y && npm pkg set type=module", + "mkdir -p src contracts abis artifacts" + ], + "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) — the reference scripts assume this layout for relative paths." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "viem-deploy", + "commands": [ + "npm install viem dotenv", + "npm install --save-dev solc typescript tsx @types/node" + ], + "description": "Install viem (v2), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling." + }, + { + "order": 3, + "action": "Create .env file and .gitignore", + "working_directory": "viem-deploy", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create .env with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly by adding PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing." + }, + { + "order": 4, + "action": "Fetch the chain configuration", + "working_directory": "viem-deploy", + "reference_file": "chainConfig.ts", + "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) — apply the same substitutions consistently in all three files." + }, + { + "order": 5, + "action": "Fetch the Storage.sol contract", + "working_directory": "viem-deploy", + "reference_file": "Storage.sol", + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." + }, + { + "order": 6, + "action": "Fetch the public client setup", + "working_directory": "viem-deploy", + "reference_file": "createClient.ts", + "description": "Fetch the reference file `createClient.ts` and save it as `src/createClient.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (the file declares the chain locally and has the same 8 placeholders). Constructs a viem `PublicClient` against the Polkadot Hub TestNet chain definition; used for reads and tx receipts." + }, + { + "order": 7, + "action": "Fetch the wallet client setup", + "working_directory": "viem-deploy", + "reference_file": "createWallet.ts", + "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders — including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." + }, + { + "order": 8, + "action": "Fetch the compilation script", + "working_directory": "viem-deploy", + "reference_file": "compile.ts", + "description": "Fetch the reference file `compile.ts` and save it as `src/compile.ts`. The script invokes `solc` against `contracts/Storage.sol` and writes ABI + bytecode artifacts to `abis/` and `artifacts/`." + }, + { + "order": 9, + "action": "Compile the Solidity contract", + "working_directory": "viem-deploy", + "commands": [ + "npx tsx src/compile.ts" + ], + "description": "Run the compile script. Verify that `abis/Storage.json` and `artifacts/Storage.bin` (or equivalent) exist before deploying." + }, + { + "order": 10, + "action": "Fetch the deployment script", + "working_directory": "viem-deploy", + "reference_file": "deploy.ts", + "description": "Fetch the reference file `deploy.ts` and save it as `src/deploy.ts`. Apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line.\n(2) Replace `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``.\n(3) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`.\n(4) Replace `INSERT_CHAIN_ID` with `420420417n` (BigInt).\n\nThe script reads from `../abis` and `../artifacts` relative to the script location, which resolves correctly when the script is in `src/`." + }, + { + "order": 11, + "action": "Deploy the contract to Polkadot Hub TestNet", + "working_directory": "viem-deploy", + "commands": [ + "npx tsx src/deploy.ts" + ], + "description": "Run the deployment script. Save the deployed contract address from the output.\n\nIf the transaction fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction fails with `fee cap too low` or similar gas errors: add a `gasPrice` override in `deploy.ts` (e.g. `gasPrice: parseGwei('5000')`)." + }, + { + "order": 12, + "action": "Fetch the interaction script and interact with the deployed contract", + "working_directory": "viem-deploy", + "reference_file": "interact.ts", + "description": "Fetch the reference file `interact.ts` and save it as `src/interact.ts`. Replace `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step and `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``. Run it with `npx tsx src/interact.ts`. Verify the value returned by reading `storedNumber` changes after calling `setNumber(value)`." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/smart-contracts/libraries/viem", + "files": [ + { + "path": "Storage.sol", + "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." + }, + { + "path": "chainConfig.ts", + "description": "Custom Polkadot Hub TestNet chain definition for viem (chain ID, RPC, native currency)" + }, + { + "path": "createClient.ts", + "description": "viem PublicClient construction against Polkadot Hub TestNet" + }, + { + "path": "createWallet.ts", + "description": "viem WalletClient construction with PRIVATE_KEY from env" + }, + { + "path": "compile.ts", + "description": "Solidity compiler wrapper that produces ABI + bytecode artifacts" + }, + { + "path": "deploy.ts", + "description": "Deploys the compiled Storage contract via viem WalletClient.deployContract" + }, + { + "path": "interact.ts", + "description": "Reads + writes the deployed Storage contract via viem readContract/writeContract" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: fee cap too low / transaction underpriced", + "cause": "Viem's default gas estimation produces a gas price below the 1000 gwei TestNet base fee.", + "resolution": "Add an explicit gasPrice override to the deployContract call: { gasPrice: parseGwei('5000') }. Import parseGwei from 'viem'. Polkadot Hub TestNet requires at least 1000 gwei; use 5000 gwei (5x) as a safe margin." + }, + { + "pattern": "Error: Chain ID mismatch", + "cause": "The Viem chain definition's id does not match the connected network's chain ID.", + "resolution": "Verify the chain object has id: 420420417. Check with: const chainId = await publicClient.getChainId(); and confirm it returns 420420417." + }, + { + "pattern": "Cannot find module 'viem'", + "cause": "Viem package not installed.", + "resolution": "Run 'npm install viem' in the viem-deploy directory." + }, + { + "pattern": "Error: insufficient funds for intrinsic transaction cost", + "cause": "The deployer account does not have enough PAS for gas.", + "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS for the deployer address. Verify balance with: await publicClient.getBalance({ address: '0x...' })" + } + ], + "supplementary_context": { + "description": "Load these pages for network details, Ethers.js comparison, or a full Hardhat-based workflow.", + "pages": [ + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Network parameters for Polkadot Hub including chain IDs, RPC URLs, and WebSocket endpoints." + }, + { + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", + "relevance": "Ethers.js equivalent workflow — use when the user prefers Ethers.js v6 over Viem." + }, + { + "slug": "smart-contracts-cookbook-dapps-zero-to-hero", + "title": "Zero to Hero Smart Contract DApp", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", + "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend — the natural next step after deploying a contract with Viem." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy and interact with a contract", + "user_says": "Deploy a smart contract to Polkadot Hub with Viem", + "actions": [ + "Create viem-deploy ESM project, install viem, dotenv, solc, tsx", + "Create .env with empty PRIVATE_KEY (user fills in)", + "Fetch chain.ts from polkadot-cookbook, verify chain ID 420420417 and TestNet RPC", + "Fetch deploy.ts, read.ts, write.ts; apply dotenv import and process.env.PRIVATE_KEY substitutions", + "Compile Solidity contract to ABI/bytecode artifacts", + "Run npx tsx deploy.ts and save deployed contract address", + "Update read.ts/write.ts with deployed address; run both to verify contract interaction" + ], + "result": "Contract deployed to Polkadot Hub TestNet; read and write operations confirmed via Viem scripts" + }, + { + "scenario": "Edge case: gas fee cap too low during deployment", + "user_says": "Deployment fails with fee cap too low error", + "actions": [ + "Open deploy.ts and locate the walletClient.deployContract call", + "Add gasPrice: parseGwei('5000') to the transaction options object", + "Import parseGwei from 'viem' at the top of the file", + "Retry with npx tsx deploy.ts" + ], + "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee" + } + ], + "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json" + }, + { + "id": "set-up-pallet-mock-runtime", + "title": "Set Up a Mock Runtime for Pallet Unit Testing", + "description": "Creates a mock runtime module for FRAME pallet unit testing using construct_runtime! and derive_impl. Use when you need a self-contained test environment for a custom FRAME pallet before writing unit tests. Configures frame_system with test defaults, satisfies pallet Config traits, and provides genesis storage helpers for different initial states. Trigger phrases: 'mock runtime pallet', 'set up pallet testing', 'construct_runtime test', 'pallet test environment', 'derive_impl TestDefaultConfig'. Requires a completed FRAME pallet in pallets/pallet-custom. Do NOT use for full parachain runtime integration testing.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/customize-runtime/pallet-development/mock-runtime.md" + ], + "primary_page": "parachains/customize-runtime/pallet-development/mock-runtime.md", + "prerequisites": { + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain (stable, via rustup)", + "Cargo", + "Completed the Make a Custom Pallet guide — the counter pallet must exist in pallets/pallet-custom" + ], + "network": [], + "tokens": [], + "wallet": [] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK parachain template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." + }, + { + "order": 2, + "action": "Build the parachain template node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." + }, + { + "order": 3, + "action": "Create mock.rs and add module declaration to lib.rs", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "commands": [ + "touch mock.rs" + ], + "description": "Create the empty mock.rs file. Then open src/lib.rs and insert the following two lines immediately after the 'pub use pallet::*;' line:\n\n```rust\n#[cfg(test)]\nmod mock;\n```\n\nThe #[cfg(test)] attribute ensures this module is only compiled when running tests, keeping it out of production builds." + }, + { + "order": 4, + "action": "Fetch and save the complete mock.rs", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "reference_file": "mock.rs", + "description": "Fetch the reference file and save it as 'mock.rs', replacing the empty placeholder. The file must contain: (1) imports including construct_runtime!, frame_system, and IdentityLookup; (2) Test runtime type and Block type alias via construct_runtime!; (3) #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] for System with AccountId = u64, Lookup = IdentityLookup, Block = Block; (4) impl pallet_custom::Config for Test with type RuntimeEvent = RuntimeEvent and type CounterMaxValue = ConstU32<1000>; (5) new_test_ext() genesis helper; (6) new_test_ext_with_counter(val: u32) helper; (7) new_test_ext_with_interactions() helper accepting counter value and Vec of (AccountId, u32) interactions." + }, + { + "order": 5, + "action": "Verify mock runtime compiles", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", + "commands": [ + "cargo test --package pallet-custom --lib" + ], + "expected_output": "test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok\ntest mock::test_genesis_config_builds ... ok", + "description": "Compile the test code including the new mock module. Two auto-generated tests will run: the construct_runtime integrity test and the genesis config build test. Both must pass. If compilation fails with 'missing field' errors, add the missing associated type to the impl pallet_custom::Config for Test block in mock.rs using a sensible default (e.g., type WeightInfo = ();)." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/mock-runtime", + "files": [ + { + "path": "mock.rs", + "description": "Complete mock runtime: construct_runtime! with System + CustomPallet, frame_system TestDefaultConfig, pallet Config impl, and three genesis storage helpers (default, counter init, interactions init)" + } + ] + }, + "error_patterns": [ + { + "pattern": "error[E0277]: the trait bound is not satisfied / missing associated type in Config impl", + "cause": "The mock.rs Config implementation is missing an associated type that the pallet's Config trait declares (e.g., WeightInfo).", + "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder — for WeightInfo: type WeightInfo = ();" + }, + { + "pattern": "error[E0412]: cannot find type in scope / unresolved import", + "cause": "An import path in mock.rs does not match the current crate layout.", + "resolution": "Verify that import paths in mock.rs match your actual pallet crate name and module structure. Check Cargo.toml for the crate name." + }, + { + "pattern": "error: proc macro `construct_runtime` panicked", + "cause": "A pallet in construct_runtime! has a misconfigured index or incompatible Config.", + "resolution": "Verify the pallet name and module path match the actual module. Ensure all required Config associated types are implemented in the mock." + } + ], + "supplementary_context": { + "description": "Load when continuing pallet development after mock runtime setup.", + "pages": [ + { + "slug": "parachains-customize-runtime-pallet-development-pallet-testing", + "title": "Unit Test Pallets", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/pallet-testing.md", + "relevance": "Next step: write comprehensive unit tests using the mock runtime." + }, + { + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", + "relevance": "Prerequisite: the custom counter pallet that this mock runtime is written for." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: create a test environment for the custom counter pallet", + "user_says": "Set up a mock runtime so I can write unit tests for my counter pallet", + "actions": [ + "Create pallets/pallet-custom/src/mock.rs", + "Add #[cfg(test)] mod mock; to lib.rs after pub use pallet::*;", + "Fetch the complete mock.rs from the polkadot-cookbook reference and save it", + "Run cargo test --package pallet-custom --lib to confirm auto-generated tests pass" + ], + "result": "Two auto-generated tests pass: construct_runtime integrity test and genesis config build test. The mock runtime is ready for unit test authoring." + }, + { + "scenario": "Edge case: pallet Config has additional associated types not in the reference mock.rs", + "user_says": "Compilation fails with 'missing associated type WeightInfo in impl pallet_custom::Config for Test'", + "actions": [ + "Open src/mock.rs", + "Add type WeightInfo = (); inside the impl pallet_custom::Config for Test block", + "Re-run cargo test --package pallet-custom --lib" + ], + "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments." + } + ], + "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs" + }, + { + "id": "unit-test-frame-pallet", + "title": "Write Unit Tests for a FRAME Pallet", + "description": "Writes a complete tests.rs module for a FRAME pallet using assert_ok!, assert_noop!, and System::assert_last_event macros. Use after completing the mock runtime guide to verify pallet logic in complete isolation. Covers all critical test categories: dispatchable success paths, arithmetic guard errors, root-only origin checks, event data correctness, user-interaction tracking, and genesis state loading. Trigger phrases: 'unit test FRAME pallet', 'test pallet logic', 'assert_noop assert_ok', 'pallet test suite', 'test dispatchable'. Requires completed create-a-pallet and set-up-pallet-mock-runtime guides. Do NOT use for integration or end-to-end testing of a full parachain.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/customize-runtime/pallet-development/pallet-testing.md" + ], + "primary_page": "parachains/customize-runtime/pallet-development/pallet-testing.md", + "prerequisites": { + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain (stable, via rustup)", + "Cargo", + "Completed the Make a Custom Pallet guide — counter pallet in pallets/pallet-custom", + "Completed the Mock Your Runtime guide — mock.rs must exist in pallets/pallet-custom/src" + ], + "network": [], + "tokens": [], + "wallet": [] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK parachain template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." + }, + { + "order": 2, + "action": "Build the parachain template node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." + }, + { + "order": 3, + "action": "Create tests.rs and add module declaration to lib.rs", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "commands": [ + "touch tests.rs" + ], + "description": "Create the empty tests.rs file. Then open src/lib.rs and add the following two lines immediately after the mock module declaration:\n\n```rust\n#[cfg(test)]\nmod tests;\n```\n\nThe module declarations in lib.rs should now read:\n#[cfg(test)] mod mock;\n#[cfg(test)] mod tests;" + }, + { + "order": 4, + "action": "Write test module imports", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "description": "Open src/tests.rs and write these imports at the top:\n\n```rust\nuse crate::{mock::*, Error, Event};\nuse frame::deps::frame_support::{assert_noop, assert_ok};\nuse frame::deps::sp_runtime::DispatchError;\n```\n\nThese bring in: all mock runtime items (Test, new_test_ext*, RuntimeOrigin, System, CustomPallet, CounterValue, UserInteractions), the pallet Error and Event enums, FRAME assertion macros, and DispatchError for origin checks." + }, + { + "order": 5, + "action": "Write basic operation and event emission tests", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion — events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() — uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() — uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() — uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" + }, + { + "order": 6, + "action": "Write error condition and access control tests", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() — uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() — uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() — uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() — uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" + }, + { + "order": 7, + "action": "Write genesis configuration and interaction tracking tests", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() — uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() — calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() — account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" + }, + { + "order": 8, + "action": "Run the complete test suite", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", + "commands": [ + "cargo test --package pallet-custom" + ], + "expected_output": "test result: ok. 15 passed; 0 failed; 0 ignored", + "description": "Run all tests. Expected output shows 15 passing tests: 13 pallet tests plus 2 auto-generated mock tests. If fewer appear, verify every function has #[test]. If an event test fails with 'left: []', confirm System::set_block_number(1) is the first call inside the execute_with closure." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-cookbook", + "branch": "master", + "base_path": "polkadot-docs/parachains/customize-runtime/pallet-development/pallet-testing", + "files": [] + }, + "error_patterns": [ + { + "pattern": "left: [], right: [EventRecord { ... }] / assertion failed on event", + "cause": "System::set_block_number(1) was not called before the dispatchable. Events are suppressed on block 0.", + "resolution": "Add System::set_block_number(1); as the first line inside the execute_with closure for any test that checks events." + }, + { + "pattern": "error: assert_noop! failed — storage was modified", + "cause": "The dispatchable modified storage before returning an error, violating the no-op contract.", + "resolution": "This is a pallet bug: all preconditions must be checked before any storage mutations. Fix the pallet extrinsic, not the test." + }, + { + "pattern": "thread 'tests::...' panicked at 'called Result::unwrap() on Err'", + "cause": "A dispatchable wrapped in assert_ok! returned an error. Usually wrong origin or incorrect genesis state.", + "resolution": "Check that the genesis helper (new_test_ext vs new_test_ext_with_counter) matches expected initial state, and the origin type matches what the extrinsic requires." + } + ], + "supplementary_context": { + "description": "Load when extending pallet testing or proceeding to benchmarking.", + "pages": [ + { + "slug": "parachains-customize-runtime-pallet-development-benchmark-pallet", + "title": "Benchmark Your Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/benchmark-pallet.md", + "relevance": "Next step: benchmark extrinsics and generate production weight files." + }, + { + "slug": "parachains-customize-runtime-pallet-development-mock-runtime", + "title": "Mock Your Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md", + "relevance": "Prerequisite: the mock runtime providing new_test_ext helpers and the Test runtime type." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: write a complete test suite for the counter pallet", + "user_says": "Write unit tests for my custom counter pallet covering increment, decrement, errors, and genesis config", + "actions": [ + "Create pallets/pallet-custom/src/tests.rs", + "Add #[cfg(test)] mod tests; to lib.rs", + "Write imports: use crate::{mock::*, Error, Event}; plus FRAME macros", + "Write increment_works, set_counter_value_works, decrement_works with System::set_block_number(1) for event checks", + "Write error tests: overflow, underflow, root-only access control", + "Write genesis_config_works and interaction tracking tests", + "Run cargo test --package pallet-custom — expect 15 passing tests" + ], + "result": "All 15 tests pass including 2 auto-generated mock tests." + }, + { + "scenario": "Edge case: event assertion fails with empty event list", + "user_says": "My event test fails: 'left: [], right: [EventRecord {...}]'", + "actions": [ + "Open the failing test in tests.rs", + "Add System::set_block_number(1); as the first line inside the execute_with closure", + "Re-run cargo test --package pallet-custom" + ], + "result": "The event test passes because events are recorded starting from block 1." + } + ] + }, + { + "id": "benchmark-frame-pallet", + "title": "Benchmark a FRAME Pallet and Generate Weight Files", + "description": "Implements full FRAME pallet benchmarking: adds benchmarking.rs with #[benchmark] functions, defines WeightInfo trait, wires it into pallet Config, configures Cargo.toml features, builds release WASM, installs frame-omni-bencher, downloads the weight template, and generates weights.rs. Use when a custom pallet needs accurate extrinsic weights before production deployment. Trigger phrases: 'benchmark pallet', 'frame-omni-bencher', 'generate weight file', 'WeightInfo', 'pallet weights production'. Requires a working parachain template with the custom pallet integrated. Do NOT use for estimating gas on EVM contracts.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/customize-runtime/pallet-development/benchmark-pallet.md" + ], + "primary_page": "parachains/customize-runtime/pallet-development/benchmark-pallet.md", + "prerequisites": { + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with wasm32-unknown-unknown target", + "Cargo", + "The custom counter pallet in pallets/pallet-custom with mock runtime set up (from mock-runtime guide)" + ], + "network": [], + "tokens": [], + "wallet": [] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK parachain template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." + }, + { + "order": 2, + "action": "Build the parachain template node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." + }, + { + "order": 3, + "action": "Create benchmarking.rs with benchmark functions", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "commands": [ + "touch benchmarking.rs" + ], + "description": "Create pallets/pallet-custom/src/benchmarking.rs with:\n\n```rust\n#![cfg(feature = \"runtime-benchmarks\")]\nuse super::*;\nuse frame::deps::frame_benchmarking::v2::*;\nuse frame::benchmarking::prelude::RawOrigin;\n\n#[benchmarks]\nmod benchmarks {\n use super::*;\n\n #[benchmark]\n fn set_counter_value() {\n let new_value: u32 = 100;\n #[extrinsic_call]\n _(RawOrigin::Root, new_value);\n assert_eq!(CounterValue::::get(), new_value);\n }\n\n #[benchmark]\n fn increment() {\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 50;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), amount);\n assert_eq!(UserInteractions::::get(caller), 1);\n }\n\n #[benchmark]\n fn decrement() {\n CounterValue::::put(100);\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 30;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), 70);\n }\n```\n\n impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);\n}\n\nFor a different pallet, update function names and assertions accordingly." + }, + { + "order": 4, + "action": "Define WeightInfo trait in weights.rs", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "commands": [ + "touch weights.rs" + ], + "description": "Create pallets/pallet-custom/src/weights.rs with a WeightInfo trait and () placeholder implementation:\n\n```rust\npub trait WeightInfo {\n fn set_counter_value() -> frame::prelude::Weight;\n fn increment() -> frame::prelude::Weight;\n fn decrement() -> frame::prelude::Weight;\n}\n\nimpl WeightInfo for () {\n fn set_counter_value() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(10_000, 0) }\n fn increment() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n fn decrement() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n}\n```\n\nThe () impl is used in tests; benchmark-generated values will replace this file in the final step." + }, + { + "order": 5, + "action": "Wire WeightInfo into pallet Config and extrinsic annotations", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", + "description": "Edit src/lib.rs with four changes:\n1. At the top add: pub mod weights; (expose weights module)\n2. After 'pub use pallet::*;' add: #[cfg(feature = \"runtime-benchmarks\")] mod benchmarking;\n3. In #[pallet::config] trait add: type WeightInfo: weights::WeightInfo;\n4. Replace hardcoded weight attributes on each extrinsic:\n - #[pallet::weight(T::WeightInfo::set_counter_value())]\n - #[pallet::weight(T::WeightInfo::increment())]\n - #[pallet::weight(T::WeightInfo::decrement())]" + }, + { + "order": 6, + "action": "Update pallet Cargo.toml with runtime-benchmarks feature", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", + "description": "Open pallets/pallet-custom/Cargo.toml and add the runtime-benchmarks feature under [features]:\n\n```toml\n[features]\ndefault = [\"std\"]\nruntime-benchmarks = [\n \"frame/runtime-benchmarks\",\n]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame/std\",\n]\n```" + }, + { + "order": 7, + "action": "Update mock.rs, runtime Cargo.toml, runtime config, and benchmarks.rs", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Four integration edits required:\n1. src/mock.rs: add type WeightInfo = (); to impl pallet_custom::Config for Test.\n2. runtime/Cargo.toml: add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks feature list.\n3. runtime/src/configs/mod.rs: add type WeightInfo = (); to impl pallet_custom::Config for Runtime (placeholder).\n4. runtime/src/benchmarks.rs: add [pallet_custom, CustomPallet] inside the define_benchmarks! macro." + }, + { + "order": 8, + "action": "Test benchmark compilation", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", + "commands": [ + "cargo test -p pallet-custom --features runtime-benchmarks" + ], + "expected_output": "test benchmarking::benchmarks::bench_set_counter_value ... ok", + "description": "Run benchmark unit tests generated by impl_benchmark_test_suite!. All three benchmark functions must pass before building the full runtime. These tests verify the benchmark code compiles and the assertions hold." + }, + { + "order": 9, + "action": "Build the release runtime with benchmarks enabled", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release --features runtime-benchmarks" + ], + "expected_output": "Finished release [optimized] target(s)", + "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking — do not use for production deployment." + }, + { + "order": 10, + "action": "Install frame-omni-bencher", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo install frame-omni-bencher --locked" + ], + "expected_output": "Installed package `frame-omni-bencher`", + "description": "Install via cargo, or download a pre-built binary from the Polkadot SDK releases page. Replace VERSION with the SDK version tag used by the parachain template (e.g., polkadot-stable2412):\nmacOS: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher-aarch64-apple-darwin && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/\nUbuntu: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/" + }, + { + "order": 11, + "action": "Create the weight Handlebars template file", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content — needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions — you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." + }, + { + "order": 12, + "action": "Execute benchmarks and generate weights.rs", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "frame-omni-bencher v1 benchmark pallet --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm --pallet pallet_custom --extrinsic \"\" --template ./pallets/pallet-custom/frame-weight-template.hbs --output ./pallets/pallet-custom/src/weights.rs" + ], + "expected_output": "Wrote weight file to ./pallets/pallet-custom/src/weights.rs", + "description": "Run benchmarks against the WASM runtime and generate production weights.rs. This overwrites the placeholder weights.rs from step 2. The output contains a SubstrateWeight struct implementing WeightInfo with actual measured values. For more precise measurements, add: --steps 50 --repeat 20" + }, + { + "order": 13, + "action": "Update runtime config to use generated weights", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/src/configs/mod.rs and update the pallet Config to use measured weights instead of ():\n\n```rust\nimpl pallet_custom::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type CounterMaxValue = ConstU32<1000>;\n type WeightInfo = pallet_custom::weights::SubstrateWeight;\n}\n```\n\nThen rebuild for production without the benchmarks flag:\ncargo build --release" + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/benchmark-pallet", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error[E0412]: cannot find type Weight / use of undeclared crate", + "cause": "The weights.rs uses frame_support types with the wrong import path for the SDK version.", + "resolution": "Use frame::prelude::Weight rather than frame_support::weights::Weight for SDK versions that use the unified frame crate." + }, + { + "pattern": "Error: No benchmarks found for pallet_custom", + "cause": "The pallet was not registered in define_benchmarks! in runtime/src/benchmarks.rs, or the pallet name is misspelled.", + "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! — use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." + }, + { + "pattern": "could not compile parachain-template-runtime / runtime-benchmarks feature error", + "cause": "The pallet's runtime-benchmarks feature was not added to the runtime's feature cascade in runtime/Cargo.toml.", + "resolution": "Add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks list in runtime/Cargo.toml." + } + ], + "supplementary_context": { + "description": "Load for context on pallet development prerequisites or parachain template.", + "pages": [ + { + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", + "relevance": "The custom counter pallet being benchmarked." + }, + { + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", + "relevance": "The parachain template runtime structure required for benchmark integration." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: benchmark the counter pallet and generate a production weight file", + "user_says": "Benchmark my counter pallet and generate weights.rs for production use", + "actions": [ + "Create benchmarking.rs with #[benchmark] functions for all three extrinsics", + "Create placeholder weights.rs with WeightInfo trait and () impl", + "Add WeightInfo to pallet Config and update #[pallet::weight] attributes in lib.rs", + "Update pallet Cargo.toml with runtime-benchmarks feature", + "Wire into runtime: mock.rs WeightInfo=(), runtime Cargo.toml, runtime Config, define_benchmarks!", + "Run cargo test -p pallet-custom --features runtime-benchmarks to verify benchmark compilation", + "cargo build --release --features runtime-benchmarks to build benchmark WASM", + "Install frame-omni-bencher, download frame-weight-template.hbs", + "Run frame-omni-bencher to generate weights.rs", + "Update runtime Config to use pallet_custom::weights::SubstrateWeight" + ], + "result": "A measured weights.rs replaces the placeholder; runtime uses accurate production weights." + }, + { + "scenario": "Edge case: frame-omni-bencher reports 'No benchmarks found'", + "user_says": "frame-omni-bencher says No benchmarks found for pallet_custom", + "actions": [ + "Open runtime/src/benchmarks.rs", + "Verify define_benchmarks! includes [pallet_custom, CustomPallet]", + "If missing, add the entry with underscore crate name and correct runtime type alias", + "Rebuild with --features runtime-benchmarks and re-run frame-omni-bencher" + ], + "result": "Benchmarks are discovered and weights.rs is generated successfully." + } + ] + }, + { + "id": "set-up-local-dev-node", + "title": "Set Up a Local Development Node", + "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/dev-environments/local-dev-node.md" + ], + "primary_page": "smart-contracts/dev-environments/local-dev-node.md", + "prerequisites": { + "runtime": [ + "Rust toolchain (stable + nightly via rustup) with wasm32-unknown-unknown target", + "Polkadot SDK build dependencies — complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", + "Git", + "At least 20 GB free disk space for the release build" + ], + "network": [], + "tokens": [], + "wallet": [] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the polkadot-sdk repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk.git", + "cd polkadot-sdk" + ], + "description": "Clone the official polkadot-sdk repository. The clone downloads several GB of source code. The repository contains both the revive-dev-node implementation and the ETH-RPC adapter." + }, + { + "order": 2, + "action": "Build the revive-dev-node binary", + "working_directory": "polkadot-sdk", + "commands": [ + "cargo build -p revive-dev-node --bin revive-dev-node --release" + ], + "expected_output": "Finished release [optimized] target(s)", + "description": "Compile the Revive Dev node in release mode. Release builds are optimized but take significantly longer (up to 30 minutes). The binary will be at polkadot-sdk/target/release/revive-dev-node. If out of memory, limit parallelism by appending: -- -j2" + }, + { + "order": 3, + "action": "Build the ETH-RPC adapter binary", + "working_directory": "polkadot-sdk", + "commands": [ + "cargo build -p pallet-revive-eth-rpc --bin eth-rpc --release" + ], + "expected_output": "Finished release [optimized] target(s)", + "description": "Compile the ETH-RPC adapter. This translates Ethereum JSON-RPC calls into Substrate requests. Binary at polkadot-sdk/target/release/eth-rpc." + }, + { + "order": 4, + "action": "Start the Revive Dev node", + "working_directory": "polkadot-sdk", + "commands": [ + "./target/release/revive-dev-node --dev" + ], + "interactive": true, + "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running — open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" + }, + { + "order": 5, + "action": "Start the ETH-RPC adapter in a new terminal", + "working_directory": "polkadot-sdk", + "commands": [ + "./target/release/eth-rpc --dev" + ], + "interactive": true, + "description": "Open a new terminal, navigate to the polkadot-sdk directory, and start the adapter with --dev. It connects to the local node and exposes Ethereum JSON-RPC at http://localhost:8545. Success is indicated by log lines showing the adapter is ready. For debug logging, prepend: RUST_LOG=\"info,eth-rpc=debug\"\n\nVerify the environment is ready by connecting Hardhat, Remix, or MetaMask to http://localhost:8545." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-cookbook", + "branch": "master", + "base_path": "polkadot-docs/smart-contracts/local-dev-node", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error: linker not found / error while loading shared libraries / could not compile", + "cause": "Missing system build dependencies such as clang, cmake, or protobuf-compiler.", + "resolution": "Complete the Install Polkadot SDK Dependencies guide. On Ubuntu: sudo apt install -y cmake clang libclang-dev protobuf-compiler. On macOS: brew install cmake protobuf." + }, + { + "pattern": "Connection refused at localhost:8545 / eth-rpc exits immediately", + "cause": "The revive-dev-node is not running when eth-rpc starts, or it crashed before the adapter connected.", + "resolution": "Ensure revive-dev-node --dev is running in a separate terminal and producing blocks before starting eth-rpc. The adapter connects via WebSocket to the node." + }, + { + "pattern": "Method not found / eth_method not supported", + "cause": "An Ethereum tool is calling a JSON-RPC method not implemented by the ETH-RPC adapter (e.g., anvil_* or debug_*).", + "resolution": "The local node supports a subset of the Ethereum JSON-RPC API. Use Polkadot Hub TestNet for full API compatibility. Check the Polkadot EVM differences documentation for supported methods." + } + ], + "supplementary_context": { + "description": "Load for guidance on connecting Ethereum tools or deploying contracts.", + "pages": [ + { + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", + "relevance": "Prerequisite: installing Rust and Polkadot SDK build dependencies." + }, + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Configure Hardhat to connect to http://localhost:8545 for local testing." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: start a local dev environment for smart contract development", + "user_says": "Set up a local Polkadot node for testing my smart contracts", + "actions": [ + "Clone polkadot-sdk from GitHub", + "Build revive-dev-node with cargo build --release in polkadot-sdk/", + "Build eth-rpc adapter with cargo build --release", + "Start revive-dev-node --dev in terminal 1", + "Start eth-rpc --dev in terminal 2", + "Confirm ETH-RPC is listening at http://localhost:8545" + ], + "result": "Local node produces blocks; ETH-RPC adapter accepts standard Ethereum JSON-RPC at localhost:8545." + }, + { + "scenario": "Edge case: cargo build takes excessively long or runs out of memory", + "user_says": "The build has been running for over an hour and my machine is unresponsive", + "actions": [ + "Cancel the build with Ctrl+C", + "Check free disk space (df -h) — at least 20 GB required", + "Check free RAM — at least 8 GB recommended", + "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" + ], + "result": "Build completes more slowly with reduced CPU and memory usage." + } + ] + }, + { + "id": "interact-with-chain-dedot", + "title": "Interact with Polkadot Chains Using Dedot", + "description": "Sets up a lightweight Dedot TypeScript client to read chain state and send transactions on any Polkadot SDK blockchain. Use when you need a modern, tree-shakable alternative to Polkadot.js for querying storage, subscribing to events, calling runtime APIs, or submitting signed extrinsics. Trigger phrases: 'use Dedot', 'query chain with Dedot', 'Dedot client', 'interact with chain TypeScript'. Output: account balance, runtime constants, and submitted transaction hash. No testnet tokens required for read-only steps.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/dedot.md" + ], + "primary_page": "reference/tools/dedot.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.polkadot.io for Polkadot mainnet)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Initialize an ESM Node.js project", + "working_directory": ".", + "commands": [ + "mkdir dedot-example && cd dedot-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory named 'dedot-example' and initialize it as an ESM Node.js project. The 'type=module' setting is required because Dedot uses ES module imports." + }, + { + "order": 2, + "action": "Install Dedot and TypeScript tooling", + "working_directory": "dedot-example", + "commands": [ + "npm install dedot", + "npm install -D @dedot/chaintypes tsx typescript" + ], + "description": "Install the 'dedot' runtime package, '@dedot/chaintypes' for per-chain type definitions and IntelliSense, and 'tsx' plus 'typescript' as dev tooling. The '@dedot/chaintypes' package enables auto-completion for chain-specific pallets and storage entries." + }, + { + "order": 3, + "action": "Fetch the DedotClient initialization script", + "working_directory": "dedot-example", + "reference_file": "client-initialization-via-ws.ts", + "description": "Fetch the reference file `client-initialization-via-ws.ts` and save it as `main.ts`. Then make these substitutions:\n(1) Replace `INSERT_ADDRESS` with the SS58 address you'll query.\n(2) Replace `wss://rpc.polkadot.io` with the target chain's WebSocket endpoint if needed.\n\nThe script connects via `WsProvider` and uses `PolkadotApi` (from `@dedot/chaintypes`) as the ChainApi interface for type safety. If you target a different chain, replace `PolkadotApi` with the appropriate ChainApi (e.g. `KusamaApi`) or omit it to fall back to `SubstrateApi`." + }, + { + "order": 4, + "action": "Append the balance query and disconnect", + "working_directory": "dedot-example", + "description": "Open `main.ts` and append the following block immediately before the end of `main()` (or wherever the connection is established) so the script queries account balance and cleanly disconnects:\n\n```typescript\nconst balance = await client.query.system.account('INSERT_ADDRESS');\nconsole.log('Free balance:', balance.data.free);\nawait client.disconnect();\n```\n\nReplace `INSERT_ADDRESS` with the same SS58 address you used in the previous step." + }, + { + "order": 5, + "action": "Run the query script", + "working_directory": "dedot-example", + "commands": [ + "npx tsx main.ts" + ], + "expected_output": "Free balance: ", + "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts — it will not throw an error." + }, + { + "order": 6, + "action": "Sign and send a transaction", + "working_directory": "dedot-example", + "description": "Fetch the reference file `sign-and-send-tx-with-keyring.ts` and save it as `send-tx.ts`. The pattern uses `@polkadot/keyring` for signing. Make these substitutions:\n(1) Replace `INSERT_DEST_ADDRESS` with the recipient SS58 address.\n(2) Replace `2_000_000_000_000n` with the desired amount in planck (1 DOT = 10^10 planck).\n\nThen install the signer dependency:\n\n```bash\nnpm install @polkadot/keyring @polkadot/util-crypto\n```\n\nRun with `npx tsx send-tx.ts`. The script will print the transaction hash on inclusion.", + "reference_file": "sign-and-send-tx-with-keyring.ts" + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/reference/tools/dedot", + "files": [ + { + "path": "client-initialization-via-ws.ts", + "description": "DedotClient initialization via WsProvider against a Polkadot Hub WebSocket endpoint, typed against PolkadotApi from @dedot/chaintypes" + }, + { + "path": "sign-and-send-tx-with-keyring.ts", + "description": "Signs and submits a Balances.transfer extrinsic using a @polkadot/keyring keypair; demonstrates the full signTx flow" + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: Cannot find package 'dedot'", + "cause": "Dependencies not installed or node_modules missing.", + "resolution": "Run 'npm install dedot' in the dedot-example directory." + }, + { + "pattern": "SyntaxError: Cannot use import statement in a module", + "cause": "Project is not configured as ESM.", + "resolution": "Run 'npm pkg set type=module' in the project directory." + }, + { + "pattern": "WebSocket connection failed / ECONNREFUSED", + "cause": "Wrong WebSocket endpoint URL or the node is unreachable.", + "resolution": "Verify the WebSocket URL is correct and the node is online. For Polkadot mainnet use wss://rpc.polkadot.io. For Polkadot Hub TestNet use wss://asset-hub-paseo.dotters.network." + }, + { + "pattern": "TypeError: Cannot read properties of undefined (reading 'free')", + "cause": "The queried account does not exist on chain (never had a balance).", + "resolution": "An account with no on-chain history returns a default AccountInfo with all balances at 0. Check that the ADDRESS constant is a valid SS58 address for the target chain. Use a funded account or a known validator address for initial testing." + } + ], + "supplementary_context": { + "description": "Load these pages when the user asks about connecting to specific chains or using alternative clients.", + "pages": [ + { + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", + "relevance": "Multi-SDK tutorial showing how to query account balance using PAPI, Polkadot.js, Dedot, and other clients side-by-side." + }, + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "How to construct and submit signed transactions using multiple SDKs including Dedot." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: query account balance", + "user_says": "Use Dedot to check the balance of a Polkadot account", + "actions": [ + "Scaffold 'dedot-example/' as an ESM Node.js project", + "Install dedot, @dedot/chaintypes, and tsx", + "Create main.ts with DedotClient connected to wss://rpc.polkadot.io", + "Query client.query.system.account for the target address", + "Run npx tsx main.ts and read the printed free balance" + ], + "result": "Free balance of the queried address printed to console in planck units." + }, + { + "scenario": "Edge case: targeting a custom Polkadot SDK chain", + "user_says": "Connect Dedot to my parachain and query a custom pallet", + "actions": [ + "Generate a ChainApi interface for the custom chain: npx dedot chaintypes -w wss://your-node.example.com", + "Import the generated ChainApi and pass it as the type parameter to DedotClient.new()", + "Access custom pallet storage via client.query..()" + ], + "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs." + } + ], + "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json" + }, + { + "id": "run-parachain-node-omni-node", + "title": "Run a Parachain Node with Polkadot Omni Node", + "description": "Installs the polkadot-omni-node pre-built binary and runs a full node for any compatible Polkadot parachain using an external chain spec file. Use when you need to run a parachain node without maintaining a custom node codebase, or when testing a runtime against a live network. Trigger phrases: 'run a parachain node', 'polkadot-omni-node', 'spin up a parachain full node', 'start omni node'. Output: syncing parachain node with WebSocket RPC at ws://localhost:9944. Requires a chain spec JSON file for the target parachain.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/omninode.md" + ], + "primary_page": "reference/tools/omninode.md", + "prerequisites": { + "runtime": [ + "macOS (Apple Silicon or Intel) or Linux (Ubuntu/Debian) for the pre-built binary path", + "Rust and Cargo (via rustup) if building from source — see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" + ], + "network": [ + "Internet access to download the binary and to sync the parachain" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Install polkadot-omni-node", + "working_directory": ".", + "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust — see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" + }, + { + "order": 2, + "action": "Verify the installation", + "working_directory": ".", + "commands": [ + "polkadot-omni-node --version" + ], + "expected_output": "polkadot-omni-node ", + "description": "Confirm the binary is installed and accessible from PATH. You should see the installed version number. If the command is not found, ensure /usr/local/bin is in your PATH, or use the full path to the binary." + }, + { + "order": 3, + "action": "Obtain a chain specification", + "working_directory": ".", + "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path — you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." + }, + { + "order": 4, + "action": "Run a parachain full node", + "working_directory": ".", + "description": "Launch the node with warp sync enabled. Replace './chain_spec.json' with the actual path to your saved chain spec file:\n\nTo see all available flags before running:\n```\npolkadot-omni-node --help\n```\n\nTo start the node:\n```\npolkadot-omni-node --chain ./chain_spec.json --sync warp\n```\n\nThe --chain flag points to the chain spec; --sync warp enables fast catch-up to the latest finalized state (historical blocks sync in the background).\n\nYou will see log lines like 'Syncing' and 'Imported #NNNN' as the node connects to peers and downloads state. The node exposes a WebSocket RPC endpoint at ws://localhost:9944 by default.\n\nTo stop the node, press Ctrl+C. For long-running production use, configure a systemd service or a process manager." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "polkadot-omni-node: command not found", + "cause": "Binary not on PATH or installation failed.", + "resolution": "Verify the binary was moved to /usr/local/bin/ and that directory is in $PATH. Run 'which polkadot-omni-node' to check. If missing, repeat the installation step." + }, + { + "pattern": "Error: The chain spec is not compatible / missing required runtime API", + "cause": "The parachain runtime does not implement all APIs required by polkadot-omni-node (e.g., GetParachainInfo, AuraApi, or required pallets).", + "resolution": "Check the runtime compatibility requirements at docs.polkadot.com/reference/tools/omninode/. The runtime must implement GetParachainInfo, AuraApi, and include the System, ParachainSystem, Aura, and ParachainInfo pallets. See the parachain template at github.com/paritytech/polkadot-sdk-parachain-template for a reference implementation." + }, + { + "pattern": "No peers found / node stuck at block 0", + "cause": "Network connectivity issue, firewall blocking P2P ports, or incorrect chain spec.", + "resolution": "Ensure outbound TCP on ports 30333/30334 is not blocked. Verify the chain spec corresponds to an active network. Check the Polkadot Discord for any network outages." + } + ], + "supplementary_context": { + "description": "Load these pages for context on parachain runtimes, chain specs, or building from source.", + "pages": [ + { + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", + "relevance": "How to build a parachain template runtime that is compatible with polkadot-omni-node." + }, + { + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", + "relevance": "Install Rust and system dependencies needed if building polkadot-omni-node from source." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: run an Asset Hub full node", + "user_says": "Use polkadot-omni-node to run an Asset Hub node", + "actions": [ + "Download the latest polkadot-omni-node binary from the Polkadot SDK releases page", + "Verify installation with polkadot-omni-node --version", + "Download the Asset Hub chain spec JSON from paritytech.github.io/chainspecs/", + "Save as chain_spec.json and run: polkadot-omni-node --chain ./chain_spec.json --sync warp" + ], + "result": "Syncing Asset Hub node with WebSocket RPC accessible at ws://localhost:9944." + }, + { + "scenario": "Edge case: build from source when no pre-built binary is available for the platform", + "user_says": "I'm on a non-standard Linux architecture — install polkadot-omni-node from source", + "actions": [ + "Install Rust via rustup and system dependencies per parachains/install-polkadot-sdk/", + "Check crates.io for the latest polkadot-omni-node version", + "Run: cargo install --locked polkadot-omni-node@INSERT_VERSION", + "Verify: polkadot-omni-node --version" + ], + "result": "polkadot-omni-node installed from source and verified on a non-standard platform." + } + ] + }, + { + "id": "interact-polkadot-node-py-substrate", + "title": "Interact with a Polkadot Node Using Python Substrate Interface", + "description": "Sets up the Python Substrate Interface library to connect to any Polkadot SDK blockchain, query on-chain storage (account balance, nonce), and submit signed extrinsics. Use when building Python scripts, data pipelines, or backend services that need to read or write chain state without a JavaScript runtime. Trigger phrases: 'Python Substrate Interface', 'py-substrate-interface', 'query Polkadot with Python', 'Substrate Python', 'substrate-interface pip'. Output: printed account balance details and transaction receipt with extrinsic and block hash.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/py-substrate-interface.md" + ], + "primary_page": "reference/tools/py-substrate-interface.md", + "prerequisites": { + "runtime": [ + "Python 3.7+", + "pip" + ], + "network": [ + "WebSocket RPC endpoint for the target Polkadot SDK node (e.g., wss://rpc.polkadot.io for Polkadot mainnet, or ws://127.0.0.1:9944 for a local node)" + ] + }, + "env_vars": [ + { + "name": "MNEMONIC", + "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file — do not ask for it in chat.", + "required": false + } + ], + "steps": [ + { + "order": 1, + "action": "Create a virtual environment and install the library", + "working_directory": ".", + "commands": [ + "python3 -m venv py-substrate-example && cd py-substrate-example", + "source bin/activate", + "pip install substrate-interface" + ], + "description": "Create an isolated virtual environment named 'py-substrate-example' and install the substrate-interface library. On Windows, replace 'source bin/activate' with '.\\Scripts\\activate'. After activation, the prompt shows '(py-substrate-example)'. All subsequent commands must be run in this activated environment." + }, + { + "order": 2, + "action": "Create the connection script", + "working_directory": "py-substrate-example", + "description": "Create 'connect.py' with the following content. Replace 'INSERT_WS_URL' with the WebSocket endpoint for your target node (e.g., 'wss://rpc.polkadot.io' for Polkadot mainnet or 'ws://127.0.0.1:9944' for a local dev node).\n\nFetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/substrate_interface.py -o connect.py\n\nThen replace INSERT_WS_URL in connect.py with the target endpoint. Run to verify the connection:\npython3 connect.py\n\nExpected output: 'Connected to chain: Polkadot' (or your target chain name). If the chain name is wrong or absent, the URL may be incorrect." + }, + { + "order": 3, + "action": "Query account balance", + "working_directory": "py-substrate-example", + "description": "Create 'read_state.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/read_state.py -o read_state.py\n\nSubstitutions required:\n- At the top of the file, add the SubstrateInterface connection (same as connect.py, replacing INSERT_WS_URL with the endpoint).\n- Replace 'INSERT_ADDRESS' with the SS58 address to query.\n\nRun:\npython3 read_state.py\n\nExpected output:\n Account Details:\n - Free Balance: \n - Reserved: \n - Nonce: \n\nThe values are in planck (smallest unit). Divide by 10^10 for DOT values on Polkadot mainnet." + }, + { + "order": 4, + "action": "Submit a balance transfer", + "working_directory": "py-substrate-example", + "description": "Create '.env' file:\nMNEMONIC=\n\nAdd '.env' to .gitignore:\necho '.env' >> .gitignore\n\nStop and ask the user to fill in their mnemonic phrase in the .env file. Do NOT ask for the mnemonic phrase in chat. Wait for confirmation before continuing.\n\nInstall dotenv:\npip install python-dotenv\n\nCreate 'send_tx.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/send_tx.py -o send_tx.py\n\nAt the top of send_tx.py, add:\n```python\nimport os\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom substrateinterface import SubstrateInterface, Keypair\n\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\nkeypair = Keypair.create_from_mnemonic(os.environ['MNEMONIC'])\n```\n\nThen add the template content below. Substitutions in send_tx.py:\n- Replace INSERT_WS_URL with the WebSocket endpoint.\n- Replace 'INSERT_ADDRESS' (in call_params 'dest') with the recipient SS58 address.\n- Replace 'INSERT_VALUE' with the transfer amount in planck (e.g., 1000000000000 for 0.1 DOT).\n\nRun:\npython3 send_tx.py\n\nExpected output:\n Transaction successful:\n - Extrinsic Hash: 0x...\n - Block Hash: 0x..." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "ModuleNotFoundError: No module named 'substrateinterface'", + "cause": "Library not installed or virtual environment not activated.", + "resolution": "Ensure the virtual environment is activated (run 'source bin/activate' on macOS/Linux or '.\\Scripts\\activate' on Windows), then run 'pip install substrate-interface'." + }, + { + "pattern": "ConnectionError / WebSocket connection refused", + "cause": "Wrong WebSocket URL or the node is not running.", + "resolution": "Verify the URL in SubstrateInterface(url=...). For a local node ensure it's running with --rpc-external or uses the default port 9944. For public endpoints, test with wss://rpc.polkadot.io." + }, + { + "pattern": "ValueError: Invalid address format", + "cause": "The address passed to the storage query is not a valid SS58-encoded address.", + "resolution": "Verify the account address is in SS58 format and matches the network's prefix. For Polkadot mainnet, addresses start with '1'; for testnet (Paseo) addresses start with a different prefix. Use polkadot.js.org/apps to validate the address." + }, + { + "pattern": "ExtrinsicFailed: Token.FundsUnavailable", + "cause": "The sender account does not have enough balance to pay the transfer amount plus transaction fees.", + "resolution": "Check the account balance with read_state.py before submitting. Ensure the 'value' parameter in call_params covers both the transfer amount and transaction fees." + } + ], + "supplementary_context": { + "description": "Load these pages for context on chain interaction patterns or the Python SDK documentation.", + "pages": [ + { + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", + "relevance": "Multi-SDK account query tutorial including a Python Substrate Interface example alongside PAPI and Polkadot.js." + }, + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "How to construct and submit transactions using multiple SDK clients including Python Substrate Interface." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: query account balance from Python", + "user_says": "Use Python to check the balance of a Polkadot account", + "actions": [ + "Create virtual environment and install substrate-interface", + "Create connect.py with SubstrateInterface(url='wss://rpc.polkadot.io') and verify connection", + "Create read_state.py with substrate.query(module='System', storage_function='Account', params=[ADDRESS])", + "Print free_balance, reserved, and nonce from account_info.value" + ], + "result": "Account balance details printed with free balance in planck." + }, + { + "scenario": "Edge case: transaction fails with ExtrinsicFailed", + "user_says": "My Python transfer script runs but the extrinsic failed", + "actions": [ + "Check receipt.is_success — if False, print receipt.error_message for the specific error", + "If FundsUnavailable: verify sender balance covers transfer amount plus fees using read_state.py", + "If BadOrigin: verify the keypair matches the sender address in the call_params 'dest' field" + ], + "result": "Root cause identified from receipt.error_message; transaction corrected and resubmitted." + } + ] + }, + { + "id": "interact-polkadot-node-subxt", + "title": "Interact with a Polkadot Node Using Subxt (Rust)", + "description": "Scaffolds a Rust project with the Subxt library to interact with any Polkadot SDK blockchain using compile-time type safety. Use when building Rust services, CLI tools, or backend applications that need to query chain state, call runtime APIs, or submit transactions without an external RPC framework. Trigger phrases: 'Subxt', 'Rust Polkadot client', 'interact with Polkadot in Rust', 'subxt-cli metadata', 'type-safe Polkadot Rust'. Output: existential deposit constant, account info, and confirmed balance transfer event.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/subxt.md" + ], + "primary_page": "reference/tools/subxt.md", + "prerequisites": { + "runtime": [ + "Rust and Cargo installed via rustup (https://rustup.rs/)", + "A Rust project directory (cargo new my_project)" + ] + }, + "env_vars": [ + { + "name": "SECRET_PHRASE", + "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file — do not ask for it in chat.", + "required": false + } + ], + "steps": [ + { + "order": 1, + "action": "Create a Rust project", + "working_directory": ".", + "commands": [ + "cargo new subxt-example && cd subxt-example" + ], + "description": "Create a new Rust binary project named 'subxt-example'. All subsequent steps run from within this directory." + }, + { + "order": 2, + "action": "Install the subxt-cli tool", + "working_directory": "subxt-example", + "description": "Install the subxt-cli command-line tool used to download chain metadata. Check https://crates.io/crates/subxt-cli for the latest version and replace INSERT_SUBXT_CLI_VERSION below:\n\ncargo install subxt-cli@INSERT_SUBXT_CLI_VERSION\n\nAs of the current reference files, the version is 0.50.0, so the command would be:\ncargo install subxt-cli@0.50.0\n\nThis installs the 'subxt' binary globally. Installation may take several minutes as it compiles from source." + }, + { + "order": 3, + "action": "Add Subxt dependencies to Cargo.toml", + "working_directory": "subxt-example", + "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." + }, + { + "order": 4, + "action": "Download chain metadata", + "working_directory": "subxt-example", + "description": "Use subxt-cli to fetch the chain's runtime metadata. Replace INSERT_NODE_URL with the WebSocket URL of the node (e.g., wss://rpc.polkadot.io for Polkadot mainnet):\n\nsubxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale\n\nThis saves the SCALE-encoded metadata to 'polkadot_metadata.scale' in the project root. The #[subxt::subxt] macro references this file at compile time. If the command hangs, verify the node URL is reachable and the node is running." + }, + { + "order": 5, + "action": "Create the main Rust source file", + "working_directory": "subxt-example", + "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." + }, + { + "order": 6, + "action": "Build and run", + "working_directory": "subxt-example", + "commands": [ + "cargo run" + ], + "expected_output": "Existential deposit: \nAccount info: \nBalance transfer successful: ", + "description": "Build and run the project. The first build will take several minutes as dependencies compile. Subsequent builds are faster. The program: (1) reads the existential deposit constant, (2) fetches account info for INSERT_ADDRESS, (3) submits a balance transfer and waits for finalization. If the transfer step fails with an error, check that the account has sufficient balance (at least INSERT_AMOUNT + gas fees) and that INSERT_SECRET_PHRASE belongs to the sender account." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error[E0425]: cannot find value / type in scope — after changing metadata", + "cause": "The generated types from #[subxt::subxt] changed after re-downloading metadata from a different chain or after a runtime upgrade.", + "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ — check the Subxt docs for the new paths." + }, + { + "pattern": "Error: Rpc error: target url is not valid", + "cause": "INSERT_NODE_URL was not replaced or the URL format is incorrect.", + "resolution": "Ensure NODE_URL in subxt.rs is a full WebSocket URL starting with ws:// or wss:// (e.g., wss://rpc.polkadot.io). HTTP URLs are not supported." + }, + { + "pattern": "invalid phrase: mnemonic / BIP39 error", + "cause": "INSERT_SECRET_PHRASE was not replaced or is malformed.", + "resolution": "Verify the mnemonic is 12 or 24 BIP39 words separated by spaces. Load it from the SECRET_PHRASE env variable rather than hardcoding. Ensure there are no leading/trailing spaces in the .env value." + }, + { + "pattern": "Module 'Balances' / ExtrinsicFailed", + "cause": "Insufficient balance, wrong destination address, or node RPC error.", + "resolution": "Check that the sender account has enough balance to cover the transfer amount plus transaction fees. Verify INSERT_DEST_ADDRESS is a valid SS58 address for the target network." + } + ], + "supplementary_context": { + "description": "Load these pages for context on chain interaction patterns or the Subxt API surface.", + "pages": [ + { + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", + "relevance": "Multi-SDK account query tutorial including Subxt alongside PAPI, Polkadot.js, and Python Substrate Interface." + }, + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "How to sign and submit transactions with multiple SDK clients — context for the Subxt transaction pattern." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: query chain constants and account balance", + "user_says": "Use Subxt in Rust to read the existential deposit and an account's balance", + "actions": [ + "Create Rust project and install subxt-cli", + "Add subxt, subxt-signer, tokio dependencies to Cargo.toml", + "Fetch chain metadata: subxt metadata --url wss://rpc.polkadot.io > polkadot_metadata.scale", + "Create subxt.rs with #[subxt::subxt] macro pointing to polkadot_metadata.scale", + "Replace INSERT_NODE_URL and INSERT_ADDRESS, then run cargo run" + ], + "result": "Existential deposit constant and account info struct printed to stdout." + }, + { + "scenario": "Edge case: runtime upgrade changes generated types", + "user_says": "My Subxt code broke after the chain did a runtime upgrade", + "actions": [ + "Re-fetch metadata: subxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale", + "Run cargo build and review compile errors for changed type paths", + "Update pallet names or storage entry paths in subxt.rs to match the new metadata" + ], + "result": "Code updated to match post-upgrade runtime types and successfully compiles and runs." + } + ] + }, + { + "id": "deploy-erc721-nft-hardhat", + "title": "Deploy an ERC-721 NFT Using Hardhat", + "description": "Scaffolds a Hardhat project manually, installs OpenZeppelin contracts and dotenv, creates a MyNFT.sol ERC-721 contract and Ignition deployment module, compiles with Cancun EVM version, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when deploying an ERC-721 NFT to Polkadot Hub. Requires testnet PAS tokens and a funded EVM wallet. Trigger phrases: 'deploy NFT Polkadot', 'ERC-721 Hardhat', 'deploy non-fungible token', 'mint NFT Polkadot Hub'. Uses OpenZeppelin Contracts v5.4.0+ requiring evmVersion cancun. Do NOT use for ERC-20 tokens; use deploy-erc20-token-hardhat instead.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" + ], + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", + "prerequisites": { + "runtime": [ + "Node.js v22.13.1 or later", + "npm" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create the project directory and initialize npm", + "working_directory": ".", + "commands": [ + "mkdir hardhat-nft-deployment", + "cd hardhat-nft-deployment", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead." + }, + { + "order": 2, + "action": "Install Hardhat, OpenZeppelin, and dotenv", + "working_directory": "hardhat-nft-deployment", + "commands": [ + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox", + "npm install @openzeppelin/contracts dotenv" + ], + "description": "Install Hardhat v2 toolchain, OpenZeppelin contracts (v5.4.0+), and dotenv for secure private key handling." + }, + { + "order": 3, + "action": "Create the Hardhat configuration file", + "working_directory": "hardhat-nft-deployment", + "description": "Create a file named 'hardhat.config.ts' with the following content:\n\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\nimport '@nomicfoundation/hardhat-toolbox';\nimport 'dotenv/config';\n\nconst config: HardhatUserConfig = {\n solidity: {\n version: '0.8.28',\n settings: { evmVersion: 'cancun' }\n },\n networks: {\n polkadotTestnet: {\n url: 'https://services.polkadothub-rpc.com/testnet',\n chainId: 420420417,\n accounts: [process.env.PRIVATE_KEY as string],\n gasPrice: 5000000000000\n }\n },\n ignition: { requiredConfirmations: 1 }\n};\n\nexport default config;\n```\n\nKey points: (1) 'dotenv/config' must be the first functional import. (2) evmVersion: 'cancun' is required for OpenZeppelin v5.4.0+ (mcopy opcode). (3) gasPrice: 5000000000000 (5000 gwei) prevents 'priority is too low' errors on TestNet. (4) requiredConfirmations: 1 prevents Ignition from misreading pending transactions as dropped." + }, + { + "order": 4, + "action": "Create .env file and .gitignore entry", + "working_directory": "hardhat-nft-deployment", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." + }, + { + "order": 5, + "action": "Create project directories", + "working_directory": "hardhat-nft-deployment", + "commands": [ + "mkdir -p contracts ignition/modules" + ], + "description": "Create the contracts and ignition/modules directories that Hardhat expects." + }, + { + "order": 6, + "action": "Create the MyNFT.sol contract", + "working_directory": "hardhat-nft-deployment", + "description": "Create 'contracts/MyNFT.sol' with the following OpenZeppelin ERC-721 contract.Use the inline content below:\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```" + }, + { + "order": 7, + "action": "Create the Ignition deployment module", + "working_directory": "hardhat-nft-deployment", + "description": "Create 'ignition/modules/MyNFT.ts'. Replace INSERT_OWNER_ADDRESS with the Ethereum address that should own the NFT contract (typically the address derived from your PRIVATE_KEY):\n\n```typescript\nimport { buildModule } from '@nomicfoundation/hardhat-ignition/modules';\n\nconst MyNFTModule = buildModule('MyNFTModule', (m) => {\n const myNFT = m.contract('MyNFT', ['INSERT_OWNER_ADDRESS']);\n return { myNFT };\n});\n\nexport default MyNFTModule;\n```" + }, + { + "order": 8, + "action": "Compile the contract", + "working_directory": "hardhat-nft-deployment", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 1 Solidity file successfully", + "description": "Compile MyNFT.sol. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in hardhat.config.ts and recompile." + }, + { + "order": 9, + "action": "Deploy the NFT contract to Polkadot Hub TestNet", + "working_directory": "hardhat-nft-deployment", + "commands": [ + "npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet" + ], + "expected_output": "MyNFTModule#MyNFT - 0x...", + "interactive": true, + "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "CompilationError: Invalid opcode: MCOPY", + "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode requiring Cancun EVM version.", + "resolution": "In hardhat.config.ts set evmVersion: 'cancun' inside solidity.settings, then run 'npx hardhat compile' again." + }, + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of deployment due to missing gasPrice or requiredConfirmations: 0.", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." + }, + { + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + } + ], + "supplementary_context": { + "description": "Load when the user asks about minting NFTs, building a dApp on the deployed contract, or ERC-721 concepts.", + "pages": [ + { + "slug": "smart-contracts-cookbook-dapps-zero-to-hero", + "title": "Zero to Hero Smart Contract DApp", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", + "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract on Polkadot Hub." + }, + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Reference for full Hardhat EVM environment setup and network configuration for Polkadot Hub." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy a new ERC-721 NFT contract", + "user_says": "Deploy an ERC-721 NFT contract to Polkadot Hub TestNet", + "actions": [ + "Create project directory 'hardhat-nft-deployment' and run npm init -y", + "Install hardhat, OpenZeppelin contracts, and dotenv", + "Create hardhat.config.ts with dotenv, evmVersion: cancun, gasPrice 5000 gwei, requiredConfirmations: 1", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Create contracts/MyNFT.sol and ignition/modules/MyNFT.ts (replacing INSERT_OWNER_ADDRESS)", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet; delegate confirmation to user" + ], + "result": "ERC-721 NFT contract deployed; contract address printed to console" + }, + { + "scenario": "Edge case: compilation fails with MCOPY opcode error", + "user_says": "Compilation fails with 'Invalid opcode: MCOPY'", + "actions": [ + "Open hardhat.config.ts", + "Set solidity to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", + "Run npx hardhat compile again" + ], + "result": "Compilation succeeds with Cancun EVM version" + } + ] + }, + { + "id": "deploy-erc721-nft-remix", + "title": "Deploy an ERC-721 NFT Using Remix IDE", + "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" + ], + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask browser extension installed and configured for Polkadot Hub TestNet", + "Testnet account funded with PAS tokens" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Open Remix IDE and create the contract file", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: (1) Navigate to https://remix.ethereum.org in your web browser. (2) Under the 'contracts' folder, click 'Create new file' and name it 'MyNFT.sol'. (3) Paste the following ERC-721 contract code:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```\n\nWait for the user to confirm the file has been created and the code pasted." + }, + { + "order": 2, + "action": "Compile the contract in Remix", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: (1) Click the 'Solidity Compiler' icon in the left panel. (2) Click the 'Compile MyNFT.sol' button. Success: the compiler icon shows a green checkmark. If errors appear, check that the contract code was pasted correctly and the Solidity version is 0.8.22+." + }, + { + "order": 3, + "action": "Connect MetaMask to Polkadot Hub TestNet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: (1) Click 'Deploy & Run Transactions' in the left panel. (2) In the 'Environment' dropdown, select 'Injected Provider - MetaMask'. (3) Approve the MetaMask connection popup. (4) Confirm MetaMask is on Polkadot Hub TestNet (chain ID 420420417). If not, add it via MetaMask Settings > Networks with RPC https://services.polkadothub-rpc.com/testnet. Confirm the funded account is visible." + }, + { + "order": 4, + "action": "Deploy the NFT contract", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: (1) Ensure 'MyNFT' is selected in the Contract dropdown. (2) Expand the Deploy section and enter the constructor argument: the owner address (typically your MetaMask wallet address). (3) Click 'Deploy'. (4) Confirm the transaction in MetaMask. Once deployed, the contract appears in 'Deployed Contracts'. Record the contract address." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "MetaMask: Wrong network / chain ID mismatch", + "cause": "MetaMask is connected to a different network than Polkadot Hub TestNet.", + "resolution": "Switch to or add Polkadot Hub TestNet in MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet." + }, + { + "pattern": "insufficient funds for gas", + "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", + "resolution": "Get testnet PAS tokens from https://faucet.polkadot.io/ for the connected account, then retry." + }, + { + "pattern": "Compilation errors in Remix", + "cause": "Contract code syntax error or Solidity version mismatch.", + "resolution": "Verify the contract code was pasted correctly. In the Solidity Compiler tab, ensure the compiler version is 0.8.22 or later." + } + ], + "supplementary_context": { + "description": "Load when the user asks about connecting Remix to Polkadot Hub or wants CLI-based NFT deployment.", + "pages": [ + { + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", + "relevance": "How to connect Remix IDE to Polkadot Hub and configure MetaMask for the TestNet." + }, + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-nft-nft-hardhat", + "title": "Deploy an ERC-721 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", + "relevance": "Alternative CLI-based NFT deployment using Hardhat — better for production or CI/CD workflows." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy ERC-721 NFT via Remix IDE", + "user_says": "Deploy an ERC-721 NFT using Remix", + "actions": [ + "Open https://remix.ethereum.org and create contracts/MyNFT.sol with the ERC-721 code", + "Compile via the Solidity Compiler plugin", + "Connect MetaMask with Injected Provider to Polkadot Hub TestNet", + "Enter owner address as constructor argument and click Deploy", + "Confirm transaction in MetaMask" + ], + "result": "ERC-721 NFT contract deployed; contract address shown in Remix Deployed Contracts panel" + }, + { + "scenario": "Edge case: user wants a custom NFT with additional features", + "user_says": "I want to customize my NFT with a supply cap or royalties", + "actions": [ + "Direct user to https://wizard.openzeppelin.com/polkadot to generate a custom ERC-721 contract", + "Paste the generated code into Remix as contracts/MyNFT.sol", + "Proceed with the standard compile and deploy steps" + ], + "result": "Custom ERC-721 contract with user-specified extensions compiled and deployed via Remix" + } + ] + }, + { + "id": "set-up-foundry-polkadot-hub", + "title": "Use Foundry with Polkadot Hub", + "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/dev-environments/foundry.md" + ], + "primary_page": "smart-contracts/dev-environments/foundry.md", + "prerequisites": { + "runtime": [ + "Unix-based OS (Linux or macOS) or Windows WSL", + "Git installed", + "curl installed" + ], + "network": [ + "Polkadot Hub TestNet (chain ID 420420417) — supported natively by Foundry nightly as --chain polkadot-testnet" + ], + "tokens": [ + "Testnet PAS tokens for deployment — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account. Loaded via 'source .env' into the shell session. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Install Foundry nightly build", + "working_directory": ".", + "commands": [ + "curl -L https://foundry.paradigm.xyz | bash", + "foundryup --version nightly" + ], + "expected_output": "forge Version: (nightly build version string)", + "description": "Install foundryup then the Foundry nightly build. Nightly is required — stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." + }, + { + "order": 2, + "action": "Initialize a new Foundry project", + "working_directory": ".", + "commands": [ + "forge init my-foundry-project", + "cd my-foundry-project" + ], + "description": "Create a new Foundry project with src/ (contracts), script/ (deployment scripts), test/ (Solidity tests), lib/ (dependencies), and foundry.toml. A sample Counter.sol is placed in src/." + }, + { + "order": 3, + "action": "Configure foundry.toml for Polkadot Hub TestNet", + "working_directory": "my-foundry-project", + "description": "Replace the contents of 'foundry.toml' with the following. Choose Blockscout (no API key) or Routescan:\n\nBlockscout:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"\", url = \"https://blockscout-testnet.polkadot.io/api?\" }\n```\n\nRoutescan:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"verifyContract\", url = \"https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan\" }\n```\n\nWith this config, use --chain polkadot-testnet in commands without specifying the RPC URL explicitly." + }, + { + "order": 4, + "action": "Create .env file and load private key", + "working_directory": "my-foundry-project", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env with their 0x-prefixed private key for a testnet-funded account. Do NOT ask for the private key in chat. After confirmation, instruct the user to run 'source .env' in their terminal to load the variable into their shell session." + }, + { + "order": 5, + "action": "Compile contracts", + "working_directory": "my-foundry-project", + "commands": [ + "forge build" + ], + "expected_output": "Compiler run successful!", + "description": "Compile all Solidity contracts in src/. Artifacts output to out/. The sample Counter.sol should compile without errors." + }, + { + "order": 6, + "action": "Deploy a contract to Polkadot Hub TestNet", + "working_directory": "my-foundry-project", + "commands": [ + "forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url https://services.polkadothub-rpc.com/testnet --private-key $PRIVATE_KEY --broadcast" + ], + "expected_output": "Deployed to: 0x...", + "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address — needed for verification and interaction." + }, + { + "order": 7, + "action": "Verify the contract on the block explorer", + "working_directory": "my-foundry-project", + "description": "Replace INSERT_CONTRACT_ADDRESS with the address from step 6.\n\nBlockscout:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet\n```\n\nRoutescan:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --verifier-url 'https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan' --etherscan-api-key 'verifyContract' --chain polkadot-testnet\n```\n\nExpected output: 'Response: OK' and a URL to the verified contract." + }, + { + "order": 8, + "action": "Interact with the contract using Cast", + "working_directory": "my-foundry-project", + "description": "Replace INSERT_CONTRACT_ADDRESS and INSERT_ACCOUNT_ADDRESS with actual values.\n\nRead from contract:\n```bash\ncast call INSERT_CONTRACT_ADDRESS 'number()(uint256)' --chain polkadot-testnet\n```\n\nWrite to contract:\n```bash\ncast send INSERT_CONTRACT_ADDRESS 'setNumber(uint256)' 42 --chain polkadot-testnet --private-key $PRIVATE_KEY\n```\n\nCheck balance:\n```bash\ncast balance INSERT_ACCOUNT_ADDRESS --chain polkadot-testnet\n```" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "foundryup: command not found / forge: command not found", + "cause": "Foundry was installed but PATH was not updated for the current shell session.", + "resolution": "Run 'source ~/.bashrc' (or '~/.zshrc' on macOS) or start a new terminal." + }, + { + "pattern": "Error: Chain 'polkadot-testnet' not found / unknown chain", + "cause": "Using the Foundry stable release instead of nightly. Stable does not include Polkadot chain definitions.", + "resolution": "Install the nightly build: run 'foundryup --version nightly'." + }, + { + "pattern": "Error: Priority is too low / insufficient funds", + "cause": "Account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet PAS from https://faucet.polkadot.io/. The Foundry nightly build handles gas pricing automatically for Polkadot chains." + }, + { + "pattern": "forge test: tests pass locally but fail on TestNet", + "cause": "forge test runs against Anvil (Ethereum semantics), not Polkadot Hub. Existential deposit and gas model differ.", + "resolution": "Use forge test only for unit testing contract logic. For Polkadot-specific behavior, test against a local dev node or TestNet." + } + ], + "supplementary_context": { + "description": "Load when the user wants to understand Polkadot Hub EVM differences or compare Foundry with Hardhat.", + "pages": [ + { + "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", + "title": "EVM vs PVM", + "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md", + "relevance": "Explains differences between Polkadot Hub EVM and standard Ethereum including gas model and existential deposit." + }, + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Network details for Polkadot Hub TestNet and MainNet RPC endpoints and chain IDs." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: install Foundry and deploy a contract", + "user_says": "Set up Foundry and deploy a contract to Polkadot Hub TestNet", + "actions": [ + "Install Foundry nightly: curl -L https://foundry.paradigm.xyz | bash && foundryup --version nightly", + "Run forge init my-foundry-project", + "Configure foundry.toml with Polkadot TestNet verifier settings", + "Create .env with PRIVATE_KEY; ask user to fill in and run source .env", + "Run forge build to compile", + "Run forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url ... --private-key $PRIVATE_KEY --broadcast", + "Verify: forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet" + ], + "result": "Contract compiled, deployed, and verified on Polkadot Hub TestNet" + }, + { + "scenario": "Edge case: stable Foundry installed instead of nightly", + "user_says": "I get 'unknown chain polkadot-testnet' when running forge create", + "actions": [ + "Run 'forge --version' to check build type", + "If not nightly, run 'foundryup --version nightly' to upgrade", + "Retry the forge create command" + ], + "result": "Foundry upgraded to nightly; polkadot-testnet chain recognized and deployment succeeds" + } + ] + }, + { + "id": "spawn-test-network-zombienet", + "title": "Spawn and Test a Parachain Network with Zombienet", + "description": "Installs Zombienet (executable, Nix, or Docker), downloads polkadot and polkadot-parachain binaries, writes a TOML network config defining relay chain validators and parachain collators, spawns the network via the native provider, writes .zndsl test files using Zombienet's DSL, and runs the test suite. Use when spawning and testing ephemeral Polkadot SDK networks locally. Trigger phrases: 'Zombienet', 'spawn local parachain', 'test parachain network', 'local relay chain Zombienet', 'zndsl test'. Requires polkadot and polkadot-parachain binaries.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/zombienet.md" + ], + "primary_page": "reference/tools/zombienet.md", + "prerequisites": { + "runtime": [ + "Unix-based OS (Linux or macOS) or Windows WSL", + "polkadot and polkadot-parachain binaries — downloaded via: zombienet setup polkadot polkadot-parachain" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Install Zombienet", + "working_directory": ".", + "commands": [ + "curl -L https://github.com/paritytech/zombienet/releases/latest/download/zombienet-linux-x64 -o zombienet", + "chmod +x zombienet", + "mv zombienet /usr/local/bin/zombienet", + "zombienet version" + ], + "description": "Download the Zombienet executable for Linux x64. For macOS replace 'zombienet-linux-x64' with 'zombienet-macos'. For macOS Gatekeeper issues run: xattr -d com.apple.quarantine zombienet. The 'zombienet version' command confirms successful installation. Docker alternative: docker run -it --rm -v $(pwd):/home/nonroot/zombie-net/host-current-files paritytech/zombienet" + }, + { + "order": 2, + "action": "Download required node binaries", + "working_directory": ".", + "commands": [ + "zombienet setup polkadot polkadot-parachain" + ], + "description": "Download the polkadot and polkadot-parachain binaries for the native provider. Zombienet downloads these to a local path and adds them to PATH for the session. Verify availability: 'polkadot --version' and 'polkadot-parachain --version'." + }, + { + "order": 3, + "action": "Create a network configuration file", + "working_directory": ".", + "description": "Create 'network.toml' with a basic network configuration. This defines a relay chain with two validators and a parachain with one collator. If using the Polkadot Hub TestNet chain spec first download it:\n```bash\nwget https://paseo-r2.zondax.ch/chain-specs/paseo-asset-hub.json\n```\n\nThen create network.toml:\n```toml\n[settings]\ntimeout = 1000\n\n[relaychain]\nchain = \"paseo\"\ndefault_command = \"polkadot\"\n\n [[relaychain.nodes]]\n name = \"alice\"\n validator = true\n\n [[relaychain.nodes]]\n name = \"bob\"\n validator = true\n\n[[parachains]]\nid = 1000\nchain_spec_path = \"./paseo-asset-hub.json\"\n\n [parachains.collator]\n name = \"collator-01\"\n command = \"polkadot-parachain\"\n```\n\nAdjust id, chain_spec_path, and command fields to match your target network. For relay-only tests, omit the [[parachains]] section." + }, + { + "order": 4, + "action": "Spawn the network", + "working_directory": ".", + "commands": [ + "zombienet spawn network.toml --provider native" + ], + "expected_output": "Network launched\nRPC endpoints:", + "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal — the network persists until you stop it with Ctrl+C." + }, + { + "order": 5, + "action": "Write a test file", + "working_directory": ".", + "description": "Create 'network-test.zndsl' to test the spawned network:\n\n```\nDescription: Basic network health test\nNetwork: ./network.toml\nCreds: config\n\nalice: is up\nbob: is up\nalice: parachain 1000 is registered within 200 seconds\nalice: parachain 1000 block height is at least 10 within 300 seconds\n```\n\nCommon assertion types: node health ('alice: is up'), parachain registration, block height, metrics ('alice: reports node_roles is 4'), and log patterns ('alice: log line matches glob \"Imported #1\" within 10 seconds'). Remove parachain assertions if your network has no parachain." + }, + { + "order": 6, + "action": "Run the test suite", + "working_directory": ".", + "commands": [ + "zombienet test network-test.zndsl --provider native" + ], + "expected_output": "PASS", + "description": "Execute the test suite against the running network. Each assertion is evaluated in order; Zombienet reports PASS or FAIL. Exit code 0 = all pass, 1 = any failure. Ensure the network from step 4 is still running before executing tests." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "polkadot: binary not found / command not found", + "cause": "polkadot or polkadot-parachain binaries are not in PATH.", + "resolution": "Run 'zombienet setup polkadot polkadot-parachain' to download the binaries, then add them to PATH: export PATH=$PATH:." + }, + { + "pattern": "macOS: cannot be opened because it is from an unidentified developer", + "cause": "macOS Gatekeeper is blocking the Zombienet executable.", + "resolution": "Run: xattr -d com.apple.quarantine zombienet" + }, + { + "pattern": "Timeout: network did not start within timeout seconds", + "cause": "Binary download was slow or the chain spec file is missing or invalid.", + "resolution": "Increase the timeout value in the [settings] section of network.toml. Verify chain_spec_path exists and is valid." + }, + { + "pattern": "Test FAIL: parachain X is not registered within N seconds", + "cause": "Parachain onboarding takes time or the collator failed to start.", + "resolution": "Increase the 'within N seconds' timeout in the .zndsl assertion. Review Zombienet logs for collator startup errors." + } + ], + "supplementary_context": { + "description": "Load when the user wants more Zombienet configuration options or wants to build and deploy the parachain template first.", + "pages": [ + { + "slug": "parachains-testing-run-a-parachain-network", + "title": "Run a Parachain Network", + "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md", + "relevance": "Full tutorial for spawning a local parachain test network including building the parachain template binary." + }, + { + "slug": "parachains-testing-fork-a-parachain", + "title": "Fork a Parachain Using Chopsticks", + "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", + "relevance": "Alternative testing approach using Chopsticks to fork a live parachain for isolated state testing." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: spawn and test a relay+parachain network", + "user_says": "Spawn a local Zombienet network and run tests", + "actions": [ + "Install Zombienet executable and run zombienet setup polkadot polkadot-parachain", + "Download paseo-asset-hub.json chain spec if using Polkadot Hub parachain", + "Create network.toml with relay chain (alice, bob) and parachain (id=1000)", + "Run zombienet spawn network.toml --provider native in a separate terminal", + "Create network-test.zndsl with health and parachain assertions", + "Run zombienet test network-test.zndsl --provider native" + ], + "result": "Local network spawns successfully; all test assertions PASS" + }, + { + "scenario": "Edge case: test relay chain only without a parachain", + "user_says": "Spawn just a relay chain network for testing consensus behavior", + "actions": [ + "Remove the [[parachains]] section from network.toml", + "Remove parachain assertions from network-test.zndsl", + "Run zombienet spawn network.toml --provider native", + "Run zombienet test network-test.zndsl --provider native" + ], + "result": "Relay chain with alice and bob validators spawns; health assertions pass" + } + ] + }, + { + "id": "deploy-interact-contracts-web3js", + "title": "Deploy and Interact with Smart Contracts Using Web3.js", + "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", + "version": "1.0.1", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/libraries/web3-js.md" + ], + "primary_page": "smart-contracts/libraries/web3-js.md", + "prerequisites": { + "runtime": [ + "Node.js v22.13.1 or later", + "npm v6.13.4 or later" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for deployment gas — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer account. Used in deploy.js and updateStorage.js. Must be funded with testnet PAS. Never commit to version control.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create project directory and initialize npm", + "working_directory": ".", + "commands": [ + "mkdir web3js-project", + "cd web3js-project", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project." + }, + { + "order": 2, + "action": "Install Web3.js and solc", + "working_directory": "web3js-project", + "commands": [ + "npm install web3", + "npm install --save-dev solc" + ], + "description": "Install the Web3.js library for blockchain interaction and solc for compiling Solidity contracts to EVM bytecode." + }, + { + "order": 3, + "action": "Create project directories and the Storage contract", + "working_directory": "web3js-project", + "commands": [ + "mkdir -p contracts scripts abis artifacts" + ], + "description": "Create the expected directory structure (contracts/, scripts/, abis/, artifacts/), then fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter:\n\n```solidity\n// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.9;\n\ncontract Storage {\n uint256 public storedNumber;\n function setNumber(uint256 _newNumber) public { storedNumber = _newNumber; }\n}\n```", + "reference_file": "Storage.sol" + }, + { + "order": 4, + "action": "Create the provider connection script and verify connectivity", + "working_directory": "web3js-project", + "description": "Fetch the reference file `connectToProvider.js` and save it as `scripts/connectToProvider.js`. Then make these substitutions:\n(1) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`\n(2) Replace `INSERT_CHAIN_ID` with `420420417`\n(3) Replace `INSERT_CHAIN_NAME` with `polkadot-hub-testnet`\n\nThen verify: `node scripts/connectToProvider.js`\nExpected output: `Connected to chain ID: 420420417`", + "reference_file": "connectToProvider.js" + }, + { + "order": 5, + "action": "Create the compile script and compile the contract", + "working_directory": "web3js-project", + "commands": [ + "node scripts/compile.js" + ], + "expected_output": "ABI and bytecode saved", + "description": "Fetch the reference file `compile.js` and save it as `scripts/compile.js`. The script reads `contracts/Storage.sol`, compiles it with solc, saves ABI to `abis/Storage.json` and bytecode to `artifacts/Storage.bin`. Then run it to compile.", + "reference_file": "compile.js" + }, + { + "order": 6, + "action": "Create the deployment script and deploy the contract", + "working_directory": "web3js-project", + "description": "Fetch the reference file `deploy.js` and save it as `scripts/deploy.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY` (do NOT hardcode the key)\n(2) Ensure the RPC URL points to `https://services.polkadothub-rpc.com/testnet`\n\nSet `PRIVATE_KEY` in your environment: `export PRIVATE_KEY=0x...` (never commit this). Then deploy:\n\n```bash\nnode scripts/deploy.js\n```\n\nExpected: contract address saved in `contract-address.json`. Record this address.", + "reference_file": "deploy.js" + }, + { + "order": 7, + "action": "Create the interaction script and interact with the contract", + "working_directory": "web3js-project", + "description": "Fetch the reference file `updateStorage.js` and save it as `scripts/updateStorage.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`\n(2) Replace `INSERT_CONTRACT_ADDRESS` with the address from step 6, or ensure `contract-address.json` is read automatically\n(3) Verify the ABI references `abis/Storage.json`\n\nThen run: `node scripts/updateStorage.js`\nExpected: current stored value displayed, then updated to 1.", + "reference_file": "updateStorage.js" + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "base_path": ".snippets/code/smart-contracts/libraries/web3-js", + "files": [ + { + "path": "Storage.sol", + "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." + }, + { + "path": "connectToProvider.js", + "description": "Web3.js provider connection to Polkadot Hub TestNet" + }, + { + "path": "compile.js", + "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts" + }, + { + "path": "deploy.js", + "description": "Deploys the compiled Storage contract via Web3.js Contract.deploy().send()" + }, + { + "path": "updateStorage.js", + "description": "Calls `setNumber(value)` on the deployed Storage contract via Web3.js, then reads `storedNumber` to confirm." + } + ], + "branch": "master" + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Deployer account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", + "resolution": "Get testnet PAS from https://faucet.polkadot.io/ and verify the funded account is used in deploy.js." + }, + { + "pattern": "Error: Cannot find module 'web3'", + "cause": "Web3.js dependency was not installed.", + "resolution": "Run 'npm install web3' in the web3js-project directory." + }, + { + "pattern": "Error reading ABI or bytecode file / file not found", + "cause": "Compile step was skipped or output files are not in expected locations.", + "resolution": "Run 'node scripts/compile.js' first to generate abis/Storage.json and artifacts/Storage.bin." + }, + { + "pattern": "Error: cannot connect to provider / network timeout", + "cause": "Wrong RPC URL or TestNet is temporarily unavailable.", + "resolution": "Verify the RPC URL is 'https://services.polkadothub-rpc.com/testnet'. Check Polkadot status pages for TestNet outages." + } + ], + "supplementary_context": { + "description": "Load when the user wants a modern alternative to Web3.js or asks about contract interaction options.", + "pages": [ + { + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", + "relevance": "Recommended modern alternative to Web3.js for deploying and interacting with contracts on Polkadot Hub." + }, + { + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", + "relevance": "TypeScript-native alternative library for Polkadot Hub contract interactions." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy a Storage contract and interact with it", + "user_says": "Deploy a smart contract to Polkadot Hub using Web3.js", + "actions": [ + "Create web3js-project, npm init -y, install web3 and solc", + "Create contracts/Storage.sol with the storage contract", + "Create and run scripts/connectToProvider.js (replace INSERT_RPC_URL, INSERT_CHAIN_ID, INSERT_CHAIN_NAME)", + "Create and run scripts/compile.js to generate ABI and bytecode", + "Create scripts/deploy.js, replace INSERT_PRIVATE_KEY with process.env.PRIVATE_KEY, run it", + "Create scripts/updateStorage.js, replace INSERT_PRIVATE_KEY and INSERT_CONTRACT_ADDRESS, run it" + ], + "result": "Storage contract deployed; read and write interactions confirmed" + }, + { + "scenario": "Edge case: user wants a modern library instead of Web3.js", + "user_says": "Web3.js is sunset, what should I use instead?", + "actions": [ + "Note that Web3.js has been sunset", + "Recommend Ethers.js (deploy-contracts-ethers-js) or viem (deploy-contracts-viem) as actively maintained alternatives" + ], + "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction" + } + ], + "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json" + }, + { + "id": "add-existing-pallet-to-runtime", + "title": "Add an Existing Pallet to a Parachain Runtime", + "description": "Integrates an existing Polkadot SDK pallet into a local parachain runtime. Covers adding the pallet crate to Cargo.toml, implementing the pallet's Config trait in runtime/src/lib.rs, registering the pallet in the construct_runtime! macro, compiling the runtime, generating a chain spec, and running the parachain locally to verify the pallet is active. Use when extending a Polkadot SDK parachain template with standard SDK pallets (pallet-utility, pallet-multisig, pallet-proxy, etc.). Trigger phrases: 'add pallet to runtime', 'integrate pallet parachain', 'pallet-utility runtime', 'extend parachain runtime', 'construct_runtime pallet'. Prerequisite: working parachain template dev environment (see set-up-the-parachain-template). Final GUI verification uses Polkadot.js Apps.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/customize-runtime/add-existing-pallets.md" + ], + "primary_page": "parachains/customize-runtime/add-existing-pallets.md", + "prerequisites": { + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with the wasm32-unknown-unknown target (`rustup target add wasm32-unknown-unknown`)", + "Polkadot SDK system dependencies installed (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" + ], + "network": [ + "No external network required — all compilation and testing is local" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK parachain template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." + }, + { + "order": 2, + "action": "Build the parachain template node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." + }, + { + "order": 3, + "action": "Identify the pallet version to use", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "grep 'polkadot-sdk' runtime/Cargo.toml | head -5" + ], + "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version — you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." + }, + { + "order": 4, + "action": "Add the pallet dependency to runtime/Cargo.toml", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 — the output should not show dependency resolution errors." + }, + { + "order": 5, + "action": "Implement the pallet's Config trait in runtime/src/lib.rs", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified — the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." + }, + { + "order": 6, + "action": "Register the pallet in construct_runtime!", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes — choose carefully as it cannot be changed without a migration. Save the file." + }, + { + "order": 7, + "action": "Compile the runtime", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release 2>&1 | tail -20" + ], + "description": "Build the full parachain node (includes the runtime WASM blob). This typically takes 5-15 minutes on first build. A successful build ends with: Compiling node-template vX.X.X ... Finished release [optimized] target(s). If compilation fails with 'the trait `Config` for `pallet_utility` is not satisfied', check that all required associated types are implemented in step 3." + }, + { + "order": 8, + "action": "Generate a fresh chain spec and start the node locally", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/local-spec.json", + "./target/release/parachain-template-node --dev --tmp --chain /tmp/local-spec.json" + ], + "description": "Generate a development chain spec and start the node in --dev mode. The --tmp flag stores chain data in a temporary directory. Once the node is running and producing blocks (look for: Imported #1 in logs), open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics. Verify that 'utility' appears in the pallet dropdown as a callable extrinsic module." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-cookbook", + "branch": "master", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error[E0277]: the trait `pallet_utility::Config` is not implemented for `Runtime`", + "cause": "The impl pallet_utility::Config for Runtime block is missing or has a typo in runtime/src/lib.rs.", + "resolution": "Ensure the impl block is present and all required associated types are specified. Run cargo check -p runtime to see the full list of missing types. Each missing type will be listed as 'required by this bound in pallet_utility::Config'." + }, + { + "pattern": "error[E0432]: unresolved import `pallet_utility`", + "cause": "pallet-utility was not added to runtime/Cargo.toml, or it was added but Cargo has not re-resolved dependencies.", + "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again — Cargo will fetch and compile the new dependency." + }, + { + "pattern": "Utility pallet does not appear in Polkadot.js Apps extrinsics", + "cause": "The pallet was registered in construct_runtime! but with the wrong name, or the node is running an old binary without the updated runtime.", + "resolution": "Stop the node, rebuild with cargo build --release, regenerate the chain spec, and restart. Verify the construct_runtime! entry name exactly matches what you expect to see in the UI." + } + ], + "supplementary_context": { + "description": "Load these pages for parachain template setup or for adding multiple pallet instances.", + "pages": [ + { + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", + "relevance": "Rust toolchain and system dependencies required before building any parachain runtime." + }, + { + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", + "relevance": "Prerequisite: setting up the parachain template development environment." + }, + { + "slug": "parachains-customize-runtime-add-pallet-instances", + "title": "Add Multiple Pallet Instances", + "url": "https://docs.polkadot.com/parachains/customize-runtime/add-pallet-instances.md", + "relevance": "Next step: adding multiple instances of the same pallet (e.g., two pallet-collective instances)." + }, + { + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", + "relevance": "Building a custom FRAME pallet from scratch and integrating it into the runtime." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: add pallet-utility to a parachain runtime", + "user_says": "Add pallet-utility to my parachain runtime", + "actions": [ + "Check the existing Polkadot SDK version in runtime/Cargo.toml", + "Add pallet-utility dependency with matching version and default-features = false", + "Add pallet-utility/std to the std feature list", + "Implement pallet_utility::Config for Runtime in runtime/src/lib.rs with all required associated types", + "Add Utility: pallet_utility entry to construct_runtime!", + "Run cargo build --release", + "Generate chain spec and start node with --dev --tmp", + "Verify 'utility' appears in Polkadot.js Apps extrinsics" + ], + "result": "pallet-utility integrated into the runtime; utility.batch, utility.batchAll, and utility.dispatchAs are callable on-chain" + }, + { + "scenario": "Edge case: compilation fails with missing associated type in Config", + "user_says": "I get a compiler error about missing associated types in my Config impl", + "actions": [ + "Read the full compiler error — each missing type is listed with 'required by this bound'", + "Add each missing associated type to the impl block", + "Refer to the pallet's source (src/lib.rs) or docs for the expected type definitions", + "Re-run cargo build --release" + ], + "result": "All required Config associated types satisfied; compilation succeeds" + } + ] + }, + { + "id": "configure-multiple-pallet-instances", + "title": "Configure Multiple Instances of a Pallet in a Runtime", + "description": "Configures two independent instances of an instantiable Polkadot SDK pallet within a single parachain runtime. Uses pallet-collective as the example to demonstrate adding TechnicalCommittee (Instance1) and Council (Instance2) with separate membership and voting parameters. Covers identifying instantiable pallets, adding per-instance Cargo dependencies, implementing Config(Instance1) and Config(Instance2) traits, registering both instances in construct_runtime!, compiling, and verifying instance independence. Use when a runtime needs two separate governance bodies, two token pools, or any other scenario requiring two isolated instances of the same pallet logic. Trigger phrases: 'multiple pallet instances', 'two collective instances', 'pallet-collective council technical committee', 'instantiable pallet runtime'. Prerequisite: working parachain template (see add-existing-pallet-to-runtime).", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/customize-runtime/add-pallet-instances.md" + ], + "primary_page": "parachains/customize-runtime/add-pallet-instances.md", + "prerequisites": { + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with wasm32-unknown-unknown target", + "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)", + "Familiarity with adding a single pallet to a runtime (see add-existing-pallet-to-runtime)" + ], + "network": [ + "No external network required — local development only" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK parachain template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." + }, + { + "order": 2, + "action": "Build the parachain template node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." + }, + { + "order": 3, + "action": "Verify pallet supports instantiation", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Not all pallets support multiple instances. An instantiable pallet uses a second generic type parameter (the Instance) in its Config trait definition, e.g.: `pub trait Config: frame_system::Config`. Check the pallet's source or documentation for this pattern. pallet-collective and pallet-membership are standard examples. If the pallet does not have the Instance generic, it cannot be instantiated multiple times." + }, + { + "order": 4, + "action": "Add pallet dependencies to runtime/Cargo.toml", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances — add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" + }, + { + "order": 5, + "action": "Implement Config for TechnicalCommittee", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/src/lib.rs. Add the first instance's Config implementation. For pallet-collective as TechnicalCommittee:\n\n```rust\ntype TechnicalCommitteeInstance = pallet_collective::Instance1;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<5>;\n type MaxProposals = ConstU32<100>;\n type MaxMembers = ConstU32<100>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nSubstitute `ConstU32` values with the desired governance parameters." + }, + { + "order": 6, + "action": "Implement Config for Council", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage — `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." + }, + { + "order": 7, + "action": "Register both instances in construct_runtime!", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics — choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." + }, + { + "order": 8, + "action": "Compile the runtime", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release 2>&1 | tail -20" + ], + "description": "Build the parachain node. Compilation succeeds when both instance Config impls are complete and construct_runtime! entries are correct. Expected output: Finished release [optimized] target(s). If you see 'conflicting implementations of trait Config', check that each instance type alias (TechnicalCommitteeInstance, CouncilInstance) is distinct." + }, + { + "order": 9, + "action": "Verify instance independence", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/instances-spec.json", + "./target/release/parachain-template-node --dev --tmp --chain /tmp/instances-spec.json" + ], + "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics — verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-cookbook", + "branch": "master", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error[E0119]: conflicting implementations of trait `pallet_collective::Config`", + "cause": "Two impl blocks are targeting the same instance type — usually because both use the default instance () instead of Instance1/Instance2.", + "resolution": "Ensure each impl block explicitly names a different instance: impl pallet_collective::Config and impl pallet_collective::Config. Type aliases (type TechnicalCommitteeInstance = pallet_collective::Instance1;) help clarify intent." + }, + { + "pattern": "error: the pallet does not implement the Instance trait", + "cause": "The pallet does not support instantiation — it lacks the I: 'static = () generic in its Config trait.", + "resolution": "Only instantiable pallets (those with a second generic parameter) can be used this way. Check the pallet source or use a different pallet that supports multiple instances." + }, + { + "pattern": "Only one instance visible in Polkadot.js Apps", + "cause": "One construct_runtime! entry is missing or has a typo.", + "resolution": "Verify both entries are present in construct_runtime! with distinct names and correct instance specifiers: TechnicalCommittee: pallet_collective:: and Council: pallet_collective::. Rebuild and restart the node." + } + ], + "supplementary_context": { + "description": "Load these pages for single pallet integration or custom pallet development.", + "pages": [ + { + "slug": "parachains-customize-runtime-add-existing-pallets", + "title": "Add an Existing Pallet to the Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", + "relevance": "Prerequisite: adding a single pallet to a runtime — covers the Cargo, Config, and construct_runtime! basics." + }, + { + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", + "relevance": "Building a custom pallet from scratch — next step after mastering pallet integration." + }, + { + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", + "relevance": "Rust toolchain and build dependency installation." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: add TechnicalCommittee and Council as two pallet-collective instances", + "user_says": "Add a TechnicalCommittee and a Council to my parachain runtime using pallet-collective", + "actions": [ + "Verify pallet-collective has the I: 'static = () generic — confirm it is instantiable", + "Add pallet-collective to runtime/Cargo.toml with default-features = false and add to std features", + "Implement pallet_collective::Config as TechnicalCommitteeInstance with committee-appropriate parameters", + "Implement pallet_collective::Config as CouncilInstance with council-appropriate parameters", + "Add TechnicalCommittee: pallet_collective:: and Council: pallet_collective:: to construct_runtime!", + "Run cargo build --release", + "Start node in --dev mode and verify both 'technicalCommittee' and 'council' appear in Polkadot.js Apps" + ], + "result": "Two independent pallet-collective instances registered — TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" + }, + { + "scenario": "Edge case: runtime compiles but only one instance shows in the UI", + "user_says": "My runtime compiled but I only see one collective in Polkadot.js Apps", + "actions": [ + "Check construct_runtime! for both TechnicalCommittee and Council entries", + "Verify each entry uses the correct instance specifier: :: and ::", + "Rebuild with cargo build --release", + "Regenerate chain spec and restart node" + ], + "result": "Both collective instances visible and callable in Polkadot.js Apps" + } + ] + }, + { + "id": "create-frame-pallet", + "title": "Create a Custom FRAME Pallet", + "description": "Guides through building a custom FRAME pallet from scratch using the Polkadot SDK. Uses a counter pallet (increment and decrement extrinsics with bounded storage) as the example to demonstrate all core FRAME patterns: pallet directory layout, Cargo.toml configuration, #[pallet::config], #[pallet::storage], #[pallet::event], #[pallet::error], #[pallet::call], and genesis config. After implementing the pallet, integrates it into the parachain runtime (Cargo dependency, Config impl, construct_runtime!), compiles, and runs locally. Use when building any new runtime module with on-chain state and extrinsics. Trigger phrases: 'create FRAME pallet', 'custom pallet from scratch', 'write a pallet', 'FRAME storage events extrinsics', 'build counter pallet'. Prerequisites: Polkadot SDK installed, parachain template set up. Verification step uses Polkadot.js Apps GUI.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/customize-runtime/pallet-development/create-a-pallet.md" + ], + "primary_page": "parachains/customize-runtime/pallet-development/create-a-pallet.md", + "prerequisites": { + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown)", + "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" + ], + "network": [ + "No external network required — all work is local" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Clone the Polkadot SDK parachain template", + "working_directory": ".", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." + }, + { + "order": 2, + "action": "Build the parachain template node in release mode", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." + }, + { + "order": 3, + "action": "Create the pallet directory and Cargo.toml", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "mkdir -p pallets/counter/src", + "touch pallets/counter/src/lib.rs" + ], + "description": "Create the pallet directory structure inside the parachain template workspace. The pallet name (counter) becomes the crate name. Create pallets/counter/Cargo.toml with the following content, replacing with the version matching your other workspace pallets:\n\n```toml\n[package]\nname = \"pallet-counter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ncodec = { package = \"parity-scale-codec\", version = \"3\", default-features = false, features = [\"derive\"] }\nscale-info = { version = \"2\", default-features = false, features = [\"derive\"] }\nframe-support = { version = \"\", default-features = false }\nframe-system = { version = \"\", default-features = false }\nframe-benchmarking = { version = \"\", default-features = false, optional = true }\n\n[features]\ndefault = [\"std\"]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame-support/std\",\n \"frame-system/std\",\n]\nruntime-benchmarks = [\"frame-benchmarking/runtime-benchmarks\"]\n```\n\nAlso add pallet-counter to the workspace's root Cargo.toml members list:\nmembers = [\n ...\n \"pallets/counter\",\n]" + }, + { + "order": 4, + "action": "Implement the pallet in pallets/counter/src/lib.rs", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Write the pallet implementation in pallets/counter/src/lib.rs. A minimal counter pallet contains:\n\n#![cfg_attr(not(feature = \"std\"), no_std)]\n\npub use pallet::*;\n\n```rust\n#[frame_support::pallet]\npub mod pallet {\n use frame_support::pallet_prelude::*;\n use frame_system::pallet_prelude::*;\n\n #[pallet::pallet]\n pub struct Pallet(_);\n\n #[pallet::config]\n pub trait Config: frame_system::Config {\n type RuntimeEvent: From> + IsType<::RuntimeEvent>;\n #[pallet::constant]\n type MaxValue: Get;\n }\n```\n\n #[pallet::storage]\n pub type CounterValue = StorageValue<_, u32, ValueQuery>;\n\n```rust\n #[pallet::event]\n #[pallet::generate_deposit(pub(super) fn deposit_event)]\n pub enum Event {\n CounterIncremented { new_value: u32 },\n CounterDecremented { new_value: u32 },\n }\n\n #[pallet::error]\n pub enum Error {\n CounterOverflow,\n CounterUnderflow,\n }\n\n #[pallet::call]\n impl Pallet {\n #[pallet::call_index(0)]\n #[pallet::weight(10_000)]\n pub fn increment(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_add(1).ok_or(Error::::CounterOverflow)?;\n ensure!(new_val <= T::MaxValue::get(), Error::::CounterOverflow);\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterIncremented { new_value: new_val });\n Ok(())\n }\n\n #[pallet::call_index(1)]\n #[pallet::weight(10_000)]\n pub fn decrement(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_sub(1).ok_or(Error::::CounterUnderflow)?;\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterDecremented { new_value: new_val });\n Ok(())\n }\n }\n}\n```\n\nThe MaxValue constant bounds the counter to prevent overflow. checked_add/checked_sub replace panicking arithmetic." + }, + { + "order": 5, + "action": "Add pallet-counter to runtime/Cargo.toml", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/Cargo.toml and add pallet-counter as a workspace dependency:\n\n```toml\n[dependencies]\n...\npallet-counter = { path = \"../pallets/counter\", default-features = false }\n\nAdd to the std feature list:\n[features]\nstd = [\n ...\n \"pallet-counter/std\",\n]\n```\n\nNote: path = \"../pallets/counter\" is a relative path from the runtime/ directory to the pallet directory." + }, + { + "order": 6, + "action": "Implement pallet_counter::Config in runtime/src/lib.rs", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 — adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." + }, + { + "order": 7, + "action": "Register the pallet in construct_runtime!", + "working_directory": "polkadot-sdk-parachain-template", + "description": "Find construct_runtime! in runtime/src/lib.rs and add the counter pallet:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n Counter: pallet_counter,\n }\n);\n```\n\nThe name Counter is the on-chain pallet identifier. Save the file." + }, + { + "order": 8, + "action": "Compile and run locally", + "working_directory": "polkadot-sdk-parachain-template", + "commands": [ + "cargo build --release 2>&1 | tail -20", + "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/counter-spec.json", + "./target/release/parachain-template-node --dev --tmp --chain /tmp/counter-spec.json" + ], + "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics — 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-cookbook", + "branch": "master", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "error[E0412]: cannot find type `ConstU32` in this scope", + "cause": "ConstU32 is not imported in runtime/src/lib.rs.", + "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it — search for existing ConstU32 usages to confirm." + }, + { + "pattern": "error[E0277]: the trait `pallet_counter::Config` is not implemented", + "cause": "The impl pallet_counter::Config for Runtime block is missing or has a missing associated type.", + "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type — add them one by one." + }, + { + "pattern": "error: failed to select a version for `pallet-counter` / could not find Cargo.toml", + "cause": "The path in runtime/Cargo.toml does not match the actual pallet directory location.", + "resolution": "Verify the relative path: from runtime/, pallets/counter/ should be at ../pallets/counter. Run ls ../pallets/counter/Cargo.toml from inside the runtime/ directory to confirm the path exists." + }, + { + "pattern": "CounterOverflow error when submitting increment", + "cause": "The counter has reached MaxValue (100 by default).", + "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design — the error confirms the overflow protection is working." + } + ], + "supplementary_context": { + "description": "Load these pages for runtime setup prerequisites or next steps in pallet development.", + "pages": [ + { + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", + "relevance": "Rust and system dependencies required before building any FRAME pallet." + }, + { + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", + "relevance": "Setting up the parachain template workspace where the custom pallet lives." + }, + { + "slug": "parachains-customize-runtime-add-existing-pallets", + "title": "Add an Existing Pallet to the Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", + "relevance": "Adding an existing SDK pallet to a runtime — prerequisite concepts for runtime integration." + }, + { + "slug": "parachains-customize-runtime-pallet-development-mock-runtime", + "title": "Mock Your Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md", + "relevance": "Next step: setting up a mock runtime for unit testing the custom pallet." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: build a counter pallet with increment and decrement extrinsics", + "user_says": "Create a custom FRAME pallet from scratch", + "actions": [ + "Create pallets/counter/src/ directory and lib.rs file", + "Write Cargo.toml with frame-support, frame-system, parity-scale-codec, and scale-info dependencies", + "Add pallet-counter to workspace root Cargo.toml members", + "Implement the pallet in lib.rs: Config trait, StorageValue, Event enum, Error enum, and two call extrinsics (increment/decrement) with overflow/underflow protection", + "Add pallet-counter path dependency to runtime/Cargo.toml", + "Implement pallet_counter::Config for Runtime with MaxValue = ConstU32<100>", + "Add Counter: pallet_counter to construct_runtime!", + "Run cargo build --release", + "Start node in --dev mode; submit increment extrinsic via Polkadot.js Apps; verify CounterValue storage" + ], + "result": "Custom counter pallet deployed locally; increment and decrement extrinsics callable on-chain; CounterValue storage updates correctly with overflow protection" + }, + { + "scenario": "Edge case: adding a genesis config to set an initial counter value", + "user_says": "How do I set the initial counter value to 10 at genesis?", + "actions": [ + "Add a #[pallet::genesis_config] struct and #[pallet::genesis_build] impl to the pallet's lib.rs to accept an initial_value", + "In the runtime's chain_spec.rs (or equivalent), include Counter: CounterConfig { initial_value: 10 } in the genesis state", + "Rebuild and regenerate the chain spec to include the genesis config" + ], + "result": "Node starts with CounterValue pre-set to 10; visible in Developer > Chain State > counter.counterValue immediately after genesis" + } + ] + }, + { + "id": "retrieve-runtime-metadata", + "title": "Retrieve Polkadot Runtime Metadata", + "description": "Provides three methods for fetching Polkadot SDK runtime metadata: curl JSON-RPC (state_getMetadata, returns raw hex bytes), subxt CLI (human-readable JSON, ideal for inspection), and Polkadot.js Apps RPC UI (interactive). Use when building client libraries, inspecting pallet structure, or verifying runtime composition after an upgrade. Trigger phrases: 'get runtime metadata', 'state_getMetadata', 'inspect pallet calls', 'fetch chain metadata', 'subxt metadata', 'what pallets are on chain'. No private key or wallet required. The metadata output describes all pallets, storage items, calls, events, errors, and constants in the current runtime.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/parachains/chain-data.md" + ], + "primary_page": "reference/parachains/chain-data.md", + "prerequisites": { + "runtime": [ + "curl (Method A — available by default on macOS and most Linux distros)", + "subxt CLI (Method B — install with: cargo install subxt-cli)" + ], + "network": [ + "An accessible RPC endpoint for the target Polkadot SDK node (e.g. https://rpc.polkadot.io for mainnet, or http://127.0.0.1:9944 for a local dev node)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "(Method A) Fetch metadata via curl JSON-RPC", + "working_directory": ".", + "commands": [ + "curl -H \"Content-Type: application/json\" -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\"}' https://rpc.polkadot.io" + ], + "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string — not human-readable. Use Method B or C if you need human-readable JSON.", + "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":\"0x6d657461...\",\"id\":1}" + }, + { + "order": 2, + "action": "(Method B) Fetch metadata as human-readable JSON using subxt CLI", + "working_directory": ".", + "commands": [ + "subxt metadata --url wss://rpc.polkadot.io --format json > spec.json" + ], + "description": "Install the subxt CLI if not already installed: cargo install subxt-cli. Run subxt metadata to fetch and decode the metadata to JSON. Replace wss://rpc.polkadot.io with your node's WebSocket endpoint (wss:// for remote, ws:// for local). For a local dev node: subxt metadata --url ws://127.0.0.1:9944 --format json > spec.json. You can also explore metadata interactively at https://paritytech.github.io/subxt-explorer/.", + "expected_output": "spec.json created containing the runtime metadata in human-readable JSON format" + }, + { + "order": 3, + "action": "(Method C) Retrieve metadata via Polkadot.js Apps RPC UI", + "working_directory": ".", + "description": "Navigate to https://polkadot.js.org/apps/#/rpc and connect to your target node. Click the Developer dropdown and select RPC Calls. 1. Select state as the endpoint. 2. Select getMetadata(at) as the method. 3. Click Submit RPC Call. The metadata is returned in JSON format in the UI. This is the most interactive option but is not scriptable." + }, + { + "order": 4, + "action": "Interpret the metadata output", + "working_directory": ".", + "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type → find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions — do not hardcode them." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "curl returns connection refused or timeout", + "cause": "The RPC endpoint URL is incorrect, the node is not running, or the endpoint does not accept HTTP connections.", + "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections — use subxt (Method B) with ws:// or wss:// instead of curl." + }, + { + "pattern": "subxt metadata fails with 'failed to fetch metadata'", + "cause": "The WebSocket URL is incorrect, the node is not running, or the subxt-cli version is incompatible with the node's metadata version.", + "resolution": "Verify the WebSocket URL (wss:// for TLS, ws:// for local). Confirm the node is running. Update subxt-cli: cargo install subxt-cli --force." + }, + { + "pattern": "Metadata output shows unexpected pallets or an empty pallets array", + "cause": "The node connected to is a different chain than expected, or the genesis has not been customized.", + "resolution": "Verify the RPC endpoint points to the intended chain. For local dev nodes ensure the chain spec matches the expected runtime." + } + ], + "supplementary_context": { + "description": "Load these pages for context on how metadata is used in client applications or SDK-level interactions.", + "pages": [ + { + "slug": "reference-tools-subxt", + "title": "Subxt Rust API", + "url": "https://docs.polkadot.com/reference/tools/subxt.md", + "relevance": "Subxt library usage for generating typed client code from runtime metadata — the primary consumer of metadata in Rust applications." + }, + { + "slug": "reference-parachains-data-encoding", + "title": "Data Encoding", + "url": "https://docs.polkadot.com/reference/parachains/data-encoding.md", + "relevance": "SCALE encoding details that explain why the raw curl metadata output is hex-encoded bytes rather than readable JSON." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: inspect pallet list and call signatures of Polkadot mainnet", + "user_says": "Get the runtime metadata from the Polkadot mainnet node", + "actions": [ + "Run: subxt metadata --url wss://rpc.polkadot.io --format json > spec.json", + "Open spec.json and locate the pallets array", + "Find a specific pallet by name (e.g. Balances) and follow its calls type index to read available calls" + ], + "result": "spec.json contains the complete runtime metadata in human-readable JSON; pallets, calls, storage items, events, and types are all inspectable." + }, + { + "scenario": "Edge case: inspect metadata from a local dev node", + "user_says": "How do I get the metadata from my local dev node at ws://127.0.0.1:9944?", + "actions": [ + "Verify the local node is running (polkadot-omni-node --dev or similar)", + "Run: subxt metadata --url ws://127.0.0.1:9944 --format json > local-spec.json", + "Or use curl: curl -H \"Content-Type: application/json\" -d '{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"state_getMetadata\"}' http://127.0.0.1:9944" + ], + "result": "Metadata from the local dev node retrieved; reflects the exact runtime version currently running locally." + } + ] + }, + { + "id": "use-polkadot-js-api", + "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", + "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode — new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "reference/tools/polkadot-js-api.md" + ], + "primary_page": "reference/tools/polkadot-js-api.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "network": [ + "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.ibp.network/polkadot for Polkadot mainnet)" + ] + }, + "env_vars": [ + { + "name": "WS_ENDPOINT", + "description": "WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet).", + "required": true + }, + { + "name": "MNEMONIC", + "description": "12 or 24-word seed phrase for the signing account. Required only for the transaction-sending step.", + "required": false + } + ], + "steps": [ + { + "order": 1, + "action": "Initialize ESM Node.js project and install @polkadot/api", + "working_directory": ".", + "commands": [ + "mkdir polkadot-api-demo && cd polkadot-api-demo", + "npm init -y && npm pkg set type=module", + "npm install @polkadot/api @polkadot/keyring dotenv" + ], + "description": "Create a new directory named polkadot-api-demo and initialize it as an ESM Node.js project. The type=module flag is required because @polkadot packages use ESM-only imports. Install dotenv for secure credential loading." + }, + { + "order": 2, + "action": "Create .env file and add to .gitignore", + "working_directory": "polkadot-api-demo", + "commands": [ + "printf 'WS_ENDPOINT=\\nMNEMONIC=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly — do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." + }, + { + "order": 3, + "action": "Create the chain-query script", + "working_directory": "polkadot-api-demo", + "description": "Create a file named query.ts with the following content. Replace INSERT_ADDRESS with the SS58-encoded address to query (e.g., an address from the create-polkadot-account skill output):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const chain = await api.rpc.system.chain();\n const existentialDeposit = api.consts.balances.existentialDeposit;\n console.log(`Connected to: ${chain}`);\n console.log(`Existential deposit: ${existentialDeposit.toHuman()}`);\n const account = await api.query.system.account('INSERT_ADDRESS');\n console.log('Account data:', account.toHuman());\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);" + }, + { + "order": 4, + "action": "Run the query script", + "working_directory": "polkadot-api-demo", + "commands": [ + "npx tsx query.ts" + ], + "expected_output": "Connected to: Polkadot (or your target chain name)\nExistential deposit: 10 mDOT (or equivalent)\nAccount data: { nonce: ..., data: { free: ..., reserved: ... } }" + }, + { + "order": 5, + "action": "Create and run the transaction script", + "working_directory": "polkadot-api-demo", + "description": "Create a file named transfer.ts. Only proceed if MNEMONIC is set in .env. Replace INSERT_RECIPIENT with the destination SS58 address and INSERT_AMOUNT with the transfer amount in planck units (e.g., 1000000000000 = 1 DOT with 10 decimals, or 1000000000000000000 = 1 PAS with 18 decimals):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider, Keyring } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const keyring = new Keyring({ type: 'sr25519' });\n const sender = keyring.addFromUri(process.env.MNEMONIC!);\n console.log(`Sending from: ${sender.address}`);\n const txHash = await api.tx.balances\n .transferKeepAlive('INSERT_RECIPIENT', INSERT_AMOUNT)\n .signAndSend(sender);\n console.log(`Transaction hash: ${txHash}`);\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);\n\nThen run: npx tsx transfer.ts" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: WebSocket is not open / ECONNREFUSED", + "cause": "The WS_ENDPOINT URL is unreachable or the protocol is wrong.", + "resolution": "Verify WS_ENDPOINT in .env starts with wss:// (not https://). Check the node is online. For Polkadot Hub TestNet use wss://services.polkadothub-rpc.com/testnet." + }, + { + "pattern": "SyntaxError: Cannot use import statement in a module", + "cause": "The project is not configured as an ESM module.", + "resolution": "Run 'npm pkg set type=module' in the polkadot-api-demo directory, then retry." + }, + { + "pattern": "Error: Cannot find module '@polkadot/api'", + "cause": "Dependencies not installed.", + "resolution": "Run 'npm install @polkadot/api @polkadot/keyring dotenv' in the polkadot-api-demo directory." + }, + { + "pattern": "RpcError: 1010: Invalid Transaction: Inability to pay some fees", + "cause": "The sender account has insufficient funds to pay transaction fees.", + "resolution": "Fund the sender account with tokens. For Polkadot Hub TestNet use the faucet at https://faucet.polkadot.io/." + } + ], + "supplementary_context": { + "description": "Load these pages when the user asks about API alternatives, migration paths, or needs deeper context on chain interactions.", + "pages": [ + { + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md", + "relevance": "Polkadot API (PAPI) — the recommended replacement for @polkadot/api in new projects." + }, + { + "slug": "reference-tools-dedot", + "title": "Dedot", + "url": "https://docs.polkadot.com/reference/tools/dedot.md", + "relevance": "Dedot — modern lightweight alternative to @polkadot/api with better TypeScript inference." + }, + { + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", + "relevance": "Multi-SDK transaction guide covering PAPI, Polkadot.js, Dedot, and Subxt variants." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: query account balance", + "user_says": "Use @polkadot/api to check an account balance on Polkadot Hub", + "actions": [ + "Initialize ESM Node.js project and install @polkadot/api", + "Create .env with WS_ENDPOINT set to the Polkadot Hub TestNet WebSocket URL", + "Create query.ts with ApiPromise.create() and api.query.system.account()", + "Run npx tsx query.ts and read account balance output" + ], + "result": "Account free/reserved/frozen balances printed to console; chain name and existential deposit shown" + }, + { + "scenario": "Edge case: user is starting a new project", + "user_says": "I want to build a new Polkadot app with polkadot.js api", + "actions": [ + "Surface maintenance-mode warning: @polkadot/api is in maintenance mode", + "Recommend PAPI (reference-tools-papi) or Dedot (reference-tools-dedot) for new projects", + "If user confirms they want @polkadot/api anyway, proceed with installation steps" + ], + "result": "User informed of deprecation status; skill proceeds with @polkadot/api if that is the deliberate choice" + } + ] + }, + { + "id": "deploy-basic-contract-remix", + "title": "Deploy a Basic Smart Contract with Remix IDE", + "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" + ], + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", + "Chain ID: 420420417" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ (rate limit: check faucet page)" + ], + "wallet": [ + "MetaMask browser extension installed and configured for Polkadot Hub TestNet", + "Wallet funded with testnet PAS tokens" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Verify MetaMask is connected to Polkadot Hub TestNet", + "working_directory": ".", + "description": "Instruct the user: Open MetaMask and confirm the selected network is 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add the network. Also confirm the account balance shows testnet PAS tokens. If the balance is zero, visit https://faucet.polkadot.io/ to request tokens. Wait for the user to confirm both conditions before proceeding." + }, + { + "order": 2, + "action": "Open Remix IDE and locate Storage.sol", + "working_directory": ".", + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed." + }, + { + "order": 3, + "action": "Compile Storage.sol in Remix", + "working_directory": ".", + "description": "Instruct the user: Click the Solidity Compiler icon (second icon in the left sidebar). Confirm the compiler version is 0.8.x or higher. Click the blue 'Compile 1_Storage.sol' button. Wait for the green checkmark indicating successful compilation. If there are errors, verify the compiler version matches the pragma in the file.", + "expected_output": "Green checkmark on the Solidity Compiler sidebar icon; no errors shown." + }, + { + "order": 4, + "action": "Deploy to Polkadot Hub TestNet via MetaMask", + "working_directory": ".", + "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", + "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel." + }, + { + "order": 5, + "action": "Interact with the deployed Storage contract", + "working_directory": ".", + "description": "Instruct the user: In the 'Deployed Contracts' panel, expand the deployed Storage contract. To write a value: enter a number in the field next to the 'store' button and click 'store'. Confirm the MetaMask transaction. To read the stored value: click the 'retrieve' button (no transaction needed). The return value shows in the Remix console below.", + "expected_output": "retrieve() returns the number stored in the previous store() call." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "MetaMask: User denied account access / MetaMask not connected", + "cause": "MetaMask connection was rejected or MetaMask is not installed.", + "resolution": "Click the MetaMask extension icon and ensure it is unlocked. Re-select 'Injected Provider - MetaMask' in the Remix Environment dropdown and approve the connection prompt." + }, + { + "pattern": "Transaction failed: insufficient funds for gas", + "cause": "The connected MetaMask account has no testnet PAS tokens.", + "resolution": "Visit https://faucet.polkadot.io/ to obtain testnet PAS tokens. Ensure the faucet is sending to the correct address shown in MetaMask." + }, + { + "pattern": "Wrong network in MetaMask / chain ID mismatch", + "cause": "MetaMask is connected to a different network instead of Polkadot Hub TestNet.", + "resolution": "Open MetaMask and switch to 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add it." + }, + { + "pattern": "Solidity compile error: pragma mismatch", + "cause": "The Remix compiler version does not match the pragma in Storage.sol.", + "resolution": "In the Solidity Compiler tab, select a compiler version compatible with the pragma at the top of the contract file." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to set up MetaMask, obtain tokens, or move to a CLI-based deployment workflow.", + "pages": [ + { + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", + "relevance": "How to configure MetaMask or Talisman for Polkadot Hub TestNet." + }, + { + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md", + "relevance": "How to obtain testnet PAS tokens for deployment transactions." + }, + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", + "relevance": "CLI-based alternative: deploy the same Storage contract using Hardhat and a local toolchain." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: first contract deployment", + "user_says": "Deploy a smart contract on Polkadot Hub using Remix IDE", + "actions": [ + "Verify MetaMask is on Polkadot Hub TestNet and account has PAS tokens", + "Open remix.ethereum.org and locate contracts/1_Storage.sol", + "Compile with Solidity Compiler tab", + "Select Injected Provider - MetaMask in Deploy tab and click Deploy", + "Confirm MetaMask transaction and wait for mining" + ], + "result": "Storage contract deployed; appears in Remix Deployed Contracts panel with callable store/retrieve functions" + }, + { + "scenario": "Edge case: no testnet tokens", + "user_says": "I try to deploy but MetaMask says insufficient funds", + "actions": [ + "Direct user to https://faucet.polkadot.io/ to request testnet PAS tokens", + "Ask user to confirm the faucet sent tokens to the correct address", + "Once confirmed, retry the Deploy step in Remix" + ], + "result": "User obtains testnet PAS tokens and deployment proceeds successfully" + } + ] + }, + { + "id": "connect-remix-polkadot", + "title": "Connect Remix IDE to Polkadot Hub", + "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/dev-environments/remix.md" + ], + "primary_page": "smart-contracts/dev-environments/remix.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed and unlocked with Polkadot Hub TestNet configured (chain ID 420420417)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Open Remix IDE", + "working_directory": ".", + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. Wait for the IDE to fully load (the file explorer appears on the left). Ensure MetaMask is unlocked and set to Polkadot Hub TestNet before proceeding." + }, + { + "order": 2, + "action": "Navigate to the Deploy and Run Transactions tab", + "working_directory": ".", + "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left." + }, + { + "order": 3, + "action": "Select Injected Provider - MetaMask", + "working_directory": ".", + "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." + }, + { + "order": 4, + "action": "Verify the connection", + "working_directory": ".", + "description": "Instruct the user: Confirm the following in the Deploy panel: 1) The Account field shows your MetaMask wallet address. 2) The network indicator shows chain ID 420420417 (Polkadot Hub TestNet). If the chain ID shows a different value, switch networks in MetaMask to Polkadot Hub TestNet and the Remix panel will update automatically.", + "expected_output": "Remix Deploy panel shows MetaMask account address and Polkadot Hub TestNet (chain ID 420420417)." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "MetaMask not detected / No injected Web3 provider", + "cause": "MetaMask extension is not installed or is disabled in the browser.", + "resolution": "Install the MetaMask browser extension from https://metamask.io and reload Remix. Ensure the extension is enabled for the remix.ethereum.org domain." + }, + { + "pattern": "Wrong network shown in Remix (chain ID is not 420420417)", + "cause": "MetaMask is connected to a different chain.", + "resolution": "Open MetaMask and manually switch to Polkadot Hub TestNet. If Polkadot Hub TestNet is not in the list, follow the connect-wallet-polkadot-hub skill to add it." + }, + { + "pattern": "Remix Deploy panel shows 'JavaScript VM' after trying to switch", + "cause": "The injected provider selection was not saved or MetaMask was not connected.", + "resolution": "Click the Environment dropdown again and re-select 'Injected Provider - MetaMask'. Approve the MetaMask connection popup if it appears." + } + ], + "supplementary_context": { + "description": "Load these pages for wallet setup or when the user wants to deploy a contract after connecting.", + "pages": [ + { + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", + "relevance": "How to add Polkadot Hub TestNet to MetaMask before using Remix." + }, + { + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-remix", + "title": "Deploy a Basic Contract with Remix IDE", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", + "relevance": "Next step after connecting Remix: deploy the default Storage.sol contract." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: connect Remix before deployment", + "user_says": "Connect Remix IDE to Polkadot Hub so I can deploy a contract", + "actions": [ + "Open https://remix.ethereum.org", + "Click Deploy and Run Transactions tab", + "Select Injected Provider - MetaMask in the Environment dropdown", + "Approve the MetaMask connection popup", + "Verify account address and chain ID 420420417 appear in the Deploy panel" + ], + "result": "Remix IDE is connected to Polkadot Hub TestNet via MetaMask and ready for contract deployment" + }, + { + "scenario": "Edge case: MetaMask on wrong network", + "user_says": "Remix shows the wrong chain ID after connecting", + "actions": [ + "Ask user to open MetaMask and switch to Polkadot Hub TestNet", + "If network not present, direct to connect-wallet-polkadot-hub skill", + "Confirm Remix updates to show chain ID 420420417 automatically" + ], + "result": "Remix shows correct network after MetaMask network switch" + } + ] + }, + { + "id": "connect-wallet-polkadot-hub", + "title": "Connect a Wallet to Polkadot Hub", + "description": "Guides the user through adding Polkadot Hub TestNet (RPC, chain ID 420420417, PAS token) to MetaMask or Talisman and switching the active network. All steps are browser extension GUI interactions. Use before deploying smart contracts, using Remix IDE with Polkadot Hub, or obtaining testnet tokens. Trigger phrases: 'connect metamask polkadot hub', 'add polkadot network metamask', 'set up wallet polkadot testnet', 'configure talisman polkadot hub'. Outcome: wallet shows Polkadot Hub TestNet as active network and is ready for on-chain interactions.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/integrations/wallets.md" + ], + "primary_page": "smart-contracts/integrations/wallets.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed (https://metamask.io) OR Talisman installed (https://talisman.xyz)" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Open MetaMask network settings", + "working_directory": ".", + "description": "Instruct the user: Click the MetaMask extension icon in the browser toolbar to open it. Click the network selector at the top of the MetaMask popup (shows 'Ethereum Mainnet' by default). Click 'Add network' at the bottom of the network list. On the Add Network page, click 'Add a network manually'." + }, + { + "order": 2, + "action": "Enter Polkadot Hub TestNet network details", + "working_directory": ".", + "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." + }, + { + "order": 3, + "action": "Switch to Polkadot Hub TestNet", + "working_directory": ".", + "description": "Instruct the user: In MetaMask, click the network selector again. Select 'Polkadot Hub TestNet' from the list. The header now shows 'Polkadot Hub TestNet' and the balance shows PAS.", + "expected_output": "MetaMask header displays 'Polkadot Hub TestNet'; account balance shows PAS balance." + }, + { + "order": 4, + "action": "Obtain testnet PAS tokens if needed", + "working_directory": ".", + "description": "If the PAS balance is 0, instruct the user: Visit https://faucet.polkadot.io/ in the browser. Paste the MetaMask account address (click the address in MetaMask to copy it). Select 'Polkadot Hub TestNet' in the faucet network dropdown. Click 'Get tokens'. Tokens typically arrive within 30 seconds.", + "expected_output": "MetaMask PAS balance shows a non-zero amount." + }, + { + "order": 5, + "action": "Alternative: Add Polkadot Hub TestNet to Talisman", + "working_directory": ".", + "description": "If the user prefers Talisman instead of MetaMask, instruct them: Open the Talisman extension and click the settings icon. Go to Networks and Tokens, then Add Network. Enter the same network details as step 2: RPC URL: https://services.polkadothub-rpc.com/testnet, Chain ID: 420420417, Symbol: PAS. Save and switch to the Polkadot Hub TestNet network in Talisman." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Could not fetch chain ID / Invalid RPC URL", + "cause": "The RPC URL was entered incorrectly or the TestNet endpoint is temporarily unavailable.", + "resolution": "Verify the RPC URL is exactly: https://services.polkadothub-rpc.com/testnet (no trailing slash). Check the Polkadot Hub TestNet status page or Discord for outages." + }, + { + "pattern": "Chain ID mismatch: expected 420420417", + "cause": "A different endpoint was used that returns a different chain ID.", + "resolution": "Delete the incorrectly configured network from MetaMask and re-add it using the exact RPC URL and chain ID 420420417." + }, + { + "pattern": "Network added but balance shows 0 PAS", + "cause": "Account not funded with testnet tokens.", + "resolution": "Visit https://faucet.polkadot.io/, paste your address, select Polkadot Hub TestNet, and request tokens." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs network reference data or wants to proceed to contract deployment after wallet setup.", + "pages": [ + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Full network connection reference with RPC URLs, chain IDs, and WSS endpoints for all Polkadot Hub environments." + }, + { + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md", + "relevance": "Step-by-step guide to obtaining testnet PAS tokens from the Polkadot faucet." + }, + { + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", + "relevance": "How to connect Remix IDE to Polkadot Hub using the configured MetaMask wallet." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: add Polkadot Hub TestNet to MetaMask", + "user_says": "Add Polkadot Hub TestNet to MetaMask and get some test tokens", + "actions": [ + "Open MetaMask, click network selector, click Add network, then Add a network manually", + "Enter network name, RPC URL (https://services.polkadothub-rpc.com/testnet), chain ID 420420417, symbol PAS", + "Save and switch to Polkadot Hub TestNet", + "Visit https://faucet.polkadot.io/ and request testnet PAS tokens" + ], + "result": "MetaMask shows Polkadot Hub TestNet as active network with a non-zero PAS balance" + }, + { + "scenario": "Edge case: RPC URL rejected by MetaMask", + "user_says": "MetaMask says it could not fetch the chain ID when I enter the RPC URL", + "actions": [ + "Verify the RPC URL is exactly https://services.polkadothub-rpc.com/testnet with no trailing slash", + "Check whether the Polkadot Hub TestNet is experiencing downtime", + "Retry adding the network once the endpoint is confirmed reachable" + ], + "result": "Network added successfully once the correct RPC URL is verified and the endpoint is reachable" + } + ] + }, + { + "id": "use-wagmi-polkadot-hub", + "title": "Build a Wagmi dApp Connected to Polkadot Hub", + "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo — all code is inlined in the steps.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/libraries/wagmi.md" + ], + "primary_page": "smart-contracts/libraries/wagmi.md", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "wallet": [ + "MetaMask or any EIP-1193 wallet installed in the browser" + ], + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "tokens": [ + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Scaffold the Next.js project non-interactively", + "working_directory": ".", + "commands": [ + "npx create-next-app@15 wagmi-dapp --ts --no-eslint --no-tailwind --no-src-dir --app --use-npm --skip-install", + "cd wagmi-dapp", + "npm install" + ], + "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input — `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm — skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." + }, + { + "order": 2, + "action": "Install Wagmi v3 and peer dependencies", + "working_directory": "wagmi-dapp", + "commands": [ + "npm install wagmi viem@2.x @tanstack/react-query" + ], + "description": "Install wagmi (React hooks for Ethereum), viem v2 (low-level EVM transport), and @tanstack/react-query (required peer dependency for Wagmi v3 data-fetching hooks)." + }, + { + "order": 3, + "action": "Create the Wagmi config file with Polkadot Hub chain", + "working_directory": "wagmi-dapp", + "description": "Create a new file at src/wagmi.ts (or wagmi.ts in the project root if using the src/ layout). Paste the following content exactly:\n\n```typescript\nimport { createConfig, http } from 'wagmi';\nimport { defineChain } from 'viem';\nimport { injected } from 'wagmi/connectors';\n\nexport const polkadotHubTestnet = defineChain({\n id: 420420417,\n name: 'Polkadot Hub TestNet',\n nativeCurrency: { name: 'PAS', symbol: 'PAS', decimals: 18 },\n rpcUrls: {\n default: { http: ['https://services.polkadothub-rpc.com/testnet'] },\n },\n});\n\nexport const config = createConfig({\n chains: [polkadotHubTestnet],\n connectors: [injected()],\n transports: {\n [polkadotHubTestnet.id]: http(),\n },\n});\n```\n\nThis configures Wagmi to use Polkadot Hub TestNet as the only chain with the injected (MetaMask) connector." + }, + { + "order": 4, + "action": "Wrap the app with WagmiProvider and QueryClientProvider", + "working_directory": "wagmi-dapp", + "description": "Open src/app/layout.tsx (or pages/_app.tsx if using pages router). Add a client-side providers wrapper. Create src/app/providers.tsx with:\n\n```typescript\n'use client';\nimport { WagmiProvider } from 'wagmi';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { config } from '../wagmi';\n\nconst queryClient = new QueryClient();\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n return (\n \n \n {children}\n \n \n );\n}\n```\n\nThen wrap the body in src/app/layout.tsx with: import { Providers } from './providers'; and wrap {children} with Providers." + }, + { + "order": 5, + "action": "Add wallet connection component", + "working_directory": "wagmi-dapp", + "description": "Create src/components/ConnectWallet.tsx with the following content:\n\n```typescript\n'use client';\nimport { useAccount, useConnect, useDisconnect } from 'wagmi';\n\nexport function ConnectWallet() {\n const { address, isConnected } = useAccount();\n const { connect, connectors } = useConnect();\n const { disconnect } = useDisconnect();\n\n if (isConnected) {\n return (\n
\n

Connected: {address}

\n \n
\n );\n }\n\n return (\n
\n {connectors.map((connector) => (\n \n ))}\n
\n );\n}\n```\n\nThis renders a connect/disconnect button using the injected (MetaMask) connector." + }, + { + "order": 6, + "action": "Add block number query using useBlockNumber", + "working_directory": "wagmi-dapp", + "description": "Create src/components/BlockNumber.tsx with:\n\n```typescript\n'use client';\nimport { useBlockNumber } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nexport function BlockNumber() {\n const { data: blockNumber } = useBlockNumber({\n chainId: polkadotHubTestnet.id,\n watch: true,\n });\n return

Current block: {blockNumber?.toString() ?? 'Loading...'}

;\n}\n```\n\nThe watch: true option polls for new blocks and updates the component automatically." + }, + { + "order": 7, + "action": "Add Storage contract read interaction using useReadContract", + "working_directory": "wagmi-dapp", + "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type — without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." + }, + { + "order": 8, + "action": "Add Storage contract write interaction using useWriteContract", + "working_directory": "wagmi-dapp", + "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from ." + }, + { + "order": 9, + "action": "Assemble components in the main page and start the dev server", + "working_directory": "wagmi-dapp", + "commands": [ + "npm run dev" + ], + "description": "Open src/app/page.tsx and replace its content with:\n\n```typescript\nimport { ConnectWallet } from '../components/ConnectWallet';\nimport { BlockNumber } from '../components/BlockNumber';\nimport { StorageRead } from '../components/StorageRead';\nimport { StorageWrite } from '../components/StorageWrite';\n\nexport default function Home() {\n return (\n
\n

Wagmi Polkadot Hub dApp

\n \n \n \n \n
\n );\n}\n```\n\nThen run npm run dev to start the Next.js dev server on http://localhost:3000.", + "expected_output": "Next.js dev server running at http://localhost:3000" + }, + { + "order": 10, + "action": "Connect wallet and test contract interactions in the browser", + "working_directory": "wagmi-dapp", + "interactive": true, + "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation — approve it. After confirmation, the stored value should update to the new number.", + "expected_output": "Wallet connected, block number displayed, Storage contract read/write working" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: No connectors found / useConnect: No connector available", + "cause": "WagmiProvider not wrapping the component tree, or connectors not configured.", + "resolution": "Ensure the Providers wrapper (WagmiProvider + QueryClientProvider) wraps the entire app in layout.tsx and that the config in wagmi.ts includes the injected() connector." + }, + { + "pattern": "ChainMismatchError: Chain ID 420420417 not supported", + "cause": "The wallet is connected to a different chain than Polkadot Hub TestNet.", + "resolution": "In MetaMask, switch to the Polkadot Hub TestNet network (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill to add the network if it is not present." + }, + { + "pattern": "TypeError: Cannot read properties of undefined (reading 'toString') on blockNumber", + "cause": "useBlockNumber returned undefined before the first block is fetched.", + "resolution": "The component handles this with the null-coalescing operator (blockNumber?.toString() ?? 'Loading...'). This is expected on initial render and resolves automatically." + }, + { + "pattern": "Error: wagmi requires react-query v5 or higher", + "cause": "Incompatible @tanstack/react-query version installed.", + "resolution": "Run: npm install @tanstack/react-query@latest to ensure v5+ is installed." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs MetaMask setup, faucet tokens, or wants to use viem directly instead of Wagmi.", + "pages": [ + { + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", + "relevance": "Lower-level viem library for direct contract calls without React hooks — useful when Wagmi is not needed." + }, + { + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required before the wallet connection step works." + }, + { + "slug": "smart-contracts-libraries-wagmi", + "title": "Wagmi for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/wagmi.md", + "relevance": "Full Wagmi source page with the pre-deployed Storage contract address and complete code snippets." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: scaffold a Wagmi dApp and connect to Polkadot Hub TestNet", + "user_says": "Help me build a Next.js app with Wagmi that connects to Polkadot Hub", + "actions": [ + "Scaffold wagmi-dapp with create-next-app@15 non-interactively", + "Install wagmi, viem@2.x, and @tanstack/react-query", + "Create wagmi.ts with polkadotHubTestnet chain definition and createConfig", + "Wrap the app with WagmiProvider and QueryClientProvider in providers.tsx", + "Add ConnectWallet, BlockNumber, StorageRead, StorageWrite components", + "Start dev server and delegate browser testing to the user" + ], + "result": "Next.js app at localhost:3000 with wallet connection and live block number display" + }, + { + "scenario": "Edge case: user wants to call their own deployed contract instead of the pre-deployed Storage", + "user_says": "I deployed my own ERC-20 token — how do I call it with Wagmi useReadContract?", + "actions": [ + "Replace CONTRACT_ADDRESS in StorageRead.tsx with the user's deployed contract address", + "Replace STORAGE_ABI with the ERC-20 ABI fragment for the desired function (e.g. balanceOf)", + "Update functionName and args accordingly", + "Keep chainId: polkadotHubTestnet.id to ensure the call targets the correct network" + ], + "result": "useReadContract returns the result from the user's deployed contract on Polkadot Hub TestNet" + } + ] + }, + { + "id": "deploy-interact-contracts-web3py", + "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", + "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/libraries/web3-py.md" + ], + "primary_page": "smart-contracts/libraries/web3-py.md", + "prerequisites": { + "runtime": [ + "Python 3.9+ with pip", + "Solidity compiler (installed automatically by py-solc-x)" + ], + "network": [ + "Polkadot Hub TestNet — RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" + ], + "tokens": [ + "Small PAS balance for deployment gas — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (PRIVATE_KEY stored in .env — never enter in chat)" + ] + }, + "env_vars": [ + { + "name": "PRIVATE_KEY", + "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", + "required": true + } + ], + "steps": [ + { + "order": 1, + "action": "Create project directory and Python virtual environment", + "working_directory": ".", + "commands": [ + "mkdir web3py-contracts && cd web3py-contracts", + "python3 -m venv venv", + "source venv/bin/activate" + ], + "description": "Create a new directory named 'web3py-contracts', create a Python virtual environment inside it, and activate it. On Windows use 'venv\\Scripts\\activate' instead." + }, + { + "order": 2, + "action": "Install Python dependencies", + "working_directory": "web3py-contracts", + "commands": [ + "pip install web3 py-solc-x python-dotenv" + ], + "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." + }, + { + "order": 3, + "action": "Create the .env file with PRIVATE_KEY", + "working_directory": "web3py-contracts", + "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" + }, + { + "order": 4, + "action": "Create the Storage.sol Solidity contract", + "working_directory": "web3py-contracts", + "description": "Create a file named 'Storage.sol' with the following content:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ncontract Storage {\n uint256 private storedNumber;\n\n function store(uint256 num) public {\n storedNumber = num;\n }\n\n function retrieve() public view returns (uint256) {\n return storedNumber;\n }\n}\n```\n\nThis is a simple Storage contract with a store(uint256) write function and a retrieve() read function." + }, + { + "order": 5, + "action": "Create compile.py to compile the Solidity contract", + "working_directory": "web3py-contracts", + "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", + "commands": [ + "python compile.py" + ], + "expected_output": "Compilation successful — compiled.json written." + }, + { + "order": 6, + "action": "Create deploy.py to deploy the contract", + "working_directory": "web3py-contracts", + "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", + "commands": [ + "python deploy.py" + ], + "expected_output": "Deploying from account: 0x...\nContract deployed at: 0x...\nAddress saved to contract_address.txt" + }, + { + "order": 7, + "action": "Create interact.py to call store and retrieve", + "working_directory": "web3py-contracts", + "description": "Create a file named 'interact.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\naccount = w3.eth.account.from_key(PRIVATE_KEY)\n\nwith open('contract_address.txt') as f:\n contract_address = f.read().strip()\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\nabi = compiled['contracts']['Storage.sol']['Storage']['abi']\n\ncontract = w3.eth.contract(address=contract_address, abi=abi)\n\n# Read current value\nvalue = contract.functions.retrieve().call()\nprint(f'Current stored value: {value}')\n\n# Write new value\nnonce = w3.eth.get_transaction_count(account.address)\ntx = contract.functions.store(42).build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\nprint(f'store(42) tx: {receipt.transactionHash.hex()}')\n\n# Verify\nvalue = contract.functions.retrieve().call()\nprint(f'Updated stored value: {value}')\n```", + "commands": [ + "python interact.py" + ], + "expected_output": "Current stored value: 0\nstore(42) tx: 0x...\nUpdated stored value: 42" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "AssertionError: Failed to connect to RPC endpoint", + "cause": "The RPC URL is unreachable or the TestNet is down.", + "resolution": "Verify the URL https://services.polkadothub-rpc.com/testnet is correct and reachable. Check Polkadot Hub TestNet status." + }, + { + "pattern": "ValueError: insufficient funds for gas * price + value", + "cause": "The deployer account has no PAS tokens.", + "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS tokens for your account address." + }, + { + "pattern": "KeyError: 'PRIVATE_KEY'", + "cause": "The .env file is missing or PRIVATE_KEY is not defined in it.", + "resolution": "Create a .env file in the web3py-contracts directory with the line: PRIVATE_KEY=0xYOUR_KEY. Ensure python-dotenv is installed and load_dotenv() is called before accessing os.environ['PRIVATE_KEY']." + }, + { + "pattern": "SolcError: Source file requires different compiler version", + "cause": "Solidity pragma version mismatch.", + "resolution": "Ensure install_solc('0.8.20') is called in compile.py before compile_standard. The py-solc-x library downloads the compiler if not already cached." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to understand the Polkadot Hub network params or wants to use JavaScript/TypeScript instead of Python.", + "pages": [ + { + "slug": "smart-contracts-libraries-web3-py", + "title": "Web3.py", + "url": "https://docs.polkadot.com/smart-contracts/libraries/web3-py.md", + "relevance": "Full Web3.py source page with complete code snippets and additional contract examples." + }, + { + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", + "relevance": "Ethers.js library — recommended JavaScript alternative to the sunset Web3.js library." + }, + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Full network connection reference with RPC URL, chain ID, and WSS endpoints for Polkadot Hub." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy a Storage contract to Polkadot Hub TestNet using Python", + "user_says": "Deploy a smart contract to Polkadot Hub using Python and Web3.py", + "actions": [ + "Create web3py-contracts/ directory and Python virtualenv", + "Install web3, py-solc-x, python-dotenv via pip", + "Instruct user to add PRIVATE_KEY to .env file directly (not in chat)", + "Create Storage.sol, compile.py, deploy.py, interact.py from inline code", + "Run python compile.py then python deploy.py", + "Run python interact.py to verify store/retrieve" + ], + "result": "Contract deployed and verified on Polkadot Hub TestNet; stored value updated to 42" + }, + { + "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", + "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?", + "actions": [ + "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", + "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", + "Update deploy.py: change compiled['contracts']['YourFile.sol']['YourContract'] to match", + "Update interact.py: change the ABI extraction path and function calls to match the user's contract ABI" + ], + "result": "Custom contract compiled and deployed; interact.py calls the contract's specific functions" + } + ] + }, + { + "id": "interact-erc20-precompile-polkadot-hub", + "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", + "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/precompiles/erc20.md" + ], + "primary_page": "smart-contracts/precompiles/erc20.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ], + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "tokens": [ + "PAS balance in MetaMask for transfer/approve operations — obtain from https://faucet.polkadot.io/" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Find the ERC-20 precompile address from the source page", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." + }, + { + "order": 2, + "action": "Connect MetaMask to Polkadot Hub TestNet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open MetaMask and switch to the Polkadot Hub TestNet network (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet). If the network is not added yet, use the add-polkadot-hub-testnet-metamask skill to add it. Verify your PAS balance is non-zero before proceeding." + }, + { + "order": 3, + "action": "Open Remix IDE and create the ERC-20 interface file", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), create a new file named 'IERC20.sol'. Paste the standard ERC-20 interface into the file:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function balanceOf(address account) external view returns (uint256);\n function transfer(address to, uint256 amount) external returns (bool);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n```\n\nClick the Solidity Compiler tab (shield icon), set the compiler version to 0.8.20, and click 'Compile IERC20.sol'." + }, + { + "order": 4, + "action": "Connect Remix to MetaMask and load the precompile using At Address", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." + }, + { + "order": 5, + "action": "Call balanceOf to check PAS token balance", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", + "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18" + }, + { + "order": 6, + "action": "Call approve and transfer functions", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", + "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Remix: 'At Address' button disabled or no output", + "cause": "The IERC20.sol file was not compiled successfully before clicking At Address.", + "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." + }, + { + "pattern": "MetaMask: Wrong network — expected Custom (420420417)", + "cause": "MetaMask is still on Ethereum Mainnet or another network.", + "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." + }, + { + "pattern": "Transaction reverted: insufficient balance", + "cause": "Attempting to transfer more PAS than the account holds.", + "resolution": "Call balanceOf first to confirm the available balance, then use an amount in wei that does not exceed it." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs the precompile address, wants to add MetaMask, or wants to call the precompile from a contract.", + "pages": [ + { + "slug": "smart-contracts-precompiles-erc20", + "title": "Interact with the ERC20 Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", + "relevance": "Full ERC-20 precompile source page with the exact precompile address, complete ABI, and additional usage examples." + }, + { + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite for this skill." + }, + { + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: check PAS balance via ERC-20 precompile in Remix", + "user_says": "How do I call balanceOf on the ERC-20 precompile in Remix?", + "actions": [ + "Load source page to find the ERC-20 precompile address", + "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", + "Create IERC20.sol in Remix with the standard ERC-20 interface and compile it", + "Set Environment to Injected Provider - MetaMask in Deploy and Run tab", + "Click At Address with the precompile address", + "Expand balanceOf, enter the MetaMask address, and click call" + ], + "result": "uint256 PAS balance in wei displayed in Remix console" + }, + { + "scenario": "Edge case: user wants to call the precompile from a Solidity contract", + "user_says": "How do I use the ERC-20 precompile from inside my Solidity contract?", + "actions": [ + "Load source page to get the ERC-20 precompile address", + "In the Solidity contract, import or define the IERC20 interface", + "Store the precompile address as a constant: IERC20 constant PAS_ERC20 = IERC20(PRECOMPILE_ADDRESS)", + "Call PAS_ERC20.balanceOf(msg.sender) or PAS_ERC20.transfer(to, amount) as needed", + "Deploy the wrapper contract via Remix with MetaMask connected to Polkadot Hub TestNet" + ], + "result": "Solidity contract calls the ERC-20 precompile to read balances or transfer native PAS tokens" + } + ] + }, + { + "id": "interact-storage-precompile-remix", + "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", + "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/precompiles/storage.md" + ], + "primary_page": "smart-contracts/precompiles/storage.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ], + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "tokens": [ + "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Find the Storage precompile address from the source page", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." + }, + { + "order": 2, + "action": "Connect MetaMask to Polkadot Hub TestNet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance." + }, + { + "order": 3, + "action": "Create the Storage precompile interface in Remix", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'IStorage.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IStorage {\n function setBytes(bytes32 key, bytes memory value) external;\n function getBytes(bytes32 key) external view returns (bytes memory);\n}\n```\n\nGo to the Solidity Compiler tab, set the version to 0.8.20, and click 'Compile IStorage.sol'. Confirm there are no errors." + }, + { + "order": 4, + "action": "Connect Remix to MetaMask and load the precompile using At Address", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection popup. Verify the network displays 'Custom (420420417) network'. Select 'IStorage' in the Contract dropdown. Paste the Storage precompile address (from step 1) into the 'At Address' input. Click 'At Address'. An IStorage instance should appear in the Deployed Contracts section." + }, + { + "order": 5, + "action": "Call setBytes to store a value", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", + "expected_output": "Transaction confirmed; tx hash shown in Remix console" + }, + { + "order": 6, + "action": "Call getBytes to retrieve the stored value", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: In the IStorage instance, expand 'getBytes'. Enter the same bytes32 key used in setBytes. Click 'call' (blue button). The output displays the stored bytes value in hex. To decode: the hex 0x68656c6c6f decodes to 'hello' in UTF-8.", + "expected_output": "bytes output matching the hex value stored in setBytes" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Remix: At Address button grayed out", + "cause": "IStorage.sol was not compiled, or the wrong contract is selected in the dropdown.", + "resolution": "Compile IStorage.sol first via the Solidity Compiler tab, then return to Deploy and Run. Ensure 'IStorage' is selected in the Contract dropdown before clicking At Address." + }, + { + "pattern": "getBytes returns 0x (empty bytes)", + "cause": "The key used in getBytes does not match the key used in setBytes, or the setBytes transaction did not confirm.", + "resolution": "Ensure the bytes32 key in getBytes is identical to the one used in setBytes. Check Remix console for the setBytes transaction hash to confirm it was mined." + }, + { + "pattern": "MetaMask: Transaction failed with out of gas", + "cause": "Gas estimate too low for setBytes with a large value.", + "resolution": "In MetaMask, increase the gas limit manually before confirming the setBytes transaction." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs the precompile address, wants to call the precompile from Solidity, or needs Remix connection help.", + "pages": [ + { + "slug": "smart-contracts-precompiles-storage", + "title": "Interact with the Storage Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/storage.md", + "relevance": "Full Storage precompile source page with the exact precompile address, full ABI, and key encoding examples." + }, + { + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." + }, + { + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: store and retrieve bytes using the Storage precompile", + "user_says": "How do I use the Storage precompile in Remix to store a value?", + "actions": [ + "Load source page to get the Storage precompile address", + "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", + "Create IStorage.sol in Remix with setBytes/getBytes interface and compile it", + "Set Environment to Injected Provider - MetaMask, select IStorage, click At Address with the precompile address", + "Call setBytes with a bytes32 key (hex-padded) and hex-encoded bytes value", + "Call getBytes with the same key to verify the stored value" + ], + "result": "Value stored on-chain via setBytes; getBytes returns the same hex-encoded bytes" + }, + { + "scenario": "Edge case: user wants to use the Storage precompile from within a Solidity contract", + "user_says": "Can I call the Storage precompile from inside my own contract?", + "actions": [ + "Load source page to get the precompile address", + "In the user's contract, import or define the IStorage interface", + "Store the precompile address as a constant: IStorage constant STORE = IStorage(PRECOMPILE_ADDRESS)", + "Call STORE.setBytes(key, value) and STORE.getBytes(key) in contract functions", + "Deploy the wrapper contract in Remix targeting Polkadot Hub TestNet" + ], + "result": "User's contract reads and writes key-value data through the Storage precompile" + } + ] + }, + { + "id": "interact-system-precompile-remix", + "title": "Interact with the System Precompile on Polkadot Hub via Remix", + "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/precompiles/system.md" + ], + "primary_page": "smart-contracts/precompiles/system.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ], + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "tokens": [ + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Find the System precompile address from the source page", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." + }, + { + "order": 2, + "action": "Connect MetaMask to Polkadot Hub TestNet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance." + }, + { + "order": 3, + "action": "Create the System precompile interface in Remix", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." + }, + { + "order": 4, + "action": "Connect Remix to MetaMask and load the precompile using At Address", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection. Verify the network shows 'Custom (420420417) network'. Select 'ISystem' in the Contract dropdown. Paste the System precompile address (from step 1) into 'At Address'. Click 'At Address'. An ISystem instance should appear in the Deployed Contracts section." + }, + { + "order": 5, + "action": "Call blake2b to hash data", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", + "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data" + }, + { + "order": 6, + "action": "Call verifySignature for sr25519 verification", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Expand the 'verifySignature' function. To use this function you need three values:\n\n- INSERT_SIGNATURE: Replace with a real sr25519 signature as hex bytes (64 bytes, 128 hex chars). Generate a signature using a Polkadot.js signer or subkey tool: subkey sign --scheme sr25519 --suri '//Alice' --message 'your_message'\n\n- INSERT_PUBLIC_KEY: Replace with the sr25519 public key (32 bytes, 64 hex chars) corresponding to the signer.\n\n- message: The message bytes that were signed (hex-encoded).\n\nEnter the three values in the 'sig', 'pubKey', and 'message' fields respectively, then click 'call'. The function returns true if the signature is valid, false otherwise.", + "expected_output": "bool: true if the sr25519 signature is valid for the given pubKey and message" + }, + { + "order": 7, + "action": "Call accountExists to check account presence", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Expand the 'accountExists' function. Enter an Ethereum-format address (0x...) in the 'account' field. Click 'call'. The function returns true if the account exists on-chain (i.e. has been funded or deployed), false if it has never received any funds or activity.", + "expected_output": "bool: true if the account exists on Polkadot Hub TestNet, false otherwise" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Remix: call reverts on verifySignature with invalid inputs", + "cause": "The sig or pubKey bytes are not the correct length or format for sr25519.", + "resolution": "sr25519 signatures are 64 bytes (128 hex chars with 0x prefix = 130 chars total). Public keys are 32 bytes (64 hex chars). Use the subkey tool to generate a valid signature: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'. Ensure all inputs are hex-encoded with 0x prefix." + }, + { + "pattern": "At Address: no contract at address", + "cause": "Incorrect precompile address entered, or MetaMask connected to the wrong network.", + "resolution": "Re-load the source page to get the correct System precompile address. Verify MetaMask shows chain ID 420420417." + }, + { + "pattern": "ISystem.sol compilation error: function not found in interface", + "cause": "The interface in ISystem.sol may not match the exact ABI exposed by the precompile.", + "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/system.md for the authoritative ABI and update the interface accordingly." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs the precompile address, wants to use sr25519 signing tools, or needs Remix setup help.", + "pages": [ + { + "slug": "smart-contracts-precompiles-system", + "title": "Interact with the System Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/system.md", + "relevance": "Full System precompile source page with the exact precompile address, authoritative ABI, and all function signatures." + }, + { + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." + }, + { + "slug": "smart-contracts-precompiles-erc20", + "title": "Interact with the ERC20 Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", + "relevance": "ERC-20 precompile — often used alongside the System precompile in contract interactions." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: compute a BLAKE2b hash using the System precompile in Remix", + "user_says": "How do I use the System precompile to hash data with BLAKE2b in Remix?", + "actions": [ + "Load source page to get the System precompile address", + "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", + "Create ISystem.sol in Remix with the BLAKE2 and other function signatures, then compile", + "Set Environment to Injected Provider - MetaMask, select ISystem, click At Address with the precompile address", + "Expand blake2b, enter hex-encoded input data (e.g. 0x68656c6c6f for 'hello'), click call", + "Record the BLAKE2b hash output" + ], + "result": "BLAKE2b hash of the input bytes returned as hex in the Remix console" + }, + { + "scenario": "Edge case: user wants to verify an sr25519 signature from a Polkadot account", + "user_says": "Can I verify a Polkadot sr25519 signature on-chain using the System precompile?", + "actions": [ + "Generate a test signature using subkey: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'", + "Note the 64-byte signature hex and the 32-byte Alice public key hex", + "In Remix, expand verifySignature on the ISystem instance", + "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", + "Click call and confirm the result is true" + ], + "result": "bool true returned — sr25519 signature verified on-chain via the System precompile" + } + ] + }, + { + "id": "interact-xcm-precompile-remix", + "title": "Interact with the XCM Precompile via Remix IDE", + "description": "Guides you through creating an IXcm Solidity interface in Remix, loading the XCM precompile, and calling weighMessage, execute, and send functions. Use when you need to test XCM message dispatch from a smart contract, estimate XCM execution weight, or send cross-chain messages from Polkadot Hub via Solidity. All interaction is browser-based using Remix IDE with MetaMask. Trigger phrases: 'XCM precompile', 'send XCM from contract', 'weighMessage', 'xcm execute', 'cross-chain from Solidity', 'IXcm interface'. Output: XCM weight estimates and on-chain transaction receipts.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/precompiles/xcm.md" + ], + "primary_page": "smart-contracts/precompiles/xcm.md", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ], + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "tokens": [ + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Find the XCM precompile address and IXcm ABI from the source page", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." + }, + { + "order": 2, + "action": "Connect MetaMask to Polkadot Hub TestNet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance for gas fees." + }, + { + "order": 3, + "action": "Create the IXcm interface in Remix and compile it", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." + }, + { + "order": 4, + "action": "Load the XCM precompile using At Address in Remix", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." + }, + { + "order": 5, + "action": "Call weighMessage to estimate XCM execution cost", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", + "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost" + }, + { + "order": 6, + "action": "Call execute to run an XCM message locally", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", + "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs" + }, + { + "order": 7, + "action": "Call send to dispatch an XCM to another chain", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", + "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Remix: transaction reverted on execute or send", + "cause": "SCALE-encoded XCM bytes are malformed or the weight limit is too low.", + "resolution": "Verify the encoding against the example on the source page. Use weighMessage first to obtain the correct maxWeight. Ensure the correct precompile address is used for TestNet." + }, + { + "pattern": "At Address: no contract at address", + "cause": "Incorrect precompile address entered, or MetaMask is connected to the wrong network.", + "resolution": "Reload the source page to get the authoritative XCM precompile address. Verify MetaMask shows chain ID 420420417." + }, + { + "pattern": "IXcm.sol compilation error: function not found", + "cause": "The interface definition does not match the ABI the precompile exposes.", + "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md for the authoritative ABI and update IXcm.sol accordingly." + }, + { + "pattern": "MetaMask: insufficient funds for gas", + "cause": "PAS balance too low to cover gas on Polkadot Hub TestNet (base fee: 1000 gwei).", + "resolution": "Obtain PAS tokens from https://faucet.polkadot.io/. Ensure the account has at least 0.01 PAS before submitting state-changing calls." + } + ], + "supplementary_context": { + "description": "Load these pages for XCM encoding details, precompile addresses, and cross-chain messaging concepts.", + "pages": [ + { + "slug": "smart-contracts-precompiles-xcm", + "title": "Interact with the XCM Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/xcm.md", + "relevance": "Authoritative XCM precompile address, IXcm interface definition, and SCALE-encoded message examples." + }, + { + "slug": "reference-tools-xcm-tools", + "title": "XCM Tools", + "url": "https://docs.polkadot.com/reference/tools/xcm-tools.md", + "relevance": "XCM tooling for encoding, debugging, and monitoring message delivery." + }, + { + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md", + "relevance": "Polkadot Hub TestNet network details and RPC endpoints." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: estimate XCM execution fee before sending", + "user_says": "How much will it cost to send an XCM message from my contract on Polkadot Hub?", + "actions": [ + "Load XCM precompile address and IXcm ABI from source page", + "Create and compile IXcm.sol in Remix", + "Load precompile via At Address", + "Call weighMessage with encoded destination and message bytes", + "Read the returned weight estimate" + ], + "result": "Weight estimate (refTime and proofSize) indicating the execution cost on the destination chain" + }, + { + "scenario": "Edge case: execute call reverts due to malformed SCALE encoding", + "user_says": "My XCM execute call keeps reverting in Remix", + "actions": [ + "Compare SCALE-encoded message bytes against the source page example", + "Call weighMessage first to confirm the message decodes correctly", + "Check that maxWeight is at least the refTime returned by weighMessage", + "If revert persists, reload IXcm.sol ABI from source page to confirm interface matches precompile" + ], + "result": "Root cause identified (malformed encoding or wrong interface); corrected message executes successfully" + } + ] + }, + { + "id": "deploy-parachain-to-polkadot-testnet", + "title": "Deploy a Parachain to the Polkadot TestNet (Paseo)", + "description": "Covers the end-to-end parachain launch workflow on Paseo TestNet: obtain PAS tokens, reserve a para ID via Polkadot.js Apps, generate collator keys with subkey (Docker), build plain and raw chain specs with chain-spec-builder, export genesis wasm and state with polkadot-omni-node, register a parathread on-chain, generate a node key, start the collator, and insert session keys via RPC. Use this skill after completing the set-up-parachain-template skill. Trigger phrases: 'deploy parachain', 'register parachain Paseo', 'launch parachain TestNet', 'reserve para ID', 'parachain registration'. Part of the three-page launch series: set-up-the-parachain-template to deploy-to-polkadot to obtain-coretime.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "parachains/launch-a-parachain/deploy-to-polkadot.md" + ], + "primary_page": "parachains/launch-a-parachain/deploy-to-polkadot.md", + "prerequisites": { + "runtime": [ + "Polkadot SDK parachain template built in release mode (see set-up-parachain-template skill)", + "Rust and Cargo installed", + "Docker installed (for subkey key generation)", + "polkadot-omni-node binary: cargo install polkadot-omni-node", + "chain-spec-builder binary: cargo install chain-spec-builder" + ], + "network": [ + "Paseo TestNet — wss://rpc.ibp.network/paseo", + "Server with TCP port 30333 open for P2P collator connections" + ], + "tokens": [ + "PAS tokens from https://faucet.polkadot.io/ — required for para ID reservation and parathread registration fees" + ], + "wallet": [ + "Polkadot.js browser extension (https://polkadot.js.org/extension/) with a PAS-funded account" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Obtain PAS tokens from the faucet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open https://faucet.polkadot.io/, select the 'Paseo' network, paste your SS58 account address, and submit. The faucet delivers 500 PAS per request. Verify the balance in Polkadot.js Apps before proceeding." + }, + { + "order": 2, + "action": "Reserve a para ID on Paseo TestNet", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID — record it as PARA_ID (needed in all subsequent steps).", + "expected_output": "Para ID assigned and visible in the Parathreads tab" + }, + { + "order": 3, + "action": "Generate collator session keys using subkey (Docker)", + "working_directory": ".", + "commands": [ + "docker run --rm -it parity/subkey generate --scheme sr25519", + "docker run --rm -it parity/subkey generate --scheme ed25519" + ], + "description": "Run both commands. The first produces an SR25519 key (used for Aura block production); the second produces an ED25519 key (used for GRANDPA finality). Record both seed phrases and Account IDs securely. Never share the seed phrases. Save SR25519 Account ID as AURA_KEY and ED25519 Account ID as GRANDPA_KEY." + }, + { + "order": 4, + "action": "Build the plain chain spec", + "working_directory": "parachain-template", + "commands": [ + "chain-spec-builder create --relay-chain paseo --para-id INSERT_PARA_ID --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm named-preset development" + ], + "description": "Replace INSERT_PARA_ID with the value of PARA_ID from step 2 (e.g., 2000). This produces 'chain_spec.json'. Then open 'chain_spec.json' and make these edits: (1) Set the 'id' field to a unique string (e.g., 'my-parachain-paseo'). (2) Insert AURA_KEY and GRANDPA_KEY (from step 3) into the 'aura' and 'grandpa' genesis sections. (3) Confirm 'para_id' equals PARA_ID. Save the file." + }, + { + "order": 5, + "action": "Build the raw chain spec", + "working_directory": "parachain-template", + "commands": [ + "polkadot-omni-node build-spec --chain chain_spec.json --raw > chain_spec_raw.json" + ], + "description": "Converts the human-readable chain spec to SCALE-encoded raw format. Verify 'chain_spec_raw.json' was created and is non-empty.", + "expected_output": "chain_spec_raw.json created (non-empty JSON file)" + }, + { + "order": 6, + "action": "Export genesis wasm and state", + "working_directory": "parachain-template", + "commands": [ + "polkadot-omni-node export-genesis-wasm --chain chain_spec_raw.json genesis_wasm", + "polkadot-omni-node export-genesis-state --chain chain_spec_raw.json genesis_state" + ], + "description": "Produces 'genesis_wasm' (runtime Wasm bytecode) and 'genesis_state' (initial chain state). Both files are required for the on-chain registration in step 7." + }, + { + "order": 7, + "action": "Register the parathread on-chain", + "working_directory": ".", + "interactive": true, + "description": "Delegate to the user: In Polkadot.js Apps (connected to Paseo), navigate to Network > Parachains > Parathreads. Find your reserved PARA_ID and click 'Register'. In the modal, upload 'genesis_wasm' for 'Genesis WASM' and 'genesis_state' for 'Initial state'. Submit and sign. After finalization, the status changes from 'Reserved' to 'Onboarding'. Registration takes effect at the next epoch (up to 600 blocks on Paseo).", + "expected_output": "Parathread status changes to 'Onboarding' in the Parathreads tab" + }, + { + "order": 8, + "action": "Generate a P2P node key for the collator", + "working_directory": "parachain-template", + "commands": [ + "polkadot-omni-node generate-node-key --file node-key.dat" + ], + "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure — losing it changes the peer ID on next restart." + }, + { + "order": 9, + "action": "Start the collator node", + "working_directory": "parachain-template", + "commands": [ + "./target/release/parachain-template-node --collator --chain chain_spec_raw.json --node-key-file node-key.dat --port 30333 --rpc-port 9944 --rpc-methods=Unsafe -- --chain paseo --sync warp" + ], + "description": "The '--' separator splits collator arguments from the embedded relay chain (Paseo) arguments. '--sync warp' enables fast warp sync for Paseo. The collator first syncs the relay chain (several minutes). Wait for log messages containing 'Parachain synced' before inserting session keys in step 10." + }, + { + "order": 10, + "action": "Insert session keys into the collator keystore", + "working_directory": ".", + "commands": [ + "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"aura\",\"INSERT_AURA_SEED_PHRASE\",\"INSERT_AURA_PUBLIC_KEY_HEX\"],\"id\":1}'", + "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"gran\",\"INSERT_GRANDPA_SEED_PHRASE\",\"INSERT_GRANDPA_PUBLIC_KEY_HEX\"],\"id\":1}'" + ], + "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history — they contain seed phrases. Run them directly in the server terminal.", + "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1} for each key insertion" + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "chain-spec-builder: cannot find runtime Wasm file", + "cause": "The parachain was not built in release mode or the Wasm path is incorrect.", + "resolution": "Run 'cargo build --release' in the parachain-template directory. The Wasm artifact is at: target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm" + }, + { + "pattern": "Parathread registration fails: 'ParaAlreadyExists' or 'NotRegistrar'", + "cause": "The para ID was not properly reserved, or the registration account differs from the manager.", + "resolution": "Verify that PARA_ID was successfully reserved in step 2 using the correct account. Use the same account for the registration in step 7." + }, + { + "pattern": "Collator log: 'Relay chain does not contain our parachain'", + "cause": "Parathread registration has not finalized on-chain, or chain_spec_raw.json has the wrong para_id.", + "resolution": "Verify the Parathreads tab shows 'Onboarding' for PARA_ID. Confirm chain_spec_raw.json contains the correct para_id. Registration takes up to one epoch (600 blocks on Paseo)." + }, + { + "pattern": "author_insertKey: 'Method not found'", + "cause": "The collator was started without '--rpc-methods=Unsafe'.", + "resolution": "Stop the collator, add '--rpc-methods=Unsafe' to the startup command, and restart. Never expose unsafe RPC methods to external network interfaces." + } + ], + "supplementary_context": { + "description": "Load these pages for template setup, coretime, and local test network guidance.", + "pages": [ + { + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", + "relevance": "Required prerequisite: build the parachain template locally before deploying to TestNet." + }, + { + "slug": "parachains-launch-a-parachain-obtain-coretime", + "title": "Obtain Coretime", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/obtain-coretime.md", + "relevance": "Next step: obtain on-demand or bulk coretime to activate parachain block production." + }, + { + "slug": "parachains-testing-run-a-parachain-network", + "title": "Run a Parachain Network", + "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md", + "relevance": "Test the parachain locally with Zombienet before deploying to TestNet." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: first deployment to Paseo after building the template", + "user_says": "I built the parachain template — how do I deploy it to Paseo TestNet?", + "actions": [ + "Get PAS tokens from faucet", + "Reserve a para ID via Polkadot.js Apps", + "Generate Aura (SR25519) and GRANDPA (ED25519) collator keys with subkey Docker", + "Build plain chain spec, embed keys, build raw chain spec, export genesis data", + "Register parathread on-chain via Polkadot.js Apps", + "Generate node key, start collator, insert session keys via RPC" + ], + "result": "Parachain registered on Paseo TestNet with status 'Onboarding'; collator is running and syncing" + }, + { + "scenario": "Edge case: collator shows 0 peers after launch", + "user_says": "My collator started but shows 0 peers and is not syncing the relay chain", + "actions": [ + "Verify port 30333 is open on the server firewall", + "Confirm the startup command includes the '-- --chain paseo' relay chain section", + "Add a known Paseo bootnode with '--bootnodes /dns/...' to the startup flags", + "Wait 10 minutes for peer discovery — initial warp sync connection can be slow" + ], + "result": "Collator finds relay chain peers and begins syncing; block production starts after parathread onboards" + } + ] + }, + { + "id": "connect-polkadot-hub-testnet", + "title": "Connect to Polkadot Hub and Get Test Tokens", + "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/connect.md" + ], + "primary_page": "smart-contracts/connect.md", + "prerequisites": { + "wallet": [ + "MetaMask browser extension installed (https://metamask.io/download/)" + ], + "network": [ + "Internet access to Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", + "Internet access to the faucet: https://faucet.polkadot.io/" + ] + }, + "env_vars": [], + "steps": [ + { + "order": 1, + "action": "Add Polkadot Hub TestNet to MetaMask", + "working_directory": ".", + "description": "In MetaMask, open Settings > Networks > Add a network > Add a network manually. Enter these parameters: Network name: 'Polkadot Hub TestNet'; New RPC URL: 'https://services.polkadothub-rpc.com/testnet'; Chain ID: '420420417'; Currency symbol: 'PAS'. For the Block Explorer URL, check https://docs.polkadot.com/smart-contracts/connect/ for the current explorer URL, as it may change. Click Save and switch MetaMask to this network. This step must be delegated to the user." + }, + { + "order": 2, + "action": "Request PAS test tokens from the faucet", + "working_directory": ".", + "description": "Navigate to https://faucet.polkadot.io/. Select 'Polkadot Hub TestNet', paste your 0x-prefixed EVM address, and click the request button. The faucet credits PAS tokens within seconds. Rate limit: approximately 500 PAS per 24 hours per address. This step must be delegated to the user." + }, + { + "order": 3, + "action": "Verify MetaMask balance", + "working_directory": ".", + "description": "In MetaMask, confirm the PAS balance is non-zero on the Polkadot Hub TestNet network. To verify programmatically: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"YOUR_ADDRESS\",\"latest\"],\"id\":1}'. Replace YOUR_ADDRESS with the 0x-prefixed address. A non-zero hex result confirms the account is funded." + } + ], + "reference_code": { + "repo": "none", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "MetaMask: 'Invalid chain ID' or cannot connect to network", + "cause": "Incorrect chain ID or RPC URL entered in MetaMask network settings.", + "resolution": "Verify the chain ID is exactly 420420417 (decimal) and the RPC URL is https://services.polkadothub-rpc.com/testnet. Remove and re-add the network if the error persists." + }, + { + "pattern": "Faucet rate limit: 'Already requested recently'", + "cause": "The address already received tokens within the 24-hour rate limit window.", + "resolution": "Wait 24 hours, or use a different development wallet address." + }, + { + "pattern": "RPC endpoint returns errors or is unreachable", + "cause": "TestNet may be under maintenance, or the public RPC endpoint URL has changed.", + "resolution": "Check https://docs.polkadot.com/smart-contracts/connect/ for the latest endpoint URLs. The WSS alternative is wss://services.polkadothub-rpc.com/testnet." + } + ], + "supplementary_context": { + "description": "Load these pages for development framework configuration that uses the TestNet endpoints established here.", + "pages": [ + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Hardhat network configuration using the TestNet RPC URL and chain ID provided by this skill." + }, + { + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md", + "relevance": "Detailed faucet workflow for obtaining PAS test tokens." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: set up development environment from scratch", + "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?", + "actions": [ + "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", + "Request PAS tokens from https://faucet.polkadot.io/", + "Verify funded balance in MetaMask" + ], + "result": "MetaMask connected to Polkadot Hub TestNet with funded PAS balance, ready for smart contract deployment" + }, + { + "scenario": "Edge case: looking up MainNet connection parameters", + "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?", + "actions": [ + "Reference the MainNet network parameters from the source page", + "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" + ], + "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage" + } + ] + }, + { + "id": "store-retrieve-data-bulletin-chain", + "title": "Store and Retrieve Data on the Bulletin Chain", + "description": "Stores a file on the Polkadot Bulletin Chain using PAPI TypeScript and retrieves it by CID via the IPFS gateway. Use when you need decentralized IPFS-compatible file storage for dApp assets, NFT metadata, or files under 8 MiB. Requires a Polkadot account authorized via the Bulletin Chain Console UI faucet (no programmatic self-authorization). Trigger phrases: 'store file Bulletin Chain', 'PAPI TransactionStorage store', 'Polkadot decentralized storage', 'Bulletin Chain IPFS'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "chain-interactions/store-data/bulletin-chain.md" + ], + "primary_page": "chain-interactions/store-data/bulletin-chain.md", + "prerequisites": { + "runtime": [ + "Node.js v18 or later", + "npm" + ], + "network": [ + "Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" + ], + "tokens": [ + "Bulletin Chain storage authorization (not DOT/PAS) — obtained from Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/. The authorize_account extrinsic requires Root origin; self-authorization is not available programmatically." + ], + "wallet": [ + "A Polkadot account (SS58) with active Bulletin Chain authorization, plus its 12-word mnemonic for signing" + ] + }, + "env_vars": [ + { + "name": "MNEMONIC", + "description": "12-word mnemonic of the authorized Polkadot account. Do NOT ask user to paste mnemonic in chat — they must edit .env directly.", + "required": true + } + ], + "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── (generated Bulletin Chain descriptors)\n├── store-data.ts\n├── .env\n├── .gitignore\n├── node_modules/\n└── package.json", + "steps": [ + { + "order": 1, + "action": "Authorize account via Bulletin Chain Console UI", + "working_directory": ".", + "description": "This step requires manual browser interaction — delegate to the user.\n\n1. Open https://paritytech.github.io/polkadot-bulletin-chain/ and click Connect to link a wallet (Polkadot.js, Talisman, SubWallet, or Fearless).\n2. Go to Faucet > Storage Faucet tab.\n3. Under Authorize Account, enter the number of Transactions and Bytes needed, click Authorize Account, and approve in the wallet extension.\n4. Verify authorization on the Accounts tab — note the expiration block number.\n\nNote: Authorization expires at a block. Once expired, data cannot be renewed without re-authorizing." + }, + { + "order": 2, + "action": "Create and initialize the project", + "working_directory": ".", + "commands": [ + "mkdir bulletin-store-example", + "cd bulletin-store-example", + "npm init -y", + "npm pkg set type=module" + ], + "description": "Create a new Node.js ESM project. The ESM module type is required for polkadot-api and hdkd packages." + }, + { + "order": 3, + "action": "Install dependencies", + "working_directory": "bulletin-store-example", + "commands": [ + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv tsx" + ], + "description": "Install polkadot-api (typed chain client), hdkd and hdkd-helpers (key derivation), multiformats (CID decoding), dotenv (secure mnemonic loading), and tsx (TypeScript runner)." + }, + { + "order": 4, + "action": "Add Bulletin Chain metadata", + "working_directory": "bulletin-store-example", + "commands": [ + "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" + ], + "expected_output": "Descriptors generated successfully", + "description": "Connect to the Bulletin Chain RPC, download chain metadata, and generate typed descriptors in .papi/. These descriptors provide compile-time type safety for all pallet interactions." + }, + { + "order": 5, + "action": "Create .env with mnemonic placeholder", + "working_directory": "bulletin-store-example", + "commands": [ + "printf 'MNEMONIC=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty MNEMONIC placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in MNEMONIC with the 12-word mnemonic of the authorized account. Do NOT ask for the mnemonic in chat. Wait for user confirmation before proceeding." + }, + { + "order": 6, + "action": "Create store-data.ts", + "working_directory": "bulletin-store-example", + "reference_file": "store-data.ts", + "description": "Fetch the reference file and save as store-data.ts. Then apply these modifications:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Remove `DEV_PHRASE` from the `@polkadot-labs/hdkd-helpers` import (keep `entropyToMiniSecret` and `mnemonicToEntropy`).\n3. Change `mnemonicToEntropy(DEV_PHRASE)` to `mnemonicToEntropy(process.env.MNEMONIC as string)`.\n4. Replace `'INSERT_IMAGE_PATH'` with the path to the file the user wants to store (ask the user for this path before running).\nSave the file." + }, + { + "order": 7, + "action": "Run the store script", + "working_directory": "bulletin-store-example", + "commands": [ + "npx tsx store-data.ts" + ], + "expected_output": "Image stored successfully! CID: bafk2bzace...", + "description": "Run the script to submit the file to the Bulletin Chain. On success it prints the block hash, transaction index, CID, and an IPFS gateway URL. Save the CID and (block, index) pair — needed to retrieve or renew the stored data." + } + ], + "reference_code": { + "repo": "polkadot-developers/polkadot-docs", + "branch": "master", + "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", + "files": [ + { + "path": "store-data.ts", + "description": "PAPI TypeScript script that connects to Bulletin Chain, reads a local file, and submits it via TransactionStorage.store. Uses DEV_PHRASE by default — replace with process.env.MNEMONIC as instructed in step 6." + } + ] + }, + "error_patterns": [ + { + "pattern": "Error: account has no authorization", + "cause": "The signing account has no active authorization on the Bulletin Chain, or it has expired.", + "resolution": "Re-authorize via the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ before retrying." + }, + { + "pattern": "file too large / data exceeds per-transaction limit", + "cause": "File exceeds the ~8 MiB per-transaction size limit.", + "resolution": "Split into chunks under 8 MiB and submit multiple store transactions, recording (block, index) per chunk." + }, + { + "pattern": "MNEMONIC is undefined / Cannot derive key from undefined", + "cause": ".env not populated or dotenv not loaded.", + "resolution": "Verify .env contains MNEMONIC= and that `import 'dotenv/config';` is the first line of store-data.ts." + }, + { + "pattern": "WebSocket connection failed / ECONNREFUSED wss://paseo-bulletin-rpc.polkadot.io", + "cause": "Network issue or RPC temporarily unavailable.", + "resolution": "Check internet connectivity and retry after a few minutes. Monitor the Polkadot Discord for outage announcements." + } + ], + "supplementary_context": { + "description": "Load when the user asks about Bulletin Chain architecture, data retention, retrieval methods (P2P vs IPFS gateway), or the renewal workflow.", + "pages": [ + { + "slug": "reference-polkadot-hub-data-storage", + "title": "Data Storage", + "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage.md", + "relevance": "Technical reference for TransactionStorage pallet extrinsics, storage size limits, retention period, retrieval methods (IPFS gateway, P2P Helia), and renewal mechanics." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: store an image for an NFT collection", + "user_says": "Store my NFT artwork on the Polkadot Bulletin Chain and get its IPFS CID", + "actions": [ + "Authorize Polkadot account via Console UI faucet", + "Set up Node.js ESM project and install polkadot-api + hdkd + dotenv", + "Run npx papi add bulletin to generate chain descriptors", + "Create .env with MNEMONIC, modify store-data.ts to use process.env.MNEMONIC", + "Set FILE_PATH to the image path, run npx tsx store-data.ts", + "Receive CID for use in NFT metadata" + ], + "result": "File stored on-chain; CID like bafk2bzacea6wlxy... retrievable at https://paseo-ipfs.polkadot.io/ipfs/" + }, + { + "scenario": "Edge case: stored data approaches expiry", + "user_says": "My stored data is about to expire — how do I renew it?", + "actions": [ + "Retrieve the (block, index) pair from when the data was stored", + "Confirm expiration block has not yet passed (check Bulletin Chain Explorer)", + "Call TransactionStorage.renew({block, index}) using PAPI with the same authorized signer", + "Record new (block, index) from the Renewed event for future renewals" + ], + "result": "Retention timer reset; original CID remains valid for another full retention period" + } + ] + }, + { + "id": "deploy-uniswap-v2-core-pvm", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", + "description": "Clones the polkavm-hardhat-examples repo, compiles Uniswap V2 Factory and Pair contracts to the Polkadot Virtual Machine (PVM) using the Hardhat Polkadot plugin and resolc compiler, and deploys to Polkadot Hub TestNet. Use when deploying a DEX AMM factory on Polkadot Hub with PVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 PVM', 'UniswapV2Factory PVM Polkadot', 'deploy AMM Polkadot Hub PVM', 'polkavm-hardhat-examples uniswap'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", + "prerequisites": { + "runtime": [ + "Node.js v16 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account (AH_PRIV_KEY)" + ] + }, + "env_vars": [ + { + "name": "AH_PRIV_KEY", + "description": "0x-prefixed EVM private key for the account deploying to Polkadot Hub TestNet (passetHub network). Must be funded with testnet PAS. Do NOT ask user to paste key in chat — they must edit .env directly.", + "required": true + }, + { + "name": "LOCAL_PRIV_KEY", + "description": "0x-prefixed EVM private key for local testing. For local-only use, Alice's dev key (0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133) is the default in the config. Not required for TestNet deployment.", + "required": false + } + ], + "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", + "steps": [ + { + "order": 1, + "action": "Clone the polkavm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", + "cd polkavm-hardhat-examples/uniswap-v2-polkadot" + ], + "description": "Clone the repository and navigate to the Uniswap V2 project. The hardhat.config.js already uses dotenv (require('dotenv').config()) and process.env variables — no Hardhat vars conversion needed." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "npm install" + ], + "description": "Install all project dependencies including @parity/hardhat-polkadot and the resolc PVM compiler." + }, + { + "order": 3, + "action": "Create .env with private key placeholders", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env" + ], + "description": "Create .env with empty placeholders. The .gitignore already includes .env. Stop here and ask the user to edit .env directly — fill in AH_PRIV_KEY with the 0x-prefixed private key of the account that will deploy to Polkadot Hub TestNet. Do NOT ask for the key in chat. For LOCAL_PRIV_KEY, Alice's dev key is the default in hardhat.config.js for local testing. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Compile the contracts", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 1 Solidity file successfully", + "description": "Compile the Uniswap V2 contracts to PVM bytecode using the Hardhat Polkadot plugin and the resolc compiler. Compiled artifacts (ABI and PVM bytecode) appear in the artifacts/ directory." + }, + { + "order": 5, + "action": "Deploy to Polkadot Hub TestNet", + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", + "commands": [ + "npx hardhat run scripts/deploy.js --network passetHub" + ], + "expected_output": "Factory deployed to : 0x...", + "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet via the passetHub network config. The script outputs the deployed addresses — save them, especially the Factory address, which is needed for creating liquidity pairs.\n\nNote: The documentation shows `--network polkadotHubTestNet` but the actual network name in hardhat.config.js is `passetHub`." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account not funded with testnet PAS, or network base fee (1000 gwei on Polkadot Hub TestNet) exceeds the transaction gas price.", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. If gas issues persist, add gasPrice: 5000000000000 (5000 gwei) to the passetHub network block in hardhat.config.js." + }, + { + "pattern": "Error: AH_PRIV_KEY is undefined / invalid private key", + "cause": ".env file not populated or not loaded by dotenv.", + "resolution": "Verify .env exists in uniswap-v2-polkadot/ and contains AH_PRIV_KEY=0x. The config uses require('dotenv').config() automatically." + }, + { + "pattern": "Error: network passetHub not found / unknown network", + "cause": "Using wrong --network flag; documentation shows polkadotHubTestNet but the config defines passetHub.", + "resolution": "Use --network passetHub instead of --network polkadotHubTestNet." + }, + { + "pattern": "Compilation error: resolc not found / PVM compiler unavailable", + "cause": "The resolc compiler is not installed or not accessible.", + "resolution": "Run npm install to reinstall @parity/hardhat-polkadot and its bundled resolc compiler. Verify package.json includes the plugin." + } + ], + "supplementary_context": { + "description": "Load when the user asks about PVM vs EVM differences, local development setup, or testing the deployed contracts.", + "pages": [ + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local Polkadot development node and ETH-RPC adapter to test the Uniswap V2 PVM contracts locally before deploying to TestNet." + }, + { + "slug": "smart-contracts-dev-environments-hardhat-polkadot", + "title": "Set Up Hardhat with the Polkadot Plugin", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot.md", + "relevance": "Configure the @parity/hardhat-polkadot plugin and resolc compiler for PVM smart contract development." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using PVM", + "user_says": "Deploy Uniswap V2 on Polkadot Hub using PVM", + "actions": [ + "Clone polkavm-hardhat-examples and cd uniswap-v2-polkadot", + "Run npm install", + "Create .env with AH_PRIV_KEY for testnet deployment", + "Run npx hardhat compile to compile to PVM bytecode", + "Run npx hardhat run scripts/deploy.js --network passetHub", + "Save deployed Factory and Pair addresses" + ], + "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet with PVM bytecode" + }, + { + "scenario": "Edge case: deployment fails with insufficient funds", + "user_says": "My Uniswap V2 deployment is failing with insufficient funds", + "actions": [ + "Confirm account has testnet PAS at https://faucet.polkadot.io/", + "If gas price issue, add gasPrice: 5000000000000 to passetHub config in hardhat.config.js", + "Retry deployment with npx hardhat run scripts/deploy.js --network passetHub" + ], + "result": "Deployment succeeds after funding the account and optionally raising gas price" + } + ] + }, + { + "id": "deploy-uniswap-v2-core-evm", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Core contracts (Factory and Pair) with standard Hardhat and TypeScript, converts Hardhat vars to dotenv for agent-compatible key management, and deploys to Polkadot Hub TestNet via the EVM execution path. Use when deploying a DEX AMM factory on Polkadot Hub using standard EVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 EVM Polkadot', 'UniswapV2Factory EVM Polkadot Hub', 'deploy AMM Polkadot REVM'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", + "prerequisites": { + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "steps": [ + { + "order": 1, + "action": "Clone the revm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout b0a8627059a9d9cb759682310219557550186bc4", + "cd uniswap-v2-core-hardhat" + ], + "description": "Clone the repository, check out the pinned commit (tested configuration), and navigate to the Uniswap V2 Core project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies, then additionally install dotenv. dotenv is required to replace Hardhat's interactive vars system with a .env-based private key approach usable in agent shells." + }, + { + "order": 3, + "action": "Create .env with private key placeholder", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key for the testnet deployer account. Do NOT ask for the key in chat. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee) to prevent 'Priority is too low' errors.\n5. Confirm `ignition.requiredConfirmations` is 1 (not 0) — zero-confirmation only works on local dev nodes with instant finality.\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 3 Solidity files successfully", + "description": "Compile UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair (Solidity 0.5.16). Artifacts (ABI and EVM bytecode) appear in the artifacts/ directory." + }, + { + "order": 6, + "action": "Deploy to Polkadot Hub TestNet", + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", + "commands": [ + "npx hardhat run scripts/deploy.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV2Factory deployed to: 0x...", + "description": "Deploy UniswapV2Factory, two test ERC-20 tokens, and create a trading pair. The script outputs all deployed addresses. Save the Factory address — needed for creating additional pairs and for the Periphery (Router) deployment." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." + }, + { + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses vars.get() instead of dotenv.", + "resolution": "Ensure `import 'dotenv/config';` is the first line of hardhat.config.ts and replace vars.get('TESTNET_PRIVATE_KEY') with process.env.TESTNET_PRIVATE_KEY." + }, + { + "pattern": "TESTNET_PRIVATE_KEY is undefined", + "cause": ".env not populated or not in the correct directory.", + "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ and contains TESTNET_PRIVATE_KEY=0x." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of deployment — missing gasPrice or requiredConfirmations: 0.", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy." + } + ], + "supplementary_context": { + "description": "Load when the user asks about the V2 Periphery Router deployment, local testing, or Hardhat configuration for Polkadot.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", + "title": "Uniswap V2 Periphery with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "relevance": "Next step: deploy the Uniswap V2 Router (WETH9, Router02) on top of the deployed V2 Core." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local Polkadot development node to run the test suite against a local node before TestNet deployment." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using EVM", + "user_says": "Deploy Uniswap V2 on Polkadot Hub using EVM Hardhat", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", + "Run npx hardhat compile", + "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", + "Save Factory, TokenA, TokenB, and Pair addresses" + ], + "result": "UniswapV2Factory, two test ERC-20 tokens, and a trading pair deployed to Polkadot Hub TestNet via EVM" + }, + { + "scenario": "Edge case: deployment stuck with IGN401 Transaction dropped", + "user_says": "Ignition says my V2 Core deployment transaction was dropped", + "actions": [ + "Verify gasPrice: 5000000000000 is set in polkadotTestnet network config", + "Delete ignition/deployments/ directory", + "Confirm TESTNET_PRIVATE_KEY account still has PAS balance", + "Redeploy with npx hardhat run scripts/deploy.ts --network polkadotTestnet" + ], + "result": "Clean deployment after resolving gas configuration and clearing stale Ignition state" + } + ] + }, + { + "id": "deploy-uniswap-v2-periphery-evm", + "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Periphery contracts (WETH9, Router02) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when adding the Router layer (liquidity, swaps, slippage protection) on top of Uniswap V2 Core. V2 Core Solidity dependency is a local file reference — no pre-deployed core required. Trigger phrases: 'deploy Uniswap V2 Router EVM Polkadot', 'UniswapV2Router02 Polkadot Hub', 'Uniswap periphery Polkadot REVM'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "prerequisites": { + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "steps": [ + { + "order": 1, + "action": "Clone the revm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", + "cd uniswap-v2-periphery-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V2 Periphery project. The sibling directory uniswap-v2-core-hardhat/ (at the same commit) is automatically used for the local V2 Core Solidity dependency." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies including the local V2 Core reference (resolved automatically from ../uniswap-v2-core-hardhat/). Then install dotenv to replace Hardhat's interactive vars system." + }, + { + "order": 3, + "action": "Create .env with private key placeholder", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee).\n5. Confirm `ignition.requiredConfirmations` is 1.\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 5 Solidity files successfully", + "description": "Compile the Uniswap V2 Periphery contracts using a multi-compiler setup (Solidity 0.5.16 for V2 Core dependency and 0.6.6 for Router contracts). Artifacts appear in the artifacts/ directory." + }, + { + "order": 6, + "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", + "interactive": true, + "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 using Hardhat Ignition. Ignition will prompt to confirm the target network name and chain ID — delegate this confirmation to the user. After confirmation, contracts are deployed in two batches (Factory+WETH9 in parallel, then Router02). Save all three deployed addresses." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." + }, + { + "pattern": "Error: Cannot find module '@uniswap/v2-core' / ENOENT uniswap-v2-core-hardhat", + "cause": "The local V2 Core Solidity dependency (../../uniswap-v2-core-hardhat) is not accessible. Likely cloning only the periphery subdirectory or checking out different commits for each.", + "resolution": "Ensure the full revm-hardhat-examples repo is cloned and both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ are at the same commit. Run npm install from uniswap-v2-periphery-hardhat/." + }, + { + "pattern": "vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses Hardhat vars.get() instead of dotenv.", + "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost transaction state — missing gasPrice or requiredConfirmations: 0.", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1, then redeploy." + } + ], + "supplementary_context": { + "description": "Load when the user asks about the V2 Core prerequisite, testing the Router, or V3 upgrades.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", + "title": "Uniswap V2 Core with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", + "relevance": "V2 Core tutorial covering Factory and Pair deployment — the Periphery Router builds on top of the same Core contracts included as a local dependency." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local development node to run the test suite against localNode before TestNet deployment." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V2 Periphery Router to Polkadot Hub TestNet", + "user_says": "Deploy the Uniswap V2 Router on Polkadot Hub EVM", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy with --network polkadotTestnet, confirm when prompted", + "Save WETH9, Factory, and Router02 addresses" + ], + "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet" + }, + { + "scenario": "Edge case: npm install fails with missing V2 Core module", + "user_says": "npm install is failing because it cannot find @uniswap/v2-core", + "actions": [ + "Verify the full repo is cloned (both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ must exist)", + "Ensure git checkout used the same commit for the whole repo (a871364...)", + "Re-run npm install from uniswap-v2-periphery-hardhat/ — the local file reference resolves to ../uniswap-v2-core-hardhat/" + ], + "result": "npm install succeeds and @uniswap/v2-core is resolved from the sibling directory" + } + ] + }, + { + "id": "deploy-uniswap-v3-core-evm", + "title": "Deploy Uniswap V3 Core on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Core contracts (UniswapV3Factory) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. V3 config requires bytecodeHash set to none to keep Factory under the 24KB EIP-170 contract size limit. Use when deploying a concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 EVM Polkadot', 'UniswapV3Factory Polkadot Hub', 'concentrated liquidity Polkadot REVM'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "prerequisites": { + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── NoDelegateCall.sol\n │ ├── UniswapV3Factory.sol\n │ ├── UniswapV3Pool.sol\n │ └── UniswapV3PoolDeployer.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "steps": [ + { + "order": 1, + "action": "Clone the revm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", + "cd uniswap-v3-core-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Core project." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies then add dotenv to replace Hardhat's interactive vars system with .env-based private key management." + }, + { + "order": 3, + "action": "Create .env with private key placeholder", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei).\n5. Confirm `ignition.requiredConfirmations` is 1.\n6. Preserve the existing `bytecodeHash: 'none'` in the Solidity compiler settings — this is required to keep UniswapV3Factory under the EIP-170 24KB contract size limit.\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 5 Solidity files successfully", + "description": "Compile UniswapV3Factory, UniswapV3Pool, UniswapV3PoolDeployer, NoDelegateCall, and math libraries (Solidity 0.7.6). The bytecodeHash: 'none' setting in the Solidity config is critical — without it, UniswapV3Factory exceeds the EIP-170 24KB contract size limit." + }, + { + "order": 6, + "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", + "interactive": true, + "description": "Deploy UniswapV3Factory using Hardhat Ignition. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save the deployed Factory address — it is the entry point for creating V3 pools and is needed for the Periphery (SwapRouter, NonfungiblePositionManager) deployment." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: contract code too large / Contract creation code storage out of gas", + "cause": "bytecodeHash is not set to 'none', causing UniswapV3Factory to exceed the EIP-170 24KB limit.", + "resolution": "In hardhat.config.ts under the Solidity compiler settings, ensure metadata: { bytecodeHash: 'none' } is present. Recompile after the fix." + }, + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." + }, + { + "pattern": "vars is not defined / Cannot read properties of undefined", + "cause": "hardhat.config.ts still uses Hardhat vars.get().", + "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost transaction state.", + "resolution": "Delete ignition/deployments/, verify gas config (gasPrice: 5000000000000, requiredConfirmations: 1), and redeploy." + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 Periphery (SwapRouter, NFPM) deployment, local testing, or concentrated liquidity mechanics.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", + "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", + "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager on top of the deployed V3 Factory." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Set up a local development node to run the 187-test V3 suite before TestNet deployment." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy Uniswap V3 Core to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V3 on Polkadot Hub EVM", + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice, preserve bytecodeHash: 'none'", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy UniswapV3Factory.ts --network polkadotTestnet, confirm when prompted", + "Save deployed UniswapV3Factory address" + ], + "result": "UniswapV3Factory deployed to Polkadot Hub TestNet — entry point for creating V3 concentrated-liquidity pools" + }, + { + "scenario": "Edge case: compilation fails with contract too large", + "user_says": "Hardhat compilation fails saying UniswapV3Factory is too large", + "actions": [ + "Check hardhat.config.ts Solidity compiler settings for the existence of metadata: { bytecodeHash: 'none' }", + "If missing, add it under settings: { optimizer: {...}, metadata: { bytecodeHash: 'none' } }", + "Recompile with npx hardhat compile" + ], + "result": "Compilation succeeds after setting bytecodeHash: 'none' to exclude metadata hash and keep contract under the 24KB EIP-170 limit" + } + ] + }, + { + "id": "deploy-uniswap-v3-periphery-evm", + "title": "Deploy Uniswap V3 Periphery on Polkadot Hub (EVM)", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Periphery contracts (SwapRouter, NonfungiblePositionManager) with Hardhat, converts Hardhat vars to dotenv, and deploys all four contracts (UniswapV3Factory, WETH9, SwapRouter, NonfungiblePositionManager) to Polkadot Hub TestNet in a single Hardhat Ignition run. The V3 Core contracts are resolved automatically from a local sibling package reference. Use when building a full-stack concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Periphery Polkadot', 'SwapRouter NonfungiblePositionManager Polkadot Hub', 'Uniswap V3 full deployment EVM Polkadot'.", + "version": "1.0.0", + "chain_role": "isolated", + "invocation": "user", + "workflow_pattern": "sequential", + "license": "CC-BY-4.0", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", + "prerequisites": { + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "env_vars": [ + { + "name": "TESTNET_PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "required": true + } + ], + "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-periphery-hardhat/\n ├── contracts/\n │ ├── SwapRouter.sol\n │ ├── NonfungiblePositionManager.sol\n │ ├── NonfungibleTokenPositionDescriptor.sol\n │ ├── base/\n │ ├── interfaces/\n │ ├── lens/\n │ ├── libraries/\n │ └── test/\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Periphery.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n ├── package.json\n └── tsconfig.json", + "steps": [ + { + "order": 1, + "action": "Clone the revm-hardhat-examples repository", + "working_directory": ".", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 96696ad15c3cf01b9168a71ad5114f27c34a8726", + "cd uniswap-v3-periphery-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Periphery project. The monorepo layout is critical: the Periphery project depends on V3 Core contracts through a local file reference (`\"@uniswap/v3-core\": \"file:../uniswap-v3-core-hardhat\"`), so both sibling directories must be present." + }, + { + "order": 2, + "action": "Install dependencies", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies. `npm install` resolves the local `@uniswap/v3-core` sibling package automatically — no separate step is needed. Then add `dotenv` to replace Hardhat's interactive vars system with .env-based private key management." + }, + { + "order": 3, + "action": "Create .env with private key placeholder", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." + }, + { + "order": 4, + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from \"hardhat/config\";` to `import { HardhatUserConfig } from \"hardhat/config\";`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet base fee).\n5. Confirm `ignition.requiredConfirmations` is 1 (already set).\n6. Preserve `bytecodeHash: \"none\"` in the Solidity compiler settings — required so the compiled UniswapV3Pool bytecode hash matches the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol, enabling correct CREATE2 pool address derivation.\n7. Preserve `allowUnlimitedContractSize: true` for the hardhat network — several Periphery contracts exceed the 24KB EIP-170 limit and require this for local testing.\nSave the file." + }, + { + "order": 5, + "action": "Compile the contracts", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "npx hardhat compile" + ], + "expected_output": "Compiled 39 Solidity files successfully", + "description": "Compile SwapRouter, NonfungiblePositionManager, and all supporting periphery contracts (Solidity 0.7.6). The `bytecodeHash: 'none'` setting ensures the Pool bytecode hash matches PoolAddress.sol's hardcoded constant, which is required for CREATE2 pool address computation to work correctly during swaps and LP operations." + }, + { + "order": 6, + "action": "Deploy all contracts to Polkadot Hub TestNet via Hardhat Ignition", + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" + ], + "expected_output": "UniswapV3PeripheryModule#UniswapV3Factory deployed at 0x...\nUniswapV3PeripheryModule#WETH9 deployed at 0x...\nUniswapV3PeripheryModule#SwapRouter deployed at 0x...\nUniswapV3PeripheryModule#NonfungiblePositionManager deployed at 0x...", + "interactive": true, + "description": "Deploy all four contracts using Hardhat Ignition. The module deploys UniswapV3Factory and WETH9 in the first batch (in parallel), then SwapRouter and NonfungiblePositionManager once their dependencies are ready. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save all four deployed addresses: they are needed for any downstream swap or LP interaction." + } + ], + "reference_code": { + "repo": "none", + "branch": "", + "base_path": "", + "files": [] + }, + "error_patterns": [ + { + "pattern": "Error: insufficient funds / Priority is too low", + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." + }, + { + "pattern": "vars is not defined / Cannot read properties of undefined (reading 'has')", + "cause": "hardhat.config.ts still uses Hardhat vars.get() or vars.has().", + "resolution": "Add `import 'dotenv/config';` as the first line and replace `vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`." + }, + { + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "cause": "Ignition lost track of a pending transaction due to TestNet latency or gas underpricing.", + "resolution": "Delete `ignition/deployments/`, verify gasPrice is 5000000000000 and requiredConfirmations is 1, then redeploy. If the deployment receipt exists in deployed_addresses.json but code is missing at the address, wait and verify via eth_getCode before retrying." + }, + { + "pattern": "POOL_INIT_CODE_HASH mismatch / pool address computation incorrect", + "cause": "bytecodeHash is not set to 'none', causing the compiled Pool bytecode to differ from the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol.", + "resolution": "In hardhat.config.ts under Solidity compiler settings, ensure `metadata: { bytecodeHash: 'none' }` is present. Recompile after the fix." + }, + { + "pattern": "Error: cannot estimate gas / contract deployment failed during testing", + "cause": "The hardhat network is missing allowUnlimitedContractSize: true, causing Periphery contracts that exceed 24KB to fail during local testing.", + "resolution": "In hardhat.config.ts, ensure `networks.hardhat.allowUnlimitedContractSize = true` is set. This flag only applies to the in-process Hardhat network used for testing; it is not needed for TestNet deployment." + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 Core as a prerequisite, testing against a local development node, or the Hardhat environment setup.", + "pages": [ + { + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", + "title": "Uniswap V3 Core with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "relevance": "The V3 Core contracts (UniswapV3Factory, UniswapV3Pool) are resolved automatically via local npm reference — no separate Core tutorial step required, but this page explains the Factory/Pool architecture the Periphery builds on." + }, + { + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", + "relevance": "Required for running the 39-test suite (SwapRouter and NonfungiblePositionManager tests) against a local Polkadot node via `npx hardhat test --network localNode` before TestNet deployment." + }, + { + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", + "relevance": "Reference for Hardhat network configuration, Ignition deployment options, and gas settings specific to Polkadot Hub." + } + ] + }, + "examples": [ + { + "scenario": "Common scenario: deploy all Uniswap V3 Periphery contracts to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V3 SwapRouter and NonfungiblePositionManager to Polkadot Hub", + "actions": [ + "Clone revm-hardhat-examples, check out pinned commit, cd uniswap-v3-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY placeholder, ask user to fill it in", + "Convert hardhat.config.ts: add dotenv import, replace vars.get with process.env, add gasPrice: 5000000000000", + "Run npx hardhat compile (expects 39 Solidity files compiled successfully)", + "Run npx hardhat ignition deploy UniswapV3Periphery.ts --network polkadotTestnet, confirm when prompted", + "Save deployed addresses for UniswapV3Factory, WETH9, SwapRouter, and NonfungiblePositionManager" + ], + "result": "All four Uniswap V3 Periphery contracts deployed to Polkadot Hub TestNet — SwapRouter for token swaps and NonfungiblePositionManager for concentrated liquidity LP positions" + }, + { + "scenario": "Edge case: deployment fails with IGN401 or Transaction Already Imported", + "user_says": "Ignition reports the transaction was dropped and retrying gives 'Transaction Already Imported'", + "actions": [ + "Do not retry at the same gas price — that will fail again with the same error", + "Check ignition/deployments/ and deployed_addresses.json for any partial state", + "Verify the contract is not already deployed: run eth_getCode at any addresses in deployed_addresses.json", + "If not deployed, delete the ignition/deployments/ directory", + "Confirm gasPrice: 5000000000000 and requiredConfirmations: 1 are set in hardhat.config.ts", + "Re-run: npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" + ], + "result": "Deployment completes cleanly after removing stale Ignition state and confirming gas configuration" + } + ] + } + ], + "outputs": { + "public_root": "/ai/", + "skills_dir": "skills" } - ], - "outputs": { - "public_root": "/ai/", - "skills_dir": "skills" - } } diff --git a/skill_coverage.json b/skill_coverage.json index d8e044598..d93342dc2 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,10 +1,10 @@ { "schema_version": "1", - "generated": "2026-05-19T08:28:13Z", + "generated": "2026-05-19T12:00:00Z", "summary": { "total_candidates": 142, - "up_to_date": 0, - "stale": 62, + "up_to_date": 7, + "stale": 55, "uncovered": 0, "blocked": 23, "not_applicable": 12, @@ -13,59 +13,59 @@ "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "create-polkadot-account" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/accounts/query-accounts.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "query-account-info-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/query-data/query-rest.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "query-chain-data-sidecar-rest" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/query-data/query-sdks.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "query-chain-state-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/query-data/runtime-api-calls.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "call-runtime-apis-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "calculate-transaction-fees-papi" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { "last_edited": "2026-05-19T08:20:09+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "replay-dry-run-xcm-chopsticks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { "last_edited": "2026-05-19T08:20:09+00:00", From 7f59a7e00609ba4446684b53e0513be8566398aa Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 11:46:33 +0000 Subject: [PATCH 17/26] chore: refresh skill coverage and alerts --- ref_repo_change_alerts.json | 2 +- repo_state.json | 2 +- skill_coverage.json | 286 ++++++++++++++++++------------------ 3 files changed, 145 insertions(+), 145 deletions(-) diff --git a/ref_repo_change_alerts.json b/ref_repo_change_alerts.json index ee985ddcd..fe53194bc 100644 --- a/ref_repo_change_alerts.json +++ b/ref_repo_change_alerts.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated": "2026-05-19T08:28:13Z", + "generated": "2026-05-19T11:46:33Z", "summary": { "reference_changes": 0, "skills_affected": 0 diff --git a/repo_state.json b/repo_state.json index f627fdb3b..f70b13ae2 100644 --- a/repo_state.json +++ b/repo_state.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "last_checked": "2026-05-19T08:28:13Z", + "last_checked": "2026-05-19T11:46:33Z", "docs_repo": { "branch": "master", "last_checked_commit": "cc6c24ebf10e1e4e620ad152ae9af9e3d19aa6ae" diff --git a/skill_coverage.json b/skill_coverage.json index d93342dc2..41e2e5144 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated": "2026-05-19T12:00:00Z", + "generated": "2026-05-19T11:46:33Z", "summary": { "total_candidates": 142, "up_to_date": 7, @@ -12,7 +12,7 @@ }, "pages": { "chain-interactions/accounts/create-account.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "create-polkadot-account" @@ -20,7 +20,7 @@ "status": "up_to_date" }, "chain-interactions/accounts/query-accounts.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "query-account-info-sdks" @@ -28,7 +28,7 @@ "status": "up_to_date" }, "chain-interactions/query-data/query-rest.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "query-chain-data-sidecar-rest" @@ -36,7 +36,7 @@ "status": "up_to_date" }, "chain-interactions/query-data/query-sdks.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "query-chain-state-sdks" @@ -44,7 +44,7 @@ "status": "up_to_date" }, "chain-interactions/query-data/runtime-api-calls.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "call-runtime-apis-sdks" @@ -52,7 +52,7 @@ "status": "up_to_date" }, "chain-interactions/send-transactions/calculate-transaction-fees.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "calculate-transaction-fees-papi" @@ -60,7 +60,7 @@ "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "replay-dry-run-xcm-chopsticks" @@ -68,7 +68,7 @@ "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "estimate-xcm-fees-teleport" @@ -76,13 +76,13 @@ "status": "stale" }, "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "transfer-assets-parachains-paraspell" @@ -90,7 +90,7 @@ "status": "stale" }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "pay-fees-alternative-token" @@ -98,7 +98,7 @@ "status": "stale" }, "chain-interactions/send-transactions/with-sdks.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "send-transactions-sdks" @@ -106,7 +106,7 @@ "status": "stale" }, "chain-interactions/store-data/bulletin-chain.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "store-retrieve-data-bulletin-chain" @@ -114,37 +114,37 @@ "status": "stale" }, "chain-interactions/token-operations/convert-assets.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "chain-interactions/token-operations/register-foreign-asset.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "chain-interactions/token-operations/register-local-asset.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "get-support.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "node-infrastructure/run-a-collator.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T19:00:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-node/parachain-rpc.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "run-parachain-rpc-node" @@ -152,7 +152,7 @@ "status": "stale" }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "run-polkadot-hub-rpc-node" @@ -160,31 +160,31 @@ "status": "stale" }, "node-infrastructure/run-a-node/relay-chain/bootnode.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-node/relay-chain/full-node.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:00:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-node/relay-chain/secure-wss.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-polkadot-validator-node" @@ -192,43 +192,43 @@ "status": "stale" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "node-infrastructure/run-a-validator/operational-tasks/general-management.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T20:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "node-infrastructure/run-a-validator/operational-tasks/upgrade-your-node.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "blocked" }, "node-infrastructure/run-a-validator/requirements.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "onboard-polkadot-validator" @@ -236,19 +236,19 @@ "status": "stale" }, "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "node-infrastructure/run-a-validator/staking-mechanics/rewards.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "parachains/customize-runtime/add-existing-pallets.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "add-existing-pallet-to-runtime" @@ -256,7 +256,7 @@ "status": "stale" }, "parachains/customize-runtime/add-pallet-instances.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "configure-multiple-pallet-instances" @@ -264,13 +264,13 @@ "status": "stale" }, "parachains/customize-runtime/add-smart-contract-functionality.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "benchmark-frame-pallet" @@ -278,7 +278,7 @@ "status": "stale" }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "create-frame-pallet" @@ -286,7 +286,7 @@ "status": "stale" }, "parachains/customize-runtime/pallet-development/mock-runtime.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "set-up-pallet-mock-runtime" @@ -294,7 +294,7 @@ "status": "stale" }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "unit-test-frame-pallet" @@ -302,13 +302,13 @@ "status": "stale" }, "parachains/get-started.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "parachains/install-polkadot-sdk.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "install-polkadot-sdk" @@ -316,43 +316,43 @@ "status": "stale" }, "parachains/integrations/indexers.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "parachains/integrations/oracles.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "parachains/integrations/wallets.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "parachains/interoperability/channels-between-parachains.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "parachains/interoperability/channels-with-system-parachains.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "parachains/interoperability/get-started.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-parachain-to-polkadot-testnet" @@ -360,13 +360,13 @@ "status": "stale" }, "parachains/launch-a-parachain/obtain-coretime.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:30:00Z", "skills": [], "status": "blocked" }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-parachain-template" @@ -374,103 +374,103 @@ "status": "stale" }, "parachains/runtime-maintenance/coretime-renewal.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "parachains/runtime-maintenance/runtime-upgrades.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "parachains/runtime-maintenance/storage-migrations.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "parachains/runtime-maintenance/unlock-parachains.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "parachains/testing/fork-a-parachain.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T19:30:00Z", "skills": [], "status": "blocked" }, "parachains/testing/run-a-parachain-network.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "policies/ai-chatbot-policy.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "policies/cookie-policy.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "policies/privacy-policy.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "policies/terms-of-use.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "reference/glossary.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/governance/origins-tracks.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/accounts.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/blocks-transactions-fees/blocks.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/blocks-transactions-fees/fees.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/blocks-transactions-fees/transactions.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/chain-data.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "retrieve-runtime-metadata" @@ -478,115 +478,115 @@ "status": "stale" }, "reference/parachains/consensus/async-backing.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/consensus/elastic-scaling.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/consensus/inclusion-pipeline.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/cryptography.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/data-encoding.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/interoperability.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/networks.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/node-and-runtime.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/parachains/randomness.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/assets.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/bridging.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/collectives-and-daos.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/consensus-and-security/agile-coretime.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/consensus-and-security/pos-consensus.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/consensus-and-security/relay-chain.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/data-storage.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/people-and-identity.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/polkadot-hub/smart-contracts.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/tools/chopsticks.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-chopsticks-fork" @@ -594,7 +594,7 @@ "status": "stale" }, "reference/tools/dedot.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "interact-with-chain-dedot" @@ -602,13 +602,13 @@ "status": "stale" }, "reference/tools/light-clients.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/tools/moonwall.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-e2e-testing-moonwall" @@ -616,7 +616,7 @@ "status": "stale" }, "reference/tools/omninode.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "run-parachain-node-omni-node" @@ -624,25 +624,25 @@ "status": "stale" }, "reference/tools/papi.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/tools/paraspell.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/tools/polkadart.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T21:00:00Z", "skills": [], "status": "blocked" }, "reference/tools/polkadot-js-api.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "use-polkadot-js-api" @@ -650,13 +650,13 @@ "status": "stale" }, "reference/tools/pop-cli.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T22:30:00Z", "skills": [], "status": "blocked" }, "reference/tools/py-substrate-interface.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "interact-polkadot-node-py-substrate" @@ -664,13 +664,13 @@ "status": "stale" }, "reference/tools/sidecar.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "reference/tools/subxt.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "interact-polkadot-node-subxt" @@ -678,13 +678,13 @@ "status": "stale" }, "reference/tools/xcm-tools.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "reference/tools/zombienet.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "spawn-test-network-zombienet" @@ -692,7 +692,7 @@ "status": "stale" }, "smart-contracts/connect.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "connect-polkadot-hub-testnet" @@ -700,7 +700,7 @@ "status": "stale" }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "build-dapp-viem-nextjs" @@ -708,7 +708,7 @@ "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-core-pvm" @@ -716,7 +716,7 @@ "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-core-evm" @@ -724,7 +724,7 @@ "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v2-periphery-evm" @@ -732,7 +732,7 @@ "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:30:00Z", "skills": [ "deploy-uniswap-v3-core-evm" @@ -740,7 +740,7 @@ "status": "stale" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T16:00:00Z", "skills": [ "deploy-uniswap-v3-periphery-evm" @@ -748,7 +748,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-basic-contract-hardhat" @@ -756,7 +756,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-basic-contract-remix" @@ -764,7 +764,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-erc20-token-hardhat" @@ -772,7 +772,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-erc20-token-remix" @@ -780,7 +780,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-erc721-nft-hardhat" @@ -788,7 +788,7 @@ "status": "stale" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-erc721-nft-remix" @@ -796,7 +796,7 @@ "status": "stale" }, "smart-contracts/dev-environments/foundry.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-foundry-polkadot-hub" @@ -804,7 +804,7 @@ "status": "stale" }, "smart-contracts/dev-environments/hardhat-polkadot.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-hardhat-pvm" @@ -812,7 +812,7 @@ "status": "stale" }, "smart-contracts/dev-environments/hardhat.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-hardhat-evm" @@ -820,7 +820,7 @@ "status": "stale" }, "smart-contracts/dev-environments/local-dev-node.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "set-up-local-dev-node" @@ -828,7 +828,7 @@ "status": "stale" }, "smart-contracts/dev-environments/remix.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "connect-remix-polkadot" @@ -836,67 +836,67 @@ "status": "stale" }, "smart-contracts/explorers.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/faucet.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-04-21T23:59:00Z", "skills": [], "status": "blocked" }, "smart-contracts/for-eth-devs/accounts.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/for-eth-devs/blocks-transactions-fees.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/for-eth-devs/contract-deployment.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/for-eth-devs/dual-vm-stack.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/for-eth-devs/evm-vs-pvm.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/for-eth-devs/gas-model.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/for-eth-devs/json-rpc-apis.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/get-started.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "smart-contracts/integrations/wallets.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "connect-wallet-polkadot-hub" @@ -904,7 +904,7 @@ "status": "stale" }, "smart-contracts/libraries/ethers-js.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-contracts-ethers-js" @@ -912,7 +912,7 @@ "status": "stale" }, "smart-contracts/libraries/viem.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-contracts-viem" @@ -920,7 +920,7 @@ "status": "stale" }, "smart-contracts/libraries/wagmi.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "use-wagmi-polkadot-hub" @@ -928,7 +928,7 @@ "status": "stale" }, "smart-contracts/libraries/web3-js.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": "2026-05-15T15:00:00Z", "skills": [ "deploy-interact-contracts-web3js" @@ -936,7 +936,7 @@ "status": "stale" }, "smart-contracts/libraries/web3-py.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "deploy-interact-contracts-web3py" @@ -944,13 +944,13 @@ "status": "stale" }, "smart-contracts/overview.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "not_applicable" }, "smart-contracts/precompiles/erc20.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "interact-erc20-precompile-polkadot-hub" @@ -958,13 +958,13 @@ "status": "stale" }, "smart-contracts/precompiles/eth-native.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [], "status": "supplementary" }, "smart-contracts/precompiles/storage.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "interact-storage-precompile-remix" @@ -972,7 +972,7 @@ "status": "stale" }, "smart-contracts/precompiles/system.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "interact-system-precompile-remix" @@ -980,7 +980,7 @@ "status": "stale" }, "smart-contracts/precompiles/xcm.md": { - "last_edited": "2026-05-19T08:20:09+00:00", + "last_edited": "2026-05-19T08:40:20+00:00", "last_scanned": null, "skills": [ "interact-xcm-precompile-remix" From 877962f31a2d51c0acf52a04f7f6dba330ddcd01 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 11:55:46 +0000 Subject: [PATCH 18/26] chore: auto-generate/update skills --- agent_skills_config.json | 19669 +++++++++++++++++++------------------ skill_candidates.json | 4652 ++++----- skill_coverage.json | 34 +- 3 files changed, 12197 insertions(+), 12158 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index b76763d07..7592f4b2b 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,9822 +1,9861 @@ { - "schema_version": "0.1", - "generated": "2026-05-19T12:00:00Z", - "content_hash": "sha256:3449894b97efb0b7d50d8921cfa164c68a51a6adb9a3f5766f7e69f50772d673", - "skills": [ - { - "id": "create-polkadot-account", - "title": "Create a Polkadot Account Programmatically", - "description": "Generates a new Polkadot SR25519 account (mnemonic, public key, SS58 address) using @polkadot/util-crypto and @polkadot/keyring. Use when you need to create a wallet, automate key generation, or produce a signing account for testing. Supports TypeScript (primary), Python (substrate-interface), and Rust (sp-core). Trigger phrases: 'create a Polkadot account', 'generate wallet keypair', 'new account', 'key generation'. Output: console-printed 12-word BIP39 mnemonic and SS58 address. No network connection required.", - "version": "1.0.2", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/accounts/create-account.md" - ], - "primary_page": "chain-interactions/accounts/create-account.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir account-creator && cd account-creator", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required — the @polkadot packages use ESM imports." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "account-creator", - "commands": [ - "npm install @polkadot/util-crypto @polkadot/keyring", - "npm install --save-dev typescript tsx" - ], - "description": "Install @polkadot/util-crypto (WASM-backed mnemonic generation and cryptoWaitReady), @polkadot/keyring (SR25519 keyring), and tsx (TypeScript executor)." - }, - { - "order": 3, - "action": "Fetch the account creation script", - "working_directory": "account-creator", - "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed — the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", - "reference_file": "create-account.ts" - }, - { - "order": 4, - "action": "Run the account creation script", - "working_directory": "account-creator", - "commands": [ - "npx tsx create-account.ts" - ], - "expected_output": "Address: \nMnemonic: <12-word BIP39 phrase>", - "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both — the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/accounts/create-account", - "files": [ - { - "path": "create-account.ts", - "description": "Generates a new SR25519 keypair using mnemonicGenerate(); prints SS58 address and mnemonic" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Unable to initialize the WASM interface", - "cause": "cryptoWaitReady() was not awaited before keyring operations.", - "resolution": "Ensure cryptoWaitReady() is called and awaited at the start of main(). The reference file already does this; verify the file was saved correctly." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "The project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the account-creator directory to enable ES modules." - }, - { - "pattern": "Error: Cannot find module '@polkadot/util-crypto'", - "cause": "Dependencies not installed.", - "resolution": "Run 'npm install @polkadot/util-crypto @polkadot/keyring' in the account-creator directory." - } - ], - "supplementary_context": { - "description": "Load these pages when the user asks about next steps after account creation or about account concepts.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to use the newly created account to sign and submit transactions on Polkadot Hub." - }, - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "How to query the balance and account state of the newly created account." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: generate a fresh wallet", - "user_says": "Create a new Polkadot account for me", - "actions": [ - "Scaffold 'account-creator/' as an ESM Node.js project", - "Install @polkadot/util-crypto and @polkadot/keyring", - "Fetch and save create-account.ts", - "Run npx tsx create-account.ts" - ], - "result": "A fresh SR25519 mnemonic phrase and SS58 address printed to console" - }, - { - "scenario": "Edge case: user wants Python instead of TypeScript", - "user_says": "Generate a Polkadot account using Python", - "actions": [ - "Create a Python virtual environment in 'account-creator-python/'", - "Install substrate-interface via pip", - "Create create_account.py using generate_mnemonic() and Keypair.create_from_mnemonic()", - "Run python create_account.py" - ], - "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console" - } - ], - "project_structure": "account-creator/\n├── create-account.ts\n└── package.json" - }, - { - "id": "query-account-info-sdks", + "content_hash": "sha256:ec23f14bd159940274d4ec4472e015cdf778b24d8cf13b09564282a1aff7d504", + "generated": "2026-05-19T12:00:00Z", + "outputs": { + "public_root": "/ai/", + "skills_dir": "skills" + }, + "schema_version": "0.1", + "skills": [ + { + "chain_role": "isolated", + "description": "Generates a new Polkadot SR25519 account (mnemonic, public key, SS58 address) using @polkadot/util-crypto and @polkadot/keyring. Use when you need to create a wallet, automate key generation, or produce a signing account for testing. Supports TypeScript (primary), Python (substrate-interface), and Rust (sp-core). Trigger phrases: 'create a Polkadot account', 'generate wallet keypair', 'new account', 'key generation'. Output: console-printed 12-word BIP39 mnemonic and SS58 address. No network connection required.", + "env_vars": [], + "error_patterns": [ + { + "cause": "cryptoWaitReady() was not awaited before keyring operations.", + "pattern": "Error: Unable to initialize the WASM interface", + "resolution": "Ensure cryptoWaitReady() is called and awaited at the start of main(). The reference file already does this; verify the file was saved correctly." + }, + { + "cause": "The project is not configured as ESM.", + "pattern": "SyntaxError: Cannot use import statement in a module", + "resolution": "Run 'npm pkg set type=module' in the account-creator directory to enable ES modules." + }, + { + "cause": "Dependencies not installed.", + "pattern": "Error: Cannot find module '@polkadot/util-crypto'", + "resolution": "Run 'npm install @polkadot/util-crypto @polkadot/keyring' in the account-creator directory." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'account-creator/' as an ESM Node.js project", + "Install @polkadot/util-crypto and @polkadot/keyring", + "Fetch and save create-account.ts", + "Run npx tsx create-account.ts" + ], + "result": "A fresh SR25519 mnemonic phrase and SS58 address printed to console", + "scenario": "Common scenario: generate a fresh wallet", + "user_says": "Create a new Polkadot account for me" + }, + { + "actions": [ + "Create a Python virtual environment in 'account-creator-python/'", + "Install substrate-interface via pip", + "Create create_account.py using generate_mnemonic() and Keypair.create_from_mnemonic()", + "Run python create_account.py" + ], + "result": "SR25519 keypair generated; mnemonic and SS58 address printed to console", + "scenario": "Edge case: user wants Python instead of TypeScript", + "user_says": "Generate a Polkadot account using Python" + } + ], + "id": "create-polkadot-account", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "primary_page": "chain-interactions/accounts/create-account.md", + "project_structure": "account-creator/\n├── create-account.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/accounts/create-account", + "branch": "master", + "files": [ + { + "description": "Generates a new SR25519 keypair using mnemonicGenerate(); prints SS58 address and mnemonic", + "path": "create-account.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/accounts/create-account.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir account-creator && cd account-creator", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory named 'account-creator' and initialize it as an ESM Node.js project. The 'type=module' flag is required — the @polkadot packages use ESM imports.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install @polkadot/util-crypto @polkadot/keyring", + "npm install --save-dev typescript tsx" + ], + "description": "Install @polkadot/util-crypto (WASM-backed mnemonic generation and cryptoWaitReady), @polkadot/keyring (SR25519 keyring), and tsx (TypeScript executor).", + "order": 2, + "working_directory": "account-creator" + }, + { + "action": "Fetch the account creation script", + "description": "Fetch the reference file and save it as `create-account.ts`. No substitutions needed — the script generates a fresh random account on each execution. `cryptoWaitReady()` initializes the @polkadot/util-crypto WASM module; `mnemonicGenerate(12)` produces a 12-word BIP39 phrase; the keyring derives an SR25519 account at the standard Polkadot SS58 prefix (0).", + "order": 3, + "reference_file": "create-account.ts", + "working_directory": "account-creator" + }, + { + "action": "Run the account creation script", + "commands": [ + "npx tsx create-account.ts" + ], + "description": "Execute the script. It prints a new SS58 address and 12-word mnemonic to the console. Record both — the mnemonic is required to sign transactions with this account and cannot be recovered if lost. Never share the mnemonic.", + "expected_output": "Address: \nMnemonic: <12-word BIP39 phrase>", + "order": 4, + "working_directory": "account-creator" + } + ], + "supplementary_context": { + "description": "Load these pages when the user asks about next steps after account creation or about account concepts.", + "pages": [ + { + "relevance": "How to use the newly created account to sign and submit transactions on Polkadot Hub.", + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + }, + { + "relevance": "How to query the balance and account state of the newly created account.", + "slug": "chain-interactions-accounts-query-accounts", "title": "Query Account Information with SDKs", - "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", - "version": "1.0.2", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/accounts/query-accounts.md" - ], - "primary_page": "chain-interactions/accounts/query-accounts.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub or compatible chain (e.g., wss://asset-hub-paseo.dotters.network for TestNet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-query-account-example && cd papi-query-account-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-query-account-example", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors", - "working_directory": "papi-query-account-example", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly — note the name you choose, as it must match the import in query-account.ts." - }, - { - "order": 4, - "action": "Fetch the query script", - "working_directory": "papi-query-account-example", - "reference_file": "papi/query-account.ts", - "description": "Fetch the reference file and save it as 'query-account.ts'. Then make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ACCOUNT_ADDRESS' with the SS58 address you want to query. Save the changes before running." - }, - { - "order": 5, - "action": "Run the query script", - "working_directory": "papi-query-account-example", - "commands": [ - "npx tsx query-account.ts" - ], - "expected_output": "Account Information with Nonce, Consumers, Providers, Free Balance, Reserved Balance, Frozen Balance, and Total Balance", - "description": "Execute the script. Output will display account nonce, balance breakdown (free/reserved/frozen), and total balance in raw PAS units and human-readable format (divide raw value by 10^10 for PAS)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/accounts/query-account", - "files": [ - { - "path": "papi/query-account.ts", - "description": "PAPI TypeScript script that queries System.Account storage and prints balance details" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED", - "cause": "Incorrect or unreachable WebSocket endpoint URL.", - "resolution": "Verify the WebSocket URL in query-account.ts is correct and the endpoint is accessible. For TestNet, use 'wss://asset-hub-paseo.dotters.network'." - }, - { - "pattern": "Account not found / undefined accountInfo", - "cause": "The queried address has never been funded and has no on-chain entry.", - "resolution": "The account exists only once it receives tokens. Query a funded address, or fund the address using the Polkadot faucet at https://faucet.polkadot.io/ first." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs background on account data fields or wants to send transactions from the queried account.", - "pages": [ - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI API reference for understanding query methods and typed API usage." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to send a transaction from the account after verifying its balance." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: check balance of an address", - "user_says": "Query the balance of this Polkadot address: 1abc...", - "actions": [ - "Scaffold 'papi-query-account-example/' ESM project", - "Install polkadot-api and tsx", - "Generate descriptors with npx papi add polkadotTestNet", - "Fetch query-account.ts; substitute WS endpoint and account address", - "Run npx tsx query-account.ts" - ], - "result": "Free/reserved/frozen balance and nonce printed for the specified address" - }, - { - "scenario": "Edge case: address not found (zero-balance unfunded account)", - "user_says": "Query the account but it shows no data", - "actions": [ - "Explain that Polkadot Hub only stores accounts that have been funded (existential deposit met)", - "Direct user to https://faucet.polkadot.io/ to fund the account with testnet PAS tokens", - "Re-run query-account.ts after account is funded" - ], - "result": "After funding, account data is returned correctly" - } - ], - "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json" - }, - { - "id": "query-chain-state-sdks", - "title": "Query On-Chain State with SDKs", - "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", - "version": "1.0.2", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/query-data/query-sdks.md" - ], - "primary_page": "chain-interactions/query-data/query-sdks.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://polkadot-asset-hub-rpc.polkadot.io for mainnet or wss://asset-hub-paseo.dotters.network for testnet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-query-example && cd papi-query-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-query-example' and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-query-example", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors for Polkadot Hub", - "working_directory": "papi-query-example", - "commands": [ - "npx papi add pah -n polkadot_asset_hub" - ], - "description": "Generate compile-time type descriptors using the well-known 'polkadot_asset_hub' chain name. This produces the 'pah' export in @polkadot-api/descriptors. For a different network, use the appropriate -n value from the PAPI well-known chains list, or use -w with a WebSocket URL." - }, - { - "order": 4, - "action": "Fetch and configure the balance query script", - "working_directory": "papi-query-example", - "reference_file": "papi/query-balance.ts", - "description": "Fetch the reference file and save it as 'query-balance.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://polkadot-asset-hub-rpc.polkadot.io' for mainnet). (2) Replace 'INSERT_ADDRESS' with the SS58 address whose balance you want to query. Save the file." - }, - { - "order": 5, - "action": "Fetch and configure the asset query script", - "working_directory": "papi-query-example", - "reference_file": "papi/query-asset.ts", - "description": "Fetch the reference file and save it as 'query-asset.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with the same WebSocket endpoint used above. (2) Replace 'INSERT_ADDRESS' with the address to check for USDT balance. The script queries asset ID 1984 (USDT) by default; change the asset ID constant if targeting a different asset. Save the file." - }, - { - "order": 6, - "action": "Run the scripts", - "working_directory": "papi-query-example", - "commands": [ - "npx tsx query-balance.ts", - "npx tsx query-asset.ts" - ], - "expected_output": "Account nonce and balance info, then asset metadata and balance for the specified address", - "description": "Execute both scripts in sequence. query-balance.ts shows the native PAS balance. query-asset.ts shows the USDT asset metadata and the address's USDT balance (0 if not holding USDT)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/query-data/query-sdks", - "files": [ - { - "path": "papi/query-balance.ts", - "description": "Queries System.Account storage for native PAS balance using the 'pah' descriptor" - }, - { - "path": "papi/query-asset.ts", - "description": "Queries Assets pallet for USDT (asset ID 1984) metadata and address balance" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated.", - "resolution": "Run 'npx papi add pah -n polkadot_asset_hub' in the project directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED", - "cause": "Incorrect or unreachable WebSocket endpoint.", - "resolution": "Verify the WebSocket URL. For Polkadot Hub mainnet use 'wss://polkadot-asset-hub-rpc.polkadot.io', for testnet use 'wss://asset-hub-paseo.dotters.network'." - }, - { - "pattern": "query-asset returns null or empty for the address", - "cause": "The address does not hold the queried asset (USDT asset ID 1984).", - "resolution": "This is expected for addresses with no USDT balance. The metadata query will still succeed; only the per-address balance query returns empty." - } - ], - "supplementary_context": { - "description": "Load these pages for deeper understanding of chain storage structure or related SDK tools.", - "pages": [ - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI API reference for understanding storage query methods and typed API patterns." - }, - { - "slug": "chain-interactions-query-data-runtime-api-calls", - "title": "Runtime API Calls", - "url": "https://docs.polkadot.com/chain-interactions/query-data/runtime-api-calls.md", - "relevance": "How to call runtime APIs for computed results (e.g., nonce, fee estimation) beyond static storage reads." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: read native balance of an address", - "user_says": "Query the DOT/PAS balance for address 15abc... on Polkadot Hub", - "actions": [ - "Scaffold 'papi-query-example/' ESM project", - "Install polkadot-api and tsx", - "Run npx papi add pah -n polkadot_asset_hub", - "Fetch query-balance.ts; substitute endpoint and address", - "Run npx tsx query-balance.ts" - ], - "result": "Account nonce, free/reserved/frozen PAS balance printed to console" - }, - { - "scenario": "Edge case: query a non-standard asset ID", - "user_says": "Query USDC balance instead of USDT", - "actions": [ - "Fetch query-asset.ts and follow standard setup", - "In query-asset.ts, change the asset ID constant from 1984 (USDT) to the USDC asset ID on the target chain", - "Run npx tsx query-asset.ts" - ], - "result": "USDC metadata and address balance returned" - } - ], - "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json" - }, - { - "id": "call-runtime-apis-sdks", - "title": "Call Runtime APIs with SDKs", - "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", - "version": "1.0.2", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/query-data/runtime-api-calls.md" - ], - "primary_page": "chain-interactions/query-data/runtime-api-calls.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub TestNet (e.g., wss://asset-hub-paseo.dotters.network)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-runtime-api-example && cd papi-runtime-api-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-runtime-api-example' and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-runtime-api-example", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors for Polkadot Hub TestNet", - "working_directory": "papi-runtime-api-example", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the runtime-apis.ts script. If targeting a different network, replace the WebSocket URL and update the descriptor name accordingly." - }, - { - "order": 4, - "action": "Fetch the runtime API script", - "working_directory": "papi-runtime-api-example", - "reference_file": "papi/runtime-apis.ts", - "description": "Fetch the reference file and save it as 'runtime-apis.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ADDRESS' with a valid SS58 address to query the nonce for. Save the file." - }, - { - "order": 5, - "action": "Run the runtime API script", - "working_directory": "papi-runtime-api-example", - "commands": [ - "npx tsx runtime-apis.ts" - ], - "expected_output": "Account Nonce: \nSupported Metadata Versions: []", - "description": "Execute the script. It calls AccountNonceApi.account_nonce for the provided address and Metadata.metadata_versions. Nonce for a new address is 0. Metadata versions list shows supported runtime metadata formats (e.g., [14, 15])." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/query-data/runtime-api-calls", - "files": [ - { - "path": "papi/runtime-apis.ts", - "description": "Calls AccountNonceApi.account_nonce and Metadata.metadata_versions using PAPI typed API" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." - }, - { - "pattern": "api.apis.AccountNonceApi is undefined", - "cause": "The target chain does not expose AccountNonceApi, or the descriptor was generated for a different chain.", - "resolution": "Verify the chain exposes AccountNonceApi in its runtime metadata. For Polkadot Hub TestNet, this API is available. If you generated descriptors for a different chain, regenerate with the correct endpoint." - }, - { - "pattern": "WebSocket connection failed / timeout on papi add", - "cause": "The testnet endpoint is temporarily unavailable.", - "resolution": "Polkadot Hub TestNet can be unstable. Wait a few minutes and retry. If the endpoint remains down, check the Polkadot Discord status channel. Alternatively, try a different public endpoint." - } - ], - "supplementary_context": { - "description": "Load these pages for background on available runtime APIs or to compare runtime API calls with storage queries.", - "pages": [ - { - "slug": "chain-interactions-query-data-query-sdks", - "title": "Query On-Chain State with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "Storage-based queries using the same PAPI setup — compare with runtime API calls." - }, - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI reference listing all available runtime APIs and their method signatures." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: get account nonce before constructing a transaction", - "user_says": "Call the AccountNonceApi to get the nonce for my address", - "actions": [ - "Scaffold 'papi-runtime-api-example/' ESM project", - "Install polkadot-api and tsx", - "Generate descriptors with npx papi add polkadotTestNet", - "Fetch runtime-apis.ts; substitute WS endpoint and address", - "Run npx tsx runtime-apis.ts" - ], - "result": "Account nonce and supported metadata versions printed to console" - }, - { - "scenario": "Edge case: nonce shows 0 for a new address", - "user_says": "The nonce is 0 — is that correct?", - "actions": [ - "Explain that nonce 0 means the account has not yet sent any transactions", - "Note that AccountNonceApi returns 0 for any address that has never submitted a transaction, including unfunded accounts", - "No action required — the nonce increments with each submitted transaction" - ], - "result": "User understands nonce 0 is correct for a new or never-transacted account" - } - ], - "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json" - }, - { - "id": "send-transactions-sdks", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md" + } + ] + }, + "title": "Create a Polkadot Account Programmatically", + "version": "1.0.2", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Queries Polkadot Hub for account balance, nonce, and account state using the Polkadot API (PAPI) TypeScript library. Use when you need to inspect an account's free/reserved/frozen balance or transaction count programmatically. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query account balance', 'check account info', 'get account nonce', 'read wallet balance'. No tokens required — read-only operation against any funded or unfunded address.", + "env_vars": [], + "error_patterns": [ + { + "cause": "PAPI type descriptors were not generated.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." + }, + { + "cause": "Incorrect or unreachable WebSocket endpoint URL.", + "pattern": "WebSocket connection failed / ECONNREFUSED", + "resolution": "Verify the WebSocket URL in query-account.ts is correct and the endpoint is accessible. For TestNet, use 'wss://asset-hub-paseo.dotters.network'." + }, + { + "cause": "The queried address has never been funded and has no on-chain entry.", + "pattern": "Account not found / undefined accountInfo", + "resolution": "The account exists only once it receives tokens. Query a funded address, or fund the address using the Polkadot faucet at https://faucet.polkadot.io/ first." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'papi-query-account-example/' ESM project", + "Install polkadot-api and tsx", + "Generate descriptors with npx papi add polkadotTestNet", + "Fetch query-account.ts; substitute WS endpoint and account address", + "Run npx tsx query-account.ts" + ], + "result": "Free/reserved/frozen balance and nonce printed for the specified address", + "scenario": "Common scenario: check balance of an address", + "user_says": "Query the balance of this Polkadot address: 1abc..." + }, + { + "actions": [ + "Explain that Polkadot Hub only stores accounts that have been funded (existential deposit met)", + "Direct user to https://faucet.polkadot.io/ to fund the account with testnet PAS tokens", + "Re-run query-account.ts after account is funded" + ], + "result": "After funding, account data is returned correctly", + "scenario": "Edge case: address not found (zero-balance unfunded account)", + "user_says": "Query the account but it shows no data" + } + ], + "id": "query-account-info-sdks", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for Polkadot Hub or compatible chain (e.g., wss://asset-hub-paseo.dotters.network for TestNet)" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "primary_page": "chain-interactions/accounts/query-accounts.md", + "project_structure": "papi-query-account-example/\n├── .papi/\n│ └── descriptors/\n├── query-account.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/accounts/query-account", + "branch": "master", + "files": [ + { + "description": "PAPI TypeScript script that queries System.Account storage and prints balance details", + "path": "papi/query-account.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/accounts/query-accounts.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir papi-query-account-example && cd papi-query-account-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory and initialize an ESM Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling.", + "order": 2, + "working_directory": "papi-query-account-example" + }, + { + "action": "Generate PAPI type descriptors", + "commands": [ + "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. This connects to the chain and downloads its metadata. Produces @polkadot-api/descriptors with the 'polkadotTestNet' export. If targeting a different network, replace the WebSocket URL and the descriptor name accordingly — note the name you choose, as it must match the import in query-account.ts.", + "order": 3, + "working_directory": "papi-query-account-example" + }, + { + "action": "Fetch the query script", + "description": "Fetch the reference file and save it as 'query-account.ts'. Then make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ACCOUNT_ADDRESS' with the SS58 address you want to query. Save the changes before running.", + "order": 4, + "reference_file": "papi/query-account.ts", + "working_directory": "papi-query-account-example" + }, + { + "action": "Run the query script", + "commands": [ + "npx tsx query-account.ts" + ], + "description": "Execute the script. Output will display account nonce, balance breakdown (free/reserved/frozen), and total balance in raw PAS units and human-readable format (divide raw value by 10^10 for PAS).", + "expected_output": "Account Information with Nonce, Consumers, Providers, Free Balance, Reserved Balance, Frozen Balance, and Total Balance", + "order": 5, + "working_directory": "papi-query-account-example" + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs background on account data fields or wants to send transactions from the queried account.", + "pages": [ + { + "relevance": "Full PAPI API reference for understanding query methods and typed API usage.", + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md" + }, + { + "relevance": "How to send a transaction from the account after verifying its balance.", + "slug": "chain-interactions-send-transactions-with-sdks", "title": "Send Transactions with SDKs", - "description": "Constructs, signs, and submits a Balances.transfer_keep_alive extrinsic to Polkadot Hub using the PAPI TypeScript library. Use when you need to send tokens programmatically from a funded account. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Requires a funded account mnemonic and testnet PAS tokens. Trigger phrases: 'send a transaction', 'transfer tokens programmatically', 'sign and submit extrinsic', 'send PAS balance'. Uses dotenv to protect the sender mnemonic.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/with-sdks.md" - ], - "primary_page": "chain-interactions/send-transactions/with-sdks.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://asset-hub-paseo.dotters.network for testnet)" - ], - "tokens": [ - "Testnet PAS tokens in the sender account — get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" - ], - "wallet": [ - "An SR25519 account mnemonic phrase for the sender account, funded with testnet PAS" - ] - }, - "env_vars": [ - { - "name": "SENDER_MNEMONIC", - "description": "12-word BIP39 mnemonic phrase for the sender account. Must be the mnemonic for a funded account. Never commit this value to version control.", - "required": true - }, - { - "name": "WS_ENDPOINT", - "description": "WebSocket RPC endpoint for the target chain (e.g., wss://asset-hub-paseo.dotters.network).", - "required": true - }, - { - "name": "DEST_ADDRESS", - "description": "SS58-encoded recipient address for the balance transfer.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir papi-send-tx-example && cd papi-send-tx-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'papi-send-tx-example' and initialize an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "papi-send-tx-example", - "commands": [ - "npm install polkadot-api @polkadot/util-crypto @polkadot/keyring dotenv", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api, @polkadot/keyring (for SR25519 signing), dotenv (for secure credential loading), and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors", - "working_directory": "papi-send-tx-example", - "commands": [ - "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the send-transfer script. If targeting a different network, replace the endpoint and update the descriptor name in step 5." - }, - { - "order": 4, - "action": "Create .env and .gitignore for secrets", - "working_directory": "papi-send-tx-example", - "commands": [ - "echo 'SENDER_MNEMONIC=\\nWS_ENDPOINT=wss://asset-hub-paseo.dotters.network\\nDEST_ADDRESS=' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly — fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding." - }, - { - "order": 5, - "action": "Fetch and adapt the send-transfer script", - "working_directory": "papi-send-tx-example", - "reference_file": "papi/send-transfer.ts", - "description": "Fetch the reference file and save it as 'send-transfer.ts'. Then apply these modifications: (1) Add 'import \"dotenv/config\";' as the very first line of the file. (2) Replace 'const POLKADOT_TESTNET_RPC = \\'INSERT_WS_ENDPOINT\\';' with 'const POLKADOT_TESTNET_RPC = process.env.WS_ENDPOINT!;'. (3) Replace 'const SENDER_MNEMONIC = \\'INSERT_MNEMONIC\\';' with 'const SENDER_MNEMONIC = process.env.SENDER_MNEMONIC!;'. (4) Replace 'const DEST_ADDRESS = \\'INSERT_DEST_ADDRESS\\';' with 'const DEST_ADDRESS = process.env.DEST_ADDRESS!;'. Save the file." - }, - { - "order": 6, - "action": "Verify the sender account is funded", - "working_directory": "papi-send-tx-example", - "description": "Before running, confirm the sender account has testnet PAS tokens. If not, direct the user to https://faucet.polkadot.io/?parachain=1111, paste the sender address, and request tokens. Wait for the faucet transaction to confirm (typically 1-2 minutes) before proceeding." - }, - { - "order": 7, - "action": "Submit the transaction", - "working_directory": "papi-send-tx-example", - "commands": [ - "npx tsx send-transfer.ts" - ], - "expected_output": "Connected to Polkadot Testnet\nSigning and submitting transaction...\nTransaction submitted with hash: 0x", - "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash — needed in step 8 to verify." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/send-transactions/with-sdks", - "files": [ - { - "path": "papi/send-transfer.ts", - "description": "PAPI TypeScript script that signs and submits a Balances.transfer_keep_alive extrinsic" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." - }, - { - "pattern": "Invalid mnemonic phrase / Error creating keypair", - "cause": "SENDER_MNEMONIC in .env is incorrect, has extra spaces, or is not a valid BIP39 phrase.", - "resolution": "Open .env, verify SENDER_MNEMONIC is a valid 12-word BIP39 phrase with single spaces between words, and no leading/trailing spaces." - }, - { - "pattern": "Error: 1010: Invalid Transaction: Inability to pay some fees", - "cause": "The sender account has insufficient PAS balance to cover the transfer amount plus gas fees.", - "resolution": "Get testnet tokens from https://faucet.polkadot.io/?parachain=1111. Alternatively, reduce the AMOUNT constant in send-transfer.ts to 1_000_000_000n (0.1 PAS)." - }, - { - "pattern": "Transaction submitted but not confirmed / stuck pending", - "cause": "Polkadot Hub TestNet can be unstable and drop transactions under load.", - "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction — if you see 'nonce too low', the previous transaction was included despite appearing to fail." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to fund an account, understand fees, or verify the submitted transaction.", - "pages": [ - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "How to obtain testnet PAS tokens from the Polkadot faucet." - }, - { - "slug": "chain-interactions-send-transactions-calculate-transaction-fees", - "title": "Calculate Transaction Fees", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", - "relevance": "How to estimate transaction fees before submitting to avoid insufficient balance errors." - }, - { - "slug": "chain-interactions-accounts-create-account", - "title": "Create an Account", - "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account.md", - "relevance": "How to create a new Polkadot account if the user does not yet have a sender mnemonic." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: send a small amount of PAS to another address", - "user_says": "Send 1 PAS from my funded account to address 15xyz...", - "actions": [ - "Scaffold 'papi-send-tx-example/' ESM project", - "Install polkadot-api, keyring, dotenv", - "Generate descriptors with npx papi add polkadotTestNet", - "Create .env and .gitignore; ask user to fill SENDER_MNEMONIC and DEST_ADDRESS", - "Fetch and adapt send-transfer.ts with dotenv substitutions", - "Verify sender is funded; run npx tsx send-transfer.ts" - ], - "result": "Transaction hash printed; 1 PAS (10^10 planck) transferred to the destination address" - }, - { - "scenario": "Edge case: account has no testnet tokens", - "user_says": "The transaction fails with 'inability to pay fees'", - "actions": [ - "Direct user to https://faucet.polkadot.io/?parachain=1111 with their sender address", - "Wait for faucet to credit the account (1-2 minutes)", - "Re-run send-transfer.ts" - ], - "result": "Account funded; transaction succeeds on retry" - } - ], - "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json" - }, - { - "id": "install-polkadot-sdk", - "title": "Install the Polkadot SDK", - "description": "Installs all Polkadot SDK build dependencies and compiles the SDK from source on macOS, Linux (Ubuntu/Debian/Arch/Fedora/OpenSUSE), and Windows WSL. Use when setting up a new development machine for Polkadot parachain or runtime development. Covers OS package installation, Rust toolchain setup, wasm32 target addition, git clone, and a full cargo build. Trigger phrases: 'install Polkadot SDK', 'set up parachain dev environment', 'install Rust for Polkadot', 'build polkadot-sdk', 'set up Substrate environment'. Expected outcome: polkadot binary executes and prints its version string.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/install-polkadot-sdk.md" - ], - "primary_page": "parachains/install-polkadot-sdk.md", - "prerequisites": { - "runtime": [ - "macOS 10.7+, Ubuntu/Debian/Arch/Fedora/OpenSUSE Linux, or Windows 10 v2004+ with WSL2", - "At least 10 GB free disk space and 8 GB RAM (16 GB recommended for parallel compilation)", - "Broadband internet connection (downloads several GB of Rust crates and dependencies)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install OS-level system dependencies", - "working_directory": ".", - "description": "Install the packages required to compile the Polkadot SDK. Run the command block for your OS:\n\nmacOS: First install Homebrew if missing (/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\"), then run: brew install protobuf openssl cmake\n\nUbuntu or Debian: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler\n\nArch Linux: pacman -Syu --needed --noconfirm curl git clang make protobuf\n\nFedora: sudo dnf update && sudo dnf install clang curl git openssl-devel make protobuf-compiler\n\nOpenSUSE: sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf\n\nWindows WSL (Ubuntu): open the WSL Ubuntu terminal, then run: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler" - }, - { - "order": 2, - "action": "Install Rust via rustup", - "working_directory": ".", - "commands": [ - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", - "source ~/.cargo/env" - ], - "description": "Download and run the rustup installer. When prompted, accept the default installation (press Enter or type 1). After installation completes, source the Cargo environment so the current shell session can find the Rust toolchain. On Windows WSL, use source ~/.cargo/env; on some Linux distributions use source $HOME/.cargo/env." - }, - { - "order": 3, - "action": "Configure the Rust toolchain", - "working_directory": ".", - "commands": [ - "rustup default stable", - "rustup update", - "rustup target add wasm32-unknown-unknown", - "rustup component add rust-src" - ], - "expected_output": "info: setting default toolchain to 'stable-...'", - "description": "Set the stable toolchain as default, update it to the latest release, and add the WebAssembly compilation target plus the Rust source component. Both wasm32-unknown-unknown and rust-src are required for building Substrate/Polkadot SDK runtime WASM blobs." - }, - { - "order": 4, - "action": "Clone the Polkadot SDK repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk.git", - "cd polkadot-sdk" - ], - "description": "Clone the official Polkadot SDK repository. The clone is several hundred MB and may take a few minutes on a slower connection. All subsequent steps run from inside the polkadot-sdk directory." - }, - { - "order": 5, - "action": "Compile the Polkadot SDK", - "working_directory": "polkadot-sdk", - "commands": [ - "cargo build --release --locked" - ], - "expected_output": "Compiling polkadot ... Finished release [optimized] target(s)", - "description": "Build the entire SDK in release mode. This compiles all components and verifies the full toolchain. Expect 20-60 minutes on typical developer hardware. A successful build produces binaries in target/release/ including polkadot, polkadot-parachain, polkadot-omni-node, and substrate-node." - }, - { - "order": 6, - "action": "Verify the build", - "working_directory": "polkadot-sdk", - "commands": [ - "./target/release/polkadot --version" - ], - "expected_output": "polkadot 1.x.x-xxxxxxx", - "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string — needed when matching binary versions in parachain template setup." - } - ], - "error_patterns": [ - { - "pattern": "error: linker 'cc' not found", - "cause": "C compiler and build tools are not installed.", - "resolution": "On Ubuntu/Debian run: sudo apt install build-essential clang. On macOS run: xcode-select --install. On Fedora run: sudo dnf install gcc." - }, - { - "pattern": "error: failed to run custom build command for 'openssl-sys'", - "cause": "OpenSSL development headers are missing.", - "resolution": "On Ubuntu/Debian run: sudo apt install libssl-dev. On macOS run: brew install openssl. On Fedora run: sudo dnf install openssl-devel." - }, - { - "pattern": "error[E0463]: can't find crate for 'std' ... target wasm32-unknown-unknown", - "cause": "The wasm32-unknown-unknown target is not installed.", - "resolution": "Run: rustup target add wasm32-unknown-unknown && rustup component add rust-src" - }, - { - "pattern": "LLVM ERROR: out of memory / build killed / OOM", - "cause": "Insufficient RAM for the default number of parallel compilation jobs.", - "resolution": "Limit parallelism: cargo build --release --locked -j 4. On Linux you can also temporarily increase swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile" - }, - { - "pattern": "error: failed to compile protobuf files / protoc not found", - "cause": "The protobuf compiler is missing.", - "resolution": "On Ubuntu/Debian: sudo apt install protobuf-compiler. On macOS: brew install protobuf. On Fedora: sudo dnf install protobuf-compiler." - } - ], - "supplementary_context": { - "description": "Load when the user asks about next steps after installing the SDK or about the parachain template.", - "pages": [ - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Next step after SDK installation: clone and run the Polkadot SDK parachain template locally." - }, - { - "slug": "parachains-get-started", - "title": "Get Started with Parachain Development", - "url": "https://docs.polkadot.com/parachains/get-started.md", - "relevance": "Overview of parachain development paths and starting points available after SDK setup." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: fresh Ubuntu developer machine", - "user_says": "Install the Polkadot SDK on my Linux machine", - "actions": [ - "Run apt install for required packages (git, clang, libssl-dev, etc.)", - "Install Rust via rustup and source ~/.cargo/env", - "Run rustup default stable && rustup target add wasm32-unknown-unknown && rustup component add rust-src", - "Clone https://github.com/paritytech/polkadot-sdk.git and cd polkadot-sdk", - "Run cargo build --release --locked", - "Verify with ./target/release/polkadot --version" - ], - "result": "Polkadot binary prints version string — SDK toolchain ready for parachain development" - }, - { - "scenario": "Edge case: build runs out of memory on a machine with limited RAM", - "user_says": "The build keeps getting killed or runs out of memory", - "actions": [ - "Re-run with reduced parallelism: cargo build --release --locked -j 4", - "If still failing on Linux, add swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile, then retry" - ], - "result": "Build completes with reduced parallel jobs or additional swap space" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - } - }, - { - "id": "deploy-basic-contract-hardhat", - "title": "Deploy a Basic Smart Contract with Hardhat", - "description": "Scaffolds a Hardhat v2 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Must correspond to a testnet account funded with PAS tokens. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-deployment && cd hardhat-deployment", - "npm init -y" - ], - "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage — interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps." - }, - { - "order": 2, - "action": "Install Hardhat and dependencies", - "working_directory": "hardhat-deployment", - "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", - "npm install dotenv" - ], - "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells." - }, - { - "order": 3, - "action": "Create project directory structure", - "working_directory": "hardhat-deployment", - "commands": [ - "mkdir -p contracts ignition/modules" - ], - "description": "Create the contracts and ignition/modules directories that Hardhat expects. The test directory is optional and not needed for this deployment workflow." - }, - { - "order": 4, - "action": "Create .env file and .gitignore for private key", - "working_directory": "hardhat-deployment", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5." - }, - { - "order": 5, - "action": "Fetch and configure hardhat.config.ts", - "working_directory": "hardhat-deployment", - "reference_file": "hardhat.config.ts", - "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors)." - }, - { - "order": 6, - "action": "Fetch Storage.sol contract", - "working_directory": "hardhat-deployment", - "reference_file": "Storage.sol", - "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed — this is a generic Storage contract with get/set functions." - }, - { - "order": 7, - "action": "Compile the contract", - "working_directory": "hardhat-deployment", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile Storage.sol. Successful output shows 'Compiled 1 Solidity file successfully' and creates artifacts in the artifacts/ directory. If compilation fails with 'invalid opcode: MCOPY', add 'evmVersion: \"cancun\"' inside the solidity.settings.evmVersion field of hardhat.config.ts and recompile." - }, - { - "order": 8, - "action": "Fetch the Ignition deployment module", - "working_directory": "hardhat-deployment", - "reference_file": "storage.ts", - "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed — the module deploys the Storage contract by name." - }, - { - "order": 9, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "hardhat-deployment", - "commands": [ - "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" - ], - "expected_output": "Hardhat Ignition 🚀 ... Storage deployed to: 0x...", - "interactive": true, - "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end — needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat", - "files": [ - { - "path": "hardhat.config.ts", - "description": "Hardhat config with polkadotTestnet network (RPC, chain ID, accounts via vars). Requires dotenv conversion and gasPrice addition per skill steps." - }, - { - "path": "Storage.sol", - "description": "Simple Storage contract with uint256 store/retrieve functions" - }, - { - "path": "storage.ts", - "description": "Hardhat Ignition module that deploys the Storage contract" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined (reading 'get')", - "cause": "The hardhat.config.ts still contains vars.get('PRIVATE_KEY') instead of the dotenv substitution.", - "resolution": "Open hardhat.config.ts, replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string, and add 'import \"dotenv/config\";' as the first line of the file." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "The deployer account has no testnet PAS tokens, or the gasPrice in hardhat.config.ts is below the network minimum (1000 gwei base fee).", - "resolution": "Get testnet tokens from https://faucet.polkadot.io/. Also verify the polkadotTestnet network block in hardhat.config.ts includes gasPrice: 5000000000000 (5000 gwei)." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported on retry", - "cause": "Ignition lost track of the deployment transaction. Often caused by requiredConfirmations: 0 or gasPrice below the base fee.", - "resolution": "First check if the contract was actually deployed: look in ignition/deployments/ for deployed_addresses.json. If deployed, import the existing deployment. If not deployed: (1) delete the ignition/deployments/ directory, (2) ensure hardhat.config.ts has gasPrice: 5000000000000 and ignition.requiredConfirmations: 1, (3) redeploy." - }, - { - "pattern": "CompilationError: Invalid opcode: MCOPY", - "cause": "The Solidity contract uses OpenZeppelin v5.4.0+ which requires the Cancun EVM version.", - "resolution": "In hardhat.config.ts, add evmVersion: 'cancun' inside the solidity compiler settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }" - } - ], - "supplementary_context": { - "description": "Load when the user wants to build on this deployment or needs to connect a frontend.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", - "title": "Deploy an ERC-20 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "Next step: deploy an ERC-20 token using the same Hardhat workflow." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Hardhat configuration reference for Polkadot Hub including verification setup." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a first smart contract to Polkadot Hub", - "user_says": "Deploy a Storage contract to Polkadot Hub TestNet using Hardhat", - "actions": [ - "Scaffold hardhat-deployment/ with npm init -y", - "Install hardhat, toolbox, dotenv", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Fetch and configure hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set requiredConfirmations: 1", - "Fetch contracts/Storage.sol", - "Run npx hardhat compile", - "Fetch ignition/modules/Storage.ts", - "Run npx hardhat ignition deploy --network polkadotTestnet; delegate confirmation prompt to user" - ], - "result": "Storage contract deployed; address printed to console" - }, - { - "scenario": "Edge case: deployment transaction gets stuck (IGN401)", - "user_says": "Ignition says the transaction was dropped and retrying gives 'Transaction Already Imported'", - "actions": [ - "Check ignition/deployments/ for deployed_addresses.json to determine if contract was actually deployed", - "If not deployed: delete ignition/deployments/, verify gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy" - ], - "result": "Fresh deployment succeeds with correct gas configuration" - } - ], - "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json" - }, - { - "id": "deploy-erc20-token-hardhat", - "title": "Deploy an ERC-20 Token Using Hardhat", - "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees and test transactions — get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer/test account. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Clone the ERC-20 template repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples/", - "cd revm-hardhat-examples/erc20-hardhat" - ], - "description": "Clone the revm-hardhat-examples repository and navigate into the erc20-hardhat subdirectory. This template contains a pre-configured Hardhat project with an OpenZeppelin ERC-20 contract, test suite, and Ignition deployment module." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npm i", - "npm install dotenv" - ], - "description": "Install all template dependencies from package.json, then additionally install dotenv. dotenv is needed to replace Hardhat's interactive vars system (which cannot be used in agent shells) with a .env-based private key approach." - }, - { - "order": 3, - "action": "Create .env file and .gitignore for private key", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file." - }, - { - "order": 5, - "action": "Compile the contract", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile the ERC-20 contract. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in the solidity compiler settings and recompile." - }, - { - "order": 6, - "action": "Run the test suite", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npx hardhat test --network polkadotTestnet" - ], - "expected_output": "7 passing", - "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output — it confirms the contract logic is correct before deployment." - }, - { - "order": 7, - "action": "Deploy the ERC-20 contract", - "working_directory": "revm-hardhat-examples/erc20-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet" - ], - "expected_output": "MyToken deployed to: 0x...", - "interactive": true, - "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success — needed for token interaction and verification." - } - ], - "error_patterns": [ - { - "pattern": "CompilationError: Invalid opcode: MCOPY", - "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode which requires Cancun EVM version. The hardhat.config.ts does not specify evmVersion: 'cancun'.", - "resolution": "In hardhat.config.ts, set evmVersion: 'cancun' inside solidity.settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }. Then recompile." - }, - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of deployment, commonly due to missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user wants to build a dApp on top of the deployed ERC-20 or interact with it.", - "pages": [ - { - "slug": "smart-contracts-cookbook-dapps-zero-to-hero", - "title": "Zero to Hero Smart Contract DApp", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract." - }, - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "How to interact with the deployed ERC-20 contract using Ethers.js." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a new ERC-20 token to Polkadot Hub", - "user_says": "Deploy an ERC-20 token to Polkadot Hub TestNet", - "actions": [ - "Clone revm-hardhat-examples and cd erc20-hardhat", - "Run npm i && npm install dotenv", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Modify hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set evmVersion cancun, confirm requiredConfirmations: 1", - "Run npx hardhat compile", - "Run npx hardhat test --network polkadotTestnet to verify contract logic", - "Run npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet; delegate confirmation to user" - ], - "result": "ERC-20 token contract deployed; address printed to console" - }, - { - "scenario": "Edge case: compilation fails with MCOPY opcode error", - "user_says": "Compilation fails with 'Invalid opcode: MCOPY'", - "actions": [ - "Open hardhat.config.ts", - "Change the solidity field to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", - "Run npx hardhat compile again" - ], - "result": "Compilation succeeds with Cancun EVM version" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - } - }, - { - "id": "set-up-hardhat-pvm", - "title": "Set Up Hardhat with the Polkadot Plugin (PVM)", - "description": "Sets up a Hardhat project using the @parity/hardhat-polkadot plugin and the resolc compiler to compile Solidity to PolkaVM (PVM) bytecode. Use when targeting Polkadot Hub's PVM runtime rather than the standard EVM runtime. Covers project initialization, resolc installation, dotenv private key setup, TestNet config with gasPrice, and a compile-verify step. Trigger phrases: 'Hardhat PVM', 'hardhat-polkadot plugin', 'compile to PolkaVM', 'resolc Hardhat', 'PVM smart contract Polkadot'. Do NOT use this skill for standard EVM deployments; use set-up-hardhat-evm instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/hardhat-polkadot.md" - ], - "primary_page": "smart-contracts/dev-environments/hardhat-polkadot.md", - "prerequisites": { - "runtime": [ - "Node.js v22.5+ and npm v10.9.0+ (required for @parity/hardhat-polkadot compatibility)", - "npm or pnpm or yarn" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-pvm-example && cd hardhat-pvm-example", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project." - }, - { - "order": 2, - "action": "Install the Polkadot Hardhat plugin and resolc compiler", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npm install --save-dev @parity/hardhat-polkadot", - "npm install --save-dev @parity/resolc", - "npm install dotenv" - ], - "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them — use latest." - }, - { - "order": 3, - "action": "Initialize the Hardhat PVM project structure", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npx hardhat-polkadot init" - ], - "interactive": true, - "description": "Run the project creation wizard. This is an interactive command — delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding." - }, - { - "order": 4, - "action": "Install all project dependencies after init", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npm install", - "echo '/ignition/deployments/' >> .gitignore" - ], - "description": "Install any additional dependencies added by the init wizard, then add ignition/deployments/ to .gitignore to avoid committing deployment state to version control." - }, - { - "order": 5, - "action": "Create .env file and .gitignore entry for private key", - "working_directory": "hardhat-pvm-example", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 6, - "action": "Fetch and configure hardhat.config.ts for TestNet", - "working_directory": "hardhat-pvm-example", - "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file." - }, - { - "order": 7, - "action": "Compile the sample contract to verify setup", - "working_directory": "hardhat-pvm-example", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled ... Solidity file(s) successfully", - "description": "Compile the sample contract generated by the init wizard. Successful compilation confirms resolc is working and the project structure is correct. The compiled artifacts appear in artifacts/contracts/. If compilation fails with resolc errors, verify @parity/resolc is installed and the version matches what the hardhat.config.ts specifies in the resolc field." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@parity/hardhat-polkadot' / resolc not found", - "cause": "The Polkadot plugin or resolc compiler was not installed.", - "resolution": "Run: npm install --save-dev @parity/hardhat-polkadot @parity/resolc in the project directory." - }, - { - "pattern": "Compilation fails with resolc errors / unsupported Solidity version", - "cause": "The resolc compiler version does not support the contract's Solidity pragma version.", - "resolution": "Check the @parity/resolc version installed and the Solidity pragma in the contract. Update the version field in hardhat.config.ts resolc settings to match what is installed, or downgrade the Solidity pragma in the contract to a supported version." - }, - { - "pattern": "Error: vars is not defined", - "cause": "hardhat.config.ts uses Hardhat's vars.get() system instead of dotenv.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - }, - { - "pattern": "Deployment hangs on local node / requiredConfirmations: 0", - "cause": "The ignition config has requiredConfirmations: 0, which causes Ignition to misinterpret pending transactions as dropped on local nodes that don't produce blocks instantly.", - "resolution": "Set ignition.requiredConfirmations: 1 in hardhat.config.ts." - }, - { - "pattern": "Binary permission issues on macOS (binary is quarantined)", - "cause": "macOS Gatekeeper quarantines downloaded binaries for dev-node and eth-rpc adapter.", - "resolution": "Run: xattr -d com.apple.quarantine /path/to/binary. Also ensure executable permission: chmod +x /path/to/binary." - } - ], - "supplementary_context": { - "description": "Load when the user wants to deploy a contract after setting up the PVM environment, or needs to understand PVM vs EVM differences.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Next step: deploy a basic contract once the Hardhat PVM environment is set up." - }, - { - "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", - "title": "EVM vs PVM", - "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md", - "relevance": "Understand the differences between EVM and PVM to choose the right compilation target." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: set up a PVM Hardhat project from scratch", - "user_says": "Set up Hardhat with the Polkadot PVM plugin", - "actions": [ - "Create hardhat-pvm-example/ and run npm init -y", - "Install @parity/hardhat-polkadot, @parity/resolc, dotenv", - "Run npx hardhat-polkadot init wizard (delegate to user)", - "Run npm install and add ignition/deployments/ to .gitignore", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Fetch and configure hardhat.config.ts: add dotenv, replace vars.get, add gasPrice 5000 gwei, requiredConfirmations: 1", - "Run npx hardhat compile to verify setup" - ], - "result": "PVM Hardhat project compiles successfully; ready to deploy contracts to Polkadot Hub" - }, - { - "scenario": "Edge case: user is on macOS and binary is quarantined", - "user_says": "The dev-node binary is blocked by macOS Gatekeeper", - "actions": [ - "Run: xattr -d com.apple.quarantine /path/to/dev-node", - "Run: chmod +x /path/to/dev-node", - "Restart the local node" - ], - "result": "Binary executes without quarantine errors" - } - ] - }, - { - "id": "set-up-hardhat-evm", - "title": "Set Up Hardhat for Polkadot Hub (EVM)", - "description": "Initializes a Hardhat v2 TypeScript project for the standard Polkadot Hub EVM runtime (not PVM). Configures the polkadotTestnet network with correct RPC URL, chain ID (420420417), gasPrice (5000 gwei), and dotenv-based private key management. Optionally configures Blockscout or Routescan contract verification. Use when setting up a reusable EVM development environment before deploying any contract. Trigger phrases: 'set up Hardhat for Polkadot EVM', 'configure Hardhat Polkadot Hub', 'Hardhat EVM Polkadot setup', 'Hardhat network config Polkadot'. Do NOT use for PVM; use set-up-hardhat-pvm instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/hardhat.md" - ], - "primary_page": "smart-contracts/dev-environments/hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js LTS (18.x, 20.x, or 22.x — even major version numbers only)", - "npm, pnpm, or yarn" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account — needed before deploying contracts" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key used in the polkadotTestnet accounts array. Fill before running any deployment. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-example && cd hardhat-example", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage — the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps." - }, - { - "order": 2, - "action": "Install Hardhat and dependencies", - "working_directory": "hardhat-example", - "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", - "npm install dotenv" - ], - "description": "Install Hardhat v2, the Hardhat Toolbox (includes ethers.js, chai, and testing utilities), TypeScript support, and dotenv. The dotenv package replaces Hardhat's interactive 'npx hardhat vars set' mechanism, which cannot be used in agent shells." - }, - { - "order": 3, - "action": "Create project directory structure", - "working_directory": "hardhat-example", - "commands": [ - "mkdir -p contracts ignition/modules test", - "echo '/ignition/deployments/' >> .gitignore", - "echo '/artifacts/' >> .gitignore", - "echo '/cache/' >> .gitignore" - ], - "description": "Create the standard Hardhat directory structure and add common output directories to .gitignore." - }, - { - "order": 4, - "action": "Create .env file and .gitignore entry for private key", - "working_directory": "hardhat-example", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable — the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat." - }, - { - "order": 5, - "action": "Fetch and configure hardhat.config.ts", - "working_directory": "hardhat-example", - "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: vars is not defined", - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - }, - { - "pattern": "HardhatError: HH8: There's one or more errors in your config / Cannot read properties of undefined (reading 'get')", - "cause": "The vars import is present but vars is undefined because the interactive vars store was never populated.", - "resolution": "Remove 'import { vars } from \"hardhat/config\";' and all vars.get() calls. Replace with process.env.PRIVATE_KEY loaded by dotenv." - }, - { - "pattern": "Error: insufficient funds for intrinsic transaction cost on deployment", - "cause": "gasPrice is missing or below the TestNet base fee of 1000 gwei.", - "resolution": "Add gasPrice: 5000000000000 to the polkadotTestnet network block in hardhat.config.ts. Also ensure the deployer account has testnet PAS from https://faucet.polkadot.io/." - }, - { - "pattern": "Cannot find module 'ts-node' / TypeScript compilation error on npx hardhat", - "cause": "TypeScript dependencies were not installed.", - "resolution": "Run: npm install --save-dev typescript ts-node @types/node in the project directory." - } - ], - "supplementary_context": { - "description": "Load when the user is ready to deploy a contract or wants to verify a deployed contract.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Next step: deploy a basic Storage contract using the configured Hardhat project." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", - "title": "Deploy an ERC-20 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "Deploy an ERC-20 token using the configured Hardhat environment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: configure a Hardhat project for Polkadot Hub from scratch", - "user_says": "Set up a Hardhat project for Polkadot Hub EVM", - "actions": [ - "Create hardhat-example/ and run npm init -y", - "Install hardhat@^2.27.0, hardhat-toolbox, typescript, ts-node, dotenv", - "Create contracts/, ignition/modules/, test/ directories and set .gitignore", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Fetch hardhat.config.ts from cookbook; add dotenv import, replace vars.get, add gasPrice 5000 gwei, add ignition.requiredConfirmations: 1" - ], - "result": "Hardhat project configured for Polkadot Hub TestNet; ready for contract development and deployment" - }, - { - "scenario": "Edge case: existing project uses Hardhat vars and breaks in CI or agent shell", - "user_says": "My Hardhat config uses vars.get and it fails with 'vars is not defined'", - "actions": [ - "Open hardhat.config.ts", - "Add 'import \"dotenv/config\";' as the first line", - "Replace all vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string", - "Remove the 'import { vars } from \"hardhat/config\";' line", - "Create .env with PRIVATE_KEY= and add .env to .gitignore" - ], - "result": "Project loads private key from .env; works in agent shells and CI without interactive prompts" - } - ] - }, - { - "id": "query-chain-data-sidecar-rest", - "title": "Query On-Chain Data with Sidecar REST API", - "description": "Queries Polkadot Hub on-chain data via curl against the Substrate API Sidecar REST service. Covers account balance-info, asset-balances (including USDT/USDC), asset metadata, and block endpoints using Parity's public Sidecar instance. Use when you need account balances, asset info, or block data without writing SDK code. Also covers running a local Sidecar instance via npm or Docker for production use. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required — read-only operations only.", - "version": "1.1.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/query-data/query-rest.md" - ], - "primary_page": "chain-interactions/query-data/query-rest.md", - "prerequisites": { - "runtime": [ - "curl (pre-installed on macOS and Linux; use WSL on Windows)", - "jq (optional, for formatted JSON output — install with: apt install jq or brew install jq)", - "Node.js v18+ and npm (only if running Sidecar locally)" - ], - "network": [ - "Parity public Sidecar endpoint: https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io (no setup required)", - "OR: WebSocket RPC endpoint for a Polkadot Hub node (if running Sidecar locally)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Choose your Sidecar endpoint", - "working_directory": ".", - "description": "You have two options:\n\n**Option A — Public endpoint (recommended for testing):** Use Parity's hosted Sidecar at `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io`. No setup required; skip to step 2.\n\n**Option B — Local Sidecar:** Run your own instance for production or high-frequency queries.\n\n```bash\n# Install globally\nnpm install -g @substrate/api-sidecar\n\n# Start with Polkadot Hub mainnet endpoint\nSAS_SUBSTRATE_URL=wss://polkadot-asset-hub-rpc.polkadot.io substrate-api-sidecar\n```\n\nWait for `SAS listening on http://127.0.0.1:8080/` before proceeding. If using the local instance, replace the base URL in all following steps with `http://127.0.0.1:8080`." - }, - { - "order": 2, - "action": "Query account balance", - "working_directory": ".", - "commands": [ - "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/balance-info\" | jq ." - ], - "expected_output": "{\"at\":{\"hash\":\"0x...\",\"height\":\"12345\"},\"nonce\":\"0\",\"tokenSymbol\":\"DOT\",\"free\":\"...\",\"reserved\":\"...\",\"frozen\":\"...\",\"transferable\":\"...\"}", - "description": "Query the balance-info endpoint. Replace `INSERT_SS58_ADDRESS` with the target SS58 address. Response fields: `free` — spendable planck; `reserved` — locked/staked; `frozen` — frozen amounts; `transferable` — actual balance available to transfer (new field); `nonce` — transaction count. Divide raw planck values by 10^10 to convert to DOT/PAS. Omit `| jq .` if jq is not installed." - }, - { - "order": 3, - "action": "Query asset balances", - "working_directory": ".", - "commands": [ - "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984\" | jq ." - ], - "expected_output": "{\"at\":{...},\"items\":[{\"assetId\":\"1984\",\"balance\":\"...\",\"isFrozen\":false,\"isSufficient\":false}]}", - "description": "Query asset balances for asset ID 1984 (USDT) held by the account. Replace `INSERT_SS58_ADDRESS` with the target SS58 address. Replace `1984` with any registered asset ID (e.g., `1337` for USDC). To query multiple assets in one request, append additional `assets[]=` parameters: `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984&assets[]=1337`. If the account holds no USDT, `items` will be an empty array." - }, - { - "order": 4, - "action": "Query asset metadata", - "working_directory": ".", - "commands": [ - "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/pallets/assets/storage/Metadata?keys[]=1984\" | jq ." - ], - "expected_output": "{\"at\":{...},\"pallet\":\"assets\",\"storageItem\":\"Metadata\",\"keys\":[\"1984\"],\"value\":{\"deposit\":\"...\",\"name\":\"0x5465746865722055534454\",\"symbol\":\"0x55534474\",\"decimals\":\"6\",\"isFrozen\":false}}", - "description": "Query metadata for asset ID 1984 (USDT). The `name` and `symbol` fields are returned as hex-encoded strings: `0x5465746865722055534454` decodes to 'Tether USD', `0x55534474` decodes to 'USDt'. Replace `1984` with any asset ID. For asset details (owner, supply, isSufficient), use `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/pallets/assets/storage/Asset?keys[]=1984`." - }, - { - "order": 5, - "action": "Query block information", - "working_directory": ".", - "commands": [ - "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/blocks/head\" | jq .", - "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/blocks/INSERT_BLOCK_NUMBER\" | jq ." - ], - "expected_output": "{\"number\":\"12345\",\"hash\":\"0x...\",\"parentHash\":\"0x...\",\"extrinsics\":[...]}", - "description": "The first command fetches the latest finalized block. The second fetches a specific block by number — replace `INSERT_BLOCK_NUMBER` with any block number. The response includes block number, hash, parentHash, stateRoot, extrinsicsRoot, and extrinsics list." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED 127.0.0.1:8080", - "cause": "Sidecar is not running (only applies when using the local instance).", - "resolution": "Start Sidecar in a separate terminal: SAS_SUBSTRATE_URL=wss://polkadot-asset-hub-rpc.polkadot.io substrate-api-sidecar. Wait for 'SAS listening' before sending curl requests. If using the public endpoint, this error does not apply." - }, - { - "pattern": "curl: (6) Could not resolve host / Connection reset / 502 Bad Gateway", - "cause": "The Sidecar endpoint is temporarily unavailable or the URL is incorrect.", - "resolution": "Verify the base URL. If using the public endpoint, check https://status.polkadot.network for outages and retry after a few minutes. If using a local instance, verify the WebSocket endpoint URL and that the Sidecar process is running." - }, - { - "pattern": "balance-info returns all-zero values for free/reserved/nonce", - "cause": "The queried account has no on-chain entry — it has never received any tokens.", - "resolution": "Fund the account at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation. Re-query the endpoint." - }, - { - "pattern": "asset-balances returns empty items array", - "cause": "The account does not hold the specified asset, or the asset ID is wrong.", - "resolution": "Verify the asset ID. USDT on Polkadot Hub is asset ID 1984. The account must hold the asset for a balance to appear." - } - ], - "supplementary_context": { - "description": "Load these pages when the user wants SDK-based querying as an alternative or needs to understand Sidecar response fields.", - "pages": [ - { - "slug": "chain-interactions-query-data-query-sdks", - "title": "Query On-Chain State with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md", - "relevance": "SDK-based alternative to Sidecar — query the same on-chain data using PAPI or Polkadot.js TypeScript code." - }, - { - "slug": "reference-tools-sidecar", - "title": "Sidecar REST API", - "url": "https://docs.polkadot.com/reference/tools/sidecar.md", - "relevance": "Full Sidecar API reference listing all available endpoints, query parameters, and response schemas." - }, - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "SDK approach to querying the same account balance data returned by the Sidecar balance-info endpoint." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance via REST without writing code", - "user_says": "Query the balance of a Polkadot Hub address using the REST API", - "actions": [ - "Use Parity's public Sidecar at https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io", - "Run curl against /accounts/{address}/balance-info", - "Divide the free field value by 10^10 to convert planck to DOT/PAS", - "Check the transferable field for the actual spendable balance" - ], - "result": "Account nonce and full balance breakdown (free, reserved, frozen, transferable) returned as JSON" - }, - { - "scenario": "Edge case: query USDT asset balance for an account", - "user_says": "How do I check if an account holds USDT on Polkadot Hub?", - "actions": [ - "Run curl against https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984", - "If items[] is non-empty, the account holds USDT (asset ID 1984)", - "Check the balance field in the response item (in smallest USDT unit, 6 decimals)" - ], - "result": "USDT balance for the account returned; empty items array if account holds no USDT" - } - ] - }, - { - "id": "calculate-transaction-fees-papi", - "title": "Estimate Transaction Fees with PAPI", - "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getEstimatedFees method. Use when you need to preview gas costs before submitting — for fee UIs, budget checks, or preflight validation. Returns estimated fee in planck and human-readable form, plus the total amount deducted (fee + transfer amount). Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getEstimatedFees PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required — fee estimation is read-only.", - "version": "1.1.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/calculate-transaction-fees.md" - ], - "primary_page": "chain-interactions/send-transactions/calculate-transaction-fees.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://pas-rpc.stakeworld.io/assethub" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir fee-calculator && cd fee-calculator", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'fee-calculator' and initialize it as an ESM Node.js project. The type=module flag is required — polkadot-api uses ESM imports." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "fee-calculator", - "commands": [ - "npm install polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install polkadot-api (PAPI) and TypeScript tooling." - }, - { - "order": 3, - "action": "Generate PAPI type descriptors", - "working_directory": "fee-calculator", - "commands": [ - "npx papi add polkadotTestNet -w wss://pas-rpc.stakeworld.io/assethub" - ], - "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. If the endpoint is unavailable, retry after a few minutes — the TestNet can be intermittently slow. Replace the -w URL if targeting a different network." - }, - { - "order": 4, - "action": "Fetch the fee estimation script", - "working_directory": "fee-calculator", - "reference_file": "papi-fee-calculator.ts", - "description": "Fetch the reference file and save it as 'papi-fee-calculator.ts'. Make these three substitutions: (1) Replace `INSERT_WS_ENDPOINT` with your WebSocket endpoint (e.g., `wss://pas-rpc.stakeworld.io/assethub`). (2) Replace `INSERT_ALICE_ADDRESS` with the sender's SS58 address. (3) Replace `INSERT_BOB_ADDRESS` with the recipient's SS58 address. Fee estimation does not sign or submit the transaction — any valid SS58 addresses work." - }, - { - "order": 5, - "action": "Run the fee estimation script", - "working_directory": "fee-calculator", - "commands": [ - "npx tsx papi-fee-calculator.ts" - ], - "expected_output": "Estimated fee: 0.001500 DOT\nTransaction amount: 1.000000 DOT\nTotal deducted: 1.001500 DOT", - "description": "Execute the script. It constructs an unsigned Balances.transfer_keep_alive transaction for 1 DOT (10^10 planck) and calls getEstimatedFees to retrieve the fee estimate without submitting. The output shows fee, transfer amount, and total in DOT units. Fees on Polkadot Hub TestNet are higher than Ethereum due to the ~1000 gwei base fee." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/send-transactions/calculate-transaction-fees", - "files": [ - { - "path": "papi-fee-calculator.ts", - "description": "Constructs a transfer_keep_alive tx and calls getEstimatedFees(); prints fee, transfer amount, and total in DOT units" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", - "cause": "PAPI type descriptors were not generated.", - "resolution": "Run 'npx papi add polkadotTestNet -w wss://pas-rpc.stakeworld.io/assethub' in the fee-calculator directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED on papi add or script start", - "cause": "Polkadot Hub TestNet endpoint is temporarily unavailable.", - "resolution": "Wait 2-5 minutes and retry. Try an alternative public endpoint if the issue persists." - }, - { - "pattern": "TypeError: tx.getEstimatedFees is not a function", - "cause": "Descriptor export name does not match the import, or polkadot-api version is below 1.0.0.", - "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api — version 1.0.0+ required." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "Project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the fee-calculator directory, then re-run." - } - ], - "supplementary_context": { - "description": "Load these pages when the user wants to submit the transaction after fee estimation or needs to understand fee mechanics.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit the same transaction whose fee was estimated — the natural next step." - }, - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Full PAPI reference including getPaymentInfo signatures and other typed transaction methods." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: preview fee before sending a transfer", - "user_says": "How much will it cost to send 1 DOT on Polkadot Hub?", - "actions": [ - "Scaffold 'fee-calculator/' as an ESM Node.js project", - "Install polkadot-api and tsx", - "Generate descriptors with npx papi add polkadotTestNet", - "Fetch papi-fee-calculator.ts; substitute WS endpoint, sender address, recipient address", - "Run npx tsx papi-fee-calculator.ts" - ], - "result": "Estimated fee in DOT plus total deducted amount printed; user can budget before submitting the transfer" - }, - { - "scenario": "Edge case: fee appears unusually high compared to Ethereum", - "user_says": "The fee estimate seems high compared to what I expected", - "actions": [ - "Explain that Polkadot Hub TestNet has a base fee of ~1000 gwei — significantly higher than Ethereum mainnet", - "Note that PAS/DOT testnet fees do not reflect real economic cost", - "Get testnet tokens from https://faucet.polkadot.io/ if the sender balance is insufficient for actual submission" - ], - "result": "User understands TestNet fee mechanics and obtains testnet tokens if needed" - } - ], - "project_structure": "fee-calculator/\n├── .papi/\n│ └── descriptors/\n├── papi-fee-calculator.ts\n└── package.json" - }, - { - "id": "transfer-assets-parachains-paraspell", - "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK", - "description": "Transfers assets cross-chain between Polkadot parachains using the ParaSpell XCM SDK. Covers the full workflow: install the SDK, build an XCM transfer, dry-run for feasibility, verify existential deposit requirements, estimate fees, and submit against Paseo testnet. Use when you need to move tokens programmatically between parachains (e.g., Asset Hub to another system parachain). Requires a funded Paseo testnet mnemonic. Trigger phrases: 'XCM transfer ParaSpell', 'cross-chain transfer Polkadot', 'transfer assets between parachains', 'send tokens XCM', 'ParaSpell SDK'. Uses dotenv for mnemonic security.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" - ], - "primary_page": "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm" - ], - "network": [ - "WebSocket RPC for Paseo Asset Hub (source): wss://sys.ibp.network/asset-hub-paseo", - "Destination parachain WebSocket RPC (e.g., wss://sys.ibp.network/people-paseo for People Chain Paseo)" - ], - "tokens": [ - "Paseo testnet PAS in the sender account — get from https://faucet.polkadot.io/ (select Paseo, then Asset Hub Paseo). Minimum 2 PAS recommended." - ], - "wallet": [ - "SR25519 account mnemonic phrase for the sender account, funded with Paseo testnet PAS" - ] - }, - "env_vars": [ - { - "name": "MNEMONIC", - "description": "12-word BIP39 mnemonic for the sender on Paseo Asset Hub. Must be funded with testnet PAS. Never commit to version control.", - "required": true - }, - { - "name": "SOURCE_RPC", - "description": "WebSocket RPC URL for the source parachain (e.g., wss://sys.ibp.network/asset-hub-paseo).", - "required": true - }, - { - "name": "DEST_ADDRESS", - "description": "SS58-encoded recipient address on the destination parachain.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Initialize the project", - "working_directory": ".", - "commands": [ - "mkdir paraspell-xcm-transfer && cd paraspell-xcm-transfer", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory 'paraspell-xcm-transfer' and initialize it as an ESM Node.js project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "paraspell-xcm-transfer", - "commands": [ - "npm install @paraspell/sdk-pjs @polkadot/api @polkadot/keyring dotenv", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Install the ParaSpell SDK (pjs variant), Polkadot.js API, keyring for signing, and dotenv for secure mnemonic loading." - }, - { - "order": 3, - "action": "Create .env and .gitignore for mnemonic", - "working_directory": "paraspell-xcm-transfer", - "commands": [ - "printf 'MNEMONIC=\\nSOURCE_RPC=wss://sys.ibp.network/asset-hub-paseo\\nDEST_ADDRESS=\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create a .env file with three placeholders and a .gitignore to exclude it. Stop here and ask the user to edit .env directly: fill MNEMONIC with their 12-word Paseo testnet mnemonic, confirm SOURCE_RPC, and set DEST_ADDRESS to the recipient SS58 address on the destination chain. Do NOT ask for the mnemonic in chat. Wait for confirmation before continuing." - }, - { - "order": 4, - "action": "Create the XCM transfer script", - "working_directory": "paraspell-xcm-transfer", - "description": "Create a file named 'xcm-transfer.ts' with the following content:\n\n```typescript\nimport \"dotenv/config\";\nimport { ApiPromise, WsProvider } from \"@polkadot/api\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { Builder } from \"@paraspell/sdk-pjs\";\n\nconst SOURCE_RPC = process.env.SOURCE_RPC!;\nconst MNEMONIC = process.env.MNEMONIC!;\nconst DEST_ADDRESS = process.env.DEST_ADDRESS!;\nconst SOURCE_CHAIN = \"AssetHubPaseo\";\nconst DEST_CHAIN = \"PeopleChainPaseo\";\nconst AMOUNT = \"10000000000\"; // 1 PAS (10 decimals)\n\nasync function main() {\n const api = await ApiPromise.create({ provider: new WsProvider(SOURCE_RPC) });\n const keyring = new Keyring({ type: \"sr25519\" });\n const sender = keyring.addFromMnemonic(MNEMONIC);\n console.log(\"Sender:\", sender.address);\n\n const dryResult = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).dryRun(sender);\n console.log(\"Dry-run:\", dryResult.success ? \"OK\" : \"FAILED\");\n if (!dryResult.success) { console.error(dryResult); process.exit(1); }\n\n const fee = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).getOriginXcmFee();\n console.log(\"Estimated fee:\", fee.toString());\n\n const tx = await Builder(api)\n .from(SOURCE_CHAIN).to(DEST_CHAIN)\n .currency({ symbol: \"PAS\" }).amount(AMOUNT)\n .address(DEST_ADDRESS).build();\n await tx.signAndSend(sender, { nonce: -1 }, ({ status, txHash }) => {\n console.log(\"Status:\", status.type);\n if (status.isFinalized) {\n console.log(\"Finalized:\", status.asFinalized.toString(), \"tx:\", txHash.toString());\n process.exit(0);\n }\n });\n}\nmain().catch((err) => { console.error(err); process.exit(1); });\n```\n\nSubstitutions: (1) Replace SOURCE_CHAIN ('AssetHubPaseo') and DEST_CHAIN ('PeopleChainPaseo') with the actual chain names from the ParaSpell supported chains list if your transfer uses different parachains — chain names are case-sensitive. (2) PAS has 10 decimals on Paseo; '10000000000' equals 1 PAS. Verify the decimal count for the asset being transferred." - }, - { - "order": 5, - "action": "Verify sender is funded and run the script", - "working_directory": "paraspell-xcm-transfer", - "commands": [ - "npx tsx xcm-transfer.ts" - ], - "expected_output": "Sender: ...\nDry-run: OK\nEstimated fee: ...\nStatus: InBlock\nFinalized: 0x...", - "description": "Before running, confirm the sender has at least 2 PAS on Paseo Asset Hub. If not, visit https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Execute the script. It dry-runs first — if dry-run fails, check the error before re-attempting. If the TestNet is unstable and the transaction does not finalize within 2 minutes, re-run the script." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: Insufficient funds / Inability to pay some fees", - "cause": "Sender account has insufficient PAS to cover transfer amount plus XCM execution fees.", - "resolution": "Fund the sender on Paseo Asset Hub at https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Wait 2 minutes for confirmation." - }, - { - "pattern": "Dry-run failed: destination existential deposit not met", - "cause": "Transfer amount is below the existential deposit on the destination chain, or recipient has no balance.", - "resolution": "Increase the transfer AMOUNT above the destination chain's existential deposit, or ensure the recipient already has a balance on the destination chain." - }, - { - "pattern": "Error: chain 'AssetHubPaseo' not found in ParaSpell", - "cause": "Chain name does not match the ParaSpell supported chain list for the SDK version installed.", - "resolution": "Check the supported chains list for your @paraspell/sdk-pjs version. Chain names are case-sensitive and version-specific. Update SOURCE_CHAIN and DEST_CHAIN in the script accordingly." - }, - { - "pattern": "Transaction submitted but never finalizes", - "cause": "Paseo TestNet instability — the TestNet can drop transactions under load.", - "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script — nonce: -1 fetches the current nonce automatically." - } - ], - "supplementary_context": { - "description": "Load these pages for background on XCM mechanics, fee estimation, or to debug a failing transfer.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", - "title": "XCM Fee Estimation", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "Detailed XCM fee estimation covering local, delivery, and remote execution components." - }, - { - "slug": "reference-tools-paraspell", - "title": "ParaSpell XCM SDK", - "url": "https://docs.polkadot.com/reference/tools/paraspell.md", - "relevance": "Full ParaSpell SDK reference including supported chains, asset IDs, and Builder API documentation." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", - "title": "Replay and Dry Run XCMs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "How to debug a failing XCM transfer by replaying it with Chopsticks before submitting to TestNet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: transfer PAS from Asset Hub Paseo to People Chain Paseo", - "user_says": "Transfer 1 PAS from Asset Hub to People Chain on Paseo testnet", - "actions": [ - "Scaffold 'paraspell-xcm-transfer/' as an ESM project", - "Install @paraspell/sdk-pjs, @polkadot/api, keyring, dotenv", - "Create .env; ask user to fill MNEMONIC and DEST_ADDRESS without asking in chat", - "Create xcm-transfer.ts with the inline script", - "Verify sender has 2+ PAS at https://faucet.polkadot.io/", - "Run npx tsx xcm-transfer.ts" - ], - "result": "Dry-run passes, fee estimated, transaction finalized, PAS transferred cross-chain" - }, - { - "scenario": "Edge case: dry-run fails with existential deposit error", - "user_says": "The dry-run fails saying the destination existential deposit is not met", - "actions": [ - "Check the existential deposit for the destination chain using the ParaSpell SDK", - "Increase AMOUNT in the script above the existential deposit threshold", - "Re-run xcm-transfer.ts" - ], - "result": "Dry-run passes once the transfer amount covers the destination existential deposit" - } - ] - }, - { - "id": "pay-fees-alternative-token", - "title": "Pay Transaction Fees with an Alternative Token", - "description": "Forks Polkadot Hub state locally with Chopsticks and submits a transaction where fees are paid in USDT (asset ID 1984) rather than native PAS, using PAPI's ChargeAssetTxPayment signed extension. Use when demonstrating or testing the asset-conversion fee proxy mechanism without spending real testnet tokens. Uses Alice's dev account via Chopsticks mock-signature mode — no faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Covers PAPI, Polkadot.js, and Subxt alternatives from the source page.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/pay-fees-with-different-tokens.md" - ], - "primary_page": "chain-interactions/send-transactions/pay-fees-with-different-tokens.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm", - "npx (bundled with npm v5.2+ — no separate install needed)" - ], - "network": [ - "Internet connection to fork chain state from wss://asset-hub-paseo.dotters.network — Chopsticks downloads state on first run" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Create project directory and install dependencies", - "working_directory": ".", - "commands": [ - "mkdir fee-proxy-demo && cd fee-proxy-demo", - "npm init -y && npm pkg set type=module", - "npm install @acala-network/chopsticks polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Create a new directory 'fee-proxy-demo', initialize as ESM, and install Chopsticks and PAPI. Chopsticks forks Polkadot Hub state locally; PAPI constructs and submits the fee-proxy transaction against the fork." - }, - { - "order": 2, - "action": "Create Chopsticks configuration", - "working_directory": "fee-proxy-demo", - "description": "Create a file named 'chopsticks.yml' with the following content:\n\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db.sqlite\nport: 8000\nmock-signature-host: true\n```\n\nThe 'mock-signature-host: true' setting allows transactions to be submitted with Alice's well-known dev key without a real private key — this is what makes the demo work without real credentials. The 'db' field caches forked state locally so subsequent runs start faster." - }, - { - "order": 3, - "action": "Start the Chopsticks fork", - "working_directory": "fee-proxy-demo", - "commands": [ - "npx @acala-network/chopsticks --config=chopsticks.yml" - ], - "expected_output": "Listening on port 8000", - "description": "Start the Chopsticks fork in a dedicated terminal. Chopsticks downloads Polkadot Hub state from the TestNet endpoint and starts a local fork at ws://localhost:8000. The first run downloads several MB of state — subsequent runs use db.sqlite. Once 'Listening on port 8000' appears, keep this running and open a second terminal for step 4." - }, - { - "order": 4, - "action": "Generate PAPI type descriptors for the local fork", - "working_directory": "fee-proxy-demo", - "commands": [ - "npx papi add localFork -w ws://localhost:8000" - ], - "description": "Run this command in a second terminal while Chopsticks (step 3) is still running. Generates compile-time type descriptors named 'localFork' for the forked chain. Produces the 'localFork' export in @polkadot-api/descriptors. The descriptors reflect the exact runtime version of the forked state." - }, - { - "order": 5, - "action": "Create the fee-proxy script", - "working_directory": "fee-proxy-demo", - "description": "Create a file named 'fee-proxy.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { localFork } from \"@polkadot-api/descriptors\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nconst ALICE = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\nconst BOB = \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\";\nconst USDT_ID = 1984;\n\nasync function main() {\n const client = createClient(getWsProvider(\"ws://localhost:8000\"));\n const api = client.getTypedApi(localFork);\n\n const transfer = api.tx.Balances.transfer_keep_alive({\n dest: { type: \"Id\", value: BOB },\n value: 1_000_000_000n,\n });\n\n // Wrap in asset-conversion fee proxy — pay fees in USDT\n const feeProxyTx = api.tx.AssetConversionTxPayment.asset_conversion_fee_proxy(\n USDT_ID,\n transfer\n );\n\n // Mock signer works because Chopsticks has mock-signature-host: true\n const mockSigner = getPolkadotSigner(\n ALICE,\n \"Sr25519\",\n async () => new Uint8Array(64)\n );\n\n const result = await feeProxyTx.signAndSubmit(mockSigner);\n console.log(\"Included in block:\", result.block.hash);\n console.log(\"Fees paid in USDT asset ID:\", USDT_ID);\n client.destroy();\n}\nmain().catch(console.error);\n```\n\nNote: The exact pallet name for fee proxy varies by runtime version. If 'AssetConversionTxPayment' is not in the typed API, check the source page for the correct pallet and extrinsic name for the chain version in your fork. The mock signer returns an empty signature accepted by Chopsticks." - }, - { - "order": 6, - "action": "Run the fee-proxy script against the local fork", - "working_directory": "fee-proxy-demo", - "commands": [ - "npx tsx fee-proxy.ts" - ], - "expected_output": "Included in block: 0x...\nFees paid in USDT asset ID: 1984", - "description": "Execute the script while Chopsticks is still running. The script submits the fee-proxy transaction; Chopsticks validates it against the forked state and includes it in a mock block. If the pallet name 'AssetConversionTxPayment' does not exist in localFork, inspect api.tx in the TypeScript IDE and check the source page for the correct name." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED ws://localhost:8000", - "cause": "Chopsticks is not running.", - "resolution": "Start Chopsticks first: npx @acala-network/chopsticks --config=chopsticks.yml. Wait for 'Listening on port 8000' before running the script or the papi add command." - }, - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or localFork is undefined", - "cause": "PAPI type descriptors were not generated against the Chopsticks fork endpoint.", - "resolution": "With Chopsticks running, execute: npx papi add localFork -w ws://localhost:8000. Then re-run the script." - }, - { - "pattern": "api.tx.AssetConversionTxPayment is undefined", - "cause": "The forked chain version uses a different pallet name for asset-based fee payment.", - "resolution": "Check available pallets by inspecting api.tx in a TypeScript IDE, or consult the source page for the correct pallet name. The pallet may be named 'AssetTxPayment' depending on the runtime version." - }, - { - "pattern": "Alice has insufficient USDT balance to pay fees", - "cause": "Alice's account in the forked state does not hold USDT (asset ID 1984).", - "resolution": "Use Chopsticks' dev_setStorage RPC method to seed Alice's USDT asset balance in the forked state, or identify a USDT-holding account in the forked state and use that address instead." - } - ], - "supplementary_context": { - "description": "Load these pages to understand fee mechanics, Chopsticks setup, or to compare with standard fee estimation.", - "pages": [ - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Full Chopsticks reference including all config options, RPC override methods, and mock-signature-host details." - }, - { - "slug": "chain-interactions-send-transactions-calculate-transaction-fees", - "title": "Calculate Transaction Fees", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md", - "relevance": "How to estimate fees for a standard transaction — compare with the fee proxy approach to understand the difference." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: demonstrate paying fees in USDT on a local fork", - "user_says": "Show me how to pay Polkadot Hub transaction fees in USDT instead of PAS", - "actions": [ - "Create 'fee-proxy-demo/' with ESM Node.js init", - "Install Chopsticks and polkadot-api", - "Create chopsticks.yml pointing to wss://asset-hub-paseo.dotters.network with mock-signature-host: true", - "Start Chopsticks in a dedicated terminal; wait for 'Listening on port 8000'", - "Generate PAPI descriptors with npx papi add localFork -w ws://localhost:8000", - "Create fee-proxy.ts with inline script; run npx tsx fee-proxy.ts" - ], - "result": "Transaction included in a local mock block; fees deducted from Alice's USDT balance instead of PAS" - }, - { - "scenario": "Edge case: Alice has no USDT in the forked state", - "user_says": "The script fails saying Alice has no USDT balance", - "actions": [ - "Use Chopsticks' dev_setStorage RPC to set Alice's USDT asset balance in the forked state", - "Alternatively, query the forked state for an account with USDT balance and use that address as sender", - "Re-run fee-proxy.ts" - ], - "result": "Script succeeds once Alice's USDT balance is seeded or a funded account is substituted" - } - ] - }, - { - "id": "run-parachain-rpc-node", - "title": "Run a Parachain RPC Node", - "description": "Runs a Polkadot system parachain RPC node (using People Chain as example) in archive or pruned mode via Docker or systemd. Covers Docker image selection, binary installation, systemd service configuration, and sync verification. A substitution table adapts the steps for Collectives, BridgeHub, and other system parachains. Use when you need a self-hosted parachain RPC endpoint. Trigger phrases: 'run parachain node', 'People Chain RPC node', 'parachain RPC Docker', 'set up system parachain node', 'run parachain RPC systemd'. No tokens required.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "node-infrastructure/run-a-node/parachain-rpc.md" - ], - "primary_page": "node-infrastructure/run-a-node/parachain-rpc.md", - "prerequisites": { - "runtime": [ - "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", - "Disk: minimum 500 GB SSD for pruned node; 2+ TB SSD for archive node", - "RAM: minimum 8 GB; 16 GB recommended for archive nodes", - "CPU: 4+ cores recommended" - ], - "network": [ - "Open TCP ports: 30333 (P2P), 9944 (WebSocket RPC), 9615 (Prometheus metrics, optional)", - "Stable broadband connection for initial sync (several hundred GB download)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Choose deployment method and sync type", - "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (steps 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (requires 2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nParachain substitution table — replace chain names and image tags accordingly:\n- People Chain Polkadot: --chain=people-polkadot, relay --chain=polkadot\n- Collectives Polkadot: --chain=collectives-polkadot, relay --chain=polkadot\n- BridgeHub Polkadot: --chain=bridge-hub-polkadot, relay --chain=polkadot\n- People Chain Paseo: --chain=people-paseo, relay --chain=paseo\nAll use the parity/polkadot-parachain Docker image." - }, - { - "order": 2, - "action": "Run the parachain node via Docker", - "working_directory": ".", - "commands": [ - "docker pull parity/polkadot-parachain:stable", - "mkdir -p /var/lib/people-chain", - "docker run -d --name people-chain-rpc --restart unless-stopped -p 30333:30333 -p 9944:9944 -p 9615:9615 -v /var/lib/people-chain:/data parity/polkadot-parachain:stable --chain=people-polkadot --base-path=/data --rpc-external --rpc-port=9944 --rpc-cors=all --prometheus-external --prometheus-port=9615 --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast" - ], - "description": "Pull the stable polkadot-parachain image and start the People Chain RPC node. Key arguments: --chain=people-polkadot selects the People Chain spec; --base-path=/data stores chain data in the mounted volume; --state-pruning=1000 --blocks-pruning=1000 keeps the last 1000 blocks (change both to 'archive' for a full archive node); --rpc-external exposes the WebSocket RPC; the -- separator and --chain=polkadot configure the embedded relay chain client. Replace /var/lib/people-chain with your data directory. For Paseo testnet People Chain, use --chain=people-paseo and --chain=paseo after the -- separator." - }, - { - "order": 3, - "action": "Run the parachain node via systemd (binary method alternative)", - "working_directory": ".", - "description": "For the systemd binary method (skip if using Docker in step 2): (1) Create system user: sudo useradd --system --no-create-home --shell /usr/sbin/nologin polkadot. (2) Create data dir: sudo mkdir -p /var/lib/people-chain && sudo chown polkadot:polkadot /var/lib/people-chain. (3) Download polkadot-parachain binary from https://github.com/paritytech/polkadot-sdk/releases (stable, amd64 or arm64) and install: sudo install -m 755 polkadot-parachain /usr/local/bin/. (4) Create /etc/systemd/system/people-chain-rpc.service:\n\n[Unit]\nDescription=People Chain RPC Node\nAfter=network.target\n\n[Service]\nUser=polkadot\nExecStart=/usr/local/bin/polkadot-parachain --chain=people-polkadot --base-path=/var/lib/people-chain --rpc-external --rpc-port=9944 --rpc-cors=all --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n\n(5) Enable and start: sudo systemctl daemon-reload && sudo systemctl enable people-chain-rpc && sudo systemctl start people-chain-rpc. Apply the same chain substitutions as in step 2." - }, - { - "order": 4, - "action": "Monitor sync progress", - "working_directory": ".", - "commands": [ - "docker logs -f people-chain-rpc 2>&1 | grep -E 'Syncing|Idle|Best|Peers'", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f people-chain-rpc' with 'sudo journalctl -fu people-chain-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." - }, - { - "order": 5, - "action": "Verify the RPC endpoint is operational", - "working_directory": ".", - "commands": [ - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_chain\",\"params\":[]}' http://localhost:9944", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"result\":\"Polkadot People\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", - "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot People' for the mainnet People Chain. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete before pointing applications at this endpoint." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: No space left on device / database write error", - "cause": "The data volume has run out of disk space.", - "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=1000). Check usage with: du -sh /var/lib/people-chain" - }, - { - "pattern": "EADDRINUSE port 9944 or 30333", - "cause": "Another process is already using the required ports.", - "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." - }, - { - "pattern": "Peers count stays at 0 after several minutes", - "cause": "P2P port 30333 is blocked by firewall, or bootnodes are temporarily unavailable.", - "resolution": "Open TCP port 30333: sudo ufw allow 30333/tcp. For cloud VMs, update the security group to allow inbound TCP on 30333. If peers still do not connect after 10 minutes, check the Polkadot network status page." - }, - { - "pattern": "Sync stalls at the same block for more than 30 minutes", - "cause": "Node cannot find peers with required state, or the embedded relay chain client is not syncing.", - "resolution": "Restart the container or service: docker restart people-chain-rpc or sudo systemctl restart people-chain-rpc. If stalling persists, try switching sync mode: replace --sync=fast with --sync=warp." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to add TLS/WSS or run the Polkadot Hub RPC node specifically.", - "pages": [ - { - "slug": "node-infrastructure-run-a-node-polkadot-hub-rpc", - "title": "Run an RPC Node for Polkadot Hub", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/polkadot-hub-rpc.md", - "relevance": "How to run an RPC node for Polkadot Hub specifically — different chain spec and optional Ethereum RPC adapter." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", - "title": "Set Up Secure WebSocket", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-full-node", - "title": "Set Up a Node", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md", - "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run a pruned People Chain RPC node via Docker", - "user_says": "Run a People Chain RPC node on Polkadot using Docker", - "actions": [ - "Pull parity/polkadot-parachain:stable", - "Create data directory /var/lib/people-chain", - "Run docker with --chain=people-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", - "Monitor sync with docker logs and system_syncState RPC", - "Verify with system_chain and system_health once currentBlock equals highestBlock" - ], - "result": "People Chain RPC node running, synced, serving requests on ws://localhost:9944" - }, - { - "scenario": "Edge case: node stalls at the same block for 30+ minutes", - "user_says": "The node has been stuck at block 12345 for 30 minutes", - "actions": [ - "Check both parachain and relay chain sync progress separately in the logs", - "Verify peers > 0 via system_health and that port 30333 is open", - "Restart: docker restart people-chain-rpc", - "If stalling persists after restart, switch to --sync=warp" - ], - "result": "Node resumes syncing after restart or sync mode change" - } - ] - }, - { - "id": "replay-dry-run-xcm-chopsticks", - "title": "Replay and Dry-Run XCMs Using Chopsticks", - "description": "Replays and dry-runs historical XCMs using Chopsticks multi-chain fork mode with PAPI TypeScript scripts. Covers building a local Wasm override for runtime logging, forking multiple chains via 'chopsticks xcm', extracting XCM call data from Subscan, submitting an XCM replay against the local fork with replay-xcm.ts, and simulating execution without state changes via dry-run-call.ts using DryRunApi.dry_run_call. Use when debugging a failing XCM, inspecting emitted events, or verifying fee coverage before resubmitting. Requires Rust build environment for Wasm compilation. Trigger phrases: 'replay XCM', 'dry-run XCM Chopsticks', 'debug XCM Polkadot', 'preview XCM execution', 'XCM dry run local fork'.", - "version": "1.1.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" - ], - "primary_page": "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "prerequisites": { - "runtime": [ - "Rust toolchain with cargo (to compile polkadot-fellows/runtimes for Wasm override)", - "Node.js v18+ and npm", - "npx (bundled with npm v5.2+)" - ], - "network": [ - "Internet access to fetch chain state for Chopsticks fork", - "Subscan or Polkadot.js Apps to locate and decode the XCM extrinsic call data" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Create project directory and install dependencies", - "working_directory": ".", - "commands": [ - "mkdir -p replay-xcm-tests && cd replay-xcm-tests", - "npm init -y && npm pkg set type=module", - "npm install -g @acala-network/chopsticks@latest", - "npm install --save-dev typescript @types/node tsx", - "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers", - "npx tsc --init" - ], - "description": "Create the working directory, initialize an ESM Node.js project, and install all dependencies. Chopsticks is installed globally to avoid version conflicts. polkadot-api, @polkadot-labs/hdkd, and @polkadot-labs/hdkd-helpers are used by the replay and dry-run scripts." - }, - { - "order": 2, - "action": "Create .env with block numbers for the fork", - "working_directory": "replay-xcm-tests", - "description": "Create a file named '.env' in replay-xcm-tests with the following content:\n\n```text\nPOLKADOT_BLOCK_NUMBER=26481107\nPOLKADOT_HUB_BLOCK_NUMBER=9079591\nACALA_BLOCK_NUMBER=8826385\n```\n\nReplace these block numbers with the heights just *before* the XCM you want to replay was sent. The block numbers anchor the Chopsticks fork to the exact chain state at the time of the original XCM execution. Using the block immediately before the XCM block ensures the fork starts from the correct pre-execution state." - }, - { - "order": 3, - "action": "Build the Polkadot Hub Wasm override for runtime logging", - "working_directory": "replay-xcm-tests", - "commands": [ - "git clone git@github.com:polkadot-fellows/runtimes.git", - "cd runtimes && cargo build --release -p asset-hub-polkadot-runtime", - "mkdir -p ../wasms", - "cp target/release/wbuild/asset-hub-polkadot-runtime/asset_hub_polkadot_runtime.compact.compressed.wasm ../wasms/", - "cd .." - ], - "description": "Clone polkadot-fellows/runtimes and compile the Polkadot Hub runtime with the release profile. The compiled Wasm enables full runtime execution logs in Chopsticks, which are essential for tracing XCM execution flow. The cargo build may take 10-30 minutes on first run. Copy the resulting compressed Wasm to the wasms/ directory in replay-xcm-tests." - }, - { - "order": 4, - "action": "Download and configure the Chopsticks config for Polkadot Hub", - "working_directory": "replay-xcm-tests", - "commands": [ - "mkdir -p configs", - "wget https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot-asset-hub.yml -O configs/polkadot-asset-hub-override.yaml" - ], - "description": "Download the Polkadot Hub Chopsticks config and add the Wasm override and runtime log level. Edit 'configs/polkadot-asset-hub-override.yaml' to add these lines:\n\n```yaml\nruntime-log-level: 5\nwasm-override: wasms/asset_hub_polkadot_runtime.compact.compressed.wasm\n```\n\nThe `runtime-log-level: 5` enables verbose runtime execution logs needed to trace XCM flow. The `wasm-override` points to the locally built Wasm from step 3." - }, - { - "order": 5, - "action": "Start the multi-chain Chopsticks fork", - "working_directory": "replay-xcm-tests", - "interactive": true, - "commands": [ - "npx @acala-network/chopsticks xcm -r polkadot -p configs/polkadot-asset-hub-override.yaml -p acala" - ], - "expected_output": "Polkadot Hub RPC on http://localhost:8000\nAcala RPC on http://localhost:8001\nPolkadot RPC on http://localhost:8002", - "description": "Start Chopsticks in XCM mode in a dedicated terminal. It forks Polkadot relay chain, Polkadot Hub (with your custom Wasm override), and Acala locally. The first run downloads several MB of chain state — subsequent runs use the cached state. Wait for all three RPC endpoint announcements before proceeding to step 6. Keep this terminal running throughout the remaining steps." - }, - { - "order": 6, - "action": "Generate PAPI type descriptors for the local fork", - "working_directory": "replay-xcm-tests", - "commands": [ - "npx papi add polkadotHub -w ws://localhost:8000" - ], - "description": "Generate compile-time PAPI type descriptors from the local Chopsticks Polkadot Hub fork. This produces the 'polkadotHub' export in @polkadot-api/descriptors, used by both replay-xcm.ts and dry-run-call.ts. Chopsticks must be running (step 5) for this command to succeed." - }, - { - "order": 7, - "action": "Locate the XCM call data on Subscan", - "working_directory": ".", - "interactive": true, - "description": "This step is manual. Open Subscan for the source chain (e.g., https://assethub-polkadot.subscan.io). Navigate to the extrinsic that sent the XCM you want to replay. Decode the call data and copy the hex-encoded call payload (starts with '0x'). For the example XCM in the reference scripts, the call data is:\n\n```\n0x1f0803010100411f0300010100fc39fcf04a8071b7409823b7c82427ce67910c6ed80aa0e5093aff234624c8200304000002043205011f0092e81d790000000000\n```\n\nSave the hex string — you will substitute it in steps 8 and 9." - }, - { - "order": 8, - "action": "Fetch and run the XCM replay script", - "working_directory": "replay-xcm-tests", - "reference_file": "replay-xcm.ts", - "commands": [ - "npx tsx replay-xcm.ts" - ], - "expected_output": "👀 Executing XCM: {...}\n📦 Included in block #...: 0x...\n📣 Event: ...", - "description": "Fetch the reference file and save it as 'replay-xcm.ts'. In the file, replace the hardcoded call data hex string in `Binary.fromHex(...)` with the hex string you copied in step 7. The script uses Alice's dev account (DEV_PHRASE) as the signer — this works against the local Chopsticks fork because mock signatures are accepted. Run the script; it submits the XCM to the local fork and logs emitted events. Chopsticks must be running (step 5)." - }, - { - "order": 9, - "action": "Fetch and run the dry-run script", - "working_directory": "replay-xcm-tests", - "reference_file": "dry-run-call.ts", - "commands": [ - "npx tsx dry-run-call.ts" - ], - "expected_output": "{ executionResult: { type: 'Success', value: {...} }, ... }", - "description": "Fetch the reference file and save it as 'dry-run-call.ts'. Replace the hardcoded call data hex string in `Binary.fromHex(...)` with the same hex from step 7. The script calls DryRunApi.dry_run_call with XCM version 5 — this simulates execution without committing any state changes. Inspect the output for executionResult: a 'Success' type means the XCM would execute successfully; an 'Err' type reveals the failure reason (e.g., TooExpensive, Barrier blocked). Chopsticks must be running (step 5)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms", - "files": [ - { - "path": "replay-xcm.ts", - "description": "Signs and submits an XCM transaction against a local Chopsticks fork using Alice's dev account; logs block inclusion and events" - }, - { - "path": "dry-run-call.ts", - "description": "Calls DryRunApi.dry_run_call to simulate XCM execution without state changes; uses XCM version 5 and logs the full dry-run result" - } - ] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED ws://localhost:8000", - "cause": "Chopsticks is not running or not yet ready.", - "resolution": "Start Chopsticks in a dedicated terminal: npx @acala-network/chopsticks xcm -r polkadot -p configs/polkadot-asset-hub-override.yaml -p acala. Wait for all three RPC endpoint announcements before running scripts." - }, - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or 'polkadotHub' export", - "cause": "PAPI type descriptors were not generated from the local fork.", - "resolution": "Ensure Chopsticks is running, then run: npx papi add polkadotHub -w ws://localhost:8000. Regenerate descriptors if the fork was restarted since the last generation." - }, - { - "pattern": "executionResult: { type: 'Err', value: { type: 'TooExpensive' } }", - "cause": "The XCM message includes insufficient fees to cover execution on the destination chain.", - "resolution": "Increase the fee amount in the BuyExecution instruction of the XCM message before re-submitting. Check the estimate-xcm-fees-teleport skill for how to calculate required fees." - }, - { - "pattern": "cargo build fails or polkadot-fellows/runtimes won't compile", - "cause": "Rust toolchain version mismatch or missing build dependencies.", - "resolution": "Ensure the Rust toolchain matches the toolchain-version in polkadot-fellows/runtimes (typically specified in rust-toolchain.toml). Run 'rustup update' and retry. The first build may take 30+ minutes." - }, - { - "pattern": "Chopsticks fork diverges or shows 'Block not found'", - "cause": "The forked block is too old or the cached state (db.sqlite) is stale.", - "resolution": "Delete db.sqlite and restart Chopsticks to re-fork from the configured block numbers. Verify the POLKADOT_HUB_BLOCK_NUMBER in .env is correct and the block predates the XCM you want to replay." - } - ], - "supplementary_context": { - "description": "Load these pages for XCM fee estimation, the ParaSpell transfer skill, or Chopsticks configuration reference.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", - "title": "XCM Fee Estimation", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "How to estimate XCM fees before submission — helps diagnose BuyExecution failures in a dry-run." - }, - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Full Chopsticks reference including all config options, block forking, and RPC override methods." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", - "title": "Transfer Assets Between Parachains", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "relevance": "How to submit a real XCM transfer between parachains — use after debugging with Chopsticks." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: debug a failing XCM by dry-running it locally", - "user_says": "An XCM transfer I submitted failed. How can I debug it without spending more tokens?", - "actions": [ - "Create 'replay-xcm-tests/' and install all dependencies including Chopsticks", - "Create .env with block numbers from just before the failed XCM", - "Build Polkadot Hub Wasm from polkadot-fellows/runtimes (enables runtime logs)", - "Configure Chopsticks with wasm-override and runtime-log-level: 5", - "Start Chopsticks XCM fork in a dedicated terminal", - "Generate PAPI descriptors from local fork: npx papi add polkadotHub -w ws://localhost:8000", - "Locate the failed XCM on Subscan and copy the encoded call data hex", - "Fetch dry-run-call.ts; substitute the call data hex", - "Run npx tsx dry-run-call.ts; inspect executionResult for the failure reason" - ], - "result": "Dry-run reveals the failure reason (e.g., TooExpensive, Barrier blocked) and emitted events without spending tokens" - }, - { - "scenario": "Edge case: replaying the XCM against the local fork for full event tracing", - "user_says": "I need to see exactly which events fire when the XCM executes", - "actions": [ - "Complete steps 1-6 to set up the Chopsticks multi-chain fork", - "Fetch replay-xcm.ts; substitute the call data hex", - "Run npx tsx replay-xcm.ts", - "Inspect the logged events from signSubmitAndWatch to trace the full XCM execution path" - ], - "result": "All emitted events (including XCM execution events on destination chain) logged to console" - } - ], - "project_structure": "replay-xcm-tests/\n├── .env\n├── .papi/\n│ └── descriptors/\n├── configs/\n│ └── polkadot-asset-hub-override.yaml\n├── wasms/\n│ └── asset_hub_polkadot_runtime.compact.compressed.wasm\n├── dry-run-call.ts\n├── replay-xcm.ts\n├── tsconfig.json\n└── package.json" - }, - { - "id": "estimate-xcm-fees-teleport", - "title": "Estimate XCM Fees for Asset Teleport", - "description": "Estimates all three fee components for an XCM teleport: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses Chopsticks to fork both Polkadot Hub TestNet and People Chain locally, then calls PAPI fee estimation methods against the forks. Use when you need to budget XCM fees before submitting a teleport, or when a BuyExecution instruction is failing due to insufficient fees. No real tokens required — Chopsticks mock-signature mode is used. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" - ], - "primary_page": "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "prerequisites": { - "runtime": [ - "Node.js v18+ and npm", - "npx (bundled with npm v5.2+)" - ], - "network": [ - "Internet connection to fork chain state from Polkadot Hub TestNet: wss://asset-hub-paseo.dotters.network", - "Internet connection to fork People Chain TestNet state" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Create project directory and install dependencies", - "working_directory": ".", - "commands": [ - "mkdir xcm-fee-estimate && cd xcm-fee-estimate", - "npm init -y && npm pkg set type=module", - "npm install @acala-network/chopsticks polkadot-api", - "npm install --save-dev @types/node tsx typescript" - ], - "description": "Create 'xcm-fee-estimate' as an ESM Node.js project and install Chopsticks (for local chain forks) and polkadot-api (PAPI, for fee estimation calls)." - }, - { - "order": 2, - "action": "Create Chopsticks configs for source and destination chains", - "working_directory": "xcm-fee-estimate", - "description": "Create two Chopsticks config files:\n\n```yaml\nFile 1 — 'chopsticks-hub.yml':\nendpoint: wss://asset-hub-paseo.dotters.network\ndb: ./db-hub.sqlite\nport: 8000\nmock-signature-host: true\n\nFile 2 — 'chopsticks-people.yml':\nendpoint: INSERT_PEOPLE_CHAIN_TESTNET_WS\ndb: ./db-people.sqlite\nport: 8001\nmock-signature-host: true\n```\n\nReplace INSERT_PEOPLE_CHAIN_TESTNET_WS with the People Chain TestNet WebSocket endpoint. The two forks run on different ports (8000 and 8001) so they can be active simultaneously." - }, - { - "order": 3, - "action": "Start both Chopsticks forks", - "working_directory": "xcm-fee-estimate", - "commands": [ - "npx @acala-network/chopsticks --config=chopsticks-hub.yml", - "npx @acala-network/chopsticks --config=chopsticks-people.yml" - ], - "expected_output": "Listening on port 8000\nListening on port 8001", - "description": "Start each Chopsticks instance in a separate terminal. The first forks Polkadot Hub TestNet on port 8000; the second forks People Chain on port 8001. Both must be running before proceeding to the next step. The first run of each downloads several MB of state and caches it to the db files." - }, - { - "order": 4, - "action": "Generate PAPI type descriptors for both forks", - "working_directory": "xcm-fee-estimate", - "commands": [ - "npx papi add hubFork -w ws://localhost:8000", - "npx papi add peopleFork -w ws://localhost:8001" - ], - "description": "Run these commands in a third terminal while both Chopsticks forks (step 3) are running. Generates compile-time PAPI descriptors named 'hubFork' and 'peopleFork'. Both descriptors must be generated before writing the estimation script." - }, - { - "order": 5, - "action": "Create the fee estimation script", - "working_directory": "xcm-fee-estimate", - "description": "Create a file named 'estimate-xcm-fees.ts' with the following content:\n\n```typescript\nimport { createClient } from \"polkadot-api\";\nimport { getWsProvider } from \"polkadot-api/ws-provider/node\";\nimport { hubFork } from \"@polkadot-api/descriptors\";\nimport { peopleFork } from \"@polkadot-api/descriptors\";\n\nconst SENDER = \"INSERT_SENDER_SS58_ADDRESS\";\nconst RECIPIENT = \"INSERT_RECIPIENT_SS58_ADDRESS\";\nconst AMOUNT = 1_000_000_000_000_000_000n; // 1 PAS (18 decimals)\n\nasync function main() {\n const hubClient = createClient(getWsProvider(\"ws://localhost:8000\"));\n const peopleClient = createClient(getWsProvider(\"ws://localhost:8001\"));\n const hubApi = hubClient.getTypedApi(hubFork);\n const peopleApi = peopleClient.getTypedApi(peopleFork);\n\n // 1. Local execution fee on Polkadot Hub (source)\n const teleportTx = hubApi.tx.PolkadotXcm.limited_teleport_assets({\n dest: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n beneficiary: { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } },\n assets: { type: \"V4\", value: [{ id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }] },\n fee_asset_item: 0,\n weight_limit: { type: \"Unlimited\" }\n });\n const localFeeInfo = await teleportTx.getPaymentInfo(SENDER);\n console.log(\"Local execution fee (planck):\", localFeeInfo.partial_fee.toString());\n\n // 2. Delivery fee\n const deliveryFee = await hubApi.apis.XcmPaymentApi.query_delivery_fees(\n { type: \"V4\", value: { parents: 0, interior: { type: \"X1\", value: { type: \"Parachain\", value: 1004 } } } },\n { type: \"V4\", value: [] }\n );\n console.log(\"Delivery fee assets:\", JSON.stringify(deliveryFee.value));\n\n // 3. Remote execution fee on People Chain (destination)\n const remoteWeight = await peopleApi.apis.XcmPaymentApi.query_xcm_weight(\n { type: \"V4\", value: [{ type: \"ReceiveTeleportedAsset\", value: [] }, { type: \"BuyExecution\", value: { fees: { id: { parents: 1, interior: { type: \"Here\" } }, fun: { type: \"Fungible\", value: AMOUNT } }, weight_limit: { type: \"Unlimited\" } } }, { type: \"DepositAsset\", value: { assets: { type: \"Wild\", value: { type: \"AllCounted\", value: 1 } }, beneficiary: { parents: 0, interior: { type: \"X1\", value: { type: \"AccountId32\", value: { network: undefined, id: RECIPIENT } } } } } }] }\n );\n const remoteAsset = { parents: 1, interior: { type: \"Here\" } };\n const remoteFee = await peopleApi.apis.XcmPaymentApi.query_weight_to_asset_fee(remoteWeight.value, { type: \"V4\", value: { Concrete: remoteAsset } });\n console.log(\"Remote execution fee (planck):\", remoteFee.value?.toString());\n```\n\n hubClient.destroy();\n peopleClient.destroy();\n}\nmain().catch(console.error);\n\nSubstitutions: (1) Replace INSERT_SENDER_SS58_ADDRESS with the sender SS58 address on Polkadot Hub. (2) Replace INSERT_RECIPIENT_SS58_ADDRESS with the recipient SS58 address on People Chain. (3) The XCM V4 MultiLocation structures and parachain ID (1004 for People Chain) are shown as examples — consult the source page for exact structures for other destination chains. (4) PAS has 18 decimals on Polkadot Hub TestNet (TestNet Chain ID: 420420417)." - }, - { - "order": 6, - "action": "Run the fee estimation script", - "working_directory": "xcm-fee-estimate", - "commands": [ - "npx tsx estimate-xcm-fees.ts" - ], - "expected_output": "Local execution fee (planck): 1500000000000000\nDelivery fee assets: [...]\nRemote execution fee (planck): 500000000000000", - "description": "Execute the script while both Chopsticks forks are running. The output shows all three fee components in planck. Divide by 10^18 to convert to PAS. The sum of all three components is the total fee budget to include in the XCM BuyExecution instruction. If any XcmPaymentApi method is not available, check the source page for the correct API method names for your chain runtime version." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ECONNREFUSED ws://localhost:8000 or ws://localhost:8001", - "cause": "One or both Chopsticks forks are not running.", - "resolution": "Start each Chopsticks instance in a separate terminal. Run 'npx @acala-network/chopsticks --config=chopsticks-hub.yml' and 'npx @acala-network/chopsticks --config=chopsticks-people.yml'. Wait for 'Listening on port 8000/8001' before proceeding." - }, - { - "pattern": "XcmPaymentApi method not found / query_delivery_fees undefined", - "cause": "The chain runtime version does not implement the XcmPaymentApi runtime API at this method name.", - "resolution": "Check the source page for the correct API method names for your runtime version. The API surface changed between XCM V3 and V4. Verify the chain runtime version in the Chopsticks fork logs." - }, - { - "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or hubFork/peopleFork undefined", - "cause": "PAPI descriptors were not generated for one or both forks.", - "resolution": "With both Chopsticks forks running, execute: npx papi add hubFork -w ws://localhost:8000 and npx papi add peopleFork -w ws://localhost:8001. Then re-run the script." - }, - { - "pattern": "Fee estimate returns 0 or undefined for remote execution fee", - "cause": "The XCM message structure passed to query_xcm_weight does not match the expected format for the chain runtime.", - "resolution": "Inspect the source page for the exact XCM instruction sequence expected by the destination chain. Verify that the MultiLocation structures use the correct XCM version (V3 vs V4) matching the chain runtime." - } - ], - "supplementary_context": { - "description": "Load these pages for XCM transfer execution, Chopsticks configuration, or debug techniques.", - "pages": [ - { - "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", - "title": "Replay and Dry Run XCMs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "How to dry-run a full XCM message after fee estimation to verify it executes correctly before submitting." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", - "title": "Transfer Assets Between Parachains", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", - "relevance": "How to submit the actual XCM teleport transfer once fees are estimated and verified." - }, - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Full Chopsticks reference including multi-chain fork setup, config options, and mock-signature details." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: calculate the total fee budget needed for a PAS teleport to People Chain", - "user_says": "How much PAS do I need to include in the BuyExecution for a teleport from Polkadot Hub to People Chain?", - "actions": [ - "Create 'xcm-fee-estimate/' as ESM Node.js project and install Chopsticks and polkadot-api", - "Create chopsticks-hub.yml (port 8000) and chopsticks-people.yml (port 8001) with mock-signature-host: true", - "Start both Chopsticks forks in separate terminals", - "Generate PAPI descriptors: npx papi add hubFork and npx papi add peopleFork", - "Create estimate-xcm-fees.ts with the inline script; substitute sender and recipient addresses", - "Run npx tsx estimate-xcm-fees.ts and sum the three fee components" - ], - "result": "All three fee components printed in planck; sum used as BuyExecution fee budget for the teleport" - }, - { - "scenario": "Edge case: remote execution fee query fails with XcmPaymentApi not found", - "user_says": "The script errors with 'query_xcm_weight is not a function' on the People Chain fork", - "actions": [ - "Check the People Chain runtime version in Chopsticks logs", - "Consult the source page for the XcmPaymentApi method names for that runtime version", - "Update the API call in estimate-xcm-fees.ts to match the correct method signature" - ], - "result": "Remote execution fee estimated using the correct XcmPaymentApi method for the chain runtime" - } - ] - }, - { - "id": "set-up-parachain-template", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + } + ] + }, + "title": "Query Account Information with SDKs", + "version": "1.0.2", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Queries on-chain storage on Polkadot Hub using PAPI TypeScript library: System.Account for account balance and Assets pallet for USDT (asset ID 1984) metadata and balance. Use when you need to read chain state programmatically beyond account info — including asset balances and pallet storage. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'query chain state', 'read on-chain storage', 'get asset balance', 'check USDT balance on Asset Hub'. No tokens required — read-only.", + "env_vars": [], + "error_patterns": [ + { + "cause": "PAPI type descriptors were not generated.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "resolution": "Run 'npx papi add pah -n polkadot_asset_hub' in the project directory." + }, + { + "cause": "Incorrect or unreachable WebSocket endpoint.", + "pattern": "WebSocket connection failed / ECONNREFUSED", + "resolution": "Verify the WebSocket URL. For Polkadot Hub mainnet use 'wss://polkadot-asset-hub-rpc.polkadot.io', for testnet use 'wss://asset-hub-paseo.dotters.network'." + }, + { + "cause": "The address does not hold the queried asset (USDT asset ID 1984).", + "pattern": "query-asset returns null or empty for the address", + "resolution": "This is expected for addresses with no USDT balance. The metadata query will still succeed; only the per-address balance query returns empty." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'papi-query-example/' ESM project", + "Install polkadot-api and tsx", + "Run npx papi add pah -n polkadot_asset_hub", + "Fetch query-balance.ts; substitute endpoint and address", + "Run npx tsx query-balance.ts" + ], + "result": "Account nonce, free/reserved/frozen PAS balance printed to console", + "scenario": "Common scenario: read native balance of an address", + "user_says": "Query the DOT/PAS balance for address 15abc... on Polkadot Hub" + }, + { + "actions": [ + "Fetch query-asset.ts and follow standard setup", + "In query-asset.ts, change the asset ID constant from 1984 (USDT) to the USDC asset ID on the target chain", + "Run npx tsx query-asset.ts" + ], + "result": "USDC metadata and address balance returned", + "scenario": "Edge case: query a non-standard asset ID", + "user_says": "Query USDC balance instead of USDT" + } + ], + "id": "query-chain-state-sdks", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://polkadot-asset-hub-rpc.polkadot.io for mainnet or wss://asset-hub-paseo.dotters.network for testnet)" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "primary_page": "chain-interactions/query-data/query-sdks.md", + "project_structure": "papi-query-example/\n├── .papi/\n│ └── descriptors/\n├── query-asset.ts\n├── query-balance.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/query-data/query-sdks", + "branch": "master", + "files": [ + { + "description": "Queries System.Account storage for native PAS balance using the 'pah' descriptor", + "path": "papi/query-balance.ts" + }, + { + "description": "Queries Assets pallet for USDT (asset ID 1984) metadata and address balance", + "path": "papi/query-asset.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/query-data/query-sdks.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir papi-query-example && cd papi-query-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'papi-query-example' and initialize an ESM Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling.", + "order": 2, + "working_directory": "papi-query-example" + }, + { + "action": "Generate PAPI type descriptors for Polkadot Hub", + "commands": [ + "npx papi add pah -n polkadot_asset_hub" + ], + "description": "Generate compile-time type descriptors using the well-known 'polkadot_asset_hub' chain name. This produces the 'pah' export in @polkadot-api/descriptors. For a different network, use the appropriate -n value from the PAPI well-known chains list, or use -w with a WebSocket URL.", + "order": 3, + "working_directory": "papi-query-example" + }, + { + "action": "Fetch and configure the balance query script", + "description": "Fetch the reference file and save it as 'query-balance.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://polkadot-asset-hub-rpc.polkadot.io' for mainnet). (2) Replace 'INSERT_ADDRESS' with the SS58 address whose balance you want to query. Save the file.", + "order": 4, + "reference_file": "papi/query-balance.ts", + "working_directory": "papi-query-example" + }, + { + "action": "Fetch and configure the asset query script", + "description": "Fetch the reference file and save it as 'query-asset.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with the same WebSocket endpoint used above. (2) Replace 'INSERT_ADDRESS' with the address to check for USDT balance. The script queries asset ID 1984 (USDT) by default; change the asset ID constant if targeting a different asset. Save the file.", + "order": 5, + "reference_file": "papi/query-asset.ts", + "working_directory": "papi-query-example" + }, + { + "action": "Run the scripts", + "commands": [ + "npx tsx query-balance.ts", + "npx tsx query-asset.ts" + ], + "description": "Execute both scripts in sequence. query-balance.ts shows the native PAS balance. query-asset.ts shows the USDT asset metadata and the address's USDT balance (0 if not holding USDT).", + "expected_output": "Account nonce and balance info, then asset metadata and balance for the specified address", + "order": 6, + "working_directory": "papi-query-example" + } + ], + "supplementary_context": { + "description": "Load these pages for deeper understanding of chain storage structure or related SDK tools.", + "pages": [ + { + "relevance": "Full PAPI API reference for understanding storage query methods and typed API patterns.", + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md" + }, + { + "relevance": "How to call runtime APIs for computed results (e.g., nonce, fee estimation) beyond static storage reads.", + "slug": "chain-interactions-query-data-runtime-api-calls", + "title": "Runtime API Calls", + "url": "https://docs.polkadot.com/chain-interactions/query-data/runtime-api-calls.md" + } + ] + }, + "title": "Query On-Chain State with SDKs", + "version": "1.0.2", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Executes Polkadot SDK runtime APIs — AccountNonceApi.account_nonce and Metadata.metadata_versions — using the PAPI TypeScript library. Use when you need computed results from runtime logic rather than raw storage reads, such as the current transaction nonce or supported metadata versions. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Trigger phrases: 'call runtime API', 'get account nonce via runtime', 'query metadata versions', 'AccountNonceApi'. No tokens required — read-only operation.", + "env_vars": [], + "error_patterns": [ + { + "cause": "PAPI type descriptors not generated.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." + }, + { + "cause": "The target chain does not expose AccountNonceApi, or the descriptor was generated for a different chain.", + "pattern": "api.apis.AccountNonceApi is undefined", + "resolution": "Verify the chain exposes AccountNonceApi in its runtime metadata. For Polkadot Hub TestNet, this API is available. If you generated descriptors for a different chain, regenerate with the correct endpoint." + }, + { + "cause": "The testnet endpoint is temporarily unavailable.", + "pattern": "WebSocket connection failed / timeout on papi add", + "resolution": "Polkadot Hub TestNet can be unstable. Wait a few minutes and retry. If the endpoint remains down, check the Polkadot Discord status channel. Alternatively, try a different public endpoint." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'papi-runtime-api-example/' ESM project", + "Install polkadot-api and tsx", + "Generate descriptors with npx papi add polkadotTestNet", + "Fetch runtime-apis.ts; substitute WS endpoint and address", + "Run npx tsx runtime-apis.ts" + ], + "result": "Account nonce and supported metadata versions printed to console", + "scenario": "Common scenario: get account nonce before constructing a transaction", + "user_says": "Call the AccountNonceApi to get the nonce for my address" + }, + { + "actions": [ + "Explain that nonce 0 means the account has not yet sent any transactions", + "Note that AccountNonceApi returns 0 for any address that has never submitted a transaction, including unfunded accounts", + "No action required — the nonce increments with each submitted transaction" + ], + "result": "User understands nonce 0 is correct for a new or never-transacted account", + "scenario": "Edge case: nonce shows 0 for a new address", + "user_says": "The nonce is 0 — is that correct?" + } + ], + "id": "call-runtime-apis-sdks", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for Polkadot Hub TestNet (e.g., wss://asset-hub-paseo.dotters.network)" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "primary_page": "chain-interactions/query-data/runtime-api-calls.md", + "project_structure": "papi-runtime-api-example/\n├── .papi/\n│ └── descriptors/\n├── runtime-apis.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/query-data/runtime-api-calls", + "branch": "master", + "files": [ + { + "description": "Calls AccountNonceApi.account_nonce and Metadata.metadata_versions using PAPI typed API", + "path": "papi/runtime-apis.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/query-data/runtime-api-calls.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir papi-runtime-api-example && cd papi-runtime-api-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'papi-runtime-api-example' and initialize an ESM Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling.", + "order": 2, + "working_directory": "papi-runtime-api-example" + }, + { + "action": "Generate PAPI type descriptors for Polkadot Hub TestNet", + "commands": [ + "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the runtime-apis.ts script. If targeting a different network, replace the WebSocket URL and update the descriptor name accordingly.", + "order": 3, + "working_directory": "papi-runtime-api-example" + }, + { + "action": "Fetch the runtime API script", + "description": "Fetch the reference file and save it as 'runtime-apis.ts'. Make these two substitutions: (1) Replace 'INSERT_WS_ENDPOINT' with your WebSocket endpoint (e.g., 'wss://asset-hub-paseo.dotters.network'). (2) Replace 'INSERT_ADDRESS' with a valid SS58 address to query the nonce for. Save the file.", + "order": 4, + "reference_file": "papi/runtime-apis.ts", + "working_directory": "papi-runtime-api-example" + }, + { + "action": "Run the runtime API script", + "commands": [ + "npx tsx runtime-apis.ts" + ], + "description": "Execute the script. It calls AccountNonceApi.account_nonce for the provided address and Metadata.metadata_versions. Nonce for a new address is 0. Metadata versions list shows supported runtime metadata formats (e.g., [14, 15]).", + "expected_output": "Account Nonce: \nSupported Metadata Versions: []", + "order": 5, + "working_directory": "papi-runtime-api-example" + } + ], + "supplementary_context": { + "description": "Load these pages for background on available runtime APIs or to compare runtime API calls with storage queries.", + "pages": [ + { + "relevance": "Storage-based queries using the same PAPI setup — compare with runtime API calls.", + "slug": "chain-interactions-query-data-query-sdks", + "title": "Query On-Chain State with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md" + }, + { + "relevance": "Full PAPI reference listing all available runtime APIs and their method signatures.", + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md" + } + ] + }, + "title": "Call Runtime APIs with SDKs", + "version": "1.0.2", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Constructs, signs, and submits a Balances.transfer_keep_alive extrinsic to Polkadot Hub using the PAPI TypeScript library. Use when you need to send tokens programmatically from a funded account. Also supports Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Requires a funded account mnemonic and testnet PAS tokens. Trigger phrases: 'send a transaction', 'transfer tokens programmatically', 'sign and submit extrinsic', 'send PAS balance'. Uses dotenv to protect the sender mnemonic.", + "env_vars": [ + { + "description": "12-word BIP39 mnemonic phrase for the sender account. Must be the mnemonic for a funded account. Never commit this value to version control.", + "name": "SENDER_MNEMONIC", + "required": true + }, + { + "description": "WebSocket RPC endpoint for the target chain (e.g., wss://asset-hub-paseo.dotters.network).", + "name": "WS_ENDPOINT", + "required": true + }, + { + "description": "SS58-encoded recipient address for the balance transfer.", + "name": "DEST_ADDRESS", + "required": true + } + ], + "error_patterns": [ + { + "cause": "PAPI type descriptors not generated.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network' in the project directory." + }, + { + "cause": "SENDER_MNEMONIC in .env is incorrect, has extra spaces, or is not a valid BIP39 phrase.", + "pattern": "Invalid mnemonic phrase / Error creating keypair", + "resolution": "Open .env, verify SENDER_MNEMONIC is a valid 12-word BIP39 phrase with single spaces between words, and no leading/trailing spaces." + }, + { + "cause": "The sender account has insufficient PAS balance to cover the transfer amount plus gas fees.", + "pattern": "Error: 1010: Invalid Transaction: Inability to pay some fees", + "resolution": "Get testnet tokens from https://faucet.polkadot.io/?parachain=1111. Alternatively, reduce the AMOUNT constant in send-transfer.ts to 1_000_000_000n (0.1 PAS)." + }, + { + "cause": "Polkadot Hub TestNet can be unstable and drop transactions under load.", + "pattern": "Transaction submitted but not confirmed / stuck pending", + "resolution": "Wait 1-2 minutes and check the block explorer for the tx hash. If not confirmed, re-run the script. The nonce increments per submitted transaction — if you see 'nonce too low', the previous transaction was included despite appearing to fail." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'papi-send-tx-example/' ESM project", + "Install polkadot-api, keyring, dotenv", + "Generate descriptors with npx papi add polkadotTestNet", + "Create .env and .gitignore; ask user to fill SENDER_MNEMONIC and DEST_ADDRESS", + "Fetch and adapt send-transfer.ts with dotenv substitutions", + "Verify sender is funded; run npx tsx send-transfer.ts" + ], + "result": "Transaction hash printed; 1 PAS (10^10 planck) transferred to the destination address", + "scenario": "Common scenario: send a small amount of PAS to another address", + "user_says": "Send 1 PAS from my funded account to address 15xyz..." + }, + { + "actions": [ + "Direct user to https://faucet.polkadot.io/?parachain=1111 with their sender address", + "Wait for faucet to credit the account (1-2 minutes)", + "Re-run send-transfer.ts" + ], + "result": "Account funded; transaction succeeds on retry", + "scenario": "Edge case: account has no testnet tokens", + "user_says": "The transaction fails with 'inability to pay fees'" + } + ], + "id": "send-transactions-sdks", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for Polkadot Hub (e.g., wss://asset-hub-paseo.dotters.network for testnet)" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "tokens": [ + "Testnet PAS tokens in the sender account — get from https://faucet.polkadot.io/?parachain=1111 (Polkadot Hub faucet)" + ], + "wallet": [ + "An SR25519 account mnemonic phrase for the sender account, funded with testnet PAS" + ] + }, + "primary_page": "chain-interactions/send-transactions/with-sdks.md", + "project_structure": "papi-send-tx-example/\n├── .papi/\n│ └── descriptors/\n├── send-transfer.ts\n├── .env\n├── .gitignore\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/send-transactions/with-sdks", + "branch": "master", + "files": [ + { + "description": "PAPI TypeScript script that signs and submits a Balances.transfer_keep_alive extrinsic", + "path": "papi/send-transfer.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/send-transactions/with-sdks.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir papi-send-tx-example && cd papi-send-tx-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'papi-send-tx-example' and initialize an ESM Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install polkadot-api @polkadot/util-crypto @polkadot/keyring", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api, @polkadot/keyring (for SR25519 signing), and TypeScript tooling.", + "order": 2, + "working_directory": "papi-send-tx-example" + }, + { + "action": "Generate PAPI type descriptors", + "commands": [ + "npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Produces the 'polkadotTestNet' export used in the send-transfer script. If targeting a different network, replace the endpoint and update the descriptor name in step 5.", + "order": 3, + "working_directory": "papi-send-tx-example" + }, + { + "action": "Create .env and .gitignore for secrets", + "commands": [ + "echo 'WS_ENDPOINT=wss://asset-hub-paseo.dotters.network\\nSENDER_MNEMONIC=\\nDEST_ADDRESS=' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create a .env file with three empty placeholders and a .gitignore that excludes it. Stop here and ask the user to edit the .env file directly — fill in SENDER_MNEMONIC with their 12-word mnemonic, confirm WS_ENDPOINT is correct, and set DEST_ADDRESS to the recipient address. Do NOT ask for the mnemonic in chat. Wait for the user to confirm the .env is filled before proceeding.", + "order": 4, + "working_directory": "papi-send-tx-example" + }, + { + "action": "Fetch and adapt the send-transfer script", + "description": "Fetch the reference file and save it as 'send-transfer.ts'. The reference script uses literal placeholders INSERT_WS_ENDPOINT, INSERT_SENDER_MNEMONIC, and INSERT_DEST_ADDRESS. Replace them with process.env.WS_ENDPOINT!, process.env.SENDER_MNEMONIC!, and process.env.DEST_ADDRESS! respectively. Add 'import \"dotenv/config\";' as the first line and install dotenv separately: npm install dotenv.", + "order": 5, + "reference_file": "papi/send-transfer.ts", + "working_directory": "papi-send-tx-example" + }, + { + "action": "Verify the sender account is funded", + "description": "Before running, confirm the sender account has testnet PAS tokens. If not, direct the user to https://faucet.polkadot.io/?parachain=1111, paste the sender address, and request tokens. Wait for the faucet transaction to confirm (typically 1-2 minutes) before proceeding.", + "order": 6, + "working_directory": "papi-send-tx-example" + }, + { + "action": "Submit the transaction", + "commands": [ + "npx tsx send-transfer.ts" + ], + "description": "Execute the script. It connects to the chain, signs the Balances.transfer_keep_alive extrinsic with the sender mnemonic, submits it, and prints the transaction hash. Save the transaction hash — needed in step 8 to verify.", + "expected_output": "Connected to Polkadot Testnet\nSigning and submitting transaction...\nTransaction submitted with hash: 0x", + "order": 7, + "working_directory": "papi-send-tx-example" + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to fund an account, understand fees, or verify the submitted transaction.", + "pages": [ + { + "relevance": "How to obtain testnet PAS tokens from the Polkadot faucet.", + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md" + }, + { + "relevance": "How to estimate transaction fees before submitting to avoid insufficient balance errors.", + "slug": "chain-interactions-send-transactions-calculate-transaction-fees", + "title": "Calculate Transaction Fees", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md" + }, + { + "relevance": "How to create a new Polkadot account if the user does not yet have a sender mnemonic.", + "slug": "chain-interactions-accounts-create-account", + "title": "Create an Account", + "url": "https://docs.polkadot.com/chain-interactions/accounts/create-account.md" + } + ] + }, + "title": "Send Transactions with SDKs", + "version": "1.0.2", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs all Polkadot SDK build dependencies and compiles the SDK from source on macOS, Linux (Ubuntu/Debian/Arch/Fedora/OpenSUSE), and Windows WSL. Use when setting up a new development machine for Polkadot parachain or runtime development. Covers OS package installation, Rust toolchain setup, wasm32 target addition, git clone, and a full cargo build. Trigger phrases: 'install Polkadot SDK', 'set up parachain dev environment', 'install Rust for Polkadot', 'build polkadot-sdk', 'set up Substrate environment'. Expected outcome: polkadot binary executes and prints its version string.", + "env_vars": [], + "error_patterns": [ + { + "cause": "C compiler and build tools are not installed.", + "pattern": "error: linker 'cc' not found", + "resolution": "On Ubuntu/Debian run: sudo apt install build-essential clang. On macOS run: xcode-select --install. On Fedora run: sudo dnf install gcc." + }, + { + "cause": "OpenSSL development headers are missing.", + "pattern": "error: failed to run custom build command for 'openssl-sys'", + "resolution": "On Ubuntu/Debian run: sudo apt install libssl-dev. On macOS run: brew install openssl. On Fedora run: sudo dnf install openssl-devel." + }, + { + "cause": "The wasm32-unknown-unknown target is not installed.", + "pattern": "error[E0463]: can't find crate for 'std' ... target wasm32-unknown-unknown", + "resolution": "Run: rustup target add wasm32-unknown-unknown && rustup component add rust-src" + }, + { + "cause": "Insufficient RAM for the default number of parallel compilation jobs.", + "pattern": "LLVM ERROR: out of memory / build killed / OOM", + "resolution": "Limit parallelism: cargo build --release --locked -j 4. On Linux you can also temporarily increase swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile" + }, + { + "cause": "The protobuf compiler is missing.", + "pattern": "error: failed to compile protobuf files / protoc not found", + "resolution": "On Ubuntu/Debian: sudo apt install protobuf-compiler. On macOS: brew install protobuf. On Fedora: sudo dnf install protobuf-compiler." + } + ], + "examples": [ + { + "actions": [ + "Run apt install for required packages (git, clang, libssl-dev, etc.)", + "Install Rust via rustup and source ~/.cargo/env", + "Run rustup default stable && rustup target add wasm32-unknown-unknown && rustup component add rust-src", + "Clone https://github.com/paritytech/polkadot-sdk.git and cd polkadot-sdk", + "Run cargo build --release --locked", + "Verify with ./target/release/polkadot --version" + ], + "result": "Polkadot binary prints version string — SDK toolchain ready for parachain development", + "scenario": "Common scenario: fresh Ubuntu developer machine", + "user_says": "Install the Polkadot SDK on my Linux machine" + }, + { + "actions": [ + "Re-run with reduced parallelism: cargo build --release --locked -j 4", + "If still failing on Linux, add swap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile, then retry" + ], + "result": "Build completes with reduced parallel jobs or additional swap space", + "scenario": "Edge case: build runs out of memory on a machine with limited RAM", + "user_says": "The build keeps getting killed or runs out of memory" + } + ], + "id": "install-polkadot-sdk", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "macOS 10.7+, Ubuntu/Debian/Arch/Fedora/OpenSUSE Linux, or Windows 10 v2004+ with WSL2", + "At least 10 GB free disk space and 8 GB RAM (16 GB recommended for parallel compilation)", + "Broadband internet connection (downloads several GB of Rust crates and dependencies)" + ] + }, + "primary_page": "parachains/install-polkadot-sdk.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "parachains/install-polkadot-sdk.md" + ], + "steps": [ + { + "action": "Install OS-level system dependencies", + "description": "Install the packages required to compile the Polkadot SDK. Run the command block for your OS:\n\nmacOS: First install Homebrew if missing (/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\"), then run: brew install protobuf openssl cmake\n\nUbuntu or Debian: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler\n\nArch Linux: pacman -Syu --needed --noconfirm curl git clang make protobuf\n\nFedora: sudo dnf update && sudo dnf install clang curl git openssl-devel make protobuf-compiler\n\nOpenSUSE: sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf\n\nWindows WSL (Ubuntu): open the WSL Ubuntu terminal, then run: sudo apt install --assume-yes git clang curl libssl-dev llvm libclang-dev libudev-dev make protobuf-compiler", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Rust via rustup", + "commands": [ + "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + "source ~/.cargo/env" + ], + "description": "Download and run the rustup installer. When prompted, accept the default installation (press Enter or type 1). After installation completes, source the Cargo environment so the current shell session can find the Rust toolchain. On Windows WSL, use source ~/.cargo/env; on some Linux distributions use source $HOME/.cargo/env.", + "order": 2, + "working_directory": "." + }, + { + "action": "Configure the Rust toolchain", + "commands": [ + "rustup default stable", + "rustup update", + "rustup target add wasm32-unknown-unknown", + "rustup component add rust-src" + ], + "description": "Set the stable toolchain as default, update it to the latest release, and add the WebAssembly compilation target plus the Rust source component. Both wasm32-unknown-unknown and rust-src are required for building Substrate/Polkadot SDK runtime WASM blobs.", + "expected_output": "info: setting default toolchain to 'stable-...'", + "order": 3, + "working_directory": "." + }, + { + "action": "Clone the Polkadot SDK repository", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk.git", + "cd polkadot-sdk" + ], + "description": "Clone the official Polkadot SDK repository. The clone is several hundred MB and may take a few minutes on a slower connection. All subsequent steps run from inside the polkadot-sdk directory.", + "order": 4, + "working_directory": "." + }, + { + "action": "Compile the Polkadot SDK", + "commands": [ + "cargo build --release --locked" + ], + "description": "Build the entire SDK in release mode. This compiles all components and verifies the full toolchain. Expect 20-60 minutes on typical developer hardware. A successful build produces binaries in target/release/ including polkadot, polkadot-parachain, polkadot-omni-node, and substrate-node.", + "expected_output": "Compiling polkadot ... Finished release [optimized] target(s)", + "order": 5, + "working_directory": "polkadot-sdk" + }, + { + "action": "Verify the build", + "commands": [ + "./target/release/polkadot --version" + ], + "description": "Confirm the polkadot binary runs and prints its version. If you see version output without errors, the development environment is correctly configured and ready for Polkadot SDK work. Save this version string — needed when matching binary versions in parachain template setup.", + "expected_output": "polkadot 1.x.x-xxxxxxx", + "order": 6, + "working_directory": "polkadot-sdk" + } + ], + "supplementary_context": { + "description": "Load when the user asks about next steps after installing the SDK or about the parachain template.", + "pages": [ + { + "relevance": "Next step after SDK installation: clone and run the Polkadot SDK parachain template locally.", + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", "title": "Set Up the Polkadot SDK Parachain Template", - "description": "Clones the Polkadot SDK Parachain Template, compiles it in release mode, and runs a local development node to verify the build and environment. Use as the first step in the parachain launch workflow before registering on Paseo testnet or a local relay chain. Requires the Polkadot SDK build environment from the install-polkadot-sdk skill. The template provides a minimal parachain runtime with Aura consensus, FRAME pallets, and a pre-configured Cumulus integration. Trigger phrases: 'parachain template', 'set up parachain Polkadot SDK', 'clone parachain template', 'build parachain template', 'Polkadot SDK parachain starter'. First step in the three-page parachain launch series.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/launch-a-parachain/set-up-the-parachain-template.md" - ], - "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", - "prerequisites": { - "runtime": [ - "Rust toolchain with wasm32-unknown-unknown target — complete the install-polkadot-sdk skill first", - "Git", - "Disk: at least 5 GB free for the build artifacts", - "RAM: 8 GB minimum; 16 GB recommended for parallel compilation" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK Parachain Template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git", - "cd polkadot-sdk-parachain-template" - ], - "description": "Clone the official Polkadot SDK Parachain Template repository. The template contains a minimal parachain runtime with Aura consensus, standard FRAME pallets, and Cumulus integration for relay chain connectivity. It includes both a node binary and a runtime Wasm blob." - }, - { - "order": 2, - "action": "Review the template directory structure", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup — the default configuration targets a local development network." - }, - { - "order": 3, - "action": "Build the parachain node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "expected_output": "Compiling parachain-template-node v0.1.0\n...\nFinished release [optimized] target(s)", - "description": "Compile the parachain node and runtime in release mode. This step is time-consuming (20-60 minutes on first build) because it compiles the entire Polkadot SDK dependency tree. Subsequent builds use the Rust incremental cache and are significantly faster. If compilation fails with 'linker not found', install the build-essential package (Linux) or Xcode command line tools (macOS). On low-memory machines (less than 8 GB RAM), reduce parallel jobs: CARGO_BUILD_JOBS=2 cargo build --release." - }, - { - "order": 4, - "action": "Verify the binary was produced", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node --version" - ], - "expected_output": "parachain-template-node 0.1.0-...", - "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully — review the cargo build output for compilation errors." - }, - { - "order": 5, - "action": "Run the node in local development mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node --dev" - ], - "expected_output": "Running in --dev mode\n...\nIdle (0 peers)", - "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain — the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "polkadot-docs/parachains/parachain-template", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0463]: can't find crate for 'std' / wasm32 target missing", - "cause": "The wasm32-unknown-unknown target is not installed in the Rust toolchain.", - "resolution": "Run: rustup target add wasm32-unknown-unknown. If using a specific toolchain version, prefix with rustup target add wasm32-unknown-unknown --toolchain stable." - }, - { - "pattern": "error: linking with 'cc' failed / linker not found", - "cause": "C/C++ build tools are not installed on the system.", - "resolution": "On Ubuntu/Debian: sudo apt install build-essential clang. On macOS: xcode-select --install. Then re-run cargo build --release." - }, - { - "pattern": "Killed / process killed during compilation / OOM", - "cause": "Insufficient RAM for parallel Rust compilation — cargo uses one thread per CPU core by default.", - "resolution": "Reduce parallel compilation jobs: CARGO_BUILD_JOBS=2 cargo build --release. Consider adding swap space on Linux: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile." - }, - { - "pattern": "error: package 'polkadot-sdk' not found / version mismatch in Cargo.lock", - "cause": "Cargo.lock is out of sync with the current Polkadot SDK release pinned in Cargo.toml.", - "resolution": "Run cargo update to refresh the lock file to the latest compatible versions, then retry cargo build --release." - } - ], - "supplementary_context": { - "description": "Load these pages for the prerequisite SDK install, the next steps in the parachain launch series, or runtime concepts.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Prerequisite: install Rust, system dependencies, and the Polkadot SDK build environment before building the template." - }, - { - "slug": "parachains-launch-a-parachain-deploy-to-polkadot", - "title": "Deploy on Polkadot", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/deploy-to-polkadot.md", - "relevance": "Next step in the parachain launch series: acquire a para ID and slot on Paseo testnet after the template is built." - }, - { - "slug": "reference-parachains-networks", - "title": "Networks", - "url": "https://docs.polkadot.com/reference/parachains/networks.md", - "relevance": "Network endpoints and chain IDs for Paseo testnet and Polkadot mainnet parachain environments." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: first-time parachain developer setting up the template", - "user_says": "How do I get the Polkadot SDK Parachain Template running on my machine?", - "actions": [ - "Confirm Rust toolchain with wasm32 target is installed (install-polkadot-sdk skill if not)", - "Clone polkadot-sdk-parachain-template from GitHub", - "Run cargo build --release in the cloned directory (allow 20-60 minutes)", - "Verify with ./target/release/parachain-template-node --version", - "Start in dev mode with ./target/release/parachain-template-node --dev to confirm it works" - ], - "result": "Template node built and running in dev mode; environment confirmed ready for the next parachain launch step" - }, - { - "scenario": "Edge case: build is killed due to out-of-memory on a low-spec machine", - "user_says": "The cargo build process gets killed partway through on my 4 GB RAM machine", - "actions": [ - "Set CARGO_BUILD_JOBS=2 to limit parallel compilation threads", - "Add at least 4 GB of swap space: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile", - "Re-run CARGO_BUILD_JOBS=2 cargo build --release" - ], - "result": "Build completes successfully with reduced memory pressure from limited parallel jobs" - } - ] - }, - { - "id": "run-polkadot-hub-rpc-node", - "title": "Run an RPC Node for Polkadot Hub", - "description": "Sets up a Polkadot Hub (Asset Hub) RPC node in archive or pruned mode via Docker or systemd, with an optional Ethereum-compatible JSON-RPC adapter (eth-rpc) for EVM tooling. Covers Docker image selection, binary installation, snapshot download for faster sync, systemd service configuration, and sync verification. Use when you need a self-hosted Polkadot Hub RPC endpoint for Substrate or Ethereum-compatible tools. Trigger phrases: 'run Polkadot Hub node', 'Asset Hub RPC node', 'Polkadot Hub Docker node', 'eth-rpc Polkadot Hub', 'Polkadot Hub archive node'. No tokens required.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "node-infrastructure/run-a-node/polkadot-hub-rpc.md" - ], - "primary_page": "node-infrastructure/run-a-node/polkadot-hub-rpc.md", - "prerequisites": { - "runtime": [ - "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", - "Disk: minimum 500 GB SSD for pruned node; 2+ TB SSD for archive node", - "RAM: minimum 8 GB; 16 GB recommended for archive nodes", - "CPU: 4+ cores recommended" - ], - "network": [ - "Open TCP ports: 30333 (P2P), 9944 (WebSocket RPC), 9615 (Prometheus metrics, optional), 8545 (Ethereum RPC if using eth-rpc adapter)", - "Stable broadband connection for initial sync (several hundred GB download)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Choose deployment method and sync type", - "working_directory": ".", - "description": "Decide on two choices before proceeding: (1) Deployment method: Docker (step 2) or systemd binary (step 3). (2) Sync type: 'archive' retains full historical state (2+ TB, enables all historical queries) vs 'pruned' retains only recent blocks (500 GB, sufficient for most uses).\n\nPolkadot Hub uses the parity/polkadot-parachain Docker image with --chain=asset-hub-polkadot. For the Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo and relay --chain=paseo. Optionally download a database snapshot from the Polkadot ecosystem snapshot providers to accelerate initial sync — check https://snapshots.polkadot.io or community providers for current snapshots." - }, - { - "order": 2, - "action": "Run the Polkadot Hub node via Docker", - "working_directory": ".", - "commands": [ - "docker pull parity/polkadot-parachain:stable", - "mkdir -p /var/lib/polkadot-hub", - "docker run -d --name polkadot-hub-rpc --restart unless-stopped -p 30333:30333 -p 9944:9944 -p 9615:9615 -v /var/lib/polkadot-hub:/data parity/polkadot-parachain:stable --chain=asset-hub-polkadot --base-path=/data --rpc-external --rpc-port=9944 --rpc-cors=all --prometheus-external --prometheus-port=9615 --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast" - ], - "description": "Pull the stable polkadot-parachain image and start the Polkadot Hub RPC node. Key arguments: --chain=asset-hub-polkadot selects the Polkadot Hub spec; --base-path=/data stores chain data in the mounted volume; --state-pruning=1000 --blocks-pruning=1000 keeps the last 1000 blocks (change both to 'archive' for a full archive node); --rpc-external exposes the WebSocket RPC on 9944; the -- separator and --chain=polkadot configure the embedded relay chain client. Replace /var/lib/polkadot-hub with your data directory. For Paseo TestNet, use --chain=asset-hub-paseo and --chain=paseo after the -- separator." - }, - { - "order": 3, - "action": "Run the Polkadot Hub node via systemd (binary method alternative)", - "working_directory": ".", - "description": "For the systemd binary method (skip if using Docker in step 2): (1) Create system user: sudo useradd --system --no-create-home --shell /usr/sbin/nologin polkadot. (2) Create data dir: sudo mkdir -p /var/lib/polkadot-hub && sudo chown polkadot:polkadot /var/lib/polkadot-hub. (3) Download polkadot-parachain binary from https://github.com/paritytech/polkadot-sdk/releases (stable, amd64 or arm64) and install: sudo install -m 755 polkadot-parachain /usr/local/bin/. (4) Create /etc/systemd/system/polkadot-hub-rpc.service:\n\n[Unit]\nDescription=Polkadot Hub RPC Node\nAfter=network.target\n\n[Service]\nUser=polkadot\nExecStart=/usr/local/bin/polkadot-parachain --chain=asset-hub-polkadot --base-path=/var/lib/polkadot-hub --rpc-external --rpc-port=9944 --rpc-cors=all --state-pruning=1000 --blocks-pruning=1000 -- --chain=polkadot --sync=fast\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n\n(5) Enable and start: sudo systemctl daemon-reload && sudo systemctl enable polkadot-hub-rpc && sudo systemctl start polkadot-hub-rpc. For archive mode, omit --state-pruning and --blocks-pruning flags." - }, - { - "order": 4, - "action": "Optionally install the Ethereum RPC adapter (eth-rpc)", - "working_directory": ".", - "commands": [ - "docker pull paritytech/frontier-node:latest", - "docker run -d --name polkadot-hub-eth-rpc --restart unless-stopped -p 8545:8545 --network host paritytech/frontier-node:latest --chain=asset-hub-polkadot --endpoint=ws://localhost:9944 --listen-addr=0.0.0.0:8545" - ], - "description": "This step is optional. The Ethereum RPC adapter (eth-rpc / Frontier) exposes an Ethereum-compatible JSON-RPC endpoint on port 8545, allowing Ethereum tooling (MetaMask, ethers.js, Hardhat) to connect to Polkadot Hub. The adapter proxies Ethereum RPC calls to the Substrate node. Run it after the main node (step 2 or 3) is fully synced and serving on ws://localhost:9944. For Paseo TestNet (chain ID: 420420417), use --chain=asset-hub-paseo. Skip this step if you only need the native Substrate WebSocket RPC endpoint." - }, - { - "order": 5, - "action": "Monitor sync progress", - "working_directory": ".", - "commands": [ - "docker logs -f polkadot-hub-rpc 2>&1 | grep -E 'Syncing|Idle|Best|Peers'", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_syncState\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":0,\"currentBlock\":12000,\"highestBlock\":15000},\"id\":1}", - "description": "Monitor sync using Docker logs (first command) or the JSON-RPC sync state query (second). For systemd, replace 'docker logs -f polkadot-hub-rpc' with 'sudo journalctl -fu polkadot-hub-rpc'. The node is fully synced when currentBlock equals highestBlock. Fast sync downloads state snapshots and is significantly faster than full sync — initial sync can take hours to days depending on hardware." - }, - { - "order": 6, - "action": "Verify the RPC endpoint is operational", - "working_directory": ".", - "commands": [ - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_chain\",\"params\":[]}' http://localhost:9944", - "curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"system_health\",\"params\":[]}' http://localhost:9944" - ], - "expected_output": "{\"result\":\"Polkadot Asset Hub\",...}\n{\"result\":{\"peers\":5,\"isSyncing\":false,\"shouldHavePeers\":true},...}", - "description": "Confirm the node serves RPC requests. system_chain should return 'Polkadot Asset Hub'. system_health should show isSyncing: false and peers greater than 0. If peers is 0, verify port 30333 is open in your firewall. If isSyncing is true, wait for sync to complete. If the eth-rpc adapter is running, also verify it: curl -s -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\",\"params\":[]}' http://localhost:8545 — should return '0x18FF5B01' (420420417 in hex for Paseo TestNet)." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: No space left on device / database write error", - "cause": "The data volume has run out of disk space.", - "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=1000). Check usage with: du -sh /var/lib/polkadot-hub." - }, - { - "pattern": "EADDRINUSE port 9944 or 30333", - "cause": "Another process is already using the required ports.", - "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." - }, - { - "pattern": "Peers count stays at 0 after several minutes", - "cause": "P2P port 30333 is blocked by firewall or the bootnodes are temporarily unavailable.", - "resolution": "Open TCP port 30333: sudo ufw allow 30333/tcp. For cloud VMs, update the security group. If peers still do not connect after 10 minutes, check the Polkadot network status." - }, - { - "pattern": "eth_chainId returns unexpected value or eth-rpc adapter cannot connect to node", - "cause": "The eth-rpc adapter endpoint does not match the running node's WebSocket address, or the node is not yet fully synced.", - "resolution": "Verify the node is fully synced (step 5). Ensure the --endpoint flag for the eth-rpc adapter points to ws://localhost:9944 and the node's --rpc-external flag is active. Restart the eth-rpc adapter after the node is synced." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to add TLS/WSS, run a parachain RPC node, or understand the chain configuration.", - "pages": [ - { - "slug": "node-infrastructure-run-a-node-parachain-rpc", - "title": "Run a Parachain RPC Node", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/parachain-rpc.md", - "relevance": "How to run RPC nodes for other system parachains (People Chain, Collectives, BridgeHub) using the same polkadot-parachain binary." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", - "title": "Set Up Secure WebSocket", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md", - "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints." - }, - { - "slug": "node-infrastructure-run-a-node-relay-chain-full-node", - "title": "Set Up a Node", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md", - "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run a pruned Polkadot Hub RPC node via Docker", - "user_says": "Run a Polkadot Hub RPC node using Docker", - "actions": [ - "Pull parity/polkadot-parachain:stable", - "Create data directory /var/lib/polkadot-hub", - "Run docker with --chain=asset-hub-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", - "Monitor sync with docker logs and system_syncState RPC", - "Verify with system_chain and system_health once currentBlock equals highestBlock" - ], - "result": "Polkadot Hub RPC node running, synced, serving requests on ws://localhost:9944" - }, - { - "scenario": "Edge case: Ethereum tooling cannot connect — eth_chainId returns wrong value", - "user_says": "MetaMask connects to port 8545 but shows the wrong chain ID", - "actions": [ - "Verify the eth-rpc adapter is configured with the correct --chain flag matching the running node", - "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01 in hex) — confirm eth_chainId returns this value", - "If chain ID is wrong, restart the eth-rpc adapter with --chain=asset-hub-paseo for TestNet or --chain=asset-hub-polkadot for mainnet", - "Update MetaMask network settings with the correct chain ID and RPC URL http://localhost:8545" - ], - "result": "MetaMask connects successfully with the correct Polkadot Hub chain ID and eth-rpc adapter" - } - ] - }, - { - "id": "set-up-polkadot-validator-node", - "title": "Set Up a Polkadot Validator Node", - "description": "Installs the three Polkadot validator binaries (polkadot, polkadot-prepare-worker, polkadot-execute-worker) needed to run a validator node, and prepares the server with NTP time synchronization and Landlock security. Covers four installation paths: GPG-verified curl download, APT package manager (Ubuntu/Debian), Docker image, or building from source. Use when provisioning a new validator server or upgrading binaries on an existing one. Trigger phrases: 'install Polkadot validator binaries', 'set up validator node', 'install polkadot-prepare-worker', 'configure validator server'. First step in the full validator onboarding flow before key management and bonding.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" - ], - "primary_page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", - "prerequisites": { - "runtime": [ - "Linux server with kernel 5.16 or later (Ubuntu 22.04 LTS or newer recommended)", - "64-bit x86-64 or ARM64 architecture", - "Root or sudo access" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Verify or install NTP time synchronization", - "working_directory": ".", - "commands": [ - "timedatectl" - ], - "expected_output": "System clock synchronized: yes", - "description": "Validators need accurate system time to avoid missing block authorship. Check NTP status with 'timedatectl'. If the output does not show 'System clock synchronized: yes', install and start NTP:\n\n sudo apt-get install ntp\n sudo ntpq -p\n\nThe 'ntpq -p' output should list one or more active peers with a '+' or '*' status prefix. Skipping NTP can cause the validator to miss blocks due to minor clock drift." - }, - { - "order": 2, - "action": "Verify Landlock security is activated", - "working_directory": ".", - "commands": [ - "dmesg | grep landlock || journalctl -kg landlock" - ], - "description": "Landlock (Linux kernel 5.13+) is required by the Polkadot validator for sandboxing. Run the command above as root. If Landlock is active, you will see kernel log entries referencing 'landlock'. If there is no output, your kernel does not have Landlock enabled. Most modern Ubuntu/Debian kernels (5.16+) include it by default. If Landlock is absent, upgrade to a supported kernel or rebuild with Landlock enabled (see https://docs.kernel.org/userspace-api/landlock.html#kernel-support). Polkadot validators can run without Landlock but will operate without the sandboxing protection." - }, - { - "order": 3, - "action": "Install the Polkadot binaries via curl with GPG verification (recommended method)", - "working_directory": ".", - "commands": [ - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot.asc", - "gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", - "gpg --verify polkadot.asc", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker.asc", - "gpg --verify polkadot-prepare-worker.asc", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker", - "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker.asc", - "gpg --verify polkadot-execute-worker.asc", - "chmod +x polkadot polkadot-prepare-worker polkadot-execute-worker", - "sudo mv polkadot polkadot-prepare-worker polkadot-execute-worker /usr/local/bin/" - ], - "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 — check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE — APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE — Docker:\n docker pull parity/polkadot:stable2512-2" - }, - { - "order": 4, - "action": "Verify all three binaries are installed and versions match", - "working_directory": ".", - "commands": [ - "polkadot --version", - "polkadot-execute-worker --version", - "polkadot-prepare-worker --version" - ], - "expected_output": "All three commands print the same version string, e.g. 'polkadot stable2512-2-...'", - "description": "Run all three version commands and confirm the version strings are identical. If one binary is missing ('command not found'), ensure it was moved to /usr/local/bin/ in step 3 and that /usr/local/bin is in $PATH (echo $PATH). All three binaries must be in the same directory for the validator to function. If versions differ, re-download the mismatched binary from the same release tag used in step 3." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "gpg: BAD signature", - "cause": "The downloaded binary was corrupted or tampered with during download.", - "resolution": "Delete all downloaded files and repeat step 3 from a trusted network connection. Do not use any binary that fails GPG verification." - }, - { - "pattern": "polkadot: command not found after installation", - "cause": "The binary was not moved to a directory in $PATH, or the directory is not in $PATH.", - "resolution": "Run 'echo $PATH' and verify /usr/local/bin is listed. If not, run 'export PATH=$PATH:/usr/local/bin'. For persistence, add this export to /etc/environment or ~/.bashrc." - }, - { - "pattern": "version mismatch between polkadot and worker binaries", - "cause": "The three binaries were downloaded from different releases.", - "resolution": "Remove all three binaries and re-download all from the same release tag (e.g., polkadot-stable2512-2) from https://github.com/paritytech/polkadot-sdk/releases. All three must share the same version to interoperate." - }, - { - "pattern": "Landlock not in dmesg output", - "cause": "The running kernel is older than 5.13 or was compiled without Landlock.", - "resolution": "Upgrade the kernel: on Ubuntu, run 'sudo apt-get install linux-generic-hwe-22.04' then reboot. Verify with 'uname -r' that the new kernel is 5.16 or later. Alternatively, consult https://docs.kernel.org/userspace-api/landlock.html#kernel-support." - } - ], - "supplementary_context": { - "description": "Load these pages for key management, starting validation, or understanding validator requirements and operational tasks.", - "pages": [ - { - "slug": "node-infrastructure-run-a-validator-requirements", - "title": "Validator Requirements", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/requirements.md", - "relevance": "Minimum hardware and skill requirements before starting a validator — read before this skill." - }, - { - "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-key-management", - "title": "Validator Key Management", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md", - "relevance": "Next step after binary installation: generating and registering session keys." - }, - { - "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-start-validating", - "title": "Start Validating", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md", - "relevance": "Syncing the node, bonding DOT, and activating the validator after binary setup." - }, - { - "slug": "node-infrastructure-run-a-validator-operational-tasks-general-management", - "title": "General Management", - "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/operational-tasks/general-management.md", - "relevance": "Prometheus and Grafana monitoring stack setup and security best practices for running validators." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: install on a fresh Ubuntu 22.04 server", - "user_says": "Install the Polkadot validator binaries on a new Ubuntu server", - "actions": [ - "Run 'timedatectl' to check NTP sync; install ntp if not synced", - "Run 'dmesg | grep landlock' to verify Landlock is active", - "Download polkadot, polkadot-prepare-worker, and polkadot-execute-worker from release polkadot-stable2512-2", - "Verify each binary with GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", - "Move all three to /usr/local/bin and run --version on each to confirm matching versions" - ], - "result": "All three binaries installed at polkadot-stable2512-2, GPG verified, and accessible system-wide; ready for key management" - }, - { - "scenario": "Edge case: APT install on a Debian-based server without curl", - "user_says": "Install Polkadot on Ubuntu using apt instead of downloading manually", - "actions": [ - "Import the Parity GPG key into /usr/share/keyrings/parity.gpg", - "Add the Parity APT repository to /etc/apt/sources.list.d/parity.list", - "Run 'apt update && apt install parity-keyring polkadot'", - "Verify with 'polkadot --version', 'polkadot-execute-worker --version', 'polkadot-prepare-worker --version'" - ], - "result": "Polkadot and both worker binaries installed via APT with automatic future upgrades via apt upgrade" - } - ] - }, - { - "id": "set-up-chopsticks-fork", - "title": "Set Up and Use Chopsticks for Chain Forking", - "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/chopsticks.md" - ], - "primary_page": "reference/tools/chopsticks.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm (included with Node.js) or yarn or pnpm" - ], - "network": [ - "Access to a WebSocket RPC endpoint for the chain to fork (e.g., wss://polkadot-rpc.dwellir.com)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Chopsticks globally", - "working_directory": ".", - "commands": [ - "npm i -g @acala-network/chopsticks@1.2.7" - ], - "expected_output": "added N packages", - "description": "Install Chopsticks 1.2.7 globally. After installation, 'chopsticks' is available system-wide. For a project-local installation instead: mkdir my-chopsticks-project && cd my-chopsticks-project && npm init -y && npm i @acala-network/chopsticks@1.2.7. With local installation, run via 'npx @acala-network/chopsticks' instead of 'chopsticks' in subsequent steps." - }, - { - "order": 2, - "action": "Create a fork configuration file", - "working_directory": ".", - "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs — download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts — useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain." - }, - { - "order": 3, - "action": "Start the chain fork", - "working_directory": ".", - "commands": [ - "npx @acala-network/chopsticks --config=polkadot.yml" - ], - "expected_output": "Running on port 8000", - "description": "Start the fork using the YAML config from step 2. Alternatively, fork without a config file using CLI flags:\n npx @acala-network/chopsticks --endpoint wss://polkadot-rpc.dwellir.com --block 100\n\nChoose the block number to fork from with --block (omit to use the chain head). Chopsticks caches state locally in an SQLite DB (set --db ./chopsticks.db to reuse between runs). When the fork is ready, you will see 'Running on port 8000'. The local fork is now accessible at ws://localhost:8000." - }, - { - "order": 4, - "action": "Interact with the fork", - "working_directory": ".", - "description": "Connect to the running fork at ws://localhost:8000.\n\n```typescript\nVia Polkadot.js API (TypeScript):\n import { ApiPromise, WsProvider } from '@polkadot/api';\n const api = await ApiPromise.create({ provider: new WsProvider('ws://localhost:8000') });\n const block = await api.rpc.chain.getBlock();\n console.log('Fork head:', block.block.header.number.toNumber());\n```\n\nVia Polkadot.js Apps UI: navigate to polkadot.js.org/apps, select network > Development > Custom, enter ws://localhost:8000, and click Switch.\n\nTo manipulate storage via the dev_setStorage WebSocket command (using wscat or a JS client):\n { \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"dev_setStorage\", \"params\": [{ \"System\": { \"Account\": [[\"0xALICE_KEY\", { \"data\": { \"free\": 1000000000000 } }]] } }] }" - }, - { - "order": 5, - "action": "Replay a historical block (optional)", - "working_directory": ".", - "commands": [ - "npx @acala-network/chopsticks run-block --endpoint wss://polkadot-rpc.dwellir.com --output-path ./block-1000-output.json --block 1000" - ], - "description": "Replay block 1000 from Polkadot and save the state diff to a JSON file. Replace the endpoint and block number as needed. The output includes detailed storage changes and runtime logs. Add --html to generate an HTML visualization of the storage diff. This is useful for debugging why a specific transaction produced unexpected state changes." - }, - { - "order": 6, - "action": "Test XCM between multiple chains (optional)", - "working_directory": ".", - "commands": [ - "npx @acala-network/chopsticks xcm --r polkadot --p moonbeam --p astar" - ], - "description": "Fork multiple chains simultaneously to test XCM message flow. The --r flag specifies the relay chain config (by name or file path) and --p specifies parachain configs. After startup, each chain runs on a different port (printed in the output). Send XCM messages between the forked chains using the Polkadot.js API or dev_* WebSocket commands. Note: XCM testing requires config files for each chain; use pre-built configs from https://github.com/AcalaNetwork/chopsticks/tree/master/configs." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Cannot connect to endpoint / WebSocket connection failed", - "cause": "The RPC endpoint URL is incorrect, the chain is temporarily unavailable, or the WSS endpoint requires authentication.", - "resolution": "Verify the endpoint URL by connecting with wscat: wscat -c wss://your-endpoint. Try a public endpoint such as wss://polkadot-rpc.dwellir.com for Polkadot. Check the chain's status page or Discord for outages." - }, - { - "pattern": "chopsticks: command not found", - "cause": "Global npm installation did not add the binary to PATH, or Chopsticks was installed locally without npx.", - "resolution": "For global installs, run 'npm root -g' to find the global bin directory and ensure it is in $PATH. For local installs, use 'npx @acala-network/chopsticks' instead of 'chopsticks'." - }, - { - "pattern": "MetaMask / Ethereum tooling cannot connect to the fork", - "cause": "Chopsticks uses the Smoldot light client which only supports the native Polkadot SDK API, not Ethereum JSON-RPC.", - "resolution": "This is a known limitation. Chopsticks forks cannot be used with MetaMask or ethers.js. For EVM-compatible local testing, use a local Polkadot Hub development node instead." - }, - { - "pattern": "Error replaying block: state root mismatch", - "cause": "The local fork has diverged from the live chain state, often because storage was manually modified before replay.", - "resolution": "Restart Chopsticks without --db to use a fresh state (or delete the SQLite db file). Avoid modifying storage with dev_setStorage before running run-block." - } - ], - "supplementary_context": { - "description": "Load these pages for parachain-specific fork tutorials, XCM debugging, or the fork-a-parachain cookbook tutorial.", - "pages": [ - { - "slug": "parachains-testing-fork-a-parachain", - "title": "Fork a Parachain Using Chopsticks", - "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", - "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks — same tool, focused on the parachain testing workflow with a CI-backed example." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", - "title": "Replay and Dry Run XCMs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", - "relevance": "Using Chopsticks to replay and dry-run XCM messages — advanced XCM debugging workflow." - }, - { - "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", - "title": "XCM Fee Estimation", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", - "relevance": "Estimating XCM fees for teleports using Chopsticks local forks of Polkadot Hub and People Chain." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: fork Polkadot mainnet and inspect state", - "user_says": "Fork the Polkadot mainnet locally with Chopsticks so I can test transactions", - "actions": [ - "Install Chopsticks 1.2.7 globally: npm i -g @acala-network/chopsticks@1.2.7", - "Download the Polkadot config from the Chopsticks repo: curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml", - "Start the fork: npx @acala-network/chopsticks --config=polkadot.yml", - "Wait for 'Running on port 8000', then connect via Polkadot.js API at ws://localhost:8000" - ], - "result": "Local Polkadot fork running at ws://localhost:8000; ready for transaction testing without spending real tokens" - }, - { - "scenario": "Edge case: fork requires a funded test account", - "user_says": "I need Alice to have tokens on my forked chain for testing", - "actions": [ - "Add an import-storage section to the YAML config overriding Alice's System.Account free balance to a large value", - "Restart the fork with the updated config", - "Alternatively, send a dev_setStorage WebSocket command after the fork is running to set Alice's balance" - ], - "result": "Alice's account has the specified token balance on the local fork; transactions that require funds now succeed" - } - ] - }, - { - "id": "set-up-e2e-testing-moonwall", - "title": "Set Up End-to-End Testing with Moonwall", - "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain — local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/moonwall.md" - ], - "primary_page": "reference/tools/moonwall.md", - "prerequisites": { - "runtime": [ - "Node.js v20.10 or higher", - "npm, yarn, or pnpm" - ], - "network": [ - "A running Polkadot SDK node or Chopsticks fork for the test environment (e.g., ws://localhost:9944 or ws://localhost:8000)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Moonwall globally", - "working_directory": ".", - "commands": [ - "npm install -g @moonwall/cli@5.18.3" - ], - "expected_output": "added N packages", - "description": "Install Moonwall 5.18.3 globally so the 'moonwall' command is available system-wide. For a project-local installation: mkdir my-moonwall-project && cd my-moonwall-project && npm init -y && npm install @moonwall/cli@5.18.3. With a local install, run Moonwall via 'npx moonwall' or add it to package.json scripts." - }, - { - "order": 2, - "action": "Initialize the Moonwall configuration via the interactive wizard", - "working_directory": ".", - "commands": [ - "moonwall init" - ], - "interactive": true, - "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user — do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding." - }, - { - "order": 3, - "action": "Review and customize moonwall.config.json", - "working_directory": ".", - "description": "Open the generated moonwall.config file and customize the environment settings. Key sections to update:\n\n - foundation.type: 'dev' (local binary), 'chopsticks' (fork), or 'read_only' (remote RPC)\n - For 'dev' foundation, set binPath to the path of your chain binary\n - connections[0].endpoint: the WebSocket URL of the node (e.g., ws://localhost:9944)\n - connections[0].type: the provider type (e.g., 'polkadotJs' for Polkadot.js API)\n\nExample configuration for testing a local dev node:\n {\n \"label\": \"my-chain-tests\",\n \"timeout\": 30000,\n \"environments\": [{\n \"name\": \"default_env\",\n \"foundation\": { \"type\": \"dev\", \"launchSpec\": [{ \"name\": \"node\", \"binPath\": \"./your-node-binary\", \"options\": [\"--dev\"] }] },\n \"testFileDir\": [\"./tests\"],\n \"connections\": [{ \"name\": \"api\", \"type\": \"polkadotJs\", \"endpoints\": [\"ws://localhost:9944\"] }]\n }]\n }" - }, - { - "order": 4, - "action": "Write a test suite", - "working_directory": ".", - "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run — do not create API connections manually." - }, - { - "order": 5, - "action": "Run the tests", - "working_directory": ".", - "commands": [ - "moonwall test default_env -c moonwall.config" - ], - "expected_output": "All test suites passed", - "description": "Run the tests against the 'default_env' environment defined in moonwall.config. Replace 'default_env' with the environment name you chose in step 2. The test runner outputs:\n - Test suite execution status (pass/fail per suite)\n - Individual test case results\n - Execution time per test\n - Detailed error logs for failed tests\n\nIf the foundation type is 'dev', Moonwall launches the node binary automatically and tears it down after tests complete. If using 'read_only' or 'chopsticks', ensure the endpoint is reachable before running tests." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "moonwall: command not found", - "cause": "Global npm installation did not add the binary to PATH.", - "resolution": "Run 'npm root -g' to find the global bin directory and verify it is in $PATH. Alternatively, use 'npx moonwall' for a local installation." - }, - { - "pattern": "Cannot find module '@moonwall/cli'", - "cause": "Moonwall was not installed or the local installation is missing.", - "resolution": "Run 'npm install @moonwall/cli@5.18.3' in the project directory. For global, run 'npm install -g @moonwall/cli@5.18.3'." - }, - { - "pattern": "Error: Cannot connect to endpoint ws://localhost:9944", - "cause": "The node specified in the moonwall.config foundation is not running, or the endpoint URL is incorrect.", - "resolution": "Start the node manually and verify it is listening on the configured port before running Moonwall. For 'dev' foundation, ensure the binPath points to an executable binary and the --dev flag is appropriate for your chain." - }, - { - "pattern": "Timeout exceeded for test suite", - "cause": "The test environment took too long to start or a test operation exceeded the global timeout.", - "resolution": "Increase the 'timeout' value in moonwall.config (e.g., from 30000 to 60000 ms). For slow chain operations like block finalization, add explicit wait-for-block assertions rather than fixed sleeps." - } - ], - "supplementary_context": { - "description": "Load these pages for Chopsticks fork setup (to use as Moonwall's test environment) or for a local development node to test against.", - "pages": [ - { - "slug": "reference-tools-chopsticks", - "title": "Chopsticks", - "url": "https://docs.polkadot.com/reference/tools/chopsticks.md", - "relevance": "Setting up a Chopsticks fork as the 'chopsticks' foundation type for Moonwall tests." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "How to run a local development node to use as the 'dev' foundation in Moonwall." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run e2e tests against a local dev node", - "user_says": "Set up Moonwall to run e2e tests for my Polkadot SDK chain", - "actions": [ - "Install Moonwall 5.18.3 globally: npm install -g @moonwall/cli@5.18.3", - "Ask the user to run 'moonwall init' and complete the interactive wizard, choosing 'dev' foundation", - "Update moonwall.config with the correct node binPath and ws://localhost:9944 connection", - "Create a test file in ./tests using describeSuite with a foundationMethods of 'dev'", - "Run 'moonwall test default_env -c moonwall.config'" - ], - "result": "Moonwall launches the node binary, runs all test suites, and reports pass/fail results per test case" - }, - { - "scenario": "Edge case: test environment fails to start", - "user_says": "Moonwall says it cannot connect to the node endpoint", - "actions": [ - "Check the foundation type in moonwall.config — for 'dev', verify binPath is correct and the binary has execute permission", - "For 'read_only' or 'chopsticks', start the node or Chopsticks fork manually and confirm it is listening on the configured endpoint", - "Increase the connection timeout in moonwall.config if the node starts slowly" - ], - "result": "Node is reachable; Moonwall connects and runs tests" - } - ] - }, - { - "id": "build-dapp-viem-nextjs", - "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js", - "description": "Scaffolds a full-stack Polkadot Hub dApp: deploys a Solidity Storage contract via Hardhat Ignition to TestNet, then builds a Next.js 14 frontend with Viem wallet connect, read, and write components. Use when building a complete end-to-end smart contract application on Polkadot Hub from scratch. Trigger phrases: 'build a dapp polkadot', 'zero to hero tutorial', 'smart contract with frontend viem', 'deploy and connect next.js'. Covers dotenv private key security, 5000 gwei gas config, evmVersion cancun, Ignition recovery, and Next.js/Viem component wiring. Output: running dApp at localhost:3000.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/dapps/zero-to-hero.md" - ], - "primary_page": "smart-contracts/cookbook/dapps/zero-to-hero.md", - "prerequisites": { - "runtime": [ - "Node.js v22+", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for contract deployment gas" - ], - "wallet": [ - "MetaMask configured for Polkadot Hub TestNet (chainId 420420417)", - "0x-prefixed EVM private key for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Clone the reference repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git" - ], - "description": "Clone revm-hardhat-examples. This repo contains the complete zero-to-hero-dapp with both the Hardhat contract project and the Next.js frontend." - }, - { - "order": 2, - "action": "Install contract project dependencies", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install Hardhat and contract dependencies, then add dotenv. dotenv replaces Hardhat's interactive vars system, which cannot be used in agent shells." - }, - { - "order": 3, - "action": "Create .env file and protect it with .gitignore", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly, filling in PRIVATE_KEY=0x. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 4, - "action": "Update hardhat.config.ts for TestNet and security", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file." - }, - { - "order": 5, - "action": "Compile the Storage contract", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile Storage.sol. If you see 'invalid opcode: MCOPY', verify evmVersion: 'cancun' is set in the Solidity compiler settings in hardhat.config.ts and recompile." - }, - { - "order": 6, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract", - "commands": [ - "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" - ], - "expected_output": "Storage deployed to: 0x...", - "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy." - }, - { - "order": 7, - "action": "Scaffold the Next.js frontend", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp", - "commands": [ - "echo 'n\\nn\\nn\\nn\\nn\\nn' | npx create-next-app@14 frontend --typescript --no-eslint --no-tailwind --no-src-dir --app" - ], - "description": "Scaffold Next.js 14 TypeScript app named 'frontend'. Piped 'n' answers handle any unexpected interactive prompts from create-next-app. If this fails due to prompt changes, run: npx create-next-app@14 frontend and answer No to all questions except TypeScript (Yes) and App Router (Yes)." - }, - { - "order": 8, - "action": "Install frontend dependencies", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", - "commands": [ - "npm install viem wagmi @tanstack/react-query" - ], - "description": "Install viem for contract interaction, wagmi for React hooks, and React Query (required by wagmi v2)." - }, - { - "order": 9, - "action": "Update contract address in contract config", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", - "description": "Open src/contracts/contract.ts (or the equivalent contract config file in the frontend). Replace the CONTRACT_ADDRESS placeholder or any hardcoded example address with the deployed contract address saved in step 6. Save the file." - }, - { - "order": 10, - "action": "Start the development server", - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend", - "commands": [ - "npm run dev" - ], - "expected_output": "ready - started server on 0.0.0.0:3000", - "description": "Start the Next.js dev server. Open http://localhost:3000 in a browser. Click 'Connect Wallet' and approve the MetaMask popup. Read the current stored value, then write a new value and confirm the MetaMask transaction. Verify the displayed value updates after the transaction is mined." - } - ], - "reference_code": { - "repo": "polkadot-developers/revm-hardhat-examples", - "branch": "master", - "base_path": "zero-to-hero-dapp", - "files": [] - }, - "error_patterns": [ - { - "pattern": "priority is too low", - "cause": "Transaction gas price is below the 1000 gwei base fee on Polkadot Hub TestNet. Default Hardhat gas estimation produces values too low for this network.", - "resolution": "Set gasPrice: 5000000000000 in the polkadotTestnet network block in hardhat.config.ts. Delete ignition/deployments/ if a prior attempt was made, then redeploy." - }, - { - "pattern": "IGN401 / Transaction dropped", - "cause": "Ignition lost track of the transaction. The transaction may have succeeded or may be stuck in the mempool.", - "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded — use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." - }, - { - "pattern": "invalid opcode: MCOPY", - "cause": "OpenZeppelin v5.4.0+ uses the mcopy opcode (Cancun EVM upgrade). Solidity defaults to an older EVM version.", - "resolution": "Add evmVersion: 'cancun' to the Solidity compiler settings in hardhat.config.ts. Recompile with 'npx hardhat compile'." - }, - { - "pattern": "Error: could not detect network", - "cause": "Incorrect RPC URL or chain ID in hardhat.config.ts.", - "resolution": "Set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417 in the polkadotTestnet network block." - }, - { - "pattern": "Module not found: viem or wagmi", - "cause": "Frontend dependencies were not installed.", - "resolution": "Run 'npm install viem wagmi @tanstack/react-query' in the zero-to-hero-dapp/frontend directory." - } - ], - "supplementary_context": { - "description": "Load these pages for Hardhat configuration reference, Viem library details, or network connection parameters.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Full Hardhat EVM configuration options for Polkadot Hub, including network settings and contract verification." - }, - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Viem library reference for chain config, wallet client, and contract interaction patterns on Polkadot Hub." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters (RPC URLs, chain IDs, WSS) for Polkadot Hub TestNet and Mainnet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: full dApp from scratch", - "user_says": "Build a smart contract dApp on Polkadot Hub with Viem and Next.js", - "actions": [ - "Clone revm-hardhat-examples and navigate to zero-to-hero-dapp/storage-contract", - "Install dependencies and create .env with PRIVATE_KEY (user fills in)", - "Update hardhat.config.ts: dotenv, gasPrice 5000 gwei, evmVersion cancun, requiredConfirmations 1", - "Compile Storage.sol and deploy to polkadotTestnet via Ignition", - "Scaffold Next.js 14 frontend with create-next-app@14", - "Install viem, wagmi, react-query; update contract.ts with deployed address", - "Run npm run dev and test at localhost:3000" - ], - "result": "Storage contract deployed to TestNet; Next.js app at localhost:3000 with working wallet connect, read, and write contract interactions via MetaMask" - }, - { - "scenario": "Edge case: Ignition reports transaction dropped but contract may have deployed", - "user_says": "Ignition says IGN401 but I don't know if the contract deployed", - "actions": [ - "Check ignition/deployments//deployed_addresses.json for a Storage address", - "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", - "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" - ], - "result": "Contract address recovered from deployment state or clean redeployment completed" - } - ] - }, - { - "id": "deploy-erc20-token-remix", - "title": "Deploy an ERC-20 Token Using Remix IDE", - "description": "Deploys a Solidity ERC-20 token contract to Polkadot Hub TestNet entirely through the Remix browser IDE with MetaMask wallet injection. Use when you prefer a no-CLI workflow or are demonstrating contract deployment to non-developers. Trigger phrases: 'deploy ERC-20 remix', 'erc20 token remix polkadot', 'deploy token browser IDE'. Covers fetching contract code from revm-hardhat-examples, Remix compile settings (evmVersion cancun for OpenZeppelin v5), MetaMask TestNet configuration, and minting tokens via the Remix UI. No local environment required beyond a browser and MetaMask.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md", - "prerequisites": { - "runtime": [ - "A modern web browser (Chrome or Firefox recommended)" - ], - "network": [ - "Polkadot Hub TestNet (chainId 420420417) — configured in MetaMask" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required to pay deployment gas" - ], - "wallet": [ - "MetaMask browser extension installed and unlocked", - "An account funded with testnet PAS on Polkadot Hub TestNet (chainId 420420417, RPC: https://services.polkadothub-rpc.com/testnet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Fetch the ERC-20 contract source", - "working_directory": ".", - "description": "Fetch the OpenZeppelin ERC-20 contract source from the reference repository. The contract file is at: https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol\n\nRun: curl -sL https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol and display the contract source to the user. The user will paste this into Remix in the next step." - }, - { - "order": 2, - "action": "Open Remix IDE and create the contract file", - "working_directory": ".", - "description": "Navigate to https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), click the 'New File' icon and name it 'MyToken.sol'. Paste the full contract source fetched in step 1 into the editor. Save with Ctrl+S (or Cmd+S on Mac)." - }, - { - "order": 3, - "action": "Configure the Solidity compiler in Remix", - "working_directory": ".", - "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown — required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'." - }, - { - "order": 4, - "action": "Add Polkadot Hub TestNet to MetaMask", - "working_directory": ".", - "description": "If MetaMask does not already have Polkadot Hub TestNet configured, add it manually:\n1. Open MetaMask and go to Settings > Networks > Add Network > Add a network manually.\n2. Enter: Network Name: Polkadot Hub TestNet; RPC URL: https://services.polkadothub-rpc.com/testnet; Chain ID: 420420417; Currency Symbol: PAS.\n3. Click Save and switch to the Polkadot Hub TestNet network.\n4. Confirm the account shown in MetaMask has PAS tokens. If not, visit https://faucet.polkadot.io/ to request testnet PAS." - }, - { - "order": 5, - "action": "Connect MetaMask to Remix and deploy", - "working_directory": ".", - "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect — click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation — review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10–30 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below." - }, - { - "order": 6, - "action": "Verify the deployment and mint tokens", - "working_directory": ".", - "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' — should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased." - } - ], - "reference_code": { - "repo": "polkadot-developers/revm-hardhat-examples", - "branch": "master", - "base_path": "erc20-hardhat", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix compilation fails with 'Unknown key evmVersion cancun'", - "cause": "Older Remix versions may not list 'cancun' in the EVM version dropdown.", - "resolution": "Update to the latest Remix version by refreshing the page. The cancun option should appear in Advanced Configuration. Alternatively, use a custom EVM version by typing 'cancun' in the input field if available." - }, - { - "pattern": "MetaMask shows wrong network after connecting to Remix", - "cause": "MetaMask is on a different network than Polkadot Hub TestNet.", - "resolution": "In MetaMask, switch to 'Polkadot Hub TestNet' (chainId 420420417) before deploying. Confirm the Remix environment section shows the correct chain ID." - }, - { - "pattern": "Transaction fails with insufficient funds", - "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", - "resolution": "Visit https://faucet.polkadot.io/, enter your MetaMask address, and request testnet PAS. Wait for the tokens to arrive (usually under 1 minute), then retry deployment." - }, - { - "pattern": "Remix shows 'Gas estimation failed'", - "cause": "Contract constructor arguments may be missing or invalid, or the MetaMask account has insufficient funds.", - "resolution": "Verify the initialOwner address is filled in correctly in the constructor arguments. Check PAS balance in MetaMask and top up via faucet if needed." - } - ], - "supplementary_context": { - "description": "Load these pages for ERC-20 Hardhat deployment (CLI alternative), network connection details, or wallet configuration.", - "pages": [ - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", - "title": "Deploy an ERC-20 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", - "relevance": "CLI-based ERC-20 deployment using Hardhat — use when the user prefers a terminal workflow over Remix." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters for manually adding Polkadot Hub to MetaMask." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask Injected Provider." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy ERC-20 via Remix", - "user_says": "Deploy an ERC-20 token on Polkadot Hub using Remix IDE", - "actions": [ - "Fetch MyToken.sol from revm-hardhat-examples and display to user", - "Open https://remix.ethereum.org, create MyToken.sol, paste contract", - "In Remix Solidity compiler, set version 0.8.20+ and EVM version 'cancun'", - "Add Polkadot Hub TestNet to MetaMask (chainId 420420417, RPC testnet URL)", - "Connect Injected Provider in Remix Deploy tab, deploy with initialOwner set to wallet address", - "Verify deployment and mint tokens via Remix UI" - ], - "result": "ERC-20 token deployed on Polkadot Hub TestNet; contract visible in Remix with working name, symbol, mint, and balanceOf functions" - }, - { - "scenario": "Edge case: user has no testnet PAS tokens", - "user_says": "Deployment failed — gas fee transaction rejected", - "actions": [ - "Direct user to https://faucet.polkadot.io/ and ask them to enter their MetaMask address", - "Wait for confirmation that PAS tokens have arrived (check MetaMask balance)", - "Retry deployment from the Remix Deploy tab" - ], - "result": "Account funded with testnet PAS; deployment proceeds successfully" - } - ] - }, - { - "id": "deploy-contracts-ethers-js", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md" + }, + { + "relevance": "Overview of parachain development paths and starting points available after SDK setup.", + "slug": "parachains-get-started", + "title": "Get Started with Parachain Development", + "url": "https://docs.polkadot.com/parachains/get-started.md" + } + ] + }, + "title": "Install the Polkadot SDK", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Scaffolds a Hardhat v2 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer account. Must correspond to a testnet account funded with PAS tokens. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "The hardhat.config.ts still contains vars.get('PRIVATE_KEY') instead of the dotenv substitution.", + "pattern": "Error: vars is not defined / Cannot read properties of undefined (reading 'get')", + "resolution": "Open hardhat.config.ts, replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string, and add 'import \"dotenv/config\";' as the first line of the file." + }, + { + "cause": "The deployer account has no testnet PAS tokens, or the gasPrice in hardhat.config.ts is below the network minimum (1000 gwei base fee).", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Get testnet tokens from https://faucet.polkadot.io/. Also verify the polkadotTestnet network block in hardhat.config.ts includes gasPrice: 5000000000000 (5000 gwei)." + }, + { + "cause": "Ignition lost track of the deployment transaction. Often caused by requiredConfirmations: 0 or gasPrice below the base fee.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported on retry", + "resolution": "First check if the contract was actually deployed: look in ignition/deployments/ for deployed_addresses.json. If deployed, import the existing deployment. If not deployed: (1) delete the ignition/deployments/ directory, (2) ensure hardhat.config.ts has gasPrice: 5000000000000 and ignition.requiredConfirmations: 1, (3) redeploy." + }, + { + "cause": "The Solidity contract uses OpenZeppelin v5.4.0+ which requires the Cancun EVM version.", + "pattern": "CompilationError: Invalid opcode: MCOPY", + "resolution": "In hardhat.config.ts, add evmVersion: 'cancun' inside the solidity compiler settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }" + } + ], + "examples": [ + { + "actions": [ + "Scaffold hardhat-deployment/ with npm init -y", + "Install hardhat, toolbox, dotenv", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Fetch and configure hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set requiredConfirmations: 1", + "Fetch contracts/Storage.sol", + "Run npx hardhat compile", + "Fetch ignition/modules/Storage.ts", + "Run npx hardhat ignition deploy --network polkadotTestnet; delegate confirmation prompt to user" + ], + "result": "Storage contract deployed; address printed to console", + "scenario": "Common scenario: deploy a first smart contract to Polkadot Hub", + "user_says": "Deploy a Storage contract to Polkadot Hub TestNet using Hardhat" + }, + { + "actions": [ + "Check ignition/deployments/ for deployed_addresses.json to determine if contract was actually deployed", + "If not deployed: delete ignition/deployments/, verify gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy" + ], + "result": "Fresh deployment succeeds with correct gas configuration", + "scenario": "Edge case: deployment transaction gets stuck (IGN401)", + "user_says": "Ignition says the transaction was dropped and retrying gives 'Transaction Already Imported'" + } + ], + "id": "deploy-basic-contract-hardhat", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "runtime": [ + "Node.js v22.13.1 or later", + "npm" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (Polkadot Hub TestNet base fee is 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", + "project_structure": "hardhat-deployment/\n├── contracts/\n│ └── Storage.sol\n├── ignition/\n│ └── modules/\n│ └── Storage.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat", + "branch": "master", + "files": [ + { + "description": "Hardhat config with polkadotTestnet network (RPC, chain ID, accounts via vars). Requires dotenv conversion and gasPrice addition per skill steps.", + "path": "hardhat.config.ts" + }, + { + "description": "Simple Storage contract with uint256 store/retrieve functions", + "path": "Storage.sol" + }, + { + "description": "Hardhat Ignition module that deploys the Storage contract", + "path": "storage.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + ], + "steps": [ + { + "action": "Create project directory and initialize npm", + "commands": [ + "mkdir hardhat-deployment && cd hardhat-deployment", + "npm init -y" + ], + "description": "Create a new directory named 'hardhat-deployment' and initialize a Node.js project. Do NOT run npx hardhat init at this stage — interactive init is non-deterministic across Hardhat versions. All project files will be fetched from the reference repository in subsequent steps.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Hardhat and dependencies", + "commands": [ + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", + "npm install dotenv" + ], + "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells.", + "order": 2, + "working_directory": "hardhat-deployment" + }, + { + "action": "Create project directory structure", + "commands": [ + "mkdir -p contracts ignition/modules" + ], + "description": "Create the contracts and ignition/modules directories that Hardhat expects. The test directory is optional and not needed for this deployment workflow.", + "order": 3, + "working_directory": "hardhat-deployment" + }, + { + "action": "Create .env file and .gitignore for private key", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and a .gitignore entry to prevent accidental commits. Stop here and ask the user to edit the .env file directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm the .env file is filled before proceeding to step 5.", + "order": 4, + "working_directory": "hardhat-deployment" + }, + { + "action": "Fetch and configure hardhat.config.ts", + "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors).", + "order": 5, + "reference_file": "hardhat.config.ts", + "working_directory": "hardhat-deployment" + }, + { + "action": "Fetch Storage.sol contract", + "description": "Fetch the reference file and save it as 'contracts/Storage.sol'. No substitutions needed — this is a generic Storage contract with get/set functions.", + "order": 6, + "reference_file": "Storage.sol", + "working_directory": "hardhat-deployment" + }, + { + "action": "Compile the contract", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile Storage.sol. Successful output shows 'Compiled 1 Solidity file successfully' and creates artifacts in the artifacts/ directory. If compilation fails with 'invalid opcode: MCOPY', add 'evmVersion: \"cancun\"' inside the solidity.settings.evmVersion field of hardhat.config.ts and recompile.", + "expected_output": "Compiled 1 Solidity file successfully", + "order": 7, + "working_directory": "hardhat-deployment" + }, + { + "action": "Fetch the Ignition deployment module", + "description": "Fetch the reference file and save it as 'ignition/modules/Storage.ts'. No substitutions needed — the module deploys the Storage contract by name.", + "order": 8, + "reference_file": "storage.ts", + "working_directory": "hardhat-deployment" + }, + { + "action": "Deploy the contract to Polkadot Hub TestNet", + "commands": [ + "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" + ], + "description": "Deploy the contract. Hardhat Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user (they must type the network name or press Enter). After confirmation, Ignition broadcasts the deployment transaction. Save the deployed contract address printed at the end — needed for contract interaction. If the transaction becomes stuck, see error_patterns for the IGN401 recovery procedure.", + "expected_output": "Hardhat Ignition 🚀 ... Storage deployed to: 0x...", + "interactive": true, + "order": 9, + "working_directory": "hardhat-deployment" + } + ], + "supplementary_context": { + "description": "Load when the user wants to build on this deployment or needs to connect a frontend.", + "pages": [ + { + "relevance": "Next step: deploy an ERC-20 token using the same Hardhat workflow.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", + "title": "Deploy an ERC-20 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" + }, + { + "relevance": "Hardhat configuration reference for Polkadot Hub including verification setup.", + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md" + } + ] + }, + "title": "Deploy a Basic Smart Contract with Hardhat", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer/test account. Must be funded with testnet PAS. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode which requires Cancun EVM version. The hardhat.config.ts does not specify evmVersion: 'cancun'.", + "pattern": "CompilationError: Invalid opcode: MCOPY", + "resolution": "In hardhat.config.ts, set evmVersion: 'cancun' inside solidity.settings: solidity: { version: '0.8.28', settings: { evmVersion: 'cancun' } }. Then recompile." + }, + { + "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + }, + { + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." + }, + { + "cause": "Ignition lost track of deployment, commonly due to missing gasPrice or requiredConfirmations: 0.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." + } + ], + "examples": [ + { + "actions": [ + "Clone revm-hardhat-examples and cd erc20-hardhat", + "Run npm i && npm install dotenv", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Modify hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set evmVersion cancun, confirm requiredConfirmations: 1", + "Run npx hardhat compile", + "Run npx hardhat test --network polkadotTestnet to verify contract logic", + "Run npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet; delegate confirmation to user" + ], + "result": "ERC-20 token contract deployed; address printed to console", + "scenario": "Common scenario: deploy a new ERC-20 token to Polkadot Hub", + "user_says": "Deploy an ERC-20 token to Polkadot Hub TestNet" + }, + { + "actions": [ + "Open hardhat.config.ts", + "Change the solidity field to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", + "Run npx hardhat compile again" + ], + "result": "Compilation succeeds with Cancun EVM version", + "scenario": "Edge case: compilation fails with MCOPY opcode error", + "user_says": "Compilation fails with 'Invalid opcode: MCOPY'" + } + ], + "id": "deploy-erc20-token-hardhat", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "runtime": [ + "Node.js v22.13.1 or later", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens for gas fees and test transactions — get from https://faucet.polkadot.io/ (base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" + ], + "steps": [ + { + "action": "Clone the ERC-20 template repository", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples/", + "cd revm-hardhat-examples/erc20-hardhat" + ], + "description": "Clone the revm-hardhat-examples repository and navigate into the erc20-hardhat subdirectory. This template contains a pre-configured Hardhat project with an OpenZeppelin ERC-20 contract, test suite, and Ignition deployment module.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm i", + "npm install dotenv" + ], + "description": "Install all template dependencies from package.json, then additionally install dotenv. dotenv is needed to replace Hardhat's interactive vars system (which cannot be used in agent shells) with a .env-based private key approach.", + "order": 2, + "working_directory": "revm-hardhat-examples/erc20-hardhat" + }, + { + "action": "Create .env file and .gitignore for private key", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding.", + "order": 3, + "working_directory": "revm-hardhat-examples/erc20-hardhat" + }, + { + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file.", + "order": 4, + "working_directory": "revm-hardhat-examples/erc20-hardhat" + }, + { + "action": "Compile the contract", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile the ERC-20 contract. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in the solidity compiler settings and recompile.", + "expected_output": "Compiled 1 Solidity file successfully", + "order": 5, + "working_directory": "revm-hardhat-examples/erc20-hardhat" + }, + { + "action": "Run the test suite", + "commands": [ + "npx hardhat test --network polkadotTestnet" + ], + "description": "Run the predefined test suite against Polkadot Hub TestNet. Tests verify token name/symbol, owner, initial supply, minting, and balance tracking. This consumes testnet PAS for gas. If the account is low on PAS, visit https://faucet.polkadot.io/ first. Save the test output — it confirms the contract logic is correct before deployment.", + "expected_output": "7 passing", + "order": 6, + "working_directory": "revm-hardhat-examples/erc20-hardhat" + }, + { + "action": "Deploy the ERC-20 contract", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network polkadotTestnet" + ], + "description": "Deploy the ERC-20 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this confirmation to the user. After confirmation, the deployment transaction is broadcast. Save the contract address printed on success — needed for token interaction and verification.", + "expected_output": "MyToken deployed to: 0x...", + "interactive": true, + "order": 7, + "working_directory": "revm-hardhat-examples/erc20-hardhat" + } + ], + "supplementary_context": { + "description": "Load when the user wants to build a dApp on top of the deployed ERC-20 or interact with it.", + "pages": [ + { + "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract.", + "slug": "smart-contracts-cookbook-dapps-zero-to-hero", + "title": "Zero to Hero Smart Contract DApp", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md" + }, + { + "relevance": "How to interact with the deployed ERC-20 contract using Ethers.js.", + "slug": "smart-contracts-libraries-ethers-js", "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "description": "Sets up a Node.js project, compiles a Solidity contract with solc, deploys to Polkadot Hub TestNet using Ethers.js v6, and interacts with the deployed contract via read and write script calls. Use when integrating Ethers.js into a new or existing Node.js project targeting Polkadot Hub. Trigger phrases: 'deploy with ethers.js polkadot', 'ethers.js contract deployment', 'use ethers polkadot hub', 'ethersjs testnet'. Covers dotenv credential setup, custom Polkadot Hub provider configuration, and the compile-deploy-interact workflow. Produces a deployed contract with verified read/write interaction.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/ethers-js.md" - ], - "primary_page": "smart-contracts/libraries/ethers-js.md", - "prerequisites": { - "runtime": [ - "Node.js v22+", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" - ], - "wallet": [ - "0x-prefixed EVM private key for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "required": true - }, - { - "name": "RPC_URL", - "description": "HTTP RPC endpoint for Polkadot Hub TestNet. Set to https://services.polkadothub-rpc.com/testnet.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create the project directory and initialize", - "working_directory": ".", - "commands": [ - "mkdir ethers-deploy && cd ethers-deploy", - "npm init -y", - "npm pkg set type=module" - ], - "description": "Create and initialize an ESM Node.js project. ESM is required for Ethers.js v6 imports." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "ethers-deploy", - "commands": [ - "npm install ethers dotenv", - "npm install --save-dev solc typescript tsx @types/node" - ], - "description": "Install ethers (v6), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling." - }, - { - "order": 3, - "action": "Create .env file and .gitignore", - "working_directory": "ethers-deploy", - "commands": [ - "printf 'PRIVATE_KEY=\\nRPC_URL=https://services.polkadothub-rpc.com/testnet\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create .env with RPC_URL pre-filled and empty PRIVATE_KEY. Stop and ask the user to edit .env directly by adding their 0x-prefixed private key as PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing." - }, - { - "order": 4, - "action": "Fetch the provider connection helper", - "working_directory": "ethers-deploy", - "reference_file": "connectToProvider.js", - "description": "Fetch the reference file `connectToProvider.js` and save it to the project root. Verify it points at the Polkadot Hub TestNet RPC URL `https://services.polkadothub-rpc.com/testnet`; if it uses a placeholder, replace `INSERT_RPC_URL` with the testnet URL." - }, - { - "order": 5, - "action": "Fetch the Storage.sol contract", - "working_directory": "ethers-deploy", - "reference_file": "Storage.sol", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." - }, - { - "order": 6, - "action": "Fetch the compilation script", - "working_directory": "ethers-deploy", - "reference_file": "compile.js", - "description": "Fetch the reference file `compile.js` and save it to the project root. The script invokes `solc` against `Storage.sol` and writes the ABI and bytecode to `artifacts/`." - }, - { - "order": 7, - "action": "Compile the Solidity contract", - "working_directory": "ethers-deploy", - "commands": [ - "node compile.js" - ], - "description": "Run the compile script to generate the contract ABI and bytecode. Confirm an `artifacts/` directory was created with `Storage.json` (or similar) before proceeding to deploy." - }, - { - "order": 8, - "action": "Fetch the deployment script", - "working_directory": "ethers-deploy", - "reference_file": "deploy.js", - "description": "Fetch the reference file `deploy.js` and save it to the project root. Then apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line (or `require(\"dotenv\").config()` for CJS).\n(2) Replace `INSERT_RPC_URL` with `process.env.RPC_URL` (or hardcode the testnet URL).\n(3) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`.\n(4) Replace any hardcoded chain ID with `420420417`." - }, - { - "order": 9, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "ethers-deploy", - "commands": [ - "node deploy.js" - ], - "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry." - }, - { - "order": 10, - "action": "Fetch the interaction script and interact with the deployed contract", - "working_directory": "ethers-deploy", - "reference_file": "checkStorage.js", - "description": "Fetch the reference file `checkStorage.js` and save it as `checkStorage.js`. Then substitute `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step. Run it with `node checkStorage.js`. Verify the output shows successful read and write operations against the deployed contract." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/smart-contracts/libraries/ethers-js", - "files": [ - { - "path": "Storage.sol", - "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." - }, - { - "path": "compile.js", - "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts for Storage.sol" - }, - { - "path": "deploy.js", - "description": "Deploys the compiled Storage contract to Polkadot Hub via ethers.js Wallet + ContractFactory" - }, - { - "path": "checkStorage.js", - "description": "Reads + writes the deployed Storage contract via ethers.js Contract.connect; calls the `storedNumber` getter and `setNumber(value)` setter." - }, - { - "path": "connectToProvider.js", - "description": "Initializes ethers.js JsonRpcProvider for Polkadot Hub TestNet" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds for intrinsic transaction cost", - "cause": "The deployer account does not have enough PAS to pay for deployment gas.", - "resolution": "Visit https://faucet.polkadot.io/, enter the deployer address, and request testnet PAS. Confirm balance in MetaMask or via eth_getBalance RPC call, then retry." - }, - { - "pattern": "Error: could not detect network", - "cause": "The RPC_URL in .env is incorrect or the endpoint is unreachable.", - "resolution": "Verify RPC_URL=https://services.polkadothub-rpc.com/testnet in .env. Test connectivity: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'" - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "Project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the ethers-deploy directory to enable ES modules." - }, - { - "pattern": "Error: transaction underpriced", - "cause": "The gas price in the transaction is below the 1000 gwei TestNet base fee.", - "resolution": "Add an explicit gasPrice to the transaction options: { gasPrice: ethers.parseUnits('5000', 'gwei') }. Polkadot Hub TestNet has a base fee of 1000 gwei." - } - ], - "supplementary_context": { - "description": "Load these pages for network details, wallet setup, or other library alternatives.", - "pages": [ - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters (RPC URLs, chain IDs, WebSocket endpoints) for all Polkadot Hub environments." - }, - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Alternative to Ethers.js: Viem deployment pattern for Polkadot Hub (more type-safe, recommended for new projects)." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "Hardhat-based deployment workflow — use when the project needs a full Hardhat setup rather than lightweight scripts." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy and interact with a Storage contract", - "user_says": "Deploy a contract to Polkadot Hub using Ethers.js", - "actions": [ - "Create ethers-deploy ESM project and install ethers, dotenv, solc, tsx", - "Create .env with RPC_URL and empty PRIVATE_KEY (user fills in)", - "Fetch compile.ts, deploy.ts, interact.ts from polkadot-cookbook", - "Apply substitutions: dotenv import, process.env.RPC_URL, process.env.PRIVATE_KEY, chainId 420420417", - "Run npx tsx compile.ts to build ABI/bytecode", - "Run npx tsx deploy.ts and save deployed contract address", - "Run npx tsx interact.ts to verify read/write contract operations" - ], - "result": "Solidity contract compiled, deployed to Polkadot Hub TestNet, and verified with read/write operations via Ethers.js scripts" - }, - { - "scenario": "Edge case: transaction underpriced error during deployment", - "user_says": "Deploy failed with transaction underpriced", - "actions": [ - "Open deploy.ts and add gasPrice override to the deployment transaction options", - "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", - "Retry with npx tsx deploy.ts" - ], - "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet" - } - ], - "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json" - }, - { - "id": "deploy-contracts-viem", - "title": "Deploy Contracts to Polkadot Hub with Viem", - "description": "Sets up a TypeScript project, compiles a Solidity contract, deploys to Polkadot Hub TestNet using Viem v2 walletClient.deployContract, and interacts with the deployed contract via publicClient and walletClient. Use when building a TypeScript project that needs direct Viem integration with Polkadot Hub without a Hardhat setup. Trigger phrases: 'deploy with viem polkadot', 'viem contract deployment', 'use viem polkadot hub', 'deploy viem typescript'. Covers custom Polkadot Hub chain definition, dotenv credential setup, and the compile-deploy-interact pattern. Companion to deploy-contracts-ethers-js.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/viem.md" - ], - "primary_page": "smart-contracts/libraries/viem.md", - "prerequisites": { - "runtime": [ - "Node.js v22+", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" - ], - "wallet": [ - "0x-prefixed EVM private key for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create the project directory and initialize", - "working_directory": ".", - "commands": [ - "mkdir viem-deploy && cd viem-deploy", - "npm init -y && npm pkg set type=module", - "mkdir -p src contracts abis artifacts" - ], - "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) — the reference scripts assume this layout for relative paths." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "viem-deploy", - "commands": [ - "npm install viem dotenv", - "npm install --save-dev solc typescript tsx @types/node" - ], - "description": "Install viem (v2), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling." - }, - { - "order": 3, - "action": "Create .env file and .gitignore", - "working_directory": "viem-deploy", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' > .gitignore" - ], - "description": "Create .env with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly by adding PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing." - }, - { - "order": 4, - "action": "Fetch the chain configuration", - "working_directory": "viem-deploy", - "reference_file": "chainConfig.ts", - "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) — apply the same substitutions consistently in all three files." - }, - { - "order": 5, - "action": "Fetch the Storage.sol contract", - "working_directory": "viem-deploy", - "reference_file": "Storage.sol", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed." - }, - { - "order": 6, - "action": "Fetch the public client setup", - "working_directory": "viem-deploy", - "reference_file": "createClient.ts", - "description": "Fetch the reference file `createClient.ts` and save it as `src/createClient.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (the file declares the chain locally and has the same 8 placeholders). Constructs a viem `PublicClient` against the Polkadot Hub TestNet chain definition; used for reads and tx receipts." - }, - { - "order": 7, - "action": "Fetch the wallet client setup", - "working_directory": "viem-deploy", - "reference_file": "createWallet.ts", - "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders — including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env." - }, - { - "order": 8, - "action": "Fetch the compilation script", - "working_directory": "viem-deploy", - "reference_file": "compile.ts", - "description": "Fetch the reference file `compile.ts` and save it as `src/compile.ts`. The script invokes `solc` against `contracts/Storage.sol` and writes ABI + bytecode artifacts to `abis/` and `artifacts/`." - }, - { - "order": 9, - "action": "Compile the Solidity contract", - "working_directory": "viem-deploy", - "commands": [ - "npx tsx src/compile.ts" - ], - "description": "Run the compile script. Verify that `abis/Storage.json` and `artifacts/Storage.bin` (or equivalent) exist before deploying." - }, - { - "order": 10, - "action": "Fetch the deployment script", - "working_directory": "viem-deploy", - "reference_file": "deploy.ts", - "description": "Fetch the reference file `deploy.ts` and save it as `src/deploy.ts`. Apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line.\n(2) Replace `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``.\n(3) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`.\n(4) Replace `INSERT_CHAIN_ID` with `420420417n` (BigInt).\n\nThe script reads from `../abis` and `../artifacts` relative to the script location, which resolves correctly when the script is in `src/`." - }, - { - "order": 11, - "action": "Deploy the contract to Polkadot Hub TestNet", - "working_directory": "viem-deploy", - "commands": [ - "npx tsx src/deploy.ts" - ], - "description": "Run the deployment script. Save the deployed contract address from the output.\n\nIf the transaction fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction fails with `fee cap too low` or similar gas errors: add a `gasPrice` override in `deploy.ts` (e.g. `gasPrice: parseGwei('5000')`)." - }, - { - "order": 12, - "action": "Fetch the interaction script and interact with the deployed contract", - "working_directory": "viem-deploy", - "reference_file": "interact.ts", - "description": "Fetch the reference file `interact.ts` and save it as `src/interact.ts`. Replace `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step and `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``. Run it with `npx tsx src/interact.ts`. Verify the value returned by reading `storedNumber` changes after calling `setNumber(value)`." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/smart-contracts/libraries/viem", - "files": [ - { - "path": "Storage.sol", - "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." - }, - { - "path": "chainConfig.ts", - "description": "Custom Polkadot Hub TestNet chain definition for viem (chain ID, RPC, native currency)" - }, - { - "path": "createClient.ts", - "description": "viem PublicClient construction against Polkadot Hub TestNet" - }, - { - "path": "createWallet.ts", - "description": "viem WalletClient construction with PRIVATE_KEY from env" - }, - { - "path": "compile.ts", - "description": "Solidity compiler wrapper that produces ABI + bytecode artifacts" - }, - { - "path": "deploy.ts", - "description": "Deploys the compiled Storage contract via viem WalletClient.deployContract" - }, - { - "path": "interact.ts", - "description": "Reads + writes the deployed Storage contract via viem readContract/writeContract" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: fee cap too low / transaction underpriced", - "cause": "Viem's default gas estimation produces a gas price below the 1000 gwei TestNet base fee.", - "resolution": "Add an explicit gasPrice override to the deployContract call: { gasPrice: parseGwei('5000') }. Import parseGwei from 'viem'. Polkadot Hub TestNet requires at least 1000 gwei; use 5000 gwei (5x) as a safe margin." - }, - { - "pattern": "Error: Chain ID mismatch", - "cause": "The Viem chain definition's id does not match the connected network's chain ID.", - "resolution": "Verify the chain object has id: 420420417. Check with: const chainId = await publicClient.getChainId(); and confirm it returns 420420417." - }, - { - "pattern": "Cannot find module 'viem'", - "cause": "Viem package not installed.", - "resolution": "Run 'npm install viem' in the viem-deploy directory." - }, - { - "pattern": "Error: insufficient funds for intrinsic transaction cost", - "cause": "The deployer account does not have enough PAS for gas.", - "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS for the deployer address. Verify balance with: await publicClient.getBalance({ address: '0x...' })" - } - ], - "supplementary_context": { - "description": "Load these pages for network details, Ethers.js comparison, or a full Hardhat-based workflow.", - "pages": [ - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network parameters for Polkadot Hub including chain IDs, RPC URLs, and WebSocket endpoints." - }, - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js equivalent workflow — use when the user prefers Ethers.js v6 over Viem." - }, - { - "slug": "smart-contracts-cookbook-dapps-zero-to-hero", - "title": "Zero to Hero Smart Contract DApp", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend — the natural next step after deploying a contract with Viem." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy and interact with a contract", - "user_says": "Deploy a smart contract to Polkadot Hub with Viem", - "actions": [ - "Create viem-deploy ESM project, install viem, dotenv, solc, tsx", - "Create .env with empty PRIVATE_KEY (user fills in)", - "Fetch chain.ts from polkadot-cookbook, verify chain ID 420420417 and TestNet RPC", - "Fetch deploy.ts, read.ts, write.ts; apply dotenv import and process.env.PRIVATE_KEY substitutions", - "Compile Solidity contract to ABI/bytecode artifacts", - "Run npx tsx deploy.ts and save deployed contract address", - "Update read.ts/write.ts with deployed address; run both to verify contract interaction" - ], - "result": "Contract deployed to Polkadot Hub TestNet; read and write operations confirmed via Viem scripts" - }, - { - "scenario": "Edge case: gas fee cap too low during deployment", - "user_says": "Deployment fails with fee cap too low error", - "actions": [ - "Open deploy.ts and locate the walletClient.deployContract call", - "Add gasPrice: parseGwei('5000') to the transaction options object", - "Import parseGwei from 'viem' at the top of the file", - "Retry with npx tsx deploy.ts" - ], - "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee" - } - ], - "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json" - }, - { - "id": "set-up-pallet-mock-runtime", - "title": "Set Up a Mock Runtime for Pallet Unit Testing", - "description": "Creates a mock runtime module for FRAME pallet unit testing using construct_runtime! and derive_impl. Use when you need a self-contained test environment for a custom FRAME pallet before writing unit tests. Configures frame_system with test defaults, satisfies pallet Config traits, and provides genesis storage helpers for different initial states. Trigger phrases: 'mock runtime pallet', 'set up pallet testing', 'construct_runtime test', 'pallet test environment', 'derive_impl TestDefaultConfig'. Requires a completed FRAME pallet in pallets/pallet-custom. Do NOT use for full parachain runtime integration testing.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/mock-runtime.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/mock-runtime.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain (stable, via rustup)", - "Cargo", - "Completed the Make a Custom Pallet guide — the counter pallet must exist in pallets/pallet-custom" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create mock.rs and add module declaration to lib.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch mock.rs" - ], - "description": "Create the empty mock.rs file. Then open src/lib.rs and insert the following two lines immediately after the 'pub use pallet::*;' line:\n\n```rust\n#[cfg(test)]\nmod mock;\n```\n\nThe #[cfg(test)] attribute ensures this module is only compiled when running tests, keeping it out of production builds." - }, - { - "order": 4, - "action": "Fetch and save the complete mock.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "reference_file": "mock.rs", - "description": "Fetch the reference file and save it as 'mock.rs', replacing the empty placeholder. The file must contain: (1) imports including construct_runtime!, frame_system, and IdentityLookup; (2) Test runtime type and Block type alias via construct_runtime!; (3) #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] for System with AccountId = u64, Lookup = IdentityLookup, Block = Block; (4) impl pallet_custom::Config for Test with type RuntimeEvent = RuntimeEvent and type CounterMaxValue = ConstU32<1000>; (5) new_test_ext() genesis helper; (6) new_test_ext_with_counter(val: u32) helper; (7) new_test_ext_with_interactions() helper accepting counter value and Vec of (AccountId, u32) interactions." - }, - { - "order": 5, - "action": "Verify mock runtime compiles", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "commands": [ - "cargo test --package pallet-custom --lib" - ], - "expected_output": "test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok\ntest mock::test_genesis_config_builds ... ok", - "description": "Compile the test code including the new mock module. Two auto-generated tests will run: the construct_runtime integrity test and the genesis config build test. Both must pass. If compilation fails with 'missing field' errors, add the missing associated type to the impl pallet_custom::Config for Test block in mock.rs using a sensible default (e.g., type WeightInfo = ();)." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/mock-runtime", - "files": [ - { - "path": "mock.rs", - "description": "Complete mock runtime: construct_runtime! with System + CustomPallet, frame_system TestDefaultConfig, pallet Config impl, and three genesis storage helpers (default, counter init, interactions init)" - } - ] - }, - "error_patterns": [ - { - "pattern": "error[E0277]: the trait bound is not satisfied / missing associated type in Config impl", - "cause": "The mock.rs Config implementation is missing an associated type that the pallet's Config trait declares (e.g., WeightInfo).", - "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder — for WeightInfo: type WeightInfo = ();" - }, - { - "pattern": "error[E0412]: cannot find type in scope / unresolved import", - "cause": "An import path in mock.rs does not match the current crate layout.", - "resolution": "Verify that import paths in mock.rs match your actual pallet crate name and module structure. Check Cargo.toml for the crate name." - }, - { - "pattern": "error: proc macro `construct_runtime` panicked", - "cause": "A pallet in construct_runtime! has a misconfigured index or incompatible Config.", - "resolution": "Verify the pallet name and module path match the actual module. Ensure all required Config associated types are implemented in the mock." - } - ], - "supplementary_context": { - "description": "Load when continuing pallet development after mock runtime setup.", - "pages": [ - { - "slug": "parachains-customize-runtime-pallet-development-pallet-testing", - "title": "Unit Test Pallets", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/pallet-testing.md", - "relevance": "Next step: write comprehensive unit tests using the mock runtime." - }, - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Prerequisite: the custom counter pallet that this mock runtime is written for." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: create a test environment for the custom counter pallet", - "user_says": "Set up a mock runtime so I can write unit tests for my counter pallet", - "actions": [ - "Create pallets/pallet-custom/src/mock.rs", - "Add #[cfg(test)] mod mock; to lib.rs after pub use pallet::*;", - "Fetch the complete mock.rs from the polkadot-cookbook reference and save it", - "Run cargo test --package pallet-custom --lib to confirm auto-generated tests pass" - ], - "result": "Two auto-generated tests pass: construct_runtime integrity test and genesis config build test. The mock runtime is ready for unit test authoring." - }, - { - "scenario": "Edge case: pallet Config has additional associated types not in the reference mock.rs", - "user_says": "Compilation fails with 'missing associated type WeightInfo in impl pallet_custom::Config for Test'", - "actions": [ - "Open src/mock.rs", - "Add type WeightInfo = (); inside the impl pallet_custom::Config for Test block", - "Re-run cargo test --package pallet-custom --lib" - ], - "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments." - } - ], - "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs" - }, - { - "id": "unit-test-frame-pallet", - "title": "Write Unit Tests for a FRAME Pallet", - "description": "Writes a complete tests.rs module for a FRAME pallet using assert_ok!, assert_noop!, and System::assert_last_event macros. Use after completing the mock runtime guide to verify pallet logic in complete isolation. Covers all critical test categories: dispatchable success paths, arithmetic guard errors, root-only origin checks, event data correctness, user-interaction tracking, and genesis state loading. Trigger phrases: 'unit test FRAME pallet', 'test pallet logic', 'assert_noop assert_ok', 'pallet test suite', 'test dispatchable'. Requires completed create-a-pallet and set-up-pallet-mock-runtime guides. Do NOT use for integration or end-to-end testing of a full parachain.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/pallet-testing.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/pallet-testing.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain (stable, via rustup)", - "Cargo", - "Completed the Make a Custom Pallet guide — counter pallet in pallets/pallet-custom", - "Completed the Mock Your Runtime guide — mock.rs must exist in pallets/pallet-custom/src" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create tests.rs and add module declaration to lib.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch tests.rs" - ], - "description": "Create the empty tests.rs file. Then open src/lib.rs and add the following two lines immediately after the mock module declaration:\n\n```rust\n#[cfg(test)]\nmod tests;\n```\n\nThe module declarations in lib.rs should now read:\n#[cfg(test)] mod mock;\n#[cfg(test)] mod tests;" - }, - { - "order": 4, - "action": "Write test module imports", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Open src/tests.rs and write these imports at the top:\n\n```rust\nuse crate::{mock::*, Error, Event};\nuse frame::deps::frame_support::{assert_noop, assert_ok};\nuse frame::deps::sp_runtime::DispatchError;\n```\n\nThese bring in: all mock runtime items (Test, new_test_ext*, RuntimeOrigin, System, CustomPallet, CounterValue, UserInteractions), the pallet Error and Event enums, FRAME assertion macros, and DispatchError for origin checks." - }, - { - "order": 5, - "action": "Write basic operation and event emission tests", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion — events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() — uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() — uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() — uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```" - }, - { - "order": 6, - "action": "Write error condition and access control tests", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() — uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() — uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() — uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() — uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```" - }, - { - "order": 7, - "action": "Write genesis configuration and interaction tracking tests", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() — uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() — calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() — account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```" - }, - { - "order": 8, - "action": "Run the complete test suite", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "commands": [ - "cargo test --package pallet-custom" - ], - "expected_output": "test result: ok. 15 passed; 0 failed; 0 ignored", - "description": "Run all tests. Expected output shows 15 passing tests: 13 pallet tests plus 2 auto-generated mock tests. If fewer appear, verify every function has #[test]. If an event test fails with 'left: []', confirm System::set_block_number(1) is the first call inside the execute_with closure." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "polkadot-docs/parachains/customize-runtime/pallet-development/pallet-testing", - "files": [] - }, - "error_patterns": [ - { - "pattern": "left: [], right: [EventRecord { ... }] / assertion failed on event", - "cause": "System::set_block_number(1) was not called before the dispatchable. Events are suppressed on block 0.", - "resolution": "Add System::set_block_number(1); as the first line inside the execute_with closure for any test that checks events." - }, - { - "pattern": "error: assert_noop! failed — storage was modified", - "cause": "The dispatchable modified storage before returning an error, violating the no-op contract.", - "resolution": "This is a pallet bug: all preconditions must be checked before any storage mutations. Fix the pallet extrinsic, not the test." - }, - { - "pattern": "thread 'tests::...' panicked at 'called Result::unwrap() on Err'", - "cause": "A dispatchable wrapped in assert_ok! returned an error. Usually wrong origin or incorrect genesis state.", - "resolution": "Check that the genesis helper (new_test_ext vs new_test_ext_with_counter) matches expected initial state, and the origin type matches what the extrinsic requires." - } - ], - "supplementary_context": { - "description": "Load when extending pallet testing or proceeding to benchmarking.", - "pages": [ - { - "slug": "parachains-customize-runtime-pallet-development-benchmark-pallet", - "title": "Benchmark Your Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/benchmark-pallet.md", - "relevance": "Next step: benchmark extrinsics and generate production weight files." - }, - { - "slug": "parachains-customize-runtime-pallet-development-mock-runtime", - "title": "Mock Your Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md", - "relevance": "Prerequisite: the mock runtime providing new_test_ext helpers and the Test runtime type." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: write a complete test suite for the counter pallet", - "user_says": "Write unit tests for my custom counter pallet covering increment, decrement, errors, and genesis config", - "actions": [ - "Create pallets/pallet-custom/src/tests.rs", - "Add #[cfg(test)] mod tests; to lib.rs", - "Write imports: use crate::{mock::*, Error, Event}; plus FRAME macros", - "Write increment_works, set_counter_value_works, decrement_works with System::set_block_number(1) for event checks", - "Write error tests: overflow, underflow, root-only access control", - "Write genesis_config_works and interaction tracking tests", - "Run cargo test --package pallet-custom — expect 15 passing tests" - ], - "result": "All 15 tests pass including 2 auto-generated mock tests." - }, - { - "scenario": "Edge case: event assertion fails with empty event list", - "user_says": "My event test fails: 'left: [], right: [EventRecord {...}]'", - "actions": [ - "Open the failing test in tests.rs", - "Add System::set_block_number(1); as the first line inside the execute_with closure", - "Re-run cargo test --package pallet-custom" - ], - "result": "The event test passes because events are recorded starting from block 1." - } - ] - }, - { - "id": "benchmark-frame-pallet", - "title": "Benchmark a FRAME Pallet and Generate Weight Files", - "description": "Implements full FRAME pallet benchmarking: adds benchmarking.rs with #[benchmark] functions, defines WeightInfo trait, wires it into pallet Config, configures Cargo.toml features, builds release WASM, installs frame-omni-bencher, downloads the weight template, and generates weights.rs. Use when a custom pallet needs accurate extrinsic weights before production deployment. Trigger phrases: 'benchmark pallet', 'frame-omni-bencher', 'generate weight file', 'WeightInfo', 'pallet weights production'. Requires a working parachain template with the custom pallet integrated. Do NOT use for estimating gas on EVM contracts.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/benchmark-pallet.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/benchmark-pallet.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with wasm32-unknown-unknown target", - "Cargo", - "The custom counter pallet in pallets/pallet-custom with mock runtime set up (from mock-runtime guide)" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create benchmarking.rs with benchmark functions", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch benchmarking.rs" - ], - "description": "Create pallets/pallet-custom/src/benchmarking.rs with:\n\n```rust\n#![cfg(feature = \"runtime-benchmarks\")]\nuse super::*;\nuse frame::deps::frame_benchmarking::v2::*;\nuse frame::benchmarking::prelude::RawOrigin;\n\n#[benchmarks]\nmod benchmarks {\n use super::*;\n\n #[benchmark]\n fn set_counter_value() {\n let new_value: u32 = 100;\n #[extrinsic_call]\n _(RawOrigin::Root, new_value);\n assert_eq!(CounterValue::::get(), new_value);\n }\n\n #[benchmark]\n fn increment() {\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 50;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), amount);\n assert_eq!(UserInteractions::::get(caller), 1);\n }\n\n #[benchmark]\n fn decrement() {\n CounterValue::::put(100);\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 30;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), 70);\n }\n```\n\n impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);\n}\n\nFor a different pallet, update function names and assertions accordingly." - }, - { - "order": 4, - "action": "Define WeightInfo trait in weights.rs", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "commands": [ - "touch weights.rs" - ], - "description": "Create pallets/pallet-custom/src/weights.rs with a WeightInfo trait and () placeholder implementation:\n\n```rust\npub trait WeightInfo {\n fn set_counter_value() -> frame::prelude::Weight;\n fn increment() -> frame::prelude::Weight;\n fn decrement() -> frame::prelude::Weight;\n}\n\nimpl WeightInfo for () {\n fn set_counter_value() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(10_000, 0) }\n fn increment() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n fn decrement() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n}\n```\n\nThe () impl is used in tests; benchmark-generated values will replace this file in the final step." - }, - { - "order": 5, - "action": "Wire WeightInfo into pallet Config and extrinsic annotations", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src", - "description": "Edit src/lib.rs with four changes:\n1. At the top add: pub mod weights; (expose weights module)\n2. After 'pub use pallet::*;' add: #[cfg(feature = \"runtime-benchmarks\")] mod benchmarking;\n3. In #[pallet::config] trait add: type WeightInfo: weights::WeightInfo;\n4. Replace hardcoded weight attributes on each extrinsic:\n - #[pallet::weight(T::WeightInfo::set_counter_value())]\n - #[pallet::weight(T::WeightInfo::increment())]\n - #[pallet::weight(T::WeightInfo::decrement())]" - }, - { - "order": 6, - "action": "Update pallet Cargo.toml with runtime-benchmarks feature", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "description": "Open pallets/pallet-custom/Cargo.toml and add the runtime-benchmarks feature under [features]:\n\n```toml\n[features]\ndefault = [\"std\"]\nruntime-benchmarks = [\n \"frame/runtime-benchmarks\",\n]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame/std\",\n]\n```" - }, - { - "order": 7, - "action": "Update mock.rs, runtime Cargo.toml, runtime config, and benchmarks.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Four integration edits required:\n1. src/mock.rs: add type WeightInfo = (); to impl pallet_custom::Config for Test.\n2. runtime/Cargo.toml: add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks feature list.\n3. runtime/src/configs/mod.rs: add type WeightInfo = (); to impl pallet_custom::Config for Runtime (placeholder).\n4. runtime/src/benchmarks.rs: add [pallet_custom, CustomPallet] inside the define_benchmarks! macro." - }, - { - "order": 8, - "action": "Test benchmark compilation", - "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom", - "commands": [ - "cargo test -p pallet-custom --features runtime-benchmarks" - ], - "expected_output": "test benchmarking::benchmarks::bench_set_counter_value ... ok", - "description": "Run benchmark unit tests generated by impl_benchmark_test_suite!. All three benchmark functions must pass before building the full runtime. These tests verify the benchmark code compiles and the assertions hold." - }, - { - "order": 9, - "action": "Build the release runtime with benchmarks enabled", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release --features runtime-benchmarks" - ], - "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking — do not use for production deployment." - }, - { - "order": 10, - "action": "Install frame-omni-bencher", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo install frame-omni-bencher --locked" - ], - "expected_output": "Installed package `frame-omni-bencher`", - "description": "Install via cargo, or download a pre-built binary from the Polkadot SDK releases page. Replace VERSION with the SDK version tag used by the parachain template (e.g., polkadot-stable2412):\nmacOS: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher-aarch64-apple-darwin && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/\nUbuntu: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/" - }, - { - "order": 11, - "action": "Create the weight Handlebars template file", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content — needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions — you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill." - }, - { - "order": 12, - "action": "Execute benchmarks and generate weights.rs", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "frame-omni-bencher v1 benchmark pallet --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm --pallet pallet_custom --extrinsic \"\" --template ./pallets/pallet-custom/frame-weight-template.hbs --output ./pallets/pallet-custom/src/weights.rs" - ], - "expected_output": "Wrote weight file to ./pallets/pallet-custom/src/weights.rs", - "description": "Run benchmarks against the WASM runtime and generate production weights.rs. This overwrites the placeholder weights.rs from step 2. The output contains a SubstrateWeight struct implementing WeightInfo with actual measured values. For more precise measurements, add: --steps 50 --repeat 20" - }, - { - "order": 13, - "action": "Update runtime config to use generated weights", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/configs/mod.rs and update the pallet Config to use measured weights instead of ():\n\n```rust\nimpl pallet_custom::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type CounterMaxValue = ConstU32<1000>;\n type WeightInfo = pallet_custom::weights::SubstrateWeight;\n}\n```\n\nThen rebuild for production without the benchmarks flag:\ncargo build --release" - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/benchmark-pallet", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0412]: cannot find type Weight / use of undeclared crate", - "cause": "The weights.rs uses frame_support types with the wrong import path for the SDK version.", - "resolution": "Use frame::prelude::Weight rather than frame_support::weights::Weight for SDK versions that use the unified frame crate." - }, - { - "pattern": "Error: No benchmarks found for pallet_custom", - "cause": "The pallet was not registered in define_benchmarks! in runtime/src/benchmarks.rs, or the pallet name is misspelled.", - "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! — use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." - }, - { - "pattern": "could not compile parachain-template-runtime / runtime-benchmarks feature error", - "cause": "The pallet's runtime-benchmarks feature was not added to the runtime's feature cascade in runtime/Cargo.toml.", - "resolution": "Add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks list in runtime/Cargo.toml." - } - ], - "supplementary_context": { - "description": "Load for context on pallet development prerequisites or parachain template.", - "pages": [ - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "The custom counter pallet being benchmarked." - }, - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "The parachain template runtime structure required for benchmark integration." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: benchmark the counter pallet and generate a production weight file", - "user_says": "Benchmark my counter pallet and generate weights.rs for production use", - "actions": [ - "Create benchmarking.rs with #[benchmark] functions for all three extrinsics", - "Create placeholder weights.rs with WeightInfo trait and () impl", - "Add WeightInfo to pallet Config and update #[pallet::weight] attributes in lib.rs", - "Update pallet Cargo.toml with runtime-benchmarks feature", - "Wire into runtime: mock.rs WeightInfo=(), runtime Cargo.toml, runtime Config, define_benchmarks!", - "Run cargo test -p pallet-custom --features runtime-benchmarks to verify benchmark compilation", - "cargo build --release --features runtime-benchmarks to build benchmark WASM", - "Install frame-omni-bencher, download frame-weight-template.hbs", - "Run frame-omni-bencher to generate weights.rs", - "Update runtime Config to use pallet_custom::weights::SubstrateWeight" - ], - "result": "A measured weights.rs replaces the placeholder; runtime uses accurate production weights." - }, - { - "scenario": "Edge case: frame-omni-bencher reports 'No benchmarks found'", - "user_says": "frame-omni-bencher says No benchmarks found for pallet_custom", - "actions": [ - "Open runtime/src/benchmarks.rs", - "Verify define_benchmarks! includes [pallet_custom, CustomPallet]", - "If missing, add the entry with underscore crate name and correct runtime type alias", - "Rebuild with --features runtime-benchmarks and re-run frame-omni-bencher" - ], - "result": "Benchmarks are discovered and weights.rs is generated successfully." - } - ] - }, - { - "id": "set-up-local-dev-node", - "title": "Set Up a Local Development Node", - "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/local-dev-node.md" - ], - "primary_page": "smart-contracts/dev-environments/local-dev-node.md", - "prerequisites": { - "runtime": [ - "Rust toolchain (stable + nightly via rustup) with wasm32-unknown-unknown target", - "Polkadot SDK build dependencies — complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", - "Git", - "At least 20 GB free disk space for the release build" - ], - "network": [], - "tokens": [], - "wallet": [] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the polkadot-sdk repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk.git", - "cd polkadot-sdk" - ], - "description": "Clone the official polkadot-sdk repository. The clone downloads several GB of source code. The repository contains both the revive-dev-node implementation and the ETH-RPC adapter." - }, - { - "order": 2, - "action": "Build the revive-dev-node binary", - "working_directory": "polkadot-sdk", - "commands": [ - "cargo build -p revive-dev-node --bin revive-dev-node --release" - ], - "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the Revive Dev node in release mode. Release builds are optimized but take significantly longer (up to 30 minutes). The binary will be at polkadot-sdk/target/release/revive-dev-node. If out of memory, limit parallelism by appending: -- -j2" - }, - { - "order": 3, - "action": "Build the ETH-RPC adapter binary", - "working_directory": "polkadot-sdk", - "commands": [ - "cargo build -p pallet-revive-eth-rpc --bin eth-rpc --release" - ], - "expected_output": "Finished release [optimized] target(s)", - "description": "Compile the ETH-RPC adapter. This translates Ethereum JSON-RPC calls into Substrate requests. Binary at polkadot-sdk/target/release/eth-rpc." - }, - { - "order": 4, - "action": "Start the Revive Dev node", - "working_directory": "polkadot-sdk", - "commands": [ - "./target/release/revive-dev-node --dev" - ], - "interactive": true, - "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running — open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"" - }, - { - "order": 5, - "action": "Start the ETH-RPC adapter in a new terminal", - "working_directory": "polkadot-sdk", - "commands": [ - "./target/release/eth-rpc --dev" - ], - "interactive": true, - "description": "Open a new terminal, navigate to the polkadot-sdk directory, and start the adapter with --dev. It connects to the local node and exposes Ethereum JSON-RPC at http://localhost:8545. Success is indicated by log lines showing the adapter is ready. For debug logging, prepend: RUST_LOG=\"info,eth-rpc=debug\"\n\nVerify the environment is ready by connecting Hardhat, Remix, or MetaMask to http://localhost:8545." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "polkadot-docs/smart-contracts/local-dev-node", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error: linker not found / error while loading shared libraries / could not compile", - "cause": "Missing system build dependencies such as clang, cmake, or protobuf-compiler.", - "resolution": "Complete the Install Polkadot SDK Dependencies guide. On Ubuntu: sudo apt install -y cmake clang libclang-dev protobuf-compiler. On macOS: brew install cmake protobuf." - }, - { - "pattern": "Connection refused at localhost:8545 / eth-rpc exits immediately", - "cause": "The revive-dev-node is not running when eth-rpc starts, or it crashed before the adapter connected.", - "resolution": "Ensure revive-dev-node --dev is running in a separate terminal and producing blocks before starting eth-rpc. The adapter connects via WebSocket to the node." - }, - { - "pattern": "Method not found / eth_method not supported", - "cause": "An Ethereum tool is calling a JSON-RPC method not implemented by the ETH-RPC adapter (e.g., anvil_* or debug_*).", - "resolution": "The local node supports a subset of the Ethereum JSON-RPC API. Use Polkadot Hub TestNet for full API compatibility. Check the Polkadot EVM differences documentation for supported methods." - } - ], - "supplementary_context": { - "description": "Load for guidance on connecting Ethereum tools or deploying contracts.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Prerequisite: installing Rust and Polkadot SDK build dependencies." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Configure Hardhat to connect to http://localhost:8545 for local testing." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: start a local dev environment for smart contract development", - "user_says": "Set up a local Polkadot node for testing my smart contracts", - "actions": [ - "Clone polkadot-sdk from GitHub", - "Build revive-dev-node with cargo build --release in polkadot-sdk/", - "Build eth-rpc adapter with cargo build --release", - "Start revive-dev-node --dev in terminal 1", - "Start eth-rpc --dev in terminal 2", - "Confirm ETH-RPC is listening at http://localhost:8545" - ], - "result": "Local node produces blocks; ETH-RPC adapter accepts standard Ethereum JSON-RPC at localhost:8545." - }, - { - "scenario": "Edge case: cargo build takes excessively long or runs out of memory", - "user_says": "The build has been running for over an hour and my machine is unresponsive", - "actions": [ - "Cancel the build with Ctrl+C", - "Check free disk space (df -h) — at least 20 GB required", - "Check free RAM — at least 8 GB recommended", - "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" - ], - "result": "Build completes more slowly with reduced CPU and memory usage." - } - ] - }, - { - "id": "interact-with-chain-dedot", - "title": "Interact with Polkadot Chains Using Dedot", - "description": "Sets up a lightweight Dedot TypeScript client to read chain state and send transactions on any Polkadot SDK blockchain. Use when you need a modern, tree-shakable alternative to Polkadot.js for querying storage, subscribing to events, calling runtime APIs, or submitting signed extrinsics. Trigger phrases: 'use Dedot', 'query chain with Dedot', 'Dedot client', 'interact with chain TypeScript'. Output: account balance, runtime constants, and submitted transaction hash. No testnet tokens required for read-only steps.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/dedot.md" - ], - "primary_page": "reference/tools/dedot.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.polkadot.io for Polkadot mainnet)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Initialize an ESM Node.js project", - "working_directory": ".", - "commands": [ - "mkdir dedot-example && cd dedot-example", - "npm init -y && npm pkg set type=module" - ], - "description": "Create a new directory named 'dedot-example' and initialize it as an ESM Node.js project. The 'type=module' setting is required because Dedot uses ES module imports." - }, - { - "order": 2, - "action": "Install Dedot and TypeScript tooling", - "working_directory": "dedot-example", - "commands": [ - "npm install dedot", - "npm install -D @dedot/chaintypes tsx typescript" - ], - "description": "Install the 'dedot' runtime package, '@dedot/chaintypes' for per-chain type definitions and IntelliSense, and 'tsx' plus 'typescript' as dev tooling. The '@dedot/chaintypes' package enables auto-completion for chain-specific pallets and storage entries." - }, - { - "order": 3, - "action": "Fetch the DedotClient initialization script", - "working_directory": "dedot-example", - "reference_file": "client-initialization-via-ws.ts", - "description": "Fetch the reference file `client-initialization-via-ws.ts` and save it as `main.ts`. Then make these substitutions:\n(1) Replace `INSERT_ADDRESS` with the SS58 address you'll query.\n(2) Replace `wss://rpc.polkadot.io` with the target chain's WebSocket endpoint if needed.\n\nThe script connects via `WsProvider` and uses `PolkadotApi` (from `@dedot/chaintypes`) as the ChainApi interface for type safety. If you target a different chain, replace `PolkadotApi` with the appropriate ChainApi (e.g. `KusamaApi`) or omit it to fall back to `SubstrateApi`." - }, - { - "order": 4, - "action": "Append the balance query and disconnect", - "working_directory": "dedot-example", - "description": "Open `main.ts` and append the following block immediately before the end of `main()` (or wherever the connection is established) so the script queries account balance and cleanly disconnects:\n\n```typescript\nconst balance = await client.query.system.account('INSERT_ADDRESS');\nconsole.log('Free balance:', balance.data.free);\nawait client.disconnect();\n```\n\nReplace `INSERT_ADDRESS` with the same SS58 address you used in the previous step." - }, - { - "order": 5, - "action": "Run the query script", - "working_directory": "dedot-example", - "commands": [ - "npx tsx main.ts" - ], - "expected_output": "Free balance: ", - "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts — it will not throw an error." - }, - { - "order": 6, - "action": "Sign and send a transaction", - "working_directory": "dedot-example", - "description": "Fetch the reference file `sign-and-send-tx-with-keyring.ts` and save it as `send-tx.ts`. The pattern uses `@polkadot/keyring` for signing. Make these substitutions:\n(1) Replace `INSERT_DEST_ADDRESS` with the recipient SS58 address.\n(2) Replace `2_000_000_000_000n` with the desired amount in planck (1 DOT = 10^10 planck).\n\nThen install the signer dependency:\n\n```bash\nnpm install @polkadot/keyring @polkadot/util-crypto\n```\n\nRun with `npx tsx send-tx.ts`. The script will print the transaction hash on inclusion.", - "reference_file": "sign-and-send-tx-with-keyring.ts" - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/reference/tools/dedot", - "files": [ - { - "path": "client-initialization-via-ws.ts", - "description": "DedotClient initialization via WsProvider against a Polkadot Hub WebSocket endpoint, typed against PolkadotApi from @dedot/chaintypes" - }, - { - "path": "sign-and-send-tx-with-keyring.ts", - "description": "Signs and submits a Balances.transfer extrinsic using a @polkadot/keyring keypair; demonstrates the full signTx flow" - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: Cannot find package 'dedot'", - "cause": "Dependencies not installed or node_modules missing.", - "resolution": "Run 'npm install dedot' in the dedot-example directory." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "Project is not configured as ESM.", - "resolution": "Run 'npm pkg set type=module' in the project directory." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED", - "cause": "Wrong WebSocket endpoint URL or the node is unreachable.", - "resolution": "Verify the WebSocket URL is correct and the node is online. For Polkadot mainnet use wss://rpc.polkadot.io. For Polkadot Hub TestNet use wss://asset-hub-paseo.dotters.network." - }, - { - "pattern": "TypeError: Cannot read properties of undefined (reading 'free')", - "cause": "The queried account does not exist on chain (never had a balance).", - "resolution": "An account with no on-chain history returns a default AccountInfo with all balances at 0. Check that the ADDRESS constant is a valid SS58 address for the target chain. Use a funded account or a known validator address for initial testing." - } - ], - "supplementary_context": { - "description": "Load these pages when the user asks about connecting to specific chains or using alternative clients.", - "pages": [ - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "Multi-SDK tutorial showing how to query account balance using PAPI, Polkadot.js, Dedot, and other clients side-by-side." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to construct and submit signed transactions using multiple SDKs including Dedot." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance", - "user_says": "Use Dedot to check the balance of a Polkadot account", - "actions": [ - "Scaffold 'dedot-example/' as an ESM Node.js project", - "Install dedot, @dedot/chaintypes, and tsx", - "Create main.ts with DedotClient connected to wss://rpc.polkadot.io", - "Query client.query.system.account for the target address", - "Run npx tsx main.ts and read the printed free balance" - ], - "result": "Free balance of the queried address printed to console in planck units." - }, - { - "scenario": "Edge case: targeting a custom Polkadot SDK chain", - "user_says": "Connect Dedot to my parachain and query a custom pallet", - "actions": [ - "Generate a ChainApi interface for the custom chain: npx dedot chaintypes -w wss://your-node.example.com", - "Import the generated ChainApi and pass it as the type parameter to DedotClient.new()", - "Access custom pallet storage via client.query..()" - ], - "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs." - } - ], - "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json" - }, - { - "id": "run-parachain-node-omni-node", - "title": "Run a Parachain Node with Polkadot Omni Node", - "description": "Installs the polkadot-omni-node pre-built binary and runs a full node for any compatible Polkadot parachain using an external chain spec file. Use when you need to run a parachain node without maintaining a custom node codebase, or when testing a runtime against a live network. Trigger phrases: 'run a parachain node', 'polkadot-omni-node', 'spin up a parachain full node', 'start omni node'. Output: syncing parachain node with WebSocket RPC at ws://localhost:9944. Requires a chain spec JSON file for the target parachain.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/omninode.md" - ], - "primary_page": "reference/tools/omninode.md", - "prerequisites": { - "runtime": [ - "macOS (Apple Silicon or Intel) or Linux (Ubuntu/Debian) for the pre-built binary path", - "Rust and Cargo (via rustup) if building from source — see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" - ], - "network": [ - "Internet access to download the binary and to sync the parachain" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install polkadot-omni-node", - "working_directory": ".", - "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust — see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```" - }, - { - "order": 2, - "action": "Verify the installation", - "working_directory": ".", - "commands": [ - "polkadot-omni-node --version" - ], - "expected_output": "polkadot-omni-node ", - "description": "Confirm the binary is installed and accessible from PATH. You should see the installed version number. If the command is not found, ensure /usr/local/bin is in your PATH, or use the full path to the binary." - }, - { - "order": 3, - "action": "Obtain a chain specification", - "working_directory": ".", - "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path — you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK." - }, - { - "order": 4, - "action": "Run a parachain full node", - "working_directory": ".", - "description": "Launch the node with warp sync enabled. Replace './chain_spec.json' with the actual path to your saved chain spec file:\n\nTo see all available flags before running:\n```\npolkadot-omni-node --help\n```\n\nTo start the node:\n```\npolkadot-omni-node --chain ./chain_spec.json --sync warp\n```\n\nThe --chain flag points to the chain spec; --sync warp enables fast catch-up to the latest finalized state (historical blocks sync in the background).\n\nYou will see log lines like 'Syncing' and 'Imported #NNNN' as the node connects to peers and downloads state. The node exposes a WebSocket RPC endpoint at ws://localhost:9944 by default.\n\nTo stop the node, press Ctrl+C. For long-running production use, configure a systemd service or a process manager." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "polkadot-omni-node: command not found", - "cause": "Binary not on PATH or installation failed.", - "resolution": "Verify the binary was moved to /usr/local/bin/ and that directory is in $PATH. Run 'which polkadot-omni-node' to check. If missing, repeat the installation step." - }, - { - "pattern": "Error: The chain spec is not compatible / missing required runtime API", - "cause": "The parachain runtime does not implement all APIs required by polkadot-omni-node (e.g., GetParachainInfo, AuraApi, or required pallets).", - "resolution": "Check the runtime compatibility requirements at docs.polkadot.com/reference/tools/omninode/. The runtime must implement GetParachainInfo, AuraApi, and include the System, ParachainSystem, Aura, and ParachainInfo pallets. See the parachain template at github.com/paritytech/polkadot-sdk-parachain-template for a reference implementation." - }, - { - "pattern": "No peers found / node stuck at block 0", - "cause": "Network connectivity issue, firewall blocking P2P ports, or incorrect chain spec.", - "resolution": "Ensure outbound TCP on ports 30333/30334 is not blocked. Verify the chain spec corresponds to an active network. Check the Polkadot Discord for any network outages." - } - ], - "supplementary_context": { - "description": "Load these pages for context on parachain runtimes, chain specs, or building from source.", - "pages": [ - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "How to build a parachain template runtime that is compatible with polkadot-omni-node." - }, - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Install Rust and system dependencies needed if building polkadot-omni-node from source." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: run an Asset Hub full node", - "user_says": "Use polkadot-omni-node to run an Asset Hub node", - "actions": [ - "Download the latest polkadot-omni-node binary from the Polkadot SDK releases page", - "Verify installation with polkadot-omni-node --version", - "Download the Asset Hub chain spec JSON from paritytech.github.io/chainspecs/", - "Save as chain_spec.json and run: polkadot-omni-node --chain ./chain_spec.json --sync warp" - ], - "result": "Syncing Asset Hub node with WebSocket RPC accessible at ws://localhost:9944." - }, - { - "scenario": "Edge case: build from source when no pre-built binary is available for the platform", - "user_says": "I'm on a non-standard Linux architecture — install polkadot-omni-node from source", - "actions": [ - "Install Rust via rustup and system dependencies per parachains/install-polkadot-sdk/", - "Check crates.io for the latest polkadot-omni-node version", - "Run: cargo install --locked polkadot-omni-node@INSERT_VERSION", - "Verify: polkadot-omni-node --version" - ], - "result": "polkadot-omni-node installed from source and verified on a non-standard platform." - } - ] - }, - { - "id": "interact-polkadot-node-py-substrate", - "title": "Interact with a Polkadot Node Using Python Substrate Interface", - "description": "Sets up the Python Substrate Interface library to connect to any Polkadot SDK blockchain, query on-chain storage (account balance, nonce), and submit signed extrinsics. Use when building Python scripts, data pipelines, or backend services that need to read or write chain state without a JavaScript runtime. Trigger phrases: 'Python Substrate Interface', 'py-substrate-interface', 'query Polkadot with Python', 'Substrate Python', 'substrate-interface pip'. Output: printed account balance details and transaction receipt with extrinsic and block hash.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/py-substrate-interface.md" - ], - "primary_page": "reference/tools/py-substrate-interface.md", - "prerequisites": { - "runtime": [ - "Python 3.7+", - "pip" - ], - "network": [ - "WebSocket RPC endpoint for the target Polkadot SDK node (e.g., wss://rpc.polkadot.io for Polkadot mainnet, or ws://127.0.0.1:9944 for a local node)" - ] - }, - "env_vars": [ - { - "name": "MNEMONIC", - "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file — do not ask for it in chat.", - "required": false - } - ], - "steps": [ - { - "order": 1, - "action": "Create a virtual environment and install the library", - "working_directory": ".", - "commands": [ - "python3 -m venv py-substrate-example && cd py-substrate-example", - "source bin/activate", - "pip install substrate-interface" - ], - "description": "Create an isolated virtual environment named 'py-substrate-example' and install the substrate-interface library. On Windows, replace 'source bin/activate' with '.\\Scripts\\activate'. After activation, the prompt shows '(py-substrate-example)'. All subsequent commands must be run in this activated environment." - }, - { - "order": 2, - "action": "Create the connection script", - "working_directory": "py-substrate-example", - "description": "Create 'connect.py' with the following content. Replace 'INSERT_WS_URL' with the WebSocket endpoint for your target node (e.g., 'wss://rpc.polkadot.io' for Polkadot mainnet or 'ws://127.0.0.1:9944' for a local dev node).\n\nFetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/substrate_interface.py -o connect.py\n\nThen replace INSERT_WS_URL in connect.py with the target endpoint. Run to verify the connection:\npython3 connect.py\n\nExpected output: 'Connected to chain: Polkadot' (or your target chain name). If the chain name is wrong or absent, the URL may be incorrect." - }, - { - "order": 3, - "action": "Query account balance", - "working_directory": "py-substrate-example", - "description": "Create 'read_state.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/read_state.py -o read_state.py\n\nSubstitutions required:\n- At the top of the file, add the SubstrateInterface connection (same as connect.py, replacing INSERT_WS_URL with the endpoint).\n- Replace 'INSERT_ADDRESS' with the SS58 address to query.\n\nRun:\npython3 read_state.py\n\nExpected output:\n Account Details:\n - Free Balance: \n - Reserved: \n - Nonce: \n\nThe values are in planck (smallest unit). Divide by 10^10 for DOT values on Polkadot mainnet." - }, - { - "order": 4, - "action": "Submit a balance transfer", - "working_directory": "py-substrate-example", - "description": "Create '.env' file:\nMNEMONIC=\n\nAdd '.env' to .gitignore:\necho '.env' >> .gitignore\n\nStop and ask the user to fill in their mnemonic phrase in the .env file. Do NOT ask for the mnemonic phrase in chat. Wait for confirmation before continuing.\n\nInstall dotenv:\npip install python-dotenv\n\nCreate 'send_tx.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/send_tx.py -o send_tx.py\n\nAt the top of send_tx.py, add:\n```python\nimport os\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom substrateinterface import SubstrateInterface, Keypair\n\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\nkeypair = Keypair.create_from_mnemonic(os.environ['MNEMONIC'])\n```\n\nThen add the template content below. Substitutions in send_tx.py:\n- Replace INSERT_WS_URL with the WebSocket endpoint.\n- Replace 'INSERT_ADDRESS' (in call_params 'dest') with the recipient SS58 address.\n- Replace 'INSERT_VALUE' with the transfer amount in planck (e.g., 1000000000000 for 0.1 DOT).\n\nRun:\npython3 send_tx.py\n\nExpected output:\n Transaction successful:\n - Extrinsic Hash: 0x...\n - Block Hash: 0x..." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "ModuleNotFoundError: No module named 'substrateinterface'", - "cause": "Library not installed or virtual environment not activated.", - "resolution": "Ensure the virtual environment is activated (run 'source bin/activate' on macOS/Linux or '.\\Scripts\\activate' on Windows), then run 'pip install substrate-interface'." - }, - { - "pattern": "ConnectionError / WebSocket connection refused", - "cause": "Wrong WebSocket URL or the node is not running.", - "resolution": "Verify the URL in SubstrateInterface(url=...). For a local node ensure it's running with --rpc-external or uses the default port 9944. For public endpoints, test with wss://rpc.polkadot.io." - }, - { - "pattern": "ValueError: Invalid address format", - "cause": "The address passed to the storage query is not a valid SS58-encoded address.", - "resolution": "Verify the account address is in SS58 format and matches the network's prefix. For Polkadot mainnet, addresses start with '1'; for testnet (Paseo) addresses start with a different prefix. Use polkadot.js.org/apps to validate the address." - }, - { - "pattern": "ExtrinsicFailed: Token.FundsUnavailable", - "cause": "The sender account does not have enough balance to pay the transfer amount plus transaction fees.", - "resolution": "Check the account balance with read_state.py before submitting. Ensure the 'value' parameter in call_params covers both the transfer amount and transaction fees." - } - ], - "supplementary_context": { - "description": "Load these pages for context on chain interaction patterns or the Python SDK documentation.", - "pages": [ - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "Multi-SDK account query tutorial including a Python Substrate Interface example alongside PAPI and Polkadot.js." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to construct and submit transactions using multiple SDK clients including Python Substrate Interface." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance from Python", - "user_says": "Use Python to check the balance of a Polkadot account", - "actions": [ - "Create virtual environment and install substrate-interface", - "Create connect.py with SubstrateInterface(url='wss://rpc.polkadot.io') and verify connection", - "Create read_state.py with substrate.query(module='System', storage_function='Account', params=[ADDRESS])", - "Print free_balance, reserved, and nonce from account_info.value" - ], - "result": "Account balance details printed with free balance in planck." - }, - { - "scenario": "Edge case: transaction fails with ExtrinsicFailed", - "user_says": "My Python transfer script runs but the extrinsic failed", - "actions": [ - "Check receipt.is_success — if False, print receipt.error_message for the specific error", - "If FundsUnavailable: verify sender balance covers transfer amount plus fees using read_state.py", - "If BadOrigin: verify the keypair matches the sender address in the call_params 'dest' field" - ], - "result": "Root cause identified from receipt.error_message; transaction corrected and resubmitted." - } - ] - }, - { - "id": "interact-polkadot-node-subxt", - "title": "Interact with a Polkadot Node Using Subxt (Rust)", - "description": "Scaffolds a Rust project with the Subxt library to interact with any Polkadot SDK blockchain using compile-time type safety. Use when building Rust services, CLI tools, or backend applications that need to query chain state, call runtime APIs, or submit transactions without an external RPC framework. Trigger phrases: 'Subxt', 'Rust Polkadot client', 'interact with Polkadot in Rust', 'subxt-cli metadata', 'type-safe Polkadot Rust'. Output: existential deposit constant, account info, and confirmed balance transfer event.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/subxt.md" - ], - "primary_page": "reference/tools/subxt.md", - "prerequisites": { - "runtime": [ - "Rust and Cargo installed via rustup (https://rustup.rs/)", - "A Rust project directory (cargo new my_project)" - ] - }, - "env_vars": [ - { - "name": "SECRET_PHRASE", - "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file — do not ask for it in chat.", - "required": false - } - ], - "steps": [ - { - "order": 1, - "action": "Create a Rust project", - "working_directory": ".", - "commands": [ - "cargo new subxt-example && cd subxt-example" - ], - "description": "Create a new Rust binary project named 'subxt-example'. All subsequent steps run from within this directory." - }, - { - "order": 2, - "action": "Install the subxt-cli tool", - "working_directory": "subxt-example", - "description": "Install the subxt-cli command-line tool used to download chain metadata. Check https://crates.io/crates/subxt-cli for the latest version and replace INSERT_SUBXT_CLI_VERSION below:\n\ncargo install subxt-cli@INSERT_SUBXT_CLI_VERSION\n\nAs of the current reference files, the version is 0.50.0, so the command would be:\ncargo install subxt-cli@0.50.0\n\nThis installs the 'subxt' binary globally. Installation may take several minutes as it compiles from source." - }, - { - "order": 3, - "action": "Add Subxt dependencies to Cargo.toml", - "working_directory": "subxt-example", - "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is." - }, - { - "order": 4, - "action": "Download chain metadata", - "working_directory": "subxt-example", - "description": "Use subxt-cli to fetch the chain's runtime metadata. Replace INSERT_NODE_URL with the WebSocket URL of the node (e.g., wss://rpc.polkadot.io for Polkadot mainnet):\n\nsubxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale\n\nThis saves the SCALE-encoded metadata to 'polkadot_metadata.scale' in the project root. The #[subxt::subxt] macro references this file at compile time. If the command hangs, verify the node URL is reachable and the node is running." - }, - { - "order": 5, - "action": "Create the main Rust source file", - "working_directory": "subxt-example", - "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation." - }, - { - "order": 6, - "action": "Build and run", - "working_directory": "subxt-example", - "commands": [ - "cargo run" - ], - "expected_output": "Existential deposit: \nAccount info: \nBalance transfer successful: ", - "description": "Build and run the project. The first build will take several minutes as dependencies compile. Subsequent builds are faster. The program: (1) reads the existential deposit constant, (2) fetches account info for INSERT_ADDRESS, (3) submits a balance transfer and waits for finalization. If the transfer step fails with an error, check that the account has sufficient balance (at least INSERT_AMOUNT + gas fees) and that INSERT_SECRET_PHRASE belongs to the sender account." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0425]: cannot find value / type in scope — after changing metadata", - "cause": "The generated types from #[subxt::subxt] changed after re-downloading metadata from a different chain or after a runtime upgrade.", - "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ — check the Subxt docs for the new paths." - }, - { - "pattern": "Error: Rpc error: target url is not valid", - "cause": "INSERT_NODE_URL was not replaced or the URL format is incorrect.", - "resolution": "Ensure NODE_URL in subxt.rs is a full WebSocket URL starting with ws:// or wss:// (e.g., wss://rpc.polkadot.io). HTTP URLs are not supported." - }, - { - "pattern": "invalid phrase: mnemonic / BIP39 error", - "cause": "INSERT_SECRET_PHRASE was not replaced or is malformed.", - "resolution": "Verify the mnemonic is 12 or 24 BIP39 words separated by spaces. Load it from the SECRET_PHRASE env variable rather than hardcoding. Ensure there are no leading/trailing spaces in the .env value." - }, - { - "pattern": "Module 'Balances' / ExtrinsicFailed", - "cause": "Insufficient balance, wrong destination address, or node RPC error.", - "resolution": "Check that the sender account has enough balance to cover the transfer amount plus transaction fees. Verify INSERT_DEST_ADDRESS is a valid SS58 address for the target network." - } - ], - "supplementary_context": { - "description": "Load these pages for context on chain interaction patterns or the Subxt API surface.", - "pages": [ - { - "slug": "chain-interactions-accounts-query-accounts", - "title": "Query Account Information with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md", - "relevance": "Multi-SDK account query tutorial including Subxt alongside PAPI, Polkadot.js, and Python Substrate Interface." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "How to sign and submit transactions with multiple SDK clients — context for the Subxt transaction pattern." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query chain constants and account balance", - "user_says": "Use Subxt in Rust to read the existential deposit and an account's balance", - "actions": [ - "Create Rust project and install subxt-cli", - "Add subxt, subxt-signer, tokio dependencies to Cargo.toml", - "Fetch chain metadata: subxt metadata --url wss://rpc.polkadot.io > polkadot_metadata.scale", - "Create subxt.rs with #[subxt::subxt] macro pointing to polkadot_metadata.scale", - "Replace INSERT_NODE_URL and INSERT_ADDRESS, then run cargo run" - ], - "result": "Existential deposit constant and account info struct printed to stdout." - }, - { - "scenario": "Edge case: runtime upgrade changes generated types", - "user_says": "My Subxt code broke after the chain did a runtime upgrade", - "actions": [ - "Re-fetch metadata: subxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale", - "Run cargo build and review compile errors for changed type paths", - "Update pallet names or storage entry paths in subxt.rs to match the new metadata" - ], - "result": "Code updated to match post-upgrade runtime types and successfully compiles and runs." - } - ] - }, - { - "id": "deploy-erc721-nft-hardhat", - "title": "Deploy an ERC-721 NFT Using Hardhat", - "description": "Scaffolds a Hardhat project manually, installs OpenZeppelin contracts and dotenv, creates a MyNFT.sol ERC-721 contract and Ignition deployment module, compiles with Cancun EVM version, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when deploying an ERC-721 NFT to Polkadot Hub. Requires testnet PAS tokens and a funded EVM wallet. Trigger phrases: 'deploy NFT Polkadot', 'ERC-721 Hardhat', 'deploy non-fungible token', 'mint NFT Polkadot Hub'. Uses OpenZeppelin Contracts v5.4.0+ requiring evmVersion cancun. Do NOT use for ERC-20 tokens; use deploy-erc20-token-hardhat instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create the project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir hardhat-nft-deployment", - "cd hardhat-nft-deployment", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead." - }, - { - "order": 2, - "action": "Install Hardhat, OpenZeppelin, and dotenv", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox", - "npm install @openzeppelin/contracts dotenv" - ], - "description": "Install Hardhat v2 toolchain, OpenZeppelin contracts (v5.4.0+), and dotenv for secure private key handling." - }, - { - "order": 3, - "action": "Create the Hardhat configuration file", - "working_directory": "hardhat-nft-deployment", - "description": "Create a file named 'hardhat.config.ts' with the following content:\n\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\nimport '@nomicfoundation/hardhat-toolbox';\nimport 'dotenv/config';\n\nconst config: HardhatUserConfig = {\n solidity: {\n version: '0.8.28',\n settings: { evmVersion: 'cancun' }\n },\n networks: {\n polkadotTestnet: {\n url: 'https://services.polkadothub-rpc.com/testnet',\n chainId: 420420417,\n accounts: [process.env.PRIVATE_KEY as string],\n gasPrice: 5000000000000\n }\n },\n ignition: { requiredConfirmations: 1 }\n};\n\nexport default config;\n```\n\nKey points: (1) 'dotenv/config' must be the first functional import. (2) evmVersion: 'cancun' is required for OpenZeppelin v5.4.0+ (mcopy opcode). (3) gasPrice: 5000000000000 (5000 gwei) prevents 'priority is too low' errors on TestNet. (4) requiredConfirmations: 1 prevents Ignition from misreading pending transactions as dropped." - }, - { - "order": 4, - "action": "Create .env file and .gitignore entry", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding." - }, - { - "order": 5, - "action": "Create project directories", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "mkdir -p contracts ignition/modules" - ], - "description": "Create the contracts and ignition/modules directories that Hardhat expects." - }, - { - "order": 6, - "action": "Create the MyNFT.sol contract", - "working_directory": "hardhat-nft-deployment", - "description": "Create 'contracts/MyNFT.sol' with the following OpenZeppelin ERC-721 contract.Use the inline content below:\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```" - }, - { - "order": 7, - "action": "Create the Ignition deployment module", - "working_directory": "hardhat-nft-deployment", - "description": "Create 'ignition/modules/MyNFT.ts'. Replace INSERT_OWNER_ADDRESS with the Ethereum address that should own the NFT contract (typically the address derived from your PRIVATE_KEY):\n\n```typescript\nimport { buildModule } from '@nomicfoundation/hardhat-ignition/modules';\n\nconst MyNFTModule = buildModule('MyNFTModule', (m) => {\n const myNFT = m.contract('MyNFT', ['INSERT_OWNER_ADDRESS']);\n return { myNFT };\n});\n\nexport default MyNFTModule;\n```" - }, - { - "order": 8, - "action": "Compile the contract", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile MyNFT.sol. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in hardhat.config.ts and recompile." - }, - { - "order": 9, - "action": "Deploy the NFT contract to Polkadot Hub TestNet", - "working_directory": "hardhat-nft-deployment", - "commands": [ - "npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet" - ], - "expected_output": "MyNFTModule#MyNFT - 0x...", - "interactive": true, - "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "CompilationError: Invalid opcode: MCOPY", - "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode requiring Cancun EVM version.", - "resolution": "In hardhat.config.ts set evmVersion: 'cancun' inside solidity.settings, then run 'npx hardhat compile' again." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of deployment due to missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." - }, - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." - } - ], - "supplementary_context": { - "description": "Load when the user asks about minting NFTs, building a dApp on the deployed contract, or ERC-721 concepts.", - "pages": [ - { - "slug": "smart-contracts-cookbook-dapps-zero-to-hero", - "title": "Zero to Hero Smart Contract DApp", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md", - "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract on Polkadot Hub." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Reference for full Hardhat EVM environment setup and network configuration for Polkadot Hub." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a new ERC-721 NFT contract", - "user_says": "Deploy an ERC-721 NFT contract to Polkadot Hub TestNet", - "actions": [ - "Create project directory 'hardhat-nft-deployment' and run npm init -y", - "Install hardhat, OpenZeppelin contracts, and dotenv", - "Create hardhat.config.ts with dotenv, evmVersion: cancun, gasPrice 5000 gwei, requiredConfirmations: 1", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Create contracts/MyNFT.sol and ignition/modules/MyNFT.ts (replacing INSERT_OWNER_ADDRESS)", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet; delegate confirmation to user" - ], - "result": "ERC-721 NFT contract deployed; contract address printed to console" - }, - { - "scenario": "Edge case: compilation fails with MCOPY opcode error", - "user_says": "Compilation fails with 'Invalid opcode: MCOPY'", - "actions": [ - "Open hardhat.config.ts", - "Set solidity to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", - "Run npx hardhat compile again" - ], - "result": "Compilation succeeds with Cancun EVM version" - } - ] - }, - { - "id": "deploy-erc721-nft-remix", - "title": "Deploy an ERC-721 NFT Using Remix IDE", - "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md", - "prerequisites": { - "network": [ - "Polkadot Hub TestNet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for gas — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "MetaMask browser extension installed and configured for Polkadot Hub TestNet", - "Testnet account funded with PAS tokens" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Open Remix IDE and create the contract file", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Navigate to https://remix.ethereum.org in your web browser. (2) Under the 'contracts' folder, click 'Create new file' and name it 'MyNFT.sol'. (3) Paste the following ERC-721 contract code:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```\n\nWait for the user to confirm the file has been created and the code pasted." - }, - { - "order": 2, - "action": "Compile the contract in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Click the 'Solidity Compiler' icon in the left panel. (2) Click the 'Compile MyNFT.sol' button. Success: the compiler icon shows a green checkmark. If errors appear, check that the contract code was pasted correctly and the Solidity version is 0.8.22+." - }, - { - "order": 3, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Click 'Deploy & Run Transactions' in the left panel. (2) In the 'Environment' dropdown, select 'Injected Provider - MetaMask'. (3) Approve the MetaMask connection popup. (4) Confirm MetaMask is on Polkadot Hub TestNet (chain ID 420420417). If not, add it via MetaMask Settings > Networks with RPC https://services.polkadothub-rpc.com/testnet. Confirm the funded account is visible." - }, - { - "order": 4, - "action": "Deploy the NFT contract", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: (1) Ensure 'MyNFT' is selected in the Contract dropdown. (2) Expand the Deploy section and enter the constructor argument: the owner address (typically your MetaMask wallet address). (3) Click 'Deploy'. (4) Confirm the transaction in MetaMask. Once deployed, the contract appears in 'Deployed Contracts'. Record the contract address." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask: Wrong network / chain ID mismatch", - "cause": "MetaMask is connected to a different network than Polkadot Hub TestNet.", - "resolution": "Switch to or add Polkadot Hub TestNet in MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet." - }, - { - "pattern": "insufficient funds for gas", - "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", - "resolution": "Get testnet PAS tokens from https://faucet.polkadot.io/ for the connected account, then retry." - }, - { - "pattern": "Compilation errors in Remix", - "cause": "Contract code syntax error or Solidity version mismatch.", - "resolution": "Verify the contract code was pasted correctly. In the Solidity Compiler tab, ensure the compiler version is 0.8.22 or later." - } - ], - "supplementary_context": { - "description": "Load when the user asks about connecting Remix to Polkadot Hub or wants CLI-based NFT deployment.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub and configure MetaMask for the TestNet." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-nft-nft-hardhat", - "title": "Deploy an ERC-721 Using Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", - "relevance": "Alternative CLI-based NFT deployment using Hardhat — better for production or CI/CD workflows." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy ERC-721 NFT via Remix IDE", - "user_says": "Deploy an ERC-721 NFT using Remix", - "actions": [ - "Open https://remix.ethereum.org and create contracts/MyNFT.sol with the ERC-721 code", - "Compile via the Solidity Compiler plugin", - "Connect MetaMask with Injected Provider to Polkadot Hub TestNet", - "Enter owner address as constructor argument and click Deploy", - "Confirm transaction in MetaMask" - ], - "result": "ERC-721 NFT contract deployed; contract address shown in Remix Deployed Contracts panel" - }, - { - "scenario": "Edge case: user wants a custom NFT with additional features", - "user_says": "I want to customize my NFT with a supply cap or royalties", - "actions": [ - "Direct user to https://wizard.openzeppelin.com/polkadot to generate a custom ERC-721 contract", - "Paste the generated code into Remix as contracts/MyNFT.sol", - "Proceed with the standard compile and deploy steps" - ], - "result": "Custom ERC-721 contract with user-specified extensions compiled and deployed via Remix" - } - ] - }, - { - "id": "set-up-foundry-polkadot-hub", - "title": "Use Foundry with Polkadot Hub", - "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/foundry.md" - ], - "primary_page": "smart-contracts/dev-environments/foundry.md", - "prerequisites": { - "runtime": [ - "Unix-based OS (Linux or macOS) or Windows WSL", - "Git installed", - "curl installed" - ], - "network": [ - "Polkadot Hub TestNet (chain ID 420420417) — supported natively by Foundry nightly as --chain polkadot-testnet" - ], - "tokens": [ - "Testnet PAS tokens for deployment — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Loaded via 'source .env' into the shell session. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Install Foundry nightly build", - "working_directory": ".", - "commands": [ - "curl -L https://foundry.paradigm.xyz | bash", - "foundryup --version nightly" - ], - "expected_output": "forge Version: (nightly build version string)", - "description": "Install foundryup then the Foundry nightly build. Nightly is required — stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'." - }, - { - "order": 2, - "action": "Initialize a new Foundry project", - "working_directory": ".", - "commands": [ - "forge init my-foundry-project", - "cd my-foundry-project" - ], - "description": "Create a new Foundry project with src/ (contracts), script/ (deployment scripts), test/ (Solidity tests), lib/ (dependencies), and foundry.toml. A sample Counter.sol is placed in src/." - }, - { - "order": 3, - "action": "Configure foundry.toml for Polkadot Hub TestNet", - "working_directory": "my-foundry-project", - "description": "Replace the contents of 'foundry.toml' with the following. Choose Blockscout (no API key) or Routescan:\n\nBlockscout:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"\", url = \"https://blockscout-testnet.polkadot.io/api?\" }\n```\n\nRoutescan:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"verifyContract\", url = \"https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan\" }\n```\n\nWith this config, use --chain polkadot-testnet in commands without specifying the RPC URL explicitly." - }, - { - "order": 4, - "action": "Create .env file and load private key", - "working_directory": "my-foundry-project", - "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env with their 0x-prefixed private key for a testnet-funded account. Do NOT ask for the private key in chat. After confirmation, instruct the user to run 'source .env' in their terminal to load the variable into their shell session." - }, - { - "order": 5, - "action": "Compile contracts", - "working_directory": "my-foundry-project", - "commands": [ - "forge build" - ], - "expected_output": "Compiler run successful!", - "description": "Compile all Solidity contracts in src/. Artifacts output to out/. The sample Counter.sol should compile without errors." - }, - { - "order": 6, - "action": "Deploy a contract to Polkadot Hub TestNet", - "working_directory": "my-foundry-project", - "commands": [ - "forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url https://services.polkadothub-rpc.com/testnet --private-key $PRIVATE_KEY --broadcast" - ], - "expected_output": "Deployed to: 0x...", - "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address — needed for verification and interaction." - }, - { - "order": 7, - "action": "Verify the contract on the block explorer", - "working_directory": "my-foundry-project", - "description": "Replace INSERT_CONTRACT_ADDRESS with the address from step 6.\n\nBlockscout:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet\n```\n\nRoutescan:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --verifier-url 'https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan' --etherscan-api-key 'verifyContract' --chain polkadot-testnet\n```\n\nExpected output: 'Response: OK' and a URL to the verified contract." - }, - { - "order": 8, - "action": "Interact with the contract using Cast", - "working_directory": "my-foundry-project", - "description": "Replace INSERT_CONTRACT_ADDRESS and INSERT_ACCOUNT_ADDRESS with actual values.\n\nRead from contract:\n```bash\ncast call INSERT_CONTRACT_ADDRESS 'number()(uint256)' --chain polkadot-testnet\n```\n\nWrite to contract:\n```bash\ncast send INSERT_CONTRACT_ADDRESS 'setNumber(uint256)' 42 --chain polkadot-testnet --private-key $PRIVATE_KEY\n```\n\nCheck balance:\n```bash\ncast balance INSERT_ACCOUNT_ADDRESS --chain polkadot-testnet\n```" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "foundryup: command not found / forge: command not found", - "cause": "Foundry was installed but PATH was not updated for the current shell session.", - "resolution": "Run 'source ~/.bashrc' (or '~/.zshrc' on macOS) or start a new terminal." - }, - { - "pattern": "Error: Chain 'polkadot-testnet' not found / unknown chain", - "cause": "Using the Foundry stable release instead of nightly. Stable does not include Polkadot chain definitions.", - "resolution": "Install the nightly build: run 'foundryup --version nightly'." - }, - { - "pattern": "Error: Priority is too low / insufficient funds", - "cause": "Account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet PAS from https://faucet.polkadot.io/. The Foundry nightly build handles gas pricing automatically for Polkadot chains." - }, - { - "pattern": "forge test: tests pass locally but fail on TestNet", - "cause": "forge test runs against Anvil (Ethereum semantics), not Polkadot Hub. Existential deposit and gas model differ.", - "resolution": "Use forge test only for unit testing contract logic. For Polkadot-specific behavior, test against a local dev node or TestNet." - } - ], - "supplementary_context": { - "description": "Load when the user wants to understand Polkadot Hub EVM differences or compare Foundry with Hardhat.", - "pages": [ - { - "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", - "title": "EVM vs PVM", - "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md", - "relevance": "Explains differences between Polkadot Hub EVM and standard Ethereum including gas model and existential deposit." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Network details for Polkadot Hub TestNet and MainNet RPC endpoints and chain IDs." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: install Foundry and deploy a contract", - "user_says": "Set up Foundry and deploy a contract to Polkadot Hub TestNet", - "actions": [ - "Install Foundry nightly: curl -L https://foundry.paradigm.xyz | bash && foundryup --version nightly", - "Run forge init my-foundry-project", - "Configure foundry.toml with Polkadot TestNet verifier settings", - "Create .env with PRIVATE_KEY; ask user to fill in and run source .env", - "Run forge build to compile", - "Run forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url ... --private-key $PRIVATE_KEY --broadcast", - "Verify: forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet" - ], - "result": "Contract compiled, deployed, and verified on Polkadot Hub TestNet" - }, - { - "scenario": "Edge case: stable Foundry installed instead of nightly", - "user_says": "I get 'unknown chain polkadot-testnet' when running forge create", - "actions": [ - "Run 'forge --version' to check build type", - "If not nightly, run 'foundryup --version nightly' to upgrade", - "Retry the forge create command" - ], - "result": "Foundry upgraded to nightly; polkadot-testnet chain recognized and deployment succeeds" - } - ] - }, - { - "id": "spawn-test-network-zombienet", - "title": "Spawn and Test a Parachain Network with Zombienet", - "description": "Installs Zombienet (executable, Nix, or Docker), downloads polkadot and polkadot-parachain binaries, writes a TOML network config defining relay chain validators and parachain collators, spawns the network via the native provider, writes .zndsl test files using Zombienet's DSL, and runs the test suite. Use when spawning and testing ephemeral Polkadot SDK networks locally. Trigger phrases: 'Zombienet', 'spawn local parachain', 'test parachain network', 'local relay chain Zombienet', 'zndsl test'. Requires polkadot and polkadot-parachain binaries.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/zombienet.md" - ], - "primary_page": "reference/tools/zombienet.md", - "prerequisites": { - "runtime": [ - "Unix-based OS (Linux or macOS) or Windows WSL", - "polkadot and polkadot-parachain binaries — downloaded via: zombienet setup polkadot polkadot-parachain" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Install Zombienet", - "working_directory": ".", - "commands": [ - "curl -L https://github.com/paritytech/zombienet/releases/latest/download/zombienet-linux-x64 -o zombienet", - "chmod +x zombienet", - "mv zombienet /usr/local/bin/zombienet", - "zombienet version" - ], - "description": "Download the Zombienet executable for Linux x64. For macOS replace 'zombienet-linux-x64' with 'zombienet-macos'. For macOS Gatekeeper issues run: xattr -d com.apple.quarantine zombienet. The 'zombienet version' command confirms successful installation. Docker alternative: docker run -it --rm -v $(pwd):/home/nonroot/zombie-net/host-current-files paritytech/zombienet" - }, - { - "order": 2, - "action": "Download required node binaries", - "working_directory": ".", - "commands": [ - "zombienet setup polkadot polkadot-parachain" - ], - "description": "Download the polkadot and polkadot-parachain binaries for the native provider. Zombienet downloads these to a local path and adds them to PATH for the session. Verify availability: 'polkadot --version' and 'polkadot-parachain --version'." - }, - { - "order": 3, - "action": "Create a network configuration file", - "working_directory": ".", - "description": "Create 'network.toml' with a basic network configuration. This defines a relay chain with two validators and a parachain with one collator. If using the Polkadot Hub TestNet chain spec first download it:\n```bash\nwget https://paseo-r2.zondax.ch/chain-specs/paseo-asset-hub.json\n```\n\nThen create network.toml:\n```toml\n[settings]\ntimeout = 1000\n\n[relaychain]\nchain = \"paseo\"\ndefault_command = \"polkadot\"\n\n [[relaychain.nodes]]\n name = \"alice\"\n validator = true\n\n [[relaychain.nodes]]\n name = \"bob\"\n validator = true\n\n[[parachains]]\nid = 1000\nchain_spec_path = \"./paseo-asset-hub.json\"\n\n [parachains.collator]\n name = \"collator-01\"\n command = \"polkadot-parachain\"\n```\n\nAdjust id, chain_spec_path, and command fields to match your target network. For relay-only tests, omit the [[parachains]] section." - }, - { - "order": 4, - "action": "Spawn the network", - "working_directory": ".", - "commands": [ - "zombienet spawn network.toml --provider native" - ], - "expected_output": "Network launched\nRPC endpoints:", - "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal — the network persists until you stop it with Ctrl+C." - }, - { - "order": 5, - "action": "Write a test file", - "working_directory": ".", - "description": "Create 'network-test.zndsl' to test the spawned network:\n\n```\nDescription: Basic network health test\nNetwork: ./network.toml\nCreds: config\n\nalice: is up\nbob: is up\nalice: parachain 1000 is registered within 200 seconds\nalice: parachain 1000 block height is at least 10 within 300 seconds\n```\n\nCommon assertion types: node health ('alice: is up'), parachain registration, block height, metrics ('alice: reports node_roles is 4'), and log patterns ('alice: log line matches glob \"Imported #1\" within 10 seconds'). Remove parachain assertions if your network has no parachain." - }, - { - "order": 6, - "action": "Run the test suite", - "working_directory": ".", - "commands": [ - "zombienet test network-test.zndsl --provider native" - ], - "expected_output": "PASS", - "description": "Execute the test suite against the running network. Each assertion is evaluated in order; Zombienet reports PASS or FAIL. Exit code 0 = all pass, 1 = any failure. Ensure the network from step 4 is still running before executing tests." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "polkadot: binary not found / command not found", - "cause": "polkadot or polkadot-parachain binaries are not in PATH.", - "resolution": "Run 'zombienet setup polkadot polkadot-parachain' to download the binaries, then add them to PATH: export PATH=$PATH:." - }, - { - "pattern": "macOS: cannot be opened because it is from an unidentified developer", - "cause": "macOS Gatekeeper is blocking the Zombienet executable.", - "resolution": "Run: xattr -d com.apple.quarantine zombienet" - }, - { - "pattern": "Timeout: network did not start within timeout seconds", - "cause": "Binary download was slow or the chain spec file is missing or invalid.", - "resolution": "Increase the timeout value in the [settings] section of network.toml. Verify chain_spec_path exists and is valid." - }, - { - "pattern": "Test FAIL: parachain X is not registered within N seconds", - "cause": "Parachain onboarding takes time or the collator failed to start.", - "resolution": "Increase the 'within N seconds' timeout in the .zndsl assertion. Review Zombienet logs for collator startup errors." - } - ], - "supplementary_context": { - "description": "Load when the user wants more Zombienet configuration options or wants to build and deploy the parachain template first.", - "pages": [ - { - "slug": "parachains-testing-run-a-parachain-network", - "title": "Run a Parachain Network", - "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md", - "relevance": "Full tutorial for spawning a local parachain test network including building the parachain template binary." - }, - { - "slug": "parachains-testing-fork-a-parachain", - "title": "Fork a Parachain Using Chopsticks", - "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md", - "relevance": "Alternative testing approach using Chopsticks to fork a live parachain for isolated state testing." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: spawn and test a relay+parachain network", - "user_says": "Spawn a local Zombienet network and run tests", - "actions": [ - "Install Zombienet executable and run zombienet setup polkadot polkadot-parachain", - "Download paseo-asset-hub.json chain spec if using Polkadot Hub parachain", - "Create network.toml with relay chain (alice, bob) and parachain (id=1000)", - "Run zombienet spawn network.toml --provider native in a separate terminal", - "Create network-test.zndsl with health and parachain assertions", - "Run zombienet test network-test.zndsl --provider native" - ], - "result": "Local network spawns successfully; all test assertions PASS" - }, - { - "scenario": "Edge case: test relay chain only without a parachain", - "user_says": "Spawn just a relay chain network for testing consensus behavior", - "actions": [ - "Remove the [[parachains]] section from network.toml", - "Remove parachain assertions from network-test.zndsl", - "Run zombienet spawn network.toml --provider native", - "Run zombienet test network-test.zndsl --provider native" - ], - "result": "Relay chain with alice and bob validators spawns; health assertions pass" - } - ] - }, - { - "id": "deploy-interact-contracts-web3js", - "title": "Deploy and Interact with Smart Contracts Using Web3.js", - "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", - "version": "1.0.1", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/web3-js.md" - ], - "primary_page": "smart-contracts/libraries/web3-js.md", - "prerequisites": { - "runtime": [ - "Node.js v22.13.1 or later", - "npm v6.13.4 or later" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" - ], - "tokens": [ - "Testnet PAS tokens for deployment gas — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the deployer account. Used in deploy.js and updateStorage.js. Must be funded with testnet PAS. Never commit to version control.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and initialize npm", - "working_directory": ".", - "commands": [ - "mkdir web3js-project", - "cd web3js-project", - "npm init -y" - ], - "description": "Create a new directory and initialize a Node.js project." - }, - { - "order": 2, - "action": "Install Web3.js and solc", - "working_directory": "web3js-project", - "commands": [ - "npm install web3", - "npm install --save-dev solc" - ], - "description": "Install the Web3.js library for blockchain interaction and solc for compiling Solidity contracts to EVM bytecode." - }, - { - "order": 3, - "action": "Create project directories and the Storage contract", - "working_directory": "web3js-project", - "commands": [ - "mkdir -p contracts scripts abis artifacts" - ], - "description": "Create the expected directory structure (contracts/, scripts/, abis/, artifacts/), then fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter:\n\n```solidity\n// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.9;\n\ncontract Storage {\n uint256 public storedNumber;\n function setNumber(uint256 _newNumber) public { storedNumber = _newNumber; }\n}\n```", - "reference_file": "Storage.sol" - }, - { - "order": 4, - "action": "Create the provider connection script and verify connectivity", - "working_directory": "web3js-project", - "description": "Fetch the reference file `connectToProvider.js` and save it as `scripts/connectToProvider.js`. Then make these substitutions:\n(1) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`\n(2) Replace `INSERT_CHAIN_ID` with `420420417`\n(3) Replace `INSERT_CHAIN_NAME` with `polkadot-hub-testnet`\n\nThen verify: `node scripts/connectToProvider.js`\nExpected output: `Connected to chain ID: 420420417`", - "reference_file": "connectToProvider.js" - }, - { - "order": 5, - "action": "Create the compile script and compile the contract", - "working_directory": "web3js-project", - "commands": [ - "node scripts/compile.js" - ], - "expected_output": "ABI and bytecode saved", - "description": "Fetch the reference file `compile.js` and save it as `scripts/compile.js`. The script reads `contracts/Storage.sol`, compiles it with solc, saves ABI to `abis/Storage.json` and bytecode to `artifacts/Storage.bin`. Then run it to compile.", - "reference_file": "compile.js" - }, - { - "order": 6, - "action": "Create the deployment script and deploy the contract", - "working_directory": "web3js-project", - "description": "Fetch the reference file `deploy.js` and save it as `scripts/deploy.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY` (do NOT hardcode the key)\n(2) Ensure the RPC URL points to `https://services.polkadothub-rpc.com/testnet`\n\nSet `PRIVATE_KEY` in your environment: `export PRIVATE_KEY=0x...` (never commit this). Then deploy:\n\n```bash\nnode scripts/deploy.js\n```\n\nExpected: contract address saved in `contract-address.json`. Record this address.", - "reference_file": "deploy.js" - }, - { - "order": 7, - "action": "Create the interaction script and interact with the contract", - "working_directory": "web3js-project", - "description": "Fetch the reference file `updateStorage.js` and save it as `scripts/updateStorage.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`\n(2) Replace `INSERT_CONTRACT_ADDRESS` with the address from step 6, or ensure `contract-address.json` is read automatically\n(3) Verify the ABI references `abis/Storage.json`\n\nThen run: `node scripts/updateStorage.js`\nExpected: current stored value displayed, then updated to 1.", - "reference_file": "updateStorage.js" - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "base_path": ".snippets/code/smart-contracts/libraries/web3-js", - "files": [ - { - "path": "Storage.sol", - "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter." - }, - { - "path": "connectToProvider.js", - "description": "Web3.js provider connection to Polkadot Hub TestNet" - }, - { - "path": "compile.js", - "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts" - }, - { - "path": "deploy.js", - "description": "Deploys the compiled Storage contract via Web3.js Contract.deploy().send()" - }, - { - "path": "updateStorage.js", - "description": "Calls `setNumber(value)` on the deployed Storage contract via Web3.js, then reads `storedNumber` to confirm." - } - ], - "branch": "master" - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Deployer account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", - "resolution": "Get testnet PAS from https://faucet.polkadot.io/ and verify the funded account is used in deploy.js." - }, - { - "pattern": "Error: Cannot find module 'web3'", - "cause": "Web3.js dependency was not installed.", - "resolution": "Run 'npm install web3' in the web3js-project directory." - }, - { - "pattern": "Error reading ABI or bytecode file / file not found", - "cause": "Compile step was skipped or output files are not in expected locations.", - "resolution": "Run 'node scripts/compile.js' first to generate abis/Storage.json and artifacts/Storage.bin." - }, - { - "pattern": "Error: cannot connect to provider / network timeout", - "cause": "Wrong RPC URL or TestNet is temporarily unavailable.", - "resolution": "Verify the RPC URL is 'https://services.polkadothub-rpc.com/testnet'. Check Polkadot status pages for TestNet outages." - } - ], - "supplementary_context": { - "description": "Load when the user wants a modern alternative to Web3.js or asks about contract interaction options.", - "pages": [ - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Recommended modern alternative to Web3.js for deploying and interacting with contracts on Polkadot Hub." - }, - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "TypeScript-native alternative library for Polkadot Hub contract interactions." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a Storage contract and interact with it", - "user_says": "Deploy a smart contract to Polkadot Hub using Web3.js", - "actions": [ - "Create web3js-project, npm init -y, install web3 and solc", - "Create contracts/Storage.sol with the storage contract", - "Create and run scripts/connectToProvider.js (replace INSERT_RPC_URL, INSERT_CHAIN_ID, INSERT_CHAIN_NAME)", - "Create and run scripts/compile.js to generate ABI and bytecode", - "Create scripts/deploy.js, replace INSERT_PRIVATE_KEY with process.env.PRIVATE_KEY, run it", - "Create scripts/updateStorage.js, replace INSERT_PRIVATE_KEY and INSERT_CONTRACT_ADDRESS, run it" - ], - "result": "Storage contract deployed; read and write interactions confirmed" - }, - { - "scenario": "Edge case: user wants a modern library instead of Web3.js", - "user_says": "Web3.js is sunset, what should I use instead?", - "actions": [ - "Note that Web3.js has been sunset", - "Recommend Ethers.js (deploy-contracts-ethers-js) or viem (deploy-contracts-viem) as actively maintained alternatives" - ], - "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction" - } - ], - "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json" - }, - { - "id": "add-existing-pallet-to-runtime", - "title": "Add an Existing Pallet to a Parachain Runtime", - "description": "Integrates an existing Polkadot SDK pallet into a local parachain runtime. Covers adding the pallet crate to Cargo.toml, implementing the pallet's Config trait in runtime/src/lib.rs, registering the pallet in the construct_runtime! macro, compiling the runtime, generating a chain spec, and running the parachain locally to verify the pallet is active. Use when extending a Polkadot SDK parachain template with standard SDK pallets (pallet-utility, pallet-multisig, pallet-proxy, etc.). Trigger phrases: 'add pallet to runtime', 'integrate pallet parachain', 'pallet-utility runtime', 'extend parachain runtime', 'construct_runtime pallet'. Prerequisite: working parachain template dev environment (see set-up-the-parachain-template). Final GUI verification uses Polkadot.js Apps.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/add-existing-pallets.md" - ], - "primary_page": "parachains/customize-runtime/add-existing-pallets.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with the wasm32-unknown-unknown target (`rustup target add wasm32-unknown-unknown`)", - "Polkadot SDK system dependencies installed (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" - ], - "network": [ - "No external network required — all compilation and testing is local" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Identify the pallet version to use", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "grep 'polkadot-sdk' runtime/Cargo.toml | head -5" - ], - "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version — you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name." - }, - { - "order": 4, - "action": "Add the pallet dependency to runtime/Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 — the output should not show dependency resolution errors." - }, - { - "order": 5, - "action": "Implement the pallet's Config trait in runtime/src/lib.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified — the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency." - }, - { - "order": 6, - "action": "Register the pallet in construct_runtime!", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes — choose carefully as it cannot be changed without a migration. Save the file." - }, - { - "order": 7, - "action": "Compile the runtime", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release 2>&1 | tail -20" - ], - "description": "Build the full parachain node (includes the runtime WASM blob). This typically takes 5-15 minutes on first build. A successful build ends with: Compiling node-template vX.X.X ... Finished release [optimized] target(s). If compilation fails with 'the trait `Config` for `pallet_utility` is not satisfied', check that all required associated types are implemented in step 3." - }, - { - "order": 8, - "action": "Generate a fresh chain spec and start the node locally", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/local-spec.json", - "./target/release/parachain-template-node --dev --tmp --chain /tmp/local-spec.json" - ], - "description": "Generate a development chain spec and start the node in --dev mode. The --tmp flag stores chain data in a temporary directory. Once the node is running and producing blocks (look for: Imported #1 in logs), open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics. Verify that 'utility' appears in the pallet dropdown as a callable extrinsic module." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0277]: the trait `pallet_utility::Config` is not implemented for `Runtime`", - "cause": "The impl pallet_utility::Config for Runtime block is missing or has a typo in runtime/src/lib.rs.", - "resolution": "Ensure the impl block is present and all required associated types are specified. Run cargo check -p runtime to see the full list of missing types. Each missing type will be listed as 'required by this bound in pallet_utility::Config'." - }, - { - "pattern": "error[E0432]: unresolved import `pallet_utility`", - "cause": "pallet-utility was not added to runtime/Cargo.toml, or it was added but Cargo has not re-resolved dependencies.", - "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again — Cargo will fetch and compile the new dependency." - }, - { - "pattern": "Utility pallet does not appear in Polkadot.js Apps extrinsics", - "cause": "The pallet was registered in construct_runtime! but with the wrong name, or the node is running an old binary without the updated runtime.", - "resolution": "Stop the node, rebuild with cargo build --release, regenerate the chain spec, and restart. Verify the construct_runtime! entry name exactly matches what you expect to see in the UI." - } - ], - "supplementary_context": { - "description": "Load these pages for parachain template setup or for adding multiple pallet instances.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Rust toolchain and system dependencies required before building any parachain runtime." - }, - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Prerequisite: setting up the parachain template development environment." - }, - { - "slug": "parachains-customize-runtime-add-pallet-instances", - "title": "Add Multiple Pallet Instances", - "url": "https://docs.polkadot.com/parachains/customize-runtime/add-pallet-instances.md", - "relevance": "Next step: adding multiple instances of the same pallet (e.g., two pallet-collective instances)." - }, - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Building a custom FRAME pallet from scratch and integrating it into the runtime." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: add pallet-utility to a parachain runtime", - "user_says": "Add pallet-utility to my parachain runtime", - "actions": [ - "Check the existing Polkadot SDK version in runtime/Cargo.toml", - "Add pallet-utility dependency with matching version and default-features = false", - "Add pallet-utility/std to the std feature list", - "Implement pallet_utility::Config for Runtime in runtime/src/lib.rs with all required associated types", - "Add Utility: pallet_utility entry to construct_runtime!", - "Run cargo build --release", - "Generate chain spec and start node with --dev --tmp", - "Verify 'utility' appears in Polkadot.js Apps extrinsics" - ], - "result": "pallet-utility integrated into the runtime; utility.batch, utility.batchAll, and utility.dispatchAs are callable on-chain" - }, - { - "scenario": "Edge case: compilation fails with missing associated type in Config", - "user_says": "I get a compiler error about missing associated types in my Config impl", - "actions": [ - "Read the full compiler error — each missing type is listed with 'required by this bound'", - "Add each missing associated type to the impl block", - "Refer to the pallet's source (src/lib.rs) or docs for the expected type definitions", - "Re-run cargo build --release" - ], - "result": "All required Config associated types satisfied; compilation succeeds" - } - ] - }, - { - "id": "configure-multiple-pallet-instances", - "title": "Configure Multiple Instances of a Pallet in a Runtime", - "description": "Configures two independent instances of an instantiable Polkadot SDK pallet within a single parachain runtime. Uses pallet-collective as the example to demonstrate adding TechnicalCommittee (Instance1) and Council (Instance2) with separate membership and voting parameters. Covers identifying instantiable pallets, adding per-instance Cargo dependencies, implementing Config(Instance1) and Config(Instance2) traits, registering both instances in construct_runtime!, compiling, and verifying instance independence. Use when a runtime needs two separate governance bodies, two token pools, or any other scenario requiring two isolated instances of the same pallet logic. Trigger phrases: 'multiple pallet instances', 'two collective instances', 'pallet-collective council technical committee', 'instantiable pallet runtime'. Prerequisite: working parachain template (see add-existing-pallet-to-runtime).", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/add-pallet-instances.md" - ], - "primary_page": "parachains/customize-runtime/add-pallet-instances.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with wasm32-unknown-unknown target", - "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)", - "Familiarity with adding a single pallet to a runtime (see add-existing-pallet-to-runtime)" - ], - "network": [ - "No external network required — local development only" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Verify pallet supports instantiation", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Not all pallets support multiple instances. An instantiable pallet uses a second generic type parameter (the Instance) in its Config trait definition, e.g.: `pub trait Config: frame_system::Config`. Check the pallet's source or documentation for this pattern. pallet-collective and pallet-membership are standard examples. If the pallet does not have the Instance generic, it cannot be instantiated multiple times." - }, - { - "order": 4, - "action": "Add pallet dependencies to runtime/Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances — add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`" - }, - { - "order": 5, - "action": "Implement Config for TechnicalCommittee", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the first instance's Config implementation. For pallet-collective as TechnicalCommittee:\n\n```rust\ntype TechnicalCommitteeInstance = pallet_collective::Instance1;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<5>;\n type MaxProposals = ConstU32<100>;\n type MaxMembers = ConstU32<100>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nSubstitute `ConstU32` values with the desired governance parameters." - }, - { - "order": 6, - "action": "Implement Config for Council", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage — `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance." - }, - { - "order": 7, - "action": "Register both instances in construct_runtime!", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics — choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry." - }, - { - "order": 8, - "action": "Compile the runtime", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release 2>&1 | tail -20" - ], - "description": "Build the parachain node. Compilation succeeds when both instance Config impls are complete and construct_runtime! entries are correct. Expected output: Finished release [optimized] target(s). If you see 'conflicting implementations of trait Config', check that each instance type alias (TechnicalCommitteeInstance, CouncilInstance) is distinct." - }, - { - "order": 9, - "action": "Verify instance independence", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/instances-spec.json", - "./target/release/parachain-template-node --dev --tmp --chain /tmp/instances-spec.json" - ], - "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics — verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0119]: conflicting implementations of trait `pallet_collective::Config`", - "cause": "Two impl blocks are targeting the same instance type — usually because both use the default instance () instead of Instance1/Instance2.", - "resolution": "Ensure each impl block explicitly names a different instance: impl pallet_collective::Config and impl pallet_collective::Config. Type aliases (type TechnicalCommitteeInstance = pallet_collective::Instance1;) help clarify intent." - }, - { - "pattern": "error: the pallet does not implement the Instance trait", - "cause": "The pallet does not support instantiation — it lacks the I: 'static = () generic in its Config trait.", - "resolution": "Only instantiable pallets (those with a second generic parameter) can be used this way. Check the pallet source or use a different pallet that supports multiple instances." - }, - { - "pattern": "Only one instance visible in Polkadot.js Apps", - "cause": "One construct_runtime! entry is missing or has a typo.", - "resolution": "Verify both entries are present in construct_runtime! with distinct names and correct instance specifiers: TechnicalCommittee: pallet_collective:: and Council: pallet_collective::. Rebuild and restart the node." - } - ], - "supplementary_context": { - "description": "Load these pages for single pallet integration or custom pallet development.", - "pages": [ - { - "slug": "parachains-customize-runtime-add-existing-pallets", - "title": "Add an Existing Pallet to the Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Prerequisite: adding a single pallet to a runtime — covers the Cargo, Config, and construct_runtime! basics." - }, - { - "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", - "title": "Create a Custom Pallet", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md", - "relevance": "Building a custom pallet from scratch — next step after mastering pallet integration." - }, - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Rust toolchain and build dependency installation." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: add TechnicalCommittee and Council as two pallet-collective instances", - "user_says": "Add a TechnicalCommittee and a Council to my parachain runtime using pallet-collective", - "actions": [ - "Verify pallet-collective has the I: 'static = () generic — confirm it is instantiable", - "Add pallet-collective to runtime/Cargo.toml with default-features = false and add to std features", - "Implement pallet_collective::Config as TechnicalCommitteeInstance with committee-appropriate parameters", - "Implement pallet_collective::Config as CouncilInstance with council-appropriate parameters", - "Add TechnicalCommittee: pallet_collective:: and Council: pallet_collective:: to construct_runtime!", - "Run cargo build --release", - "Start node in --dev mode and verify both 'technicalCommittee' and 'council' appear in Polkadot.js Apps" - ], - "result": "Two independent pallet-collective instances registered — TechnicalCommittee and Council operate with independent storage, membership, and voting parameters" - }, - { - "scenario": "Edge case: runtime compiles but only one instance shows in the UI", - "user_says": "My runtime compiled but I only see one collective in Polkadot.js Apps", - "actions": [ - "Check construct_runtime! for both TechnicalCommittee and Council entries", - "Verify each entry uses the correct instance specifier: :: and ::", - "Rebuild with cargo build --release", - "Regenerate chain spec and restart node" - ], - "result": "Both collective instances visible and callable in Polkadot.js Apps" - } - ] - }, - { - "id": "create-frame-pallet", - "title": "Create a Custom FRAME Pallet", - "description": "Guides through building a custom FRAME pallet from scratch using the Polkadot SDK. Uses a counter pallet (increment and decrement extrinsics with bounded storage) as the example to demonstrate all core FRAME patterns: pallet directory layout, Cargo.toml configuration, #[pallet::config], #[pallet::storage], #[pallet::event], #[pallet::error], #[pallet::call], and genesis config. After implementing the pallet, integrates it into the parachain runtime (Cargo dependency, Config impl, construct_runtime!), compiles, and runs locally. Use when building any new runtime module with on-chain state and extrinsics. Trigger phrases: 'create FRAME pallet', 'custom pallet from scratch', 'write a pallet', 'FRAME storage events extrinsics', 'build counter pallet'. Prerequisites: Polkadot SDK installed, parachain template set up. Verification step uses Polkadot.js Apps GUI.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/customize-runtime/pallet-development/create-a-pallet.md" - ], - "primary_page": "parachains/customize-runtime/pallet-development/create-a-pallet.md", - "prerequisites": { - "runtime": [ - "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", - "Disk: at least 5 GB free for build artifacts", - "Rust toolchain with wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown)", - "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" - ], - "network": [ - "No external network required — all work is local" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Clone the Polkadot SDK parachain template", - "working_directory": ".", - "commands": [ - "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" - ], - "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill." - }, - { - "order": 2, - "action": "Build the parachain template node in release mode", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release" - ], - "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes." - }, - { - "order": 3, - "action": "Create the pallet directory and Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "mkdir -p pallets/counter/src", - "touch pallets/counter/src/lib.rs" - ], - "description": "Create the pallet directory structure inside the parachain template workspace. The pallet name (counter) becomes the crate name. Create pallets/counter/Cargo.toml with the following content, replacing with the version matching your other workspace pallets:\n\n```toml\n[package]\nname = \"pallet-counter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ncodec = { package = \"parity-scale-codec\", version = \"3\", default-features = false, features = [\"derive\"] }\nscale-info = { version = \"2\", default-features = false, features = [\"derive\"] }\nframe-support = { version = \"\", default-features = false }\nframe-system = { version = \"\", default-features = false }\nframe-benchmarking = { version = \"\", default-features = false, optional = true }\n\n[features]\ndefault = [\"std\"]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame-support/std\",\n \"frame-system/std\",\n]\nruntime-benchmarks = [\"frame-benchmarking/runtime-benchmarks\"]\n```\n\nAlso add pallet-counter to the workspace's root Cargo.toml members list:\nmembers = [\n ...\n \"pallets/counter\",\n]" - }, - { - "order": 4, - "action": "Implement the pallet in pallets/counter/src/lib.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Write the pallet implementation in pallets/counter/src/lib.rs. A minimal counter pallet contains:\n\n#![cfg_attr(not(feature = \"std\"), no_std)]\n\npub use pallet::*;\n\n```rust\n#[frame_support::pallet]\npub mod pallet {\n use frame_support::pallet_prelude::*;\n use frame_system::pallet_prelude::*;\n\n #[pallet::pallet]\n pub struct Pallet(_);\n\n #[pallet::config]\n pub trait Config: frame_system::Config {\n type RuntimeEvent: From> + IsType<::RuntimeEvent>;\n #[pallet::constant]\n type MaxValue: Get;\n }\n```\n\n #[pallet::storage]\n pub type CounterValue = StorageValue<_, u32, ValueQuery>;\n\n```rust\n #[pallet::event]\n #[pallet::generate_deposit(pub(super) fn deposit_event)]\n pub enum Event {\n CounterIncremented { new_value: u32 },\n CounterDecremented { new_value: u32 },\n }\n\n #[pallet::error]\n pub enum Error {\n CounterOverflow,\n CounterUnderflow,\n }\n\n #[pallet::call]\n impl Pallet {\n #[pallet::call_index(0)]\n #[pallet::weight(10_000)]\n pub fn increment(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_add(1).ok_or(Error::::CounterOverflow)?;\n ensure!(new_val <= T::MaxValue::get(), Error::::CounterOverflow);\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterIncremented { new_value: new_val });\n Ok(())\n }\n\n #[pallet::call_index(1)]\n #[pallet::weight(10_000)]\n pub fn decrement(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_sub(1).ok_or(Error::::CounterUnderflow)?;\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterDecremented { new_value: new_val });\n Ok(())\n }\n }\n}\n```\n\nThe MaxValue constant bounds the counter to prevent overflow. checked_add/checked_sub replace panicking arithmetic." - }, - { - "order": 5, - "action": "Add pallet-counter to runtime/Cargo.toml", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/Cargo.toml and add pallet-counter as a workspace dependency:\n\n```toml\n[dependencies]\n...\npallet-counter = { path = \"../pallets/counter\", default-features = false }\n\nAdd to the std feature list:\n[features]\nstd = [\n ...\n \"pallet-counter/std\",\n]\n```\n\nNote: path = \"../pallets/counter\" is a relative path from the runtime/ directory to the pallet directory." - }, - { - "order": 6, - "action": "Implement pallet_counter::Config in runtime/src/lib.rs", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 — adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime." - }, - { - "order": 7, - "action": "Register the pallet in construct_runtime!", - "working_directory": "polkadot-sdk-parachain-template", - "description": "Find construct_runtime! in runtime/src/lib.rs and add the counter pallet:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n Counter: pallet_counter,\n }\n);\n```\n\nThe name Counter is the on-chain pallet identifier. Save the file." - }, - { - "order": 8, - "action": "Compile and run locally", - "working_directory": "polkadot-sdk-parachain-template", - "commands": [ - "cargo build --release 2>&1 | tail -20", - "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/counter-spec.json", - "./target/release/parachain-template-node --dev --tmp --chain /tmp/counter-spec.json" - ], - "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics — 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-cookbook", - "branch": "master", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "error[E0412]: cannot find type `ConstU32` in this scope", - "cause": "ConstU32 is not imported in runtime/src/lib.rs.", - "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it — search for existing ConstU32 usages to confirm." - }, - { - "pattern": "error[E0277]: the trait `pallet_counter::Config` is not implemented", - "cause": "The impl pallet_counter::Config for Runtime block is missing or has a missing associated type.", - "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type — add them one by one." - }, - { - "pattern": "error: failed to select a version for `pallet-counter` / could not find Cargo.toml", - "cause": "The path in runtime/Cargo.toml does not match the actual pallet directory location.", - "resolution": "Verify the relative path: from runtime/, pallets/counter/ should be at ../pallets/counter. Run ls ../pallets/counter/Cargo.toml from inside the runtime/ directory to confirm the path exists." - }, - { - "pattern": "CounterOverflow error when submitting increment", - "cause": "The counter has reached MaxValue (100 by default).", - "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design — the error confirms the overflow protection is working." - } - ], - "supplementary_context": { - "description": "Load these pages for runtime setup prerequisites or next steps in pallet development.", - "pages": [ - { - "slug": "parachains-install-polkadot-sdk", - "title": "Install Polkadot SDK", - "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md", - "relevance": "Rust and system dependencies required before building any FRAME pallet." - }, - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Setting up the parachain template workspace where the custom pallet lives." - }, - { - "slug": "parachains-customize-runtime-add-existing-pallets", - "title": "Add an Existing Pallet to the Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md", - "relevance": "Adding an existing SDK pallet to a runtime — prerequisite concepts for runtime integration." - }, - { - "slug": "parachains-customize-runtime-pallet-development-mock-runtime", - "title": "Mock Your Runtime", - "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md", - "relevance": "Next step: setting up a mock runtime for unit testing the custom pallet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: build a counter pallet with increment and decrement extrinsics", - "user_says": "Create a custom FRAME pallet from scratch", - "actions": [ - "Create pallets/counter/src/ directory and lib.rs file", - "Write Cargo.toml with frame-support, frame-system, parity-scale-codec, and scale-info dependencies", - "Add pallet-counter to workspace root Cargo.toml members", - "Implement the pallet in lib.rs: Config trait, StorageValue, Event enum, Error enum, and two call extrinsics (increment/decrement) with overflow/underflow protection", - "Add pallet-counter path dependency to runtime/Cargo.toml", - "Implement pallet_counter::Config for Runtime with MaxValue = ConstU32<100>", - "Add Counter: pallet_counter to construct_runtime!", - "Run cargo build --release", - "Start node in --dev mode; submit increment extrinsic via Polkadot.js Apps; verify CounterValue storage" - ], - "result": "Custom counter pallet deployed locally; increment and decrement extrinsics callable on-chain; CounterValue storage updates correctly with overflow protection" - }, - { - "scenario": "Edge case: adding a genesis config to set an initial counter value", - "user_says": "How do I set the initial counter value to 10 at genesis?", - "actions": [ - "Add a #[pallet::genesis_config] struct and #[pallet::genesis_build] impl to the pallet's lib.rs to accept an initial_value", - "In the runtime's chain_spec.rs (or equivalent), include Counter: CounterConfig { initial_value: 10 } in the genesis state", - "Rebuild and regenerate the chain spec to include the genesis config" - ], - "result": "Node starts with CounterValue pre-set to 10; visible in Developer > Chain State > counter.counterValue immediately after genesis" - } - ] - }, - { - "id": "retrieve-runtime-metadata", - "title": "Retrieve Polkadot Runtime Metadata", - "description": "Provides three methods for fetching Polkadot SDK runtime metadata: curl JSON-RPC (state_getMetadata, returns raw hex bytes), subxt CLI (human-readable JSON, ideal for inspection), and Polkadot.js Apps RPC UI (interactive). Use when building client libraries, inspecting pallet structure, or verifying runtime composition after an upgrade. Trigger phrases: 'get runtime metadata', 'state_getMetadata', 'inspect pallet calls', 'fetch chain metadata', 'subxt metadata', 'what pallets are on chain'. No private key or wallet required. The metadata output describes all pallets, storage items, calls, events, errors, and constants in the current runtime.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/parachains/chain-data.md" - ], - "primary_page": "reference/parachains/chain-data.md", - "prerequisites": { - "runtime": [ - "curl (Method A — available by default on macOS and most Linux distros)", - "subxt CLI (Method B — install with: cargo install subxt-cli)" - ], - "network": [ - "An accessible RPC endpoint for the target Polkadot SDK node (e.g. https://rpc.polkadot.io for mainnet, or http://127.0.0.1:9944 for a local dev node)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "(Method A) Fetch metadata via curl JSON-RPC", - "working_directory": ".", - "commands": [ - "curl -H \"Content-Type: application/json\" -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\"}' https://rpc.polkadot.io" - ], - "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string — not human-readable. Use Method B or C if you need human-readable JSON.", - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":\"0x6d657461...\",\"id\":1}" - }, - { - "order": 2, - "action": "(Method B) Fetch metadata as human-readable JSON using subxt CLI", - "working_directory": ".", - "commands": [ - "subxt metadata --url wss://rpc.polkadot.io --format json > spec.json" - ], - "description": "Install the subxt CLI if not already installed: cargo install subxt-cli. Run subxt metadata to fetch and decode the metadata to JSON. Replace wss://rpc.polkadot.io with your node's WebSocket endpoint (wss:// for remote, ws:// for local). For a local dev node: subxt metadata --url ws://127.0.0.1:9944 --format json > spec.json. You can also explore metadata interactively at https://paritytech.github.io/subxt-explorer/.", - "expected_output": "spec.json created containing the runtime metadata in human-readable JSON format" - }, - { - "order": 3, - "action": "(Method C) Retrieve metadata via Polkadot.js Apps RPC UI", - "working_directory": ".", - "description": "Navigate to https://polkadot.js.org/apps/#/rpc and connect to your target node. Click the Developer dropdown and select RPC Calls. 1. Select state as the endpoint. 2. Select getMetadata(at) as the method. 3. Click Submit RPC Call. The metadata is returned in JSON format in the UI. This is the most interactive option but is not scriptable." - }, - { - "order": 4, - "action": "Interpret the metadata output", - "working_directory": ".", - "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type → find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions — do not hardcode them." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "curl returns connection refused or timeout", - "cause": "The RPC endpoint URL is incorrect, the node is not running, or the endpoint does not accept HTTP connections.", - "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections — use subxt (Method B) with ws:// or wss:// instead of curl." - }, - { - "pattern": "subxt metadata fails with 'failed to fetch metadata'", - "cause": "The WebSocket URL is incorrect, the node is not running, or the subxt-cli version is incompatible with the node's metadata version.", - "resolution": "Verify the WebSocket URL (wss:// for TLS, ws:// for local). Confirm the node is running. Update subxt-cli: cargo install subxt-cli --force." - }, - { - "pattern": "Metadata output shows unexpected pallets or an empty pallets array", - "cause": "The node connected to is a different chain than expected, or the genesis has not been customized.", - "resolution": "Verify the RPC endpoint points to the intended chain. For local dev nodes ensure the chain spec matches the expected runtime." - } - ], - "supplementary_context": { - "description": "Load these pages for context on how metadata is used in client applications or SDK-level interactions.", - "pages": [ - { - "slug": "reference-tools-subxt", - "title": "Subxt Rust API", - "url": "https://docs.polkadot.com/reference/tools/subxt.md", - "relevance": "Subxt library usage for generating typed client code from runtime metadata — the primary consumer of metadata in Rust applications." - }, - { - "slug": "reference-parachains-data-encoding", - "title": "Data Encoding", - "url": "https://docs.polkadot.com/reference/parachains/data-encoding.md", - "relevance": "SCALE encoding details that explain why the raw curl metadata output is hex-encoded bytes rather than readable JSON." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: inspect pallet list and call signatures of Polkadot mainnet", - "user_says": "Get the runtime metadata from the Polkadot mainnet node", - "actions": [ - "Run: subxt metadata --url wss://rpc.polkadot.io --format json > spec.json", - "Open spec.json and locate the pallets array", - "Find a specific pallet by name (e.g. Balances) and follow its calls type index to read available calls" - ], - "result": "spec.json contains the complete runtime metadata in human-readable JSON; pallets, calls, storage items, events, and types are all inspectable." - }, - { - "scenario": "Edge case: inspect metadata from a local dev node", - "user_says": "How do I get the metadata from my local dev node at ws://127.0.0.1:9944?", - "actions": [ - "Verify the local node is running (polkadot-omni-node --dev or similar)", - "Run: subxt metadata --url ws://127.0.0.1:9944 --format json > local-spec.json", - "Or use curl: curl -H \"Content-Type: application/json\" -d '{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"state_getMetadata\"}' http://127.0.0.1:9944" - ], - "result": "Metadata from the local dev node retrieved; reflects the exact runtime version currently running locally." - } - ] - }, - { - "id": "use-polkadot-js-api", - "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", - "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode — new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "reference/tools/polkadot-js-api.md" - ], - "primary_page": "reference/tools/polkadot-js-api.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "network": [ - "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.ibp.network/polkadot for Polkadot mainnet)" - ] - }, - "env_vars": [ - { - "name": "WS_ENDPOINT", - "description": "WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet).", - "required": true - }, - { - "name": "MNEMONIC", - "description": "12 or 24-word seed phrase for the signing account. Required only for the transaction-sending step.", - "required": false - } - ], - "steps": [ - { - "order": 1, - "action": "Initialize ESM Node.js project and install @polkadot/api", - "working_directory": ".", - "commands": [ - "mkdir polkadot-api-demo && cd polkadot-api-demo", - "npm init -y && npm pkg set type=module", - "npm install @polkadot/api @polkadot/keyring dotenv" - ], - "description": "Create a new directory named polkadot-api-demo and initialize it as an ESM Node.js project. The type=module flag is required because @polkadot packages use ESM-only imports. Install dotenv for secure credential loading." - }, - { - "order": 2, - "action": "Create .env file and add to .gitignore", - "working_directory": "polkadot-api-demo", - "commands": [ - "printf 'WS_ENDPOINT=\\nMNEMONIC=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly — do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding." - }, - { - "order": 3, - "action": "Create the chain-query script", - "working_directory": "polkadot-api-demo", - "description": "Create a file named query.ts with the following content. Replace INSERT_ADDRESS with the SS58-encoded address to query (e.g., an address from the create-polkadot-account skill output):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const chain = await api.rpc.system.chain();\n const existentialDeposit = api.consts.balances.existentialDeposit;\n console.log(`Connected to: ${chain}`);\n console.log(`Existential deposit: ${existentialDeposit.toHuman()}`);\n const account = await api.query.system.account('INSERT_ADDRESS');\n console.log('Account data:', account.toHuman());\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);" - }, - { - "order": 4, - "action": "Run the query script", - "working_directory": "polkadot-api-demo", - "commands": [ - "npx tsx query.ts" - ], - "expected_output": "Connected to: Polkadot (or your target chain name)\nExistential deposit: 10 mDOT (or equivalent)\nAccount data: { nonce: ..., data: { free: ..., reserved: ... } }" - }, - { - "order": 5, - "action": "Create and run the transaction script", - "working_directory": "polkadot-api-demo", - "description": "Create a file named transfer.ts. Only proceed if MNEMONIC is set in .env. Replace INSERT_RECIPIENT with the destination SS58 address and INSERT_AMOUNT with the transfer amount in planck units (e.g., 1000000000000 = 1 DOT with 10 decimals, or 1000000000000000000 = 1 PAS with 18 decimals):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider, Keyring } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const keyring = new Keyring({ type: 'sr25519' });\n const sender = keyring.addFromUri(process.env.MNEMONIC!);\n console.log(`Sending from: ${sender.address}`);\n const txHash = await api.tx.balances\n .transferKeepAlive('INSERT_RECIPIENT', INSERT_AMOUNT)\n .signAndSend(sender);\n console.log(`Transaction hash: ${txHash}`);\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);\n\nThen run: npx tsx transfer.ts" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: WebSocket is not open / ECONNREFUSED", - "cause": "The WS_ENDPOINT URL is unreachable or the protocol is wrong.", - "resolution": "Verify WS_ENDPOINT in .env starts with wss:// (not https://). Check the node is online. For Polkadot Hub TestNet use wss://services.polkadothub-rpc.com/testnet." - }, - { - "pattern": "SyntaxError: Cannot use import statement in a module", - "cause": "The project is not configured as an ESM module.", - "resolution": "Run 'npm pkg set type=module' in the polkadot-api-demo directory, then retry." - }, - { - "pattern": "Error: Cannot find module '@polkadot/api'", - "cause": "Dependencies not installed.", - "resolution": "Run 'npm install @polkadot/api @polkadot/keyring dotenv' in the polkadot-api-demo directory." - }, - { - "pattern": "RpcError: 1010: Invalid Transaction: Inability to pay some fees", - "cause": "The sender account has insufficient funds to pay transaction fees.", - "resolution": "Fund the sender account with tokens. For Polkadot Hub TestNet use the faucet at https://faucet.polkadot.io/." - } - ], - "supplementary_context": { - "description": "Load these pages when the user asks about API alternatives, migration paths, or needs deeper context on chain interactions.", - "pages": [ - { - "slug": "reference-tools-papi", - "title": "Polkadot-API", - "url": "https://docs.polkadot.com/reference/tools/papi.md", - "relevance": "Polkadot API (PAPI) — the recommended replacement for @polkadot/api in new projects." - }, - { - "slug": "reference-tools-dedot", - "title": "Dedot", - "url": "https://docs.polkadot.com/reference/tools/dedot.md", - "relevance": "Dedot — modern lightweight alternative to @polkadot/api with better TypeScript inference." - }, - { - "slug": "chain-interactions-send-transactions-with-sdks", - "title": "Send Transactions with SDKs", - "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md", - "relevance": "Multi-SDK transaction guide covering PAPI, Polkadot.js, Dedot, and Subxt variants." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: query account balance", - "user_says": "Use @polkadot/api to check an account balance on Polkadot Hub", - "actions": [ - "Initialize ESM Node.js project and install @polkadot/api", - "Create .env with WS_ENDPOINT set to the Polkadot Hub TestNet WebSocket URL", - "Create query.ts with ApiPromise.create() and api.query.system.account()", - "Run npx tsx query.ts and read account balance output" - ], - "result": "Account free/reserved/frozen balances printed to console; chain name and existential deposit shown" - }, - { - "scenario": "Edge case: user is starting a new project", - "user_says": "I want to build a new Polkadot app with polkadot.js api", - "actions": [ - "Surface maintenance-mode warning: @polkadot/api is in maintenance mode", - "Recommend PAPI (reference-tools-papi) or Dedot (reference-tools-dedot) for new projects", - "If user confirms they want @polkadot/api anyway, proceed with installation steps" - ], - "result": "User informed of deprecation status; skill proceeds with @polkadot/api if that is the deliberate choice" - } - ] - }, - { - "id": "deploy-basic-contract-remix", - "title": "Deploy a Basic Smart Contract with Remix IDE", - "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" - ], - "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", - "Chain ID: 420420417" - ], - "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ (rate limit: check faucet page)" - ], - "wallet": [ - "MetaMask browser extension installed and configured for Polkadot Hub TestNet", - "Wallet funded with testnet PAS tokens" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Verify MetaMask is connected to Polkadot Hub TestNet", - "working_directory": ".", - "description": "Instruct the user: Open MetaMask and confirm the selected network is 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add the network. Also confirm the account balance shows testnet PAS tokens. If the balance is zero, visit https://faucet.polkadot.io/ to request tokens. Wait for the user to confirm both conditions before proceeding." - }, - { - "order": 2, - "action": "Open Remix IDE and locate Storage.sol", - "working_directory": ".", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed." - }, - { - "order": 3, - "action": "Compile Storage.sol in Remix", - "working_directory": ".", - "description": "Instruct the user: Click the Solidity Compiler icon (second icon in the left sidebar). Confirm the compiler version is 0.8.x or higher. Click the blue 'Compile 1_Storage.sol' button. Wait for the green checkmark indicating successful compilation. If there are errors, verify the compiler version matches the pragma in the file.", - "expected_output": "Green checkmark on the Solidity Compiler sidebar icon; no errors shown." - }, - { - "order": 4, - "action": "Deploy to Polkadot Hub TestNet via MetaMask", - "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", - "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel." - }, - { - "order": 5, - "action": "Interact with the deployed Storage contract", - "working_directory": ".", - "description": "Instruct the user: In the 'Deployed Contracts' panel, expand the deployed Storage contract. To write a value: enter a number in the field next to the 'store' button and click 'store'. Confirm the MetaMask transaction. To read the stored value: click the 'retrieve' button (no transaction needed). The return value shows in the Remix console below.", - "expected_output": "retrieve() returns the number stored in the previous store() call." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask: User denied account access / MetaMask not connected", - "cause": "MetaMask connection was rejected or MetaMask is not installed.", - "resolution": "Click the MetaMask extension icon and ensure it is unlocked. Re-select 'Injected Provider - MetaMask' in the Remix Environment dropdown and approve the connection prompt." - }, - { - "pattern": "Transaction failed: insufficient funds for gas", - "cause": "The connected MetaMask account has no testnet PAS tokens.", - "resolution": "Visit https://faucet.polkadot.io/ to obtain testnet PAS tokens. Ensure the faucet is sending to the correct address shown in MetaMask." - }, - { - "pattern": "Wrong network in MetaMask / chain ID mismatch", - "cause": "MetaMask is connected to a different network instead of Polkadot Hub TestNet.", - "resolution": "Open MetaMask and switch to 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add it." - }, - { - "pattern": "Solidity compile error: pragma mismatch", - "cause": "The Remix compiler version does not match the pragma in Storage.sol.", - "resolution": "In the Solidity Compiler tab, select a compiler version compatible with the pragma at the top of the contract file." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to set up MetaMask, obtain tokens, or move to a CLI-based deployment workflow.", - "pages": [ - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to configure MetaMask or Talisman for Polkadot Hub TestNet." - }, - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "How to obtain testnet PAS tokens for deployment transactions." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", - "title": "Deploy a Basic Contract with Hardhat", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md", - "relevance": "CLI-based alternative: deploy the same Storage contract using Hardhat and a local toolchain." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: first contract deployment", - "user_says": "Deploy a smart contract on Polkadot Hub using Remix IDE", - "actions": [ - "Verify MetaMask is on Polkadot Hub TestNet and account has PAS tokens", - "Open remix.ethereum.org and locate contracts/1_Storage.sol", - "Compile with Solidity Compiler tab", - "Select Injected Provider - MetaMask in Deploy tab and click Deploy", - "Confirm MetaMask transaction and wait for mining" - ], - "result": "Storage contract deployed; appears in Remix Deployed Contracts panel with callable store/retrieve functions" - }, - { - "scenario": "Edge case: no testnet tokens", - "user_says": "I try to deploy but MetaMask says insufficient funds", - "actions": [ - "Direct user to https://faucet.polkadot.io/ to request testnet PAS tokens", - "Ask user to confirm the faucet sent tokens to the correct address", - "Once confirmed, retry the Deploy step in Remix" - ], - "result": "User obtains testnet PAS tokens and deployment proceeds successfully" - } - ] - }, - { - "id": "connect-remix-polkadot", - "title": "Connect Remix IDE to Polkadot Hub", - "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/dev-environments/remix.md" - ], - "primary_page": "smart-contracts/dev-environments/remix.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed and unlocked with Polkadot Hub TestNet configured (chain ID 420420417)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Open Remix IDE", - "working_directory": ".", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. Wait for the IDE to fully load (the file explorer appears on the left). Ensure MetaMask is unlocked and set to Polkadot Hub TestNet before proceeding." - }, - { - "order": 2, - "action": "Navigate to the Deploy and Run Transactions tab", - "working_directory": ".", - "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left." - }, - { - "order": 3, - "action": "Select Injected Provider - MetaMask", - "working_directory": ".", - "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step." - }, - { - "order": 4, - "action": "Verify the connection", - "working_directory": ".", - "description": "Instruct the user: Confirm the following in the Deploy panel: 1) The Account field shows your MetaMask wallet address. 2) The network indicator shows chain ID 420420417 (Polkadot Hub TestNet). If the chain ID shows a different value, switch networks in MetaMask to Polkadot Hub TestNet and the Remix panel will update automatically.", - "expected_output": "Remix Deploy panel shows MetaMask account address and Polkadot Hub TestNet (chain ID 420420417)." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask not detected / No injected Web3 provider", - "cause": "MetaMask extension is not installed or is disabled in the browser.", - "resolution": "Install the MetaMask browser extension from https://metamask.io and reload Remix. Ensure the extension is enabled for the remix.ethereum.org domain." - }, - { - "pattern": "Wrong network shown in Remix (chain ID is not 420420417)", - "cause": "MetaMask is connected to a different chain.", - "resolution": "Open MetaMask and manually switch to Polkadot Hub TestNet. If Polkadot Hub TestNet is not in the list, follow the connect-wallet-polkadot-hub skill to add it." - }, - { - "pattern": "Remix Deploy panel shows 'JavaScript VM' after trying to switch", - "cause": "The injected provider selection was not saved or MetaMask was not connected.", - "resolution": "Click the Environment dropdown again and re-select 'Injected Provider - MetaMask'. Approve the MetaMask connection popup if it appears." - } - ], - "supplementary_context": { - "description": "Load these pages for wallet setup or when the user wants to deploy a contract after connecting.", - "pages": [ - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask before using Remix." - }, - { - "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-remix", - "title": "Deploy a Basic Contract with Remix IDE", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", - "relevance": "Next step after connecting Remix: deploy the default Storage.sol contract." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: connect Remix before deployment", - "user_says": "Connect Remix IDE to Polkadot Hub so I can deploy a contract", - "actions": [ - "Open https://remix.ethereum.org", - "Click Deploy and Run Transactions tab", - "Select Injected Provider - MetaMask in the Environment dropdown", - "Approve the MetaMask connection popup", - "Verify account address and chain ID 420420417 appear in the Deploy panel" - ], - "result": "Remix IDE is connected to Polkadot Hub TestNet via MetaMask and ready for contract deployment" - }, - { - "scenario": "Edge case: MetaMask on wrong network", - "user_says": "Remix shows the wrong chain ID after connecting", - "actions": [ - "Ask user to open MetaMask and switch to Polkadot Hub TestNet", - "If network not present, direct to connect-wallet-polkadot-hub skill", - "Confirm Remix updates to show chain ID 420420417 automatically" - ], - "result": "Remix shows correct network after MetaMask network switch" - } - ] - }, - { - "id": "connect-wallet-polkadot-hub", - "title": "Connect a Wallet to Polkadot Hub", - "description": "Guides the user through adding Polkadot Hub TestNet (RPC, chain ID 420420417, PAS token) to MetaMask or Talisman and switching the active network. All steps are browser extension GUI interactions. Use before deploying smart contracts, using Remix IDE with Polkadot Hub, or obtaining testnet tokens. Trigger phrases: 'connect metamask polkadot hub', 'add polkadot network metamask', 'set up wallet polkadot testnet', 'configure talisman polkadot hub'. Outcome: wallet shows Polkadot Hub TestNet as active network and is ready for on-chain interactions.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/integrations/wallets.md" - ], - "primary_page": "smart-contracts/integrations/wallets.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed (https://metamask.io) OR Talisman installed (https://talisman.xyz)" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Open MetaMask network settings", - "working_directory": ".", - "description": "Instruct the user: Click the MetaMask extension icon in the browser toolbar to open it. Click the network selector at the top of the MetaMask popup (shows 'Ethereum Mainnet' by default). Click 'Add network' at the bottom of the network list. On the Add Network page, click 'Add a network manually'." - }, - { - "order": 2, - "action": "Enter Polkadot Hub TestNet network details", - "working_directory": ".", - "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added." - }, - { - "order": 3, - "action": "Switch to Polkadot Hub TestNet", - "working_directory": ".", - "description": "Instruct the user: In MetaMask, click the network selector again. Select 'Polkadot Hub TestNet' from the list. The header now shows 'Polkadot Hub TestNet' and the balance shows PAS.", - "expected_output": "MetaMask header displays 'Polkadot Hub TestNet'; account balance shows PAS balance." - }, - { - "order": 4, - "action": "Obtain testnet PAS tokens if needed", - "working_directory": ".", - "description": "If the PAS balance is 0, instruct the user: Visit https://faucet.polkadot.io/ in the browser. Paste the MetaMask account address (click the address in MetaMask to copy it). Select 'Polkadot Hub TestNet' in the faucet network dropdown. Click 'Get tokens'. Tokens typically arrive within 30 seconds.", - "expected_output": "MetaMask PAS balance shows a non-zero amount." - }, - { - "order": 5, - "action": "Alternative: Add Polkadot Hub TestNet to Talisman", - "working_directory": ".", - "description": "If the user prefers Talisman instead of MetaMask, instruct them: Open the Talisman extension and click the settings icon. Go to Networks and Tokens, then Add Network. Enter the same network details as step 2: RPC URL: https://services.polkadothub-rpc.com/testnet, Chain ID: 420420417, Symbol: PAS. Save and switch to the Polkadot Hub TestNet network in Talisman." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Could not fetch chain ID / Invalid RPC URL", - "cause": "The RPC URL was entered incorrectly or the TestNet endpoint is temporarily unavailable.", - "resolution": "Verify the RPC URL is exactly: https://services.polkadothub-rpc.com/testnet (no trailing slash). Check the Polkadot Hub TestNet status page or Discord for outages." - }, - { - "pattern": "Chain ID mismatch: expected 420420417", - "cause": "A different endpoint was used that returns a different chain ID.", - "resolution": "Delete the incorrectly configured network from MetaMask and re-add it using the exact RPC URL and chain ID 420420417." - }, - { - "pattern": "Network added but balance shows 0 PAS", - "cause": "Account not funded with testnet tokens.", - "resolution": "Visit https://faucet.polkadot.io/, paste your address, select Polkadot Hub TestNet, and request tokens." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs network reference data or wants to proceed to contract deployment after wallet setup.", - "pages": [ - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Full network connection reference with RPC URLs, chain IDs, and WSS endpoints for all Polkadot Hub environments." - }, - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "Step-by-step guide to obtaining testnet PAS tokens from the Polkadot faucet." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub using the configured MetaMask wallet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: add Polkadot Hub TestNet to MetaMask", - "user_says": "Add Polkadot Hub TestNet to MetaMask and get some test tokens", - "actions": [ - "Open MetaMask, click network selector, click Add network, then Add a network manually", - "Enter network name, RPC URL (https://services.polkadothub-rpc.com/testnet), chain ID 420420417, symbol PAS", - "Save and switch to Polkadot Hub TestNet", - "Visit https://faucet.polkadot.io/ and request testnet PAS tokens" - ], - "result": "MetaMask shows Polkadot Hub TestNet as active network with a non-zero PAS balance" - }, - { - "scenario": "Edge case: RPC URL rejected by MetaMask", - "user_says": "MetaMask says it could not fetch the chain ID when I enter the RPC URL", - "actions": [ - "Verify the RPC URL is exactly https://services.polkadothub-rpc.com/testnet with no trailing slash", - "Check whether the Polkadot Hub TestNet is experiencing downtime", - "Retry adding the network once the endpoint is confirmed reachable" - ], - "result": "Network added successfully once the correct RPC URL is verified and the endpoint is reachable" - } - ] - }, - { - "id": "use-wagmi-polkadot-hub", - "title": "Build a Wagmi dApp Connected to Polkadot Hub", - "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo — all code is inlined in the steps.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/wagmi.md" - ], - "primary_page": "smart-contracts/libraries/wagmi.md", - "prerequisites": { - "runtime": [ - "Node.js v18+", - "npm or pnpm" - ], - "wallet": [ - "MetaMask or any EIP-1193 wallet installed in the browser" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Scaffold the Next.js project non-interactively", - "working_directory": ".", - "commands": [ - "npx create-next-app@15 wagmi-dapp --ts --no-eslint --no-tailwind --no-src-dir --app --use-npm --skip-install", - "cd wagmi-dapp", - "npm install" - ], - "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input — `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm — skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root)." - }, - { - "order": 2, - "action": "Install Wagmi v3 and peer dependencies", - "working_directory": "wagmi-dapp", - "commands": [ - "npm install wagmi viem@2.x @tanstack/react-query" - ], - "description": "Install wagmi (React hooks for Ethereum), viem v2 (low-level EVM transport), and @tanstack/react-query (required peer dependency for Wagmi v3 data-fetching hooks)." - }, - { - "order": 3, - "action": "Create the Wagmi config file with Polkadot Hub chain", - "working_directory": "wagmi-dapp", - "description": "Create a new file at src/wagmi.ts (or wagmi.ts in the project root if using the src/ layout). Paste the following content exactly:\n\n```typescript\nimport { createConfig, http } from 'wagmi';\nimport { defineChain } from 'viem';\nimport { injected } from 'wagmi/connectors';\n\nexport const polkadotHubTestnet = defineChain({\n id: 420420417,\n name: 'Polkadot Hub TestNet',\n nativeCurrency: { name: 'PAS', symbol: 'PAS', decimals: 18 },\n rpcUrls: {\n default: { http: ['https://services.polkadothub-rpc.com/testnet'] },\n },\n});\n\nexport const config = createConfig({\n chains: [polkadotHubTestnet],\n connectors: [injected()],\n transports: {\n [polkadotHubTestnet.id]: http(),\n },\n});\n```\n\nThis configures Wagmi to use Polkadot Hub TestNet as the only chain with the injected (MetaMask) connector." - }, - { - "order": 4, - "action": "Wrap the app with WagmiProvider and QueryClientProvider", - "working_directory": "wagmi-dapp", - "description": "Open src/app/layout.tsx (or pages/_app.tsx if using pages router). Add a client-side providers wrapper. Create src/app/providers.tsx with:\n\n```typescript\n'use client';\nimport { WagmiProvider } from 'wagmi';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { config } from '../wagmi';\n\nconst queryClient = new QueryClient();\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n return (\n \n \n {children}\n \n \n );\n}\n```\n\nThen wrap the body in src/app/layout.tsx with: import { Providers } from './providers'; and wrap {children} with Providers." - }, - { - "order": 5, - "action": "Add wallet connection component", - "working_directory": "wagmi-dapp", - "description": "Create src/components/ConnectWallet.tsx with the following content:\n\n```typescript\n'use client';\nimport { useAccount, useConnect, useDisconnect } from 'wagmi';\n\nexport function ConnectWallet() {\n const { address, isConnected } = useAccount();\n const { connect, connectors } = useConnect();\n const { disconnect } = useDisconnect();\n\n if (isConnected) {\n return (\n
\n

Connected: {address}

\n \n
\n );\n }\n\n return (\n
\n {connectors.map((connector) => (\n \n ))}\n
\n );\n}\n```\n\nThis renders a connect/disconnect button using the injected (MetaMask) connector." - }, - { - "order": 6, - "action": "Add block number query using useBlockNumber", - "working_directory": "wagmi-dapp", - "description": "Create src/components/BlockNumber.tsx with:\n\n```typescript\n'use client';\nimport { useBlockNumber } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nexport function BlockNumber() {\n const { data: blockNumber } = useBlockNumber({\n chainId: polkadotHubTestnet.id,\n watch: true,\n });\n return

Current block: {blockNumber?.toString() ?? 'Loading...'}

;\n}\n```\n\nThe watch: true option polls for new blocks and updates the component automatically." - }, - { - "order": 7, - "action": "Add Storage contract read interaction using useReadContract", - "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type — without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call." - }, - { - "order": 8, - "action": "Add Storage contract write interaction using useWriteContract", - "working_directory": "wagmi-dapp", - "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from ." - }, - { - "order": 9, - "action": "Assemble components in the main page and start the dev server", - "working_directory": "wagmi-dapp", - "commands": [ - "npm run dev" - ], - "description": "Open src/app/page.tsx and replace its content with:\n\n```typescript\nimport { ConnectWallet } from '../components/ConnectWallet';\nimport { BlockNumber } from '../components/BlockNumber';\nimport { StorageRead } from '../components/StorageRead';\nimport { StorageWrite } from '../components/StorageWrite';\n\nexport default function Home() {\n return (\n
\n

Wagmi Polkadot Hub dApp

\n \n \n \n \n
\n );\n}\n```\n\nThen run npm run dev to start the Next.js dev server on http://localhost:3000.", - "expected_output": "Next.js dev server running at http://localhost:3000" - }, - { - "order": 10, - "action": "Connect wallet and test contract interactions in the browser", - "working_directory": "wagmi-dapp", - "interactive": true, - "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation — approve it. After confirmation, the stored value should update to the new number.", - "expected_output": "Wallet connected, block number displayed, Storage contract read/write working" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: No connectors found / useConnect: No connector available", - "cause": "WagmiProvider not wrapping the component tree, or connectors not configured.", - "resolution": "Ensure the Providers wrapper (WagmiProvider + QueryClientProvider) wraps the entire app in layout.tsx and that the config in wagmi.ts includes the injected() connector." - }, - { - "pattern": "ChainMismatchError: Chain ID 420420417 not supported", - "cause": "The wallet is connected to a different chain than Polkadot Hub TestNet.", - "resolution": "In MetaMask, switch to the Polkadot Hub TestNet network (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill to add the network if it is not present." - }, - { - "pattern": "TypeError: Cannot read properties of undefined (reading 'toString') on blockNumber", - "cause": "useBlockNumber returned undefined before the first block is fetched.", - "resolution": "The component handles this with the null-coalescing operator (blockNumber?.toString() ?? 'Loading...'). This is expected on initial render and resolves automatically." - }, - { - "pattern": "Error: wagmi requires react-query v5 or higher", - "cause": "Incompatible @tanstack/react-query version installed.", - "resolution": "Run: npm install @tanstack/react-query@latest to ensure v5+ is installed." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs MetaMask setup, faucet tokens, or wants to use viem directly instead of Wagmi.", - "pages": [ - { - "slug": "smart-contracts-libraries-viem", - "title": "viem for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md", - "relevance": "Lower-level viem library for direct contract calls without React hooks — useful when Wagmi is not needed." - }, - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required before the wallet connection step works." - }, - { - "slug": "smart-contracts-libraries-wagmi", - "title": "Wagmi for Polkadot Hub Smart Contracts", - "url": "https://docs.polkadot.com/smart-contracts/libraries/wagmi.md", - "relevance": "Full Wagmi source page with the pre-deployed Storage contract address and complete code snippets." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: scaffold a Wagmi dApp and connect to Polkadot Hub TestNet", - "user_says": "Help me build a Next.js app with Wagmi that connects to Polkadot Hub", - "actions": [ - "Scaffold wagmi-dapp with create-next-app@15 non-interactively", - "Install wagmi, viem@2.x, and @tanstack/react-query", - "Create wagmi.ts with polkadotHubTestnet chain definition and createConfig", - "Wrap the app with WagmiProvider and QueryClientProvider in providers.tsx", - "Add ConnectWallet, BlockNumber, StorageRead, StorageWrite components", - "Start dev server and delegate browser testing to the user" - ], - "result": "Next.js app at localhost:3000 with wallet connection and live block number display" - }, - { - "scenario": "Edge case: user wants to call their own deployed contract instead of the pre-deployed Storage", - "user_says": "I deployed my own ERC-20 token — how do I call it with Wagmi useReadContract?", - "actions": [ - "Replace CONTRACT_ADDRESS in StorageRead.tsx with the user's deployed contract address", - "Replace STORAGE_ABI with the ERC-20 ABI fragment for the desired function (e.g. balanceOf)", - "Update functionName and args accordingly", - "Keep chainId: polkadotHubTestnet.id to ensure the call targets the correct network" - ], - "result": "useReadContract returns the result from the user's deployed contract on Polkadot Hub TestNet" - } - ] - }, - { - "id": "deploy-interact-contracts-web3py", - "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", - "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/libraries/web3-py.md" - ], - "primary_page": "smart-contracts/libraries/web3-py.md", - "prerequisites": { - "runtime": [ - "Python 3.9+ with pip", - "Solidity compiler (installed automatically by py-solc-x)" - ], - "network": [ - "Polkadot Hub TestNet — RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" - ], - "tokens": [ - "Small PAS balance for deployment gas — obtain from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (PRIVATE_KEY stored in .env — never enter in chat)" - ] - }, - "env_vars": [ - { - "name": "PRIVATE_KEY", - "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", - "required": true - } - ], - "steps": [ - { - "order": 1, - "action": "Create project directory and Python virtual environment", - "working_directory": ".", - "commands": [ - "mkdir web3py-contracts && cd web3py-contracts", - "python3 -m venv venv", - "source venv/bin/activate" - ], - "description": "Create a new directory named 'web3py-contracts', create a Python virtual environment inside it, and activate it. On Windows use 'venv\\Scripts\\activate' instead." - }, - { - "order": 2, - "action": "Install Python dependencies", - "working_directory": "web3py-contracts", - "commands": [ - "pip install web3 py-solc-x python-dotenv" - ], - "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary." - }, - { - "order": 3, - "action": "Create the .env file with PRIVATE_KEY", - "working_directory": "web3py-contracts", - "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore" - }, - { - "order": 4, - "action": "Create the Storage.sol Solidity contract", - "working_directory": "web3py-contracts", - "description": "Create a file named 'Storage.sol' with the following content:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ncontract Storage {\n uint256 private storedNumber;\n\n function store(uint256 num) public {\n storedNumber = num;\n }\n\n function retrieve() public view returns (uint256) {\n return storedNumber;\n }\n}\n```\n\nThis is a simple Storage contract with a store(uint256) write function and a retrieve() read function." - }, - { - "order": 5, - "action": "Create compile.py to compile the Solidity contract", - "working_directory": "web3py-contracts", - "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", - "commands": [ - "python compile.py" - ], - "expected_output": "Compilation successful — compiled.json written." - }, - { - "order": 6, - "action": "Create deploy.py to deploy the contract", - "working_directory": "web3py-contracts", - "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", - "commands": [ - "python deploy.py" - ], - "expected_output": "Deploying from account: 0x...\nContract deployed at: 0x...\nAddress saved to contract_address.txt" - }, - { - "order": 7, - "action": "Create interact.py to call store and retrieve", - "working_directory": "web3py-contracts", - "description": "Create a file named 'interact.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\naccount = w3.eth.account.from_key(PRIVATE_KEY)\n\nwith open('contract_address.txt') as f:\n contract_address = f.read().strip()\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\nabi = compiled['contracts']['Storage.sol']['Storage']['abi']\n\ncontract = w3.eth.contract(address=contract_address, abi=abi)\n\n# Read current value\nvalue = contract.functions.retrieve().call()\nprint(f'Current stored value: {value}')\n\n# Write new value\nnonce = w3.eth.get_transaction_count(account.address)\ntx = contract.functions.store(42).build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\nprint(f'store(42) tx: {receipt.transactionHash.hex()}')\n\n# Verify\nvalue = contract.functions.retrieve().call()\nprint(f'Updated stored value: {value}')\n```", - "commands": [ - "python interact.py" - ], - "expected_output": "Current stored value: 0\nstore(42) tx: 0x...\nUpdated stored value: 42" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "AssertionError: Failed to connect to RPC endpoint", - "cause": "The RPC URL is unreachable or the TestNet is down.", - "resolution": "Verify the URL https://services.polkadothub-rpc.com/testnet is correct and reachable. Check Polkadot Hub TestNet status." - }, - { - "pattern": "ValueError: insufficient funds for gas * price + value", - "cause": "The deployer account has no PAS tokens.", - "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS tokens for your account address." - }, - { - "pattern": "KeyError: 'PRIVATE_KEY'", - "cause": "The .env file is missing or PRIVATE_KEY is not defined in it.", - "resolution": "Create a .env file in the web3py-contracts directory with the line: PRIVATE_KEY=0xYOUR_KEY. Ensure python-dotenv is installed and load_dotenv() is called before accessing os.environ['PRIVATE_KEY']." - }, - { - "pattern": "SolcError: Source file requires different compiler version", - "cause": "Solidity pragma version mismatch.", - "resolution": "Ensure install_solc('0.8.20') is called in compile.py before compile_standard. The py-solc-x library downloads the compiler if not already cached." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs to understand the Polkadot Hub network params or wants to use JavaScript/TypeScript instead of Python.", - "pages": [ - { - "slug": "smart-contracts-libraries-web3-py", - "title": "Web3.py", - "url": "https://docs.polkadot.com/smart-contracts/libraries/web3-py.md", - "relevance": "Full Web3.py source page with complete code snippets and additional contract examples." - }, - { - "slug": "smart-contracts-libraries-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md", - "relevance": "Ethers.js library — recommended JavaScript alternative to the sunset Web3.js library." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Full network connection reference with RPC URL, chain ID, and WSS endpoints for Polkadot Hub." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy a Storage contract to Polkadot Hub TestNet using Python", - "user_says": "Deploy a smart contract to Polkadot Hub using Python and Web3.py", - "actions": [ - "Create web3py-contracts/ directory and Python virtualenv", - "Install web3, py-solc-x, python-dotenv via pip", - "Instruct user to add PRIVATE_KEY to .env file directly (not in chat)", - "Create Storage.sol, compile.py, deploy.py, interact.py from inline code", - "Run python compile.py then python deploy.py", - "Run python interact.py to verify store/retrieve" - ], - "result": "Contract deployed and verified on Polkadot Hub TestNet; stored value updated to 42" - }, - { - "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", - "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?", - "actions": [ - "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", - "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", - "Update deploy.py: change compiled['contracts']['YourFile.sol']['YourContract'] to match", - "Update interact.py: change the ABI extraction path and function calls to match the user's contract ABI" - ], - "result": "Custom contract compiled and deployed; interact.py calls the contract's specific functions" - } - ] - }, - { - "id": "interact-erc20-precompile-polkadot-hub", - "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", - "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/erc20.md" - ], - "primary_page": "smart-contracts/precompiles/erc20.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "PAS balance in MetaMask for transfer/approve operations — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the ERC-20 precompile address from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to the Polkadot Hub TestNet network (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet). If the network is not added yet, use the add-polkadot-hub-testnet-metamask skill to add it. Verify your PAS balance is non-zero before proceeding." - }, - { - "order": 3, - "action": "Open Remix IDE and create the ERC-20 interface file", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), create a new file named 'IERC20.sol'. Paste the standard ERC-20 interface into the file:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function balanceOf(address account) external view returns (uint256);\n function transfer(address to, uint256 amount) external returns (bool);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n```\n\nClick the Solidity Compiler tab (shield icon), set the compiler version to 0.8.20, and click 'Compile IERC20.sol'." - }, - { - "order": 4, - "action": "Connect Remix to MetaMask and load the precompile using At Address", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below." - }, - { - "order": 5, - "action": "Call balanceOf to check PAS token balance", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", - "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18" - }, - { - "order": 6, - "action": "Call approve and transfer functions", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", - "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: 'At Address' button disabled or no output", - "cause": "The IERC20.sol file was not compiled successfully before clicking At Address.", - "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." - }, - { - "pattern": "MetaMask: Wrong network — expected Custom (420420417)", - "cause": "MetaMask is still on Ethereum Mainnet or another network.", - "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." - }, - { - "pattern": "Transaction reverted: insufficient balance", - "cause": "Attempting to transfer more PAS than the account holds.", - "resolution": "Call balanceOf first to confirm the available balance, then use an amount in wei that does not exceed it." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to add MetaMask, or wants to call the precompile from a contract.", - "pages": [ - { - "slug": "smart-contracts-precompiles-erc20", - "title": "Interact with the ERC20 Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", - "relevance": "Full ERC-20 precompile source page with the exact precompile address, complete ABI, and additional usage examples." - }, - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite for this skill." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: check PAS balance via ERC-20 precompile in Remix", - "user_says": "How do I call balanceOf on the ERC-20 precompile in Remix?", - "actions": [ - "Load source page to find the ERC-20 precompile address", - "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create IERC20.sol in Remix with the standard ERC-20 interface and compile it", - "Set Environment to Injected Provider - MetaMask in Deploy and Run tab", - "Click At Address with the precompile address", - "Expand balanceOf, enter the MetaMask address, and click call" - ], - "result": "uint256 PAS balance in wei displayed in Remix console" - }, - { - "scenario": "Edge case: user wants to call the precompile from a Solidity contract", - "user_says": "How do I use the ERC-20 precompile from inside my Solidity contract?", - "actions": [ - "Load source page to get the ERC-20 precompile address", - "In the Solidity contract, import or define the IERC20 interface", - "Store the precompile address as a constant: IERC20 constant PAS_ERC20 = IERC20(PRECOMPILE_ADDRESS)", - "Call PAS_ERC20.balanceOf(msg.sender) or PAS_ERC20.transfer(to, amount) as needed", - "Deploy the wrapper contract via Remix with MetaMask connected to Polkadot Hub TestNet" - ], - "result": "Solidity contract calls the ERC-20 precompile to read balances or transfer native PAS tokens" - } - ] - }, - { - "id": "interact-storage-precompile-remix", - "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", - "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/storage.md" - ], - "primary_page": "smart-contracts/precompiles/storage.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the Storage precompile address from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance." - }, - { - "order": 3, - "action": "Create the Storage precompile interface in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'IStorage.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IStorage {\n function setBytes(bytes32 key, bytes memory value) external;\n function getBytes(bytes32 key) external view returns (bytes memory);\n}\n```\n\nGo to the Solidity Compiler tab, set the version to 0.8.20, and click 'Compile IStorage.sol'. Confirm there are no errors." - }, - { - "order": 4, - "action": "Connect Remix to MetaMask and load the precompile using At Address", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection popup. Verify the network displays 'Custom (420420417) network'. Select 'IStorage' in the Contract dropdown. Paste the Storage precompile address (from step 1) into the 'At Address' input. Click 'At Address'. An IStorage instance should appear in the Deployed Contracts section." - }, - { - "order": 5, - "action": "Call setBytes to store a value", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", - "expected_output": "Transaction confirmed; tx hash shown in Remix console" - }, - { - "order": 6, - "action": "Call getBytes to retrieve the stored value", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the IStorage instance, expand 'getBytes'. Enter the same bytes32 key used in setBytes. Click 'call' (blue button). The output displays the stored bytes value in hex. To decode: the hex 0x68656c6c6f decodes to 'hello' in UTF-8.", - "expected_output": "bytes output matching the hex value stored in setBytes" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: At Address button grayed out", - "cause": "IStorage.sol was not compiled, or the wrong contract is selected in the dropdown.", - "resolution": "Compile IStorage.sol first via the Solidity Compiler tab, then return to Deploy and Run. Ensure 'IStorage' is selected in the Contract dropdown before clicking At Address." - }, - { - "pattern": "getBytes returns 0x (empty bytes)", - "cause": "The key used in getBytes does not match the key used in setBytes, or the setBytes transaction did not confirm.", - "resolution": "Ensure the bytes32 key in getBytes is identical to the one used in setBytes. Check Remix console for the setBytes transaction hash to confirm it was mined." - }, - { - "pattern": "MetaMask: Transaction failed with out of gas", - "cause": "Gas estimate too low for setBytes with a large value.", - "resolution": "In MetaMask, increase the gas limit manually before confirming the setBytes transaction." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to call the precompile from Solidity, or needs Remix connection help.", - "pages": [ - { - "slug": "smart-contracts-precompiles-storage", - "title": "Interact with the Storage Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/storage.md", - "relevance": "Full Storage precompile source page with the exact precompile address, full ABI, and key encoding examples." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." - }, - { - "slug": "smart-contracts-integrations-wallets", - "title": "Wallets for Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md", - "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: store and retrieve bytes using the Storage precompile", - "user_says": "How do I use the Storage precompile in Remix to store a value?", - "actions": [ - "Load source page to get the Storage precompile address", - "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create IStorage.sol in Remix with setBytes/getBytes interface and compile it", - "Set Environment to Injected Provider - MetaMask, select IStorage, click At Address with the precompile address", - "Call setBytes with a bytes32 key (hex-padded) and hex-encoded bytes value", - "Call getBytes with the same key to verify the stored value" - ], - "result": "Value stored on-chain via setBytes; getBytes returns the same hex-encoded bytes" - }, - { - "scenario": "Edge case: user wants to use the Storage precompile from within a Solidity contract", - "user_says": "Can I call the Storage precompile from inside my own contract?", - "actions": [ - "Load source page to get the precompile address", - "In the user's contract, import or define the IStorage interface", - "Store the precompile address as a constant: IStorage constant STORE = IStorage(PRECOMPILE_ADDRESS)", - "Call STORE.setBytes(key, value) and STORE.getBytes(key) in contract functions", - "Deploy the wrapper contract in Remix targeting Polkadot Hub TestNet" - ], - "result": "User's contract reads and writes key-value data through the Storage precompile" - } - ] - }, - { - "id": "interact-system-precompile-remix", - "title": "Interact with the System Precompile on Polkadot Hub via Remix", - "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/system.md" - ], - "primary_page": "smart-contracts/precompiles/system.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the System precompile address from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance." - }, - { - "order": 3, - "action": "Create the System precompile interface in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'." - }, - { - "order": 4, - "action": "Connect Remix to MetaMask and load the precompile using At Address", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection. Verify the network shows 'Custom (420420417) network'. Select 'ISystem' in the Contract dropdown. Paste the System precompile address (from step 1) into 'At Address'. Click 'At Address'. An ISystem instance should appear in the Deployed Contracts section." - }, - { - "order": 5, - "action": "Call blake2b to hash data", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", - "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data" - }, - { - "order": 6, - "action": "Call verifySignature for sr25519 verification", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'verifySignature' function. To use this function you need three values:\n\n- INSERT_SIGNATURE: Replace with a real sr25519 signature as hex bytes (64 bytes, 128 hex chars). Generate a signature using a Polkadot.js signer or subkey tool: subkey sign --scheme sr25519 --suri '//Alice' --message 'your_message'\n\n- INSERT_PUBLIC_KEY: Replace with the sr25519 public key (32 bytes, 64 hex chars) corresponding to the signer.\n\n- message: The message bytes that were signed (hex-encoded).\n\nEnter the three values in the 'sig', 'pubKey', and 'message' fields respectively, then click 'call'. The function returns true if the signature is valid, false otherwise.", - "expected_output": "bool: true if the sr25519 signature is valid for the given pubKey and message" - }, - { - "order": 7, - "action": "Call accountExists to check account presence", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'accountExists' function. Enter an Ethereum-format address (0x...) in the 'account' field. Click 'call'. The function returns true if the account exists on-chain (i.e. has been funded or deployed), false if it has never received any funds or activity.", - "expected_output": "bool: true if the account exists on Polkadot Hub TestNet, false otherwise" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: call reverts on verifySignature with invalid inputs", - "cause": "The sig or pubKey bytes are not the correct length or format for sr25519.", - "resolution": "sr25519 signatures are 64 bytes (128 hex chars with 0x prefix = 130 chars total). Public keys are 32 bytes (64 hex chars). Use the subkey tool to generate a valid signature: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'. Ensure all inputs are hex-encoded with 0x prefix." - }, - { - "pattern": "At Address: no contract at address", - "cause": "Incorrect precompile address entered, or MetaMask connected to the wrong network.", - "resolution": "Re-load the source page to get the correct System precompile address. Verify MetaMask shows chain ID 420420417." - }, - { - "pattern": "ISystem.sol compilation error: function not found in interface", - "cause": "The interface in ISystem.sol may not match the exact ABI exposed by the precompile.", - "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/system.md for the authoritative ABI and update the interface accordingly." - } - ], - "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to use sr25519 signing tools, or needs Remix setup help.", - "pages": [ - { - "slug": "smart-contracts-precompiles-system", - "title": "Interact with the System Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/system.md", - "relevance": "Full System precompile source page with the exact precompile address, authoritative ABI, and all function signatures." - }, - { - "slug": "smart-contracts-dev-environments-remix", - "title": "Use the Remix IDE on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md", - "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider." - }, - { - "slug": "smart-contracts-precompiles-erc20", - "title": "Interact with the ERC20 Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md", - "relevance": "ERC-20 precompile — often used alongside the System precompile in contract interactions." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: compute a BLAKE2b hash using the System precompile in Remix", - "user_says": "How do I use the System precompile to hash data with BLAKE2b in Remix?", - "actions": [ - "Load source page to get the System precompile address", - "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create ISystem.sol in Remix with the BLAKE2 and other function signatures, then compile", - "Set Environment to Injected Provider - MetaMask, select ISystem, click At Address with the precompile address", - "Expand blake2b, enter hex-encoded input data (e.g. 0x68656c6c6f for 'hello'), click call", - "Record the BLAKE2b hash output" - ], - "result": "BLAKE2b hash of the input bytes returned as hex in the Remix console" - }, - { - "scenario": "Edge case: user wants to verify an sr25519 signature from a Polkadot account", - "user_says": "Can I verify a Polkadot sr25519 signature on-chain using the System precompile?", - "actions": [ - "Generate a test signature using subkey: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'", - "Note the 64-byte signature hex and the 32-byte Alice public key hex", - "In Remix, expand verifySignature on the ISystem instance", - "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", - "Click call and confirm the result is true" - ], - "result": "bool true returned — sr25519 signature verified on-chain via the System precompile" - } - ] - }, - { - "id": "interact-xcm-precompile-remix", - "title": "Interact with the XCM Precompile via Remix IDE", - "description": "Guides you through creating an IXcm Solidity interface in Remix, loading the XCM precompile, and calling weighMessage, execute, and send functions. Use when you need to test XCM message dispatch from a smart contract, estimate XCM execution weight, or send cross-chain messages from Polkadot Hub via Solidity. All interaction is browser-based using Remix IDE with MetaMask. Trigger phrases: 'XCM precompile', 'send XCM from contract', 'weighMessage', 'xcm execute', 'cross-chain from Solidity', 'IXcm interface'. Output: XCM weight estimates and on-chain transaction receipts.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/precompiles/xcm.md" - ], - "primary_page": "smart-contracts/precompiles/xcm.md", - "prerequisites": { - "runtime": [ - "Modern web browser (Chrome or Firefox recommended)" - ], - "wallet": [ - "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" - ], - "network": [ - "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" - ], - "tokens": [ - "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Find the XCM precompile address and IXcm ABI from the source page", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI." - }, - { - "order": 2, - "action": "Connect MetaMask to Polkadot Hub TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance for gas fees." - }, - { - "order": 3, - "action": "Create the IXcm interface in Remix and compile it", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors." - }, - { - "order": 4, - "action": "Load the XCM precompile using At Address in Remix", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section." - }, - { - "order": 5, - "action": "Call weighMessage to estimate XCM execution cost", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", - "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost" - }, - { - "order": 6, - "action": "Call execute to run an XCM message locally", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", - "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs" - }, - { - "order": 7, - "action": "Call send to dispatch an XCM to another chain", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", - "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Remix: transaction reverted on execute or send", - "cause": "SCALE-encoded XCM bytes are malformed or the weight limit is too low.", - "resolution": "Verify the encoding against the example on the source page. Use weighMessage first to obtain the correct maxWeight. Ensure the correct precompile address is used for TestNet." - }, - { - "pattern": "At Address: no contract at address", - "cause": "Incorrect precompile address entered, or MetaMask is connected to the wrong network.", - "resolution": "Reload the source page to get the authoritative XCM precompile address. Verify MetaMask shows chain ID 420420417." - }, - { - "pattern": "IXcm.sol compilation error: function not found", - "cause": "The interface definition does not match the ABI the precompile exposes.", - "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md for the authoritative ABI and update IXcm.sol accordingly." - }, - { - "pattern": "MetaMask: insufficient funds for gas", - "cause": "PAS balance too low to cover gas on Polkadot Hub TestNet (base fee: 1000 gwei).", - "resolution": "Obtain PAS tokens from https://faucet.polkadot.io/. Ensure the account has at least 0.01 PAS before submitting state-changing calls." - } - ], - "supplementary_context": { - "description": "Load these pages for XCM encoding details, precompile addresses, and cross-chain messaging concepts.", - "pages": [ - { - "slug": "smart-contracts-precompiles-xcm", - "title": "Interact with the XCM Precompile", - "url": "https://docs.polkadot.com/smart-contracts/precompiles/xcm.md", - "relevance": "Authoritative XCM precompile address, IXcm interface definition, and SCALE-encoded message examples." - }, - { - "slug": "reference-tools-xcm-tools", - "title": "XCM Tools", - "url": "https://docs.polkadot.com/reference/tools/xcm-tools.md", - "relevance": "XCM tooling for encoding, debugging, and monitoring message delivery." - }, - { - "slug": "smart-contracts-connect", - "title": "Connect to Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/connect.md", - "relevance": "Polkadot Hub TestNet network details and RPC endpoints." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: estimate XCM execution fee before sending", - "user_says": "How much will it cost to send an XCM message from my contract on Polkadot Hub?", - "actions": [ - "Load XCM precompile address and IXcm ABI from source page", - "Create and compile IXcm.sol in Remix", - "Load precompile via At Address", - "Call weighMessage with encoded destination and message bytes", - "Read the returned weight estimate" - ], - "result": "Weight estimate (refTime and proofSize) indicating the execution cost on the destination chain" - }, - { - "scenario": "Edge case: execute call reverts due to malformed SCALE encoding", - "user_says": "My XCM execute call keeps reverting in Remix", - "actions": [ - "Compare SCALE-encoded message bytes against the source page example", - "Call weighMessage first to confirm the message decodes correctly", - "Check that maxWeight is at least the refTime returned by weighMessage", - "If revert persists, reload IXcm.sol ABI from source page to confirm interface matches precompile" - ], - "result": "Root cause identified (malformed encoding or wrong interface); corrected message executes successfully" - } - ] - }, - { - "id": "deploy-parachain-to-polkadot-testnet", - "title": "Deploy a Parachain to the Polkadot TestNet (Paseo)", - "description": "Covers the end-to-end parachain launch workflow on Paseo TestNet: obtain PAS tokens, reserve a para ID via Polkadot.js Apps, generate collator keys with subkey (Docker), build plain and raw chain specs with chain-spec-builder, export genesis wasm and state with polkadot-omni-node, register a parathread on-chain, generate a node key, start the collator, and insert session keys via RPC. Use this skill after completing the set-up-parachain-template skill. Trigger phrases: 'deploy parachain', 'register parachain Paseo', 'launch parachain TestNet', 'reserve para ID', 'parachain registration'. Part of the three-page launch series: set-up-the-parachain-template to deploy-to-polkadot to obtain-coretime.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "parachains/launch-a-parachain/deploy-to-polkadot.md" - ], - "primary_page": "parachains/launch-a-parachain/deploy-to-polkadot.md", - "prerequisites": { - "runtime": [ - "Polkadot SDK parachain template built in release mode (see set-up-parachain-template skill)", - "Rust and Cargo installed", - "Docker installed (for subkey key generation)", - "polkadot-omni-node binary: cargo install polkadot-omni-node", - "chain-spec-builder binary: cargo install chain-spec-builder" - ], - "network": [ - "Paseo TestNet — wss://rpc.ibp.network/paseo", - "Server with TCP port 30333 open for P2P collator connections" - ], - "tokens": [ - "PAS tokens from https://faucet.polkadot.io/ — required for para ID reservation and parathread registration fees" - ], - "wallet": [ - "Polkadot.js browser extension (https://polkadot.js.org/extension/) with a PAS-funded account" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Obtain PAS tokens from the faucet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://faucet.polkadot.io/, select the 'Paseo' network, paste your SS58 account address, and submit. The faucet delivers 500 PAS per request. Verify the balance in Polkadot.js Apps before proceeding." - }, - { - "order": 2, - "action": "Reserve a para ID on Paseo TestNet", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID — record it as PARA_ID (needed in all subsequent steps).", - "expected_output": "Para ID assigned and visible in the Parathreads tab" - }, - { - "order": 3, - "action": "Generate collator session keys using subkey (Docker)", - "working_directory": ".", - "commands": [ - "docker run --rm -it parity/subkey generate --scheme sr25519", - "docker run --rm -it parity/subkey generate --scheme ed25519" - ], - "description": "Run both commands. The first produces an SR25519 key (used for Aura block production); the second produces an ED25519 key (used for GRANDPA finality). Record both seed phrases and Account IDs securely. Never share the seed phrases. Save SR25519 Account ID as AURA_KEY and ED25519 Account ID as GRANDPA_KEY." - }, - { - "order": 4, - "action": "Build the plain chain spec", - "working_directory": "parachain-template", - "commands": [ - "chain-spec-builder create --relay-chain paseo --para-id INSERT_PARA_ID --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm named-preset development" - ], - "description": "Replace INSERT_PARA_ID with the value of PARA_ID from step 2 (e.g., 2000). This produces 'chain_spec.json'. Then open 'chain_spec.json' and make these edits: (1) Set the 'id' field to a unique string (e.g., 'my-parachain-paseo'). (2) Insert AURA_KEY and GRANDPA_KEY (from step 3) into the 'aura' and 'grandpa' genesis sections. (3) Confirm 'para_id' equals PARA_ID. Save the file." - }, - { - "order": 5, - "action": "Build the raw chain spec", - "working_directory": "parachain-template", - "commands": [ - "polkadot-omni-node build-spec --chain chain_spec.json --raw > chain_spec_raw.json" - ], - "description": "Converts the human-readable chain spec to SCALE-encoded raw format. Verify 'chain_spec_raw.json' was created and is non-empty.", - "expected_output": "chain_spec_raw.json created (non-empty JSON file)" - }, - { - "order": 6, - "action": "Export genesis wasm and state", - "working_directory": "parachain-template", - "commands": [ - "polkadot-omni-node export-genesis-wasm --chain chain_spec_raw.json genesis_wasm", - "polkadot-omni-node export-genesis-state --chain chain_spec_raw.json genesis_state" - ], - "description": "Produces 'genesis_wasm' (runtime Wasm bytecode) and 'genesis_state' (initial chain state). Both files are required for the on-chain registration in step 7." - }, - { - "order": 7, - "action": "Register the parathread on-chain", - "working_directory": ".", - "interactive": true, - "description": "Delegate to the user: In Polkadot.js Apps (connected to Paseo), navigate to Network > Parachains > Parathreads. Find your reserved PARA_ID and click 'Register'. In the modal, upload 'genesis_wasm' for 'Genesis WASM' and 'genesis_state' for 'Initial state'. Submit and sign. After finalization, the status changes from 'Reserved' to 'Onboarding'. Registration takes effect at the next epoch (up to 600 blocks on Paseo).", - "expected_output": "Parathread status changes to 'Onboarding' in the Parathreads tab" - }, - { - "order": 8, - "action": "Generate a P2P node key for the collator", - "working_directory": "parachain-template", - "commands": [ - "polkadot-omni-node generate-node-key --file node-key.dat" - ], - "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure — losing it changes the peer ID on next restart." - }, - { - "order": 9, - "action": "Start the collator node", - "working_directory": "parachain-template", - "commands": [ - "./target/release/parachain-template-node --collator --chain chain_spec_raw.json --node-key-file node-key.dat --port 30333 --rpc-port 9944 --rpc-methods=Unsafe -- --chain paseo --sync warp" - ], - "description": "The '--' separator splits collator arguments from the embedded relay chain (Paseo) arguments. '--sync warp' enables fast warp sync for Paseo. The collator first syncs the relay chain (several minutes). Wait for log messages containing 'Parachain synced' before inserting session keys in step 10." - }, - { - "order": 10, - "action": "Insert session keys into the collator keystore", - "working_directory": ".", - "commands": [ - "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"aura\",\"INSERT_AURA_SEED_PHRASE\",\"INSERT_AURA_PUBLIC_KEY_HEX\"],\"id\":1}'", - "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"gran\",\"INSERT_GRANDPA_SEED_PHRASE\",\"INSERT_GRANDPA_PUBLIC_KEY_HEX\"],\"id\":1}'" - ], - "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history — they contain seed phrases. Run them directly in the server terminal.", - "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1} for each key insertion" - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "chain-spec-builder: cannot find runtime Wasm file", - "cause": "The parachain was not built in release mode or the Wasm path is incorrect.", - "resolution": "Run 'cargo build --release' in the parachain-template directory. The Wasm artifact is at: target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm" - }, - { - "pattern": "Parathread registration fails: 'ParaAlreadyExists' or 'NotRegistrar'", - "cause": "The para ID was not properly reserved, or the registration account differs from the manager.", - "resolution": "Verify that PARA_ID was successfully reserved in step 2 using the correct account. Use the same account for the registration in step 7." - }, - { - "pattern": "Collator log: 'Relay chain does not contain our parachain'", - "cause": "Parathread registration has not finalized on-chain, or chain_spec_raw.json has the wrong para_id.", - "resolution": "Verify the Parathreads tab shows 'Onboarding' for PARA_ID. Confirm chain_spec_raw.json contains the correct para_id. Registration takes up to one epoch (600 blocks on Paseo)." - }, - { - "pattern": "author_insertKey: 'Method not found'", - "cause": "The collator was started without '--rpc-methods=Unsafe'.", - "resolution": "Stop the collator, add '--rpc-methods=Unsafe' to the startup command, and restart. Never expose unsafe RPC methods to external network interfaces." - } - ], - "supplementary_context": { - "description": "Load these pages for template setup, coretime, and local test network guidance.", - "pages": [ - { - "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md", - "relevance": "Required prerequisite: build the parachain template locally before deploying to TestNet." - }, - { - "slug": "parachains-launch-a-parachain-obtain-coretime", - "title": "Obtain Coretime", - "url": "https://docs.polkadot.com/parachains/launch-a-parachain/obtain-coretime.md", - "relevance": "Next step: obtain on-demand or bulk coretime to activate parachain block production." - }, - { - "slug": "parachains-testing-run-a-parachain-network", - "title": "Run a Parachain Network", - "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md", - "relevance": "Test the parachain locally with Zombienet before deploying to TestNet." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: first deployment to Paseo after building the template", - "user_says": "I built the parachain template — how do I deploy it to Paseo TestNet?", - "actions": [ - "Get PAS tokens from faucet", - "Reserve a para ID via Polkadot.js Apps", - "Generate Aura (SR25519) and GRANDPA (ED25519) collator keys with subkey Docker", - "Build plain chain spec, embed keys, build raw chain spec, export genesis data", - "Register parathread on-chain via Polkadot.js Apps", - "Generate node key, start collator, insert session keys via RPC" - ], - "result": "Parachain registered on Paseo TestNet with status 'Onboarding'; collator is running and syncing" - }, - { - "scenario": "Edge case: collator shows 0 peers after launch", - "user_says": "My collator started but shows 0 peers and is not syncing the relay chain", - "actions": [ - "Verify port 30333 is open on the server firewall", - "Confirm the startup command includes the '-- --chain paseo' relay chain section", - "Add a known Paseo bootnode with '--bootnodes /dns/...' to the startup flags", - "Wait 10 minutes for peer discovery — initial warp sync connection can be slow" - ], - "result": "Collator finds relay chain peers and begins syncing; block production starts after parathread onboards" - } - ] - }, - { - "id": "connect-polkadot-hub-testnet", - "title": "Connect to Polkadot Hub and Get Test Tokens", - "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/connect.md" - ], - "primary_page": "smart-contracts/connect.md", - "prerequisites": { - "wallet": [ - "MetaMask browser extension installed (https://metamask.io/download/)" - ], - "network": [ - "Internet access to Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", - "Internet access to the faucet: https://faucet.polkadot.io/" - ] - }, - "env_vars": [], - "steps": [ - { - "order": 1, - "action": "Add Polkadot Hub TestNet to MetaMask", - "working_directory": ".", - "description": "In MetaMask, open Settings > Networks > Add a network > Add a network manually. Enter these parameters: Network name: 'Polkadot Hub TestNet'; New RPC URL: 'https://services.polkadothub-rpc.com/testnet'; Chain ID: '420420417'; Currency symbol: 'PAS'. For the Block Explorer URL, check https://docs.polkadot.com/smart-contracts/connect/ for the current explorer URL, as it may change. Click Save and switch MetaMask to this network. This step must be delegated to the user." - }, - { - "order": 2, - "action": "Request PAS test tokens from the faucet", - "working_directory": ".", - "description": "Navigate to https://faucet.polkadot.io/. Select 'Polkadot Hub TestNet', paste your 0x-prefixed EVM address, and click the request button. The faucet credits PAS tokens within seconds. Rate limit: approximately 500 PAS per 24 hours per address. This step must be delegated to the user." - }, - { - "order": 3, - "action": "Verify MetaMask balance", - "working_directory": ".", - "description": "In MetaMask, confirm the PAS balance is non-zero on the Polkadot Hub TestNet network. To verify programmatically: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"YOUR_ADDRESS\",\"latest\"],\"id\":1}'. Replace YOUR_ADDRESS with the 0x-prefixed address. A non-zero hex result confirms the account is funded." - } - ], - "reference_code": { - "repo": "none", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "MetaMask: 'Invalid chain ID' or cannot connect to network", - "cause": "Incorrect chain ID or RPC URL entered in MetaMask network settings.", - "resolution": "Verify the chain ID is exactly 420420417 (decimal) and the RPC URL is https://services.polkadothub-rpc.com/testnet. Remove and re-add the network if the error persists." - }, - { - "pattern": "Faucet rate limit: 'Already requested recently'", - "cause": "The address already received tokens within the 24-hour rate limit window.", - "resolution": "Wait 24 hours, or use a different development wallet address." - }, - { - "pattern": "RPC endpoint returns errors or is unreachable", - "cause": "TestNet may be under maintenance, or the public RPC endpoint URL has changed.", - "resolution": "Check https://docs.polkadot.com/smart-contracts/connect/ for the latest endpoint URLs. The WSS alternative is wss://services.polkadothub-rpc.com/testnet." - } - ], - "supplementary_context": { - "description": "Load these pages for development framework configuration that uses the TestNet endpoints established here.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Hardhat network configuration using the TestNet RPC URL and chain ID provided by this skill." - }, - { - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md", - "relevance": "Detailed faucet workflow for obtaining PAS test tokens." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: set up development environment from scratch", - "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?", - "actions": [ - "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", - "Request PAS tokens from https://faucet.polkadot.io/", - "Verify funded balance in MetaMask" - ], - "result": "MetaMask connected to Polkadot Hub TestNet with funded PAS balance, ready for smart contract deployment" - }, - { - "scenario": "Edge case: looking up MainNet connection parameters", - "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?", - "actions": [ - "Reference the MainNet network parameters from the source page", - "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" - ], - "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage" - } - ] - }, - { - "id": "store-retrieve-data-bulletin-chain", - "title": "Store and Retrieve Data on the Bulletin Chain", - "description": "Stores a file on the Polkadot Bulletin Chain using PAPI TypeScript and retrieves it by CID via the IPFS gateway. Use when you need decentralized IPFS-compatible file storage for dApp assets, NFT metadata, or files under 8 MiB. Requires a Polkadot account authorized via the Bulletin Chain Console UI faucet (no programmatic self-authorization). Trigger phrases: 'store file Bulletin Chain', 'PAPI TransactionStorage store', 'Polkadot decentralized storage', 'Bulletin Chain IPFS'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "chain-interactions/store-data/bulletin-chain.md" - ], - "primary_page": "chain-interactions/store-data/bulletin-chain.md", - "prerequisites": { - "runtime": [ - "Node.js v18 or later", - "npm" - ], - "network": [ - "Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" - ], - "tokens": [ - "Bulletin Chain storage authorization (not DOT/PAS) — obtained from Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/. The authorize_account extrinsic requires Root origin; self-authorization is not available programmatically." - ], - "wallet": [ - "A Polkadot account (SS58) with active Bulletin Chain authorization, plus its 12-word mnemonic for signing" - ] - }, - "env_vars": [ - { - "name": "MNEMONIC", - "description": "12-word mnemonic of the authorized Polkadot account. Do NOT ask user to paste mnemonic in chat — they must edit .env directly.", - "required": true - } - ], - "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── (generated Bulletin Chain descriptors)\n├── store-data.ts\n├── .env\n├── .gitignore\n├── node_modules/\n└── package.json", - "steps": [ - { - "order": 1, - "action": "Authorize account via Bulletin Chain Console UI", - "working_directory": ".", - "description": "This step requires manual browser interaction — delegate to the user.\n\n1. Open https://paritytech.github.io/polkadot-bulletin-chain/ and click Connect to link a wallet (Polkadot.js, Talisman, SubWallet, or Fearless).\n2. Go to Faucet > Storage Faucet tab.\n3. Under Authorize Account, enter the number of Transactions and Bytes needed, click Authorize Account, and approve in the wallet extension.\n4. Verify authorization on the Accounts tab — note the expiration block number.\n\nNote: Authorization expires at a block. Once expired, data cannot be renewed without re-authorizing." - }, - { - "order": 2, - "action": "Create and initialize the project", - "working_directory": ".", - "commands": [ - "mkdir bulletin-store-example", - "cd bulletin-store-example", - "npm init -y", - "npm pkg set type=module" - ], - "description": "Create a new Node.js ESM project. The ESM module type is required for polkadot-api and hdkd packages." - }, - { - "order": 3, - "action": "Install dependencies", - "working_directory": "bulletin-store-example", - "commands": [ - "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats dotenv tsx" - ], - "description": "Install polkadot-api (typed chain client), hdkd and hdkd-helpers (key derivation), multiformats (CID decoding), dotenv (secure mnemonic loading), and tsx (TypeScript runner)." - }, - { - "order": 4, - "action": "Add Bulletin Chain metadata", - "working_directory": "bulletin-store-example", - "commands": [ - "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" - ], - "expected_output": "Descriptors generated successfully", - "description": "Connect to the Bulletin Chain RPC, download chain metadata, and generate typed descriptors in .papi/. These descriptors provide compile-time type safety for all pallet interactions." - }, - { - "order": 5, - "action": "Create .env with mnemonic placeholder", - "working_directory": "bulletin-store-example", - "commands": [ - "printf 'MNEMONIC=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty MNEMONIC placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in MNEMONIC with the 12-word mnemonic of the authorized account. Do NOT ask for the mnemonic in chat. Wait for user confirmation before proceeding." - }, - { - "order": 6, - "action": "Create store-data.ts", - "working_directory": "bulletin-store-example", - "reference_file": "store-data.ts", - "description": "Fetch the reference file and save as store-data.ts. Then apply these modifications:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Remove `DEV_PHRASE` from the `@polkadot-labs/hdkd-helpers` import (keep `entropyToMiniSecret` and `mnemonicToEntropy`).\n3. Change `mnemonicToEntropy(DEV_PHRASE)` to `mnemonicToEntropy(process.env.MNEMONIC as string)`.\n4. Replace `'INSERT_IMAGE_PATH'` with the path to the file the user wants to store (ask the user for this path before running).\nSave the file." - }, - { - "order": 7, - "action": "Run the store script", - "working_directory": "bulletin-store-example", - "commands": [ - "npx tsx store-data.ts" - ], - "expected_output": "Image stored successfully! CID: bafk2bzace...", - "description": "Run the script to submit the file to the Bulletin Chain. On success it prints the block hash, transaction index, CID, and an IPFS gateway URL. Save the CID and (block, index) pair — needed to retrieve or renew the stored data." - } - ], - "reference_code": { - "repo": "polkadot-developers/polkadot-docs", - "branch": "master", - "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", - "files": [ - { - "path": "store-data.ts", - "description": "PAPI TypeScript script that connects to Bulletin Chain, reads a local file, and submits it via TransactionStorage.store. Uses DEV_PHRASE by default — replace with process.env.MNEMONIC as instructed in step 6." - } - ] - }, - "error_patterns": [ - { - "pattern": "Error: account has no authorization", - "cause": "The signing account has no active authorization on the Bulletin Chain, or it has expired.", - "resolution": "Re-authorize via the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ before retrying." - }, - { - "pattern": "file too large / data exceeds per-transaction limit", - "cause": "File exceeds the ~8 MiB per-transaction size limit.", - "resolution": "Split into chunks under 8 MiB and submit multiple store transactions, recording (block, index) per chunk." - }, - { - "pattern": "MNEMONIC is undefined / Cannot derive key from undefined", - "cause": ".env not populated or dotenv not loaded.", - "resolution": "Verify .env contains MNEMONIC= and that `import 'dotenv/config';` is the first line of store-data.ts." - }, - { - "pattern": "WebSocket connection failed / ECONNREFUSED wss://paseo-bulletin-rpc.polkadot.io", - "cause": "Network issue or RPC temporarily unavailable.", - "resolution": "Check internet connectivity and retry after a few minutes. Monitor the Polkadot Discord for outage announcements." - } - ], - "supplementary_context": { - "description": "Load when the user asks about Bulletin Chain architecture, data retention, retrieval methods (P2P vs IPFS gateway), or the renewal workflow.", - "pages": [ - { - "slug": "reference-polkadot-hub-data-storage", - "title": "Data Storage", - "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage.md", - "relevance": "Technical reference for TransactionStorage pallet extrinsics, storage size limits, retention period, retrieval methods (IPFS gateway, P2P Helia), and renewal mechanics." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: store an image for an NFT collection", - "user_says": "Store my NFT artwork on the Polkadot Bulletin Chain and get its IPFS CID", - "actions": [ - "Authorize Polkadot account via Console UI faucet", - "Set up Node.js ESM project and install polkadot-api + hdkd + dotenv", - "Run npx papi add bulletin to generate chain descriptors", - "Create .env with MNEMONIC, modify store-data.ts to use process.env.MNEMONIC", - "Set FILE_PATH to the image path, run npx tsx store-data.ts", - "Receive CID for use in NFT metadata" - ], - "result": "File stored on-chain; CID like bafk2bzacea6wlxy... retrievable at https://paseo-ipfs.polkadot.io/ipfs/" - }, - { - "scenario": "Edge case: stored data approaches expiry", - "user_says": "My stored data is about to expire — how do I renew it?", - "actions": [ - "Retrieve the (block, index) pair from when the data was stored", - "Confirm expiration block has not yet passed (check Bulletin Chain Explorer)", - "Call TransactionStorage.renew({block, index}) using PAPI with the same authorized signer", - "Record new (block, index) from the Renewed event for future renewals" - ], - "result": "Retention timer reset; original CID remains valid for another full retention period" - } - ] - }, - { - "id": "deploy-uniswap-v2-core-pvm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", - "description": "Clones the polkavm-hardhat-examples repo, compiles Uniswap V2 Factory and Pair contracts to the Polkadot Virtual Machine (PVM) using the Hardhat Polkadot plugin and resolc compiler, and deploys to Polkadot Hub TestNet. Use when deploying a DEX AMM factory on Polkadot Hub with PVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 PVM', 'UniswapV2Factory PVM Polkadot', 'deploy AMM Polkadot Hub PVM', 'polkavm-hardhat-examples uniswap'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", - "prerequisites": { - "runtime": [ - "Node.js v16 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account (AH_PRIV_KEY)" - ] - }, - "env_vars": [ - { - "name": "AH_PRIV_KEY", - "description": "0x-prefixed EVM private key for the account deploying to Polkadot Hub TestNet (passetHub network). Must be funded with testnet PAS. Do NOT ask user to paste key in chat — they must edit .env directly.", - "required": true - }, - { - "name": "LOCAL_PRIV_KEY", - "description": "0x-prefixed EVM private key for local testing. For local-only use, Alice's dev key (0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133) is the default in the config. Not required for TestNet deployment.", - "required": false - } - ], - "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the polkavm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", - "cd polkavm-hardhat-examples/uniswap-v2-polkadot" - ], - "description": "Clone the repository and navigate to the Uniswap V2 project. The hardhat.config.js already uses dotenv (require('dotenv').config()) and process.env variables — no Hardhat vars conversion needed." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npm install" - ], - "description": "Install all project dependencies including @parity/hardhat-polkadot and the resolc PVM compiler." - }, - { - "order": 3, - "action": "Create .env with private key placeholders", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env" - ], - "description": "Create .env with empty placeholders. The .gitignore already includes .env. Stop here and ask the user to edit .env directly — fill in AH_PRIV_KEY with the 0x-prefixed private key of the account that will deploy to Polkadot Hub TestNet. Do NOT ask for the key in chat. For LOCAL_PRIV_KEY, Alice's dev key is the default in hardhat.config.js for local testing. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Compile the contracts", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 1 Solidity file successfully", - "description": "Compile the Uniswap V2 contracts to PVM bytecode using the Hardhat Polkadot plugin and the resolc compiler. Compiled artifacts (ABI and PVM bytecode) appear in the artifacts/ directory." - }, - { - "order": 5, - "action": "Deploy to Polkadot Hub TestNet", - "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot", - "commands": [ - "npx hardhat run scripts/deploy.js --network passetHub" - ], - "expected_output": "Factory deployed to : 0x...", - "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet via the passetHub network config. The script outputs the deployed addresses — save them, especially the Factory address, which is needed for creating liquidity pairs.\n\nNote: The documentation shows `--network polkadotHubTestNet` but the actual network name in hardhat.config.js is `passetHub`." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account not funded with testnet PAS, or network base fee (1000 gwei on Polkadot Hub TestNet) exceeds the transaction gas price.", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. If gas issues persist, add gasPrice: 5000000000000 (5000 gwei) to the passetHub network block in hardhat.config.js." - }, - { - "pattern": "Error: AH_PRIV_KEY is undefined / invalid private key", - "cause": ".env file not populated or not loaded by dotenv.", - "resolution": "Verify .env exists in uniswap-v2-polkadot/ and contains AH_PRIV_KEY=0x. The config uses require('dotenv').config() automatically." - }, - { - "pattern": "Error: network passetHub not found / unknown network", - "cause": "Using wrong --network flag; documentation shows polkadotHubTestNet but the config defines passetHub.", - "resolution": "Use --network passetHub instead of --network polkadotHubTestNet." - }, - { - "pattern": "Compilation error: resolc not found / PVM compiler unavailable", - "cause": "The resolc compiler is not installed or not accessible.", - "resolution": "Run npm install to reinstall @parity/hardhat-polkadot and its bundled resolc compiler. Verify package.json includes the plugin." - } - ], - "supplementary_context": { - "description": "Load when the user asks about PVM vs EVM differences, local development setup, or testing the deployed contracts.", - "pages": [ - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local Polkadot development node and ETH-RPC adapter to test the Uniswap V2 PVM contracts locally before deploying to TestNet." - }, - { - "slug": "smart-contracts-dev-environments-hardhat-polkadot", - "title": "Set Up Hardhat with the Polkadot Plugin", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot.md", - "relevance": "Configure the @parity/hardhat-polkadot plugin and resolc compiler for PVM smart contract development." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using PVM", - "user_says": "Deploy Uniswap V2 on Polkadot Hub using PVM", - "actions": [ - "Clone polkavm-hardhat-examples and cd uniswap-v2-polkadot", - "Run npm install", - "Create .env with AH_PRIV_KEY for testnet deployment", - "Run npx hardhat compile to compile to PVM bytecode", - "Run npx hardhat run scripts/deploy.js --network passetHub", - "Save deployed Factory and Pair addresses" - ], - "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet with PVM bytecode" - }, - { - "scenario": "Edge case: deployment fails with insufficient funds", - "user_says": "My Uniswap V2 deployment is failing with insufficient funds", - "actions": [ - "Confirm account has testnet PAS at https://faucet.polkadot.io/", - "If gas price issue, add gasPrice: 5000000000000 to passetHub config in hardhat.config.js", - "Retry deployment with npx hardhat run scripts/deploy.js --network passetHub" - ], - "result": "Deployment succeeds after funding the account and optionally raising gas price" - } - ] - }, - { - "id": "deploy-uniswap-v2-core-evm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Core contracts (Factory and Pair) with standard Hardhat and TypeScript, converts Hardhat vars to dotenv for agent-compatible key management, and deploys to Polkadot Hub TestNet via the EVM execution path. Use when deploying a DEX AMM factory on Polkadot Hub using standard EVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 EVM Polkadot', 'UniswapV2Factory EVM Polkadot Hub', 'deploy AMM Polkadot REVM'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout b0a8627059a9d9cb759682310219557550186bc4", - "cd uniswap-v2-core-hardhat" - ], - "description": "Clone the repository, check out the pinned commit (tested configuration), and navigate to the Uniswap V2 Core project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies, then additionally install dotenv. dotenv is required to replace Hardhat's interactive vars system with a .env-based private key approach usable in agent shells." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key for the testnet deployer account. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee) to prevent 'Priority is too low' errors.\n5. Confirm `ignition.requiredConfirmations` is 1 (not 0) — zero-confirmation only works on local dev nodes with instant finality.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 3 Solidity files successfully", - "description": "Compile UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair (Solidity 0.5.16). Artifacts (ABI and EVM bytecode) appear in the artifacts/ directory." - }, - { - "order": 6, - "action": "Deploy to Polkadot Hub TestNet", - "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat", - "commands": [ - "npx hardhat run scripts/deploy.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV2Factory deployed to: 0x...", - "description": "Deploy UniswapV2Factory, two test ERC-20 tokens, and create a trading pair. The script outputs all deployed addresses. Save the Factory address — needed for creating additional pairs and for the Periphery (Router) deployment." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." - }, - { - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses vars.get() instead of dotenv.", - "resolution": "Ensure `import 'dotenv/config';` is the first line of hardhat.config.ts and replace vars.get('TESTNET_PRIVATE_KEY') with process.env.TESTNET_PRIVATE_KEY." - }, - { - "pattern": "TESTNET_PRIVATE_KEY is undefined", - "cause": ".env not populated or not in the correct directory.", - "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ and contains TESTNET_PRIVATE_KEY=0x." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of deployment — missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user asks about the V2 Periphery Router deployment, local testing, or Hardhat configuration for Polkadot.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", - "title": "Uniswap V2 Periphery with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", - "relevance": "Next step: deploy the Uniswap V2 Router (WETH9, Router02) on top of the deployed V2 Core." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local Polkadot development node to run the test suite against a local node before TestNet deployment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using EVM", - "user_says": "Deploy Uniswap V2 on Polkadot Hub using EVM Hardhat", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY", - "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", - "Run npx hardhat compile", - "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", - "Save Factory, TokenA, TokenB, and Pair addresses" - ], - "result": "UniswapV2Factory, two test ERC-20 tokens, and a trading pair deployed to Polkadot Hub TestNet via EVM" - }, - { - "scenario": "Edge case: deployment stuck with IGN401 Transaction dropped", - "user_says": "Ignition says my V2 Core deployment transaction was dropped", - "actions": [ - "Verify gasPrice: 5000000000000 is set in polkadotTestnet network config", - "Delete ignition/deployments/ directory", - "Confirm TESTNET_PRIVATE_KEY account still has PAS balance", - "Redeploy with npx hardhat run scripts/deploy.ts --network polkadotTestnet" - ], - "result": "Clean deployment after resolving gas configuration and clearing stale Ignition state" - } - ] - }, - { - "id": "deploy-uniswap-v2-periphery-evm", - "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Periphery contracts (WETH9, Router02) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when adding the Router layer (liquidity, swaps, slippage protection) on top of Uniswap V2 Core. V2 Core Solidity dependency is a local file reference — no pre-deployed core required. Trigger phrases: 'deploy Uniswap V2 Router EVM Polkadot', 'UniswapV2Router02 Polkadot Hub', 'Uniswap periphery Polkadot REVM'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", - "cd uniswap-v2-periphery-hardhat" - ], - "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V2 Periphery project. The sibling directory uniswap-v2-core-hardhat/ (at the same commit) is automatically used for the local V2 Core Solidity dependency." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies including the local V2 Core reference (resolved automatically from ../uniswap-v2-core-hardhat/). Then install dotenv to replace Hardhat's interactive vars system." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee).\n5. Confirm `ignition.requiredConfirmations` is 1.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 5 Solidity files successfully", - "description": "Compile the Uniswap V2 Periphery contracts using a multi-compiler setup (Solidity 0.5.16 for V2 Core dependency and 0.6.6 for Router contracts). Artifacts appear in the artifacts/ directory." - }, - { - "order": 6, - "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", - "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", - "interactive": true, - "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 using Hardhat Ignition. Ignition will prompt to confirm the target network name and chain ID — delegate this confirmation to the user. After confirmation, contracts are deployed in two batches (Factory+WETH9 in parallel, then Router02). Save all three deployed addresses." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." - }, - { - "pattern": "Error: Cannot find module '@uniswap/v2-core' / ENOENT uniswap-v2-core-hardhat", - "cause": "The local V2 Core Solidity dependency (../../uniswap-v2-core-hardhat) is not accessible. Likely cloning only the periphery subdirectory or checking out different commits for each.", - "resolution": "Ensure the full revm-hardhat-examples repo is cloned and both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ are at the same commit. Run npm install from uniswap-v2-periphery-hardhat/." - }, - { - "pattern": "vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat vars.get() instead of dotenv.", - "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost transaction state — missing gasPrice or requiredConfirmations: 0.", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1, then redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user asks about the V2 Core prerequisite, testing the Router, or V3 upgrades.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", - "title": "Uniswap V2 Core with EVM on Polkadot", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", - "relevance": "V2 Core tutorial covering Factory and Pair deployment — the Periphery Router builds on top of the same Core contracts included as a local dependency." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local development node to run the test suite against localNode before TestNet deployment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V2 Periphery Router to Polkadot Hub TestNet", - "user_says": "Deploy the Uniswap V2 Router on Polkadot Hub EVM", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY", - "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy with --network polkadotTestnet, confirm when prompted", - "Save WETH9, Factory, and Router02 addresses" - ], - "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet" - }, - { - "scenario": "Edge case: npm install fails with missing V2 Core module", - "user_says": "npm install is failing because it cannot find @uniswap/v2-core", - "actions": [ - "Verify the full repo is cloned (both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ must exist)", - "Ensure git checkout used the same commit for the whole repo (a871364...)", - "Re-run npm install from uniswap-v2-periphery-hardhat/ — the local file reference resolves to ../uniswap-v2-core-hardhat/" - ], - "result": "npm install succeeds and @uniswap/v2-core is resolved from the sibling directory" - } - ] - }, - { - "id": "deploy-uniswap-v3-core-evm", - "title": "Deploy Uniswap V3 Core on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Core contracts (UniswapV3Factory) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. V3 config requires bytecodeHash set to none to keep Factory under the 24KB EIP-170 contract size limit. Use when deploying a concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 EVM Polkadot', 'UniswapV3Factory Polkadot Hub', 'concentrated liquidity Polkadot REVM'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── NoDelegateCall.sol\n │ ├── UniswapV3Factory.sol\n │ ├── UniswapV3Pool.sol\n │ └── UniswapV3PoolDeployer.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", - "cd uniswap-v3-core-hardhat" - ], - "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Core project." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies then add dotenv to replace Hardhat's interactive vars system with .env-based private key management." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei).\n5. Confirm `ignition.requiredConfirmations` is 1.\n6. Preserve the existing `bytecodeHash: 'none'` in the Solidity compiler settings — this is required to keep UniswapV3Factory under the EIP-170 24KB contract size limit.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 5 Solidity files successfully", - "description": "Compile UniswapV3Factory, UniswapV3Pool, UniswapV3PoolDeployer, NoDelegateCall, and math libraries (Solidity 0.7.6). The bytecodeHash: 'none' setting in the Solidity config is critical — without it, UniswapV3Factory exceeds the EIP-170 24KB contract size limit." - }, - { - "order": 6, - "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", - "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", - "interactive": true, - "description": "Deploy UniswapV3Factory using Hardhat Ignition. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save the deployed Factory address — it is the entry point for creating V3 pools and is needed for the Periphery (SwapRouter, NonfungiblePositionManager) deployment." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: contract code too large / Contract creation code storage out of gas", - "cause": "bytecodeHash is not set to 'none', causing UniswapV3Factory to exceed the EIP-170 24KB limit.", - "resolution": "In hardhat.config.ts under the Solidity compiler settings, ensure metadata: { bytecodeHash: 'none' } is present. Recompile after the fix." - }, - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." - }, - { - "pattern": "vars is not defined / Cannot read properties of undefined", - "cause": "hardhat.config.ts still uses Hardhat vars.get().", - "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost transaction state.", - "resolution": "Delete ignition/deployments/, verify gas config (gasPrice: 5000000000000, requiredConfirmations: 1), and redeploy." - } - ], - "supplementary_context": { - "description": "Load when the user asks about V3 Periphery (SwapRouter, NFPM) deployment, local testing, or concentrated liquidity mechanics.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", - "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", - "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager on top of the deployed V3 Factory." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Set up a local development node to run the 187-test V3 suite before TestNet deployment." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy Uniswap V3 Core to Polkadot Hub TestNet", - "user_says": "Deploy Uniswap V3 on Polkadot Hub EVM", - "actions": [ - "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY", - "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice, preserve bytecodeHash: 'none'", - "Run npx hardhat compile", - "Run npx hardhat ignition deploy UniswapV3Factory.ts --network polkadotTestnet, confirm when prompted", - "Save deployed UniswapV3Factory address" - ], - "result": "UniswapV3Factory deployed to Polkadot Hub TestNet — entry point for creating V3 concentrated-liquidity pools" - }, - { - "scenario": "Edge case: compilation fails with contract too large", - "user_says": "Hardhat compilation fails saying UniswapV3Factory is too large", - "actions": [ - "Check hardhat.config.ts Solidity compiler settings for the existence of metadata: { bytecodeHash: 'none' }", - "If missing, add it under settings: { optimizer: {...}, metadata: { bytecodeHash: 'none' } }", - "Recompile with npx hardhat compile" - ], - "result": "Compilation succeeds after setting bytecodeHash: 'none' to exclude metadata hash and keep contract under the 24KB EIP-170 limit" - } - ] - }, - { - "id": "deploy-uniswap-v3-periphery-evm", - "title": "Deploy Uniswap V3 Periphery on Polkadot Hub (EVM)", - "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Periphery contracts (SwapRouter, NonfungiblePositionManager) with Hardhat, converts Hardhat vars to dotenv, and deploys all four contracts (UniswapV3Factory, WETH9, SwapRouter, NonfungiblePositionManager) to Polkadot Hub TestNet in a single Hardhat Ignition run. The V3 Core contracts are resolved automatically from a local sibling package reference. Use when building a full-stack concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Periphery Polkadot', 'SwapRouter NonfungiblePositionManager Polkadot Hub', 'Uniswap V3 full deployment EVM Polkadot'.", - "version": "1.0.0", - "chain_role": "isolated", - "invocation": "user", - "workflow_pattern": "sequential", - "license": "CC-BY-4.0", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" - ], - "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", - "prerequisites": { - "runtime": [ - "Node.js v22 or later", - "npm", - "Git" - ], - "network": [ - "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" - ], - "tokens": [ - "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" - ], - "wallet": [ - "An EVM private key (0x-prefixed) for a funded testnet account" - ] - }, - "env_vars": [ - { - "name": "TESTNET_PRIVATE_KEY", - "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", - "required": true - } - ], - "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-periphery-hardhat/\n ├── contracts/\n │ ├── SwapRouter.sol\n │ ├── NonfungiblePositionManager.sol\n │ ├── NonfungibleTokenPositionDescriptor.sol\n │ ├── base/\n │ ├── interfaces/\n │ ├── lens/\n │ ├── libraries/\n │ └── test/\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Periphery.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n ├── package.json\n └── tsconfig.json", - "steps": [ - { - "order": 1, - "action": "Clone the revm-hardhat-examples repository", - "working_directory": ".", - "commands": [ - "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", - "cd revm-hardhat-examples", - "git checkout 96696ad15c3cf01b9168a71ad5114f27c34a8726", - "cd uniswap-v3-periphery-hardhat" - ], - "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Periphery project. The monorepo layout is critical: the Periphery project depends on V3 Core contracts through a local file reference (`\"@uniswap/v3-core\": \"file:../uniswap-v3-core-hardhat\"`), so both sibling directories must be present." - }, - { - "order": 2, - "action": "Install dependencies", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "npm install", - "npm install dotenv" - ], - "description": "Install project dependencies. `npm install` resolves the local `@uniswap/v3-core` sibling package automatically — no separate step is needed. Then add `dotenv` to replace Hardhat's interactive vars system with .env-based private key management." - }, - { - "order": 3, - "action": "Create .env with private key placeholder", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", - "echo '.env' >> .gitignore" - ], - "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding." - }, - { - "order": 4, - "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from \"hardhat/config\";` to `import { HardhatUserConfig } from \"hardhat/config\";`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet base fee).\n5. Confirm `ignition.requiredConfirmations` is 1 (already set).\n6. Preserve `bytecodeHash: \"none\"` in the Solidity compiler settings — required so the compiled UniswapV3Pool bytecode hash matches the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol, enabling correct CREATE2 pool address derivation.\n7. Preserve `allowUnlimitedContractSize: true` for the hardhat network — several Periphery contracts exceed the 24KB EIP-170 limit and require this for local testing.\nSave the file." - }, - { - "order": 5, - "action": "Compile the contracts", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "npx hardhat compile" - ], - "expected_output": "Compiled 39 Solidity files successfully", - "description": "Compile SwapRouter, NonfungiblePositionManager, and all supporting periphery contracts (Solidity 0.7.6). The `bytecodeHash: 'none'` setting ensures the Pool bytecode hash matches PoolAddress.sol's hardcoded constant, which is required for CREATE2 pool address computation to work correctly during swaps and LP operations." - }, - { - "order": 6, - "action": "Deploy all contracts to Polkadot Hub TestNet via Hardhat Ignition", - "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat", - "commands": [ - "npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" - ], - "expected_output": "UniswapV3PeripheryModule#UniswapV3Factory deployed at 0x...\nUniswapV3PeripheryModule#WETH9 deployed at 0x...\nUniswapV3PeripheryModule#SwapRouter deployed at 0x...\nUniswapV3PeripheryModule#NonfungiblePositionManager deployed at 0x...", - "interactive": true, - "description": "Deploy all four contracts using Hardhat Ignition. The module deploys UniswapV3Factory and WETH9 in the first batch (in parallel), then SwapRouter and NonfungiblePositionManager once their dependencies are ready. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save all four deployed addresses: they are needed for any downstream swap or LP interaction." - } - ], - "reference_code": { - "repo": "none", - "branch": "", - "base_path": "", - "files": [] - }, - "error_patterns": [ - { - "pattern": "Error: insufficient funds / Priority is too low", - "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", - "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." - }, - { - "pattern": "vars is not defined / Cannot read properties of undefined (reading 'has')", - "cause": "hardhat.config.ts still uses Hardhat vars.get() or vars.has().", - "resolution": "Add `import 'dotenv/config';` as the first line and replace `vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`." - }, - { - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "cause": "Ignition lost track of a pending transaction due to TestNet latency or gas underpricing.", - "resolution": "Delete `ignition/deployments/`, verify gasPrice is 5000000000000 and requiredConfirmations is 1, then redeploy. If the deployment receipt exists in deployed_addresses.json but code is missing at the address, wait and verify via eth_getCode before retrying." - }, - { - "pattern": "POOL_INIT_CODE_HASH mismatch / pool address computation incorrect", - "cause": "bytecodeHash is not set to 'none', causing the compiled Pool bytecode to differ from the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol.", - "resolution": "In hardhat.config.ts under Solidity compiler settings, ensure `metadata: { bytecodeHash: 'none' }` is present. Recompile after the fix." - }, - { - "pattern": "Error: cannot estimate gas / contract deployment failed during testing", - "cause": "The hardhat network is missing allowUnlimitedContractSize: true, causing Periphery contracts that exceed 24KB to fail during local testing.", - "resolution": "In hardhat.config.ts, ensure `networks.hardhat.allowUnlimitedContractSize = true` is set. This flag only applies to the in-process Hardhat network used for testing; it is not needed for TestNet deployment." - } - ], - "supplementary_context": { - "description": "Load when the user asks about V3 Core as a prerequisite, testing against a local development node, or the Hardhat environment setup.", - "pages": [ - { - "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", - "title": "Uniswap V3 Core with EVM on Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", - "relevance": "The V3 Core contracts (UniswapV3Factory, UniswapV3Pool) are resolved automatically via local npm reference — no separate Core tutorial step required, but this page explains the Factory/Pool architecture the Periphery builds on." - }, - { - "slug": "smart-contracts-dev-environments-local-dev-node", - "title": "Local Development Node", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md", - "relevance": "Required for running the 39-test suite (SwapRouter and NonfungiblePositionManager tests) against a local Polkadot node via `npx hardhat test --network localNode` before TestNet deployment." - }, - { - "slug": "smart-contracts-dev-environments-hardhat", - "title": "Use Hardhat with Polkadot Hub", - "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md", - "relevance": "Reference for Hardhat network configuration, Ignition deployment options, and gas settings specific to Polkadot Hub." - } - ] - }, - "examples": [ - { - "scenario": "Common scenario: deploy all Uniswap V3 Periphery contracts to Polkadot Hub TestNet", - "user_says": "Deploy Uniswap V3 SwapRouter and NonfungiblePositionManager to Polkadot Hub", - "actions": [ - "Clone revm-hardhat-examples, check out pinned commit, cd uniswap-v3-periphery-hardhat", - "Run npm install && npm install dotenv", - "Create .env with TESTNET_PRIVATE_KEY placeholder, ask user to fill it in", - "Convert hardhat.config.ts: add dotenv import, replace vars.get with process.env, add gasPrice: 5000000000000", - "Run npx hardhat compile (expects 39 Solidity files compiled successfully)", - "Run npx hardhat ignition deploy UniswapV3Periphery.ts --network polkadotTestnet, confirm when prompted", - "Save deployed addresses for UniswapV3Factory, WETH9, SwapRouter, and NonfungiblePositionManager" - ], - "result": "All four Uniswap V3 Periphery contracts deployed to Polkadot Hub TestNet — SwapRouter for token swaps and NonfungiblePositionManager for concentrated liquidity LP positions" - }, - { - "scenario": "Edge case: deployment fails with IGN401 or Transaction Already Imported", - "user_says": "Ignition reports the transaction was dropped and retrying gives 'Transaction Already Imported'", - "actions": [ - "Do not retry at the same gas price — that will fail again with the same error", - "Check ignition/deployments/ and deployed_addresses.json for any partial state", - "Verify the contract is not already deployed: run eth_getCode at any addresses in deployed_addresses.json", - "If not deployed, delete the ignition/deployments/ directory", - "Confirm gasPrice: 5000000000000 and requiredConfirmations: 1 are set in hardhat.config.ts", - "Re-run: npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" - ], - "result": "Deployment completes cleanly after removing stale Ignition state and confirming gas configuration" - } - ] - } - ], - "outputs": { - "public_root": "/ai/", - "skills_dir": "skills" + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md" + } + ] + }, + "title": "Deploy an ERC-20 Token Using Hardhat", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up a Hardhat project using the @parity/hardhat-polkadot plugin and the resolc compiler to compile Solidity to PolkaVM (PVM) bytecode. Use when targeting Polkadot Hub's PVM runtime rather than the standard EVM runtime. Covers project initialization, resolc installation, dotenv private key setup, TestNet config with gasPrice, and a compile-verify step. Trigger phrases: 'Hardhat PVM', 'hardhat-polkadot plugin', 'compile to PolkaVM', 'resolc Hardhat', 'PVM smart contract Polkadot'. Do NOT use this skill for standard EVM deployments; use set-up-hardhat-evm instead.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "The Polkadot plugin or resolc compiler was not installed.", + "pattern": "Error: Cannot find module '@parity/hardhat-polkadot' / resolc not found", + "resolution": "Run: npm install --save-dev @parity/hardhat-polkadot @parity/resolc in the project directory." + }, + { + "cause": "The resolc compiler version does not support the contract's Solidity pragma version.", + "pattern": "Compilation fails with resolc errors / unsupported Solidity version", + "resolution": "Check the @parity/resolc version installed and the Solidity pragma in the contract. Update the version field in hardhat.config.ts resolc settings to match what is installed, or downgrade the Solidity pragma in the contract to a supported version." + }, + { + "cause": "hardhat.config.ts uses Hardhat's vars.get() system instead of dotenv.", + "pattern": "Error: vars is not defined", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + }, + { + "cause": "The ignition config has requiredConfirmations: 0, which causes Ignition to misinterpret pending transactions as dropped on local nodes that don't produce blocks instantly.", + "pattern": "Deployment hangs on local node / requiredConfirmations: 0", + "resolution": "Set ignition.requiredConfirmations: 1 in hardhat.config.ts." + }, + { + "cause": "macOS Gatekeeper quarantines downloaded binaries for dev-node and eth-rpc adapter.", + "pattern": "Binary permission issues on macOS (binary is quarantined)", + "resolution": "Run: xattr -d com.apple.quarantine /path/to/binary. Also ensure executable permission: chmod +x /path/to/binary." + } + ], + "examples": [ + { + "actions": [ + "Create hardhat-pvm-example/ and run npm init -y", + "Install @parity/hardhat-polkadot, @parity/resolc, dotenv", + "Run npx hardhat-polkadot init wizard (delegate to user)", + "Run npm install and add ignition/deployments/ to .gitignore", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Fetch and configure hardhat.config.ts: add dotenv, replace vars.get, add gasPrice 5000 gwei, requiredConfirmations: 1", + "Run npx hardhat compile to verify setup" + ], + "result": "PVM Hardhat project compiles successfully; ready to deploy contracts to Polkadot Hub", + "scenario": "Common scenario: set up a PVM Hardhat project from scratch", + "user_says": "Set up Hardhat with the Polkadot PVM plugin" + }, + { + "actions": [ + "Run: xattr -d com.apple.quarantine /path/to/dev-node", + "Run: chmod +x /path/to/dev-node", + "Restart the local node" + ], + "result": "Binary executes without quarantine errors", + "scenario": "Edge case: user is on macOS and binary is quarantined", + "user_says": "The dev-node binary is blocked by macOS Gatekeeper" + } + ], + "id": "set-up-hardhat-pvm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "runtime": [ + "Node.js v22.5+ and npm v10.9.0+ (required for @parity/hardhat-polkadot compatibility)", + "npm or pnpm or yarn" + ], + "tokens": [ + "Testnet PAS tokens — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/dev-environments/hardhat-polkadot.md", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/dev-environments/hardhat-polkadot.md" + ], + "steps": [ + { + "action": "Create project directory and initialize npm", + "commands": [ + "mkdir hardhat-pvm-example && cd hardhat-pvm-example", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install the Polkadot Hardhat plugin and resolc compiler", + "commands": [ + "npm install --save-dev @parity/hardhat-polkadot", + "npm install --save-dev @parity/resolc", + "npm install dotenv" + ], + "description": "Install the @parity/hardhat-polkadot plugin (provides the Hardhat task runner for PVM), the @parity/resolc compiler (compiles Solidity to PVM bytecode), and dotenv (for secure private key loading). Do not pin specific versions unless the docs page specifies them — use latest.", + "order": 2, + "working_directory": "hardhat-pvm-example" + }, + { + "action": "Initialize the Hardhat PVM project structure", + "commands": [ + "npx hardhat-polkadot init" + ], + "description": "Run the project creation wizard. This is an interactive command — delegate it to the user. The wizard creates the contracts/, ignition/, and test/ directories and places a sample contract and deployment module. After the wizard completes, instruct the user to confirm which sample files were created before proceeding.", + "interactive": true, + "order": 3, + "working_directory": "hardhat-pvm-example" + }, + { + "action": "Install all project dependencies after init", + "commands": [ + "npm install", + "echo '/ignition/deployments/' >> .gitignore" + ], + "description": "Install any additional dependencies added by the init wizard, then add ignition/deployments/ to .gitignore to avoid committing deployment state to version control.", + "order": 4, + "working_directory": "hardhat-pvm-example" + }, + { + "action": "Create .env file and .gitignore entry for private key", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for a testnet account funded with PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding.", + "order": 5, + "working_directory": "hardhat-pvm-example" + }, + { + "action": "Fetch and configure hardhat.config.ts for TestNet", + "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file.", + "order": 6, + "working_directory": "hardhat-pvm-example" + }, + { + "action": "Compile the sample contract to verify setup", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile the sample contract generated by the init wizard. Successful compilation confirms resolc is working and the project structure is correct. The compiled artifacts appear in artifacts/contracts/. If compilation fails with resolc errors, verify @parity/resolc is installed and the version matches what the hardhat.config.ts specifies in the resolc field.", + "expected_output": "Compiled ... Solidity file(s) successfully", + "order": 7, + "working_directory": "hardhat-pvm-example" + } + ], + "supplementary_context": { + "description": "Load when the user wants to deploy a contract after setting up the PVM environment, or needs to understand PVM vs EVM differences.", + "pages": [ + { + "relevance": "Next step: deploy a basic contract once the Hardhat PVM environment is set up.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + }, + { + "relevance": "Understand the differences between EVM and PVM to choose the right compilation target.", + "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", + "title": "EVM vs PVM", + "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md" + } + ] + }, + "title": "Set Up Hardhat with the Polkadot Plugin (PVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Initializes a Hardhat v2 TypeScript project for the standard Polkadot Hub EVM runtime (not PVM). Configures the polkadotTestnet network with correct RPC URL, chain ID (420420417), gasPrice (5000 gwei), and dotenv-based private key management. Optionally configures Blockscout or Routescan contract verification. Use when setting up a reusable EVM development environment before deploying any contract. Trigger phrases: 'set up Hardhat for Polkadot EVM', 'configure Hardhat Polkadot Hub', 'Hardhat EVM Polkadot setup', 'Hardhat network config Polkadot'. Do NOT use for PVM; use set-up-hardhat-pvm instead.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key used in the polkadotTestnet accounts array. Fill before running any deployment. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "hardhat.config.ts still uses Hardhat's vars.get() system.", + "pattern": "Error: vars is not defined", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + }, + { + "cause": "The vars import is present but vars is undefined because the interactive vars store was never populated.", + "pattern": "HardhatError: HH8: There's one or more errors in your config / Cannot read properties of undefined (reading 'get')", + "resolution": "Remove 'import { vars } from \"hardhat/config\";' and all vars.get() calls. Replace with process.env.PRIVATE_KEY loaded by dotenv." + }, + { + "cause": "gasPrice is missing or below the TestNet base fee of 1000 gwei.", + "pattern": "Error: insufficient funds for intrinsic transaction cost on deployment", + "resolution": "Add gasPrice: 5000000000000 to the polkadotTestnet network block in hardhat.config.ts. Also ensure the deployer account has testnet PAS from https://faucet.polkadot.io/." + }, + { + "cause": "TypeScript dependencies were not installed.", + "pattern": "Cannot find module 'ts-node' / TypeScript compilation error on npx hardhat", + "resolution": "Run: npm install --save-dev typescript ts-node @types/node in the project directory." + } + ], + "examples": [ + { + "actions": [ + "Create hardhat-example/ and run npm init -y", + "Install hardhat@^2.27.0, hardhat-toolbox, typescript, ts-node, dotenv", + "Create contracts/, ignition/modules/, test/ directories and set .gitignore", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Fetch hardhat.config.ts from cookbook; add dotenv import, replace vars.get, add gasPrice 5000 gwei, add ignition.requiredConfirmations: 1" + ], + "result": "Hardhat project configured for Polkadot Hub TestNet; ready for contract development and deployment", + "scenario": "Common scenario: configure a Hardhat project for Polkadot Hub from scratch", + "user_says": "Set up a Hardhat project for Polkadot Hub EVM" + }, + { + "actions": [ + "Open hardhat.config.ts", + "Add 'import \"dotenv/config\";' as the first line", + "Replace all vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string", + "Remove the 'import { vars } from \"hardhat/config\";' line", + "Create .env with PRIVATE_KEY= and add .env to .gitignore" + ], + "result": "Project loads private key from .env; works in agent shells and CI without interactive prompts", + "scenario": "Edge case: existing project uses Hardhat vars and breaks in CI or agent shell", + "user_says": "My Hardhat config uses vars.get and it fails with 'vars is not defined'" + } + ], + "id": "set-up-hardhat-evm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Node.js LTS (18.x, 20.x, or 22.x — even major version numbers only)", + "npm, pnpm, or yarn" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account — needed before deploying contracts" + ] + }, + "primary_page": "smart-contracts/dev-environments/hardhat.md", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/dev-environments/hardhat.md" + ], + "steps": [ + { + "action": "Create project directory and initialize npm", + "commands": [ + "mkdir hardhat-example && cd hardhat-example", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project. Do NOT run npx hardhat init at this stage — the interactive init wizard is non-deterministic across Hardhat v2/v3 transitions. All configuration will be set up manually in the following steps.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Hardhat and dependencies", + "commands": [ + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", + "npm install dotenv" + ], + "description": "Install Hardhat v2, the Hardhat Toolbox (includes ethers.js, chai, and testing utilities), TypeScript support, and dotenv. The dotenv package replaces Hardhat's interactive 'npx hardhat vars set' mechanism, which cannot be used in agent shells.", + "order": 2, + "working_directory": "hardhat-example" + }, + { + "action": "Create project directory structure", + "commands": [ + "mkdir -p contracts ignition/modules test", + "echo '/ignition/deployments/' >> .gitignore", + "echo '/artifacts/' >> .gitignore", + "echo '/cache/' >> .gitignore" + ], + "description": "Create the standard Hardhat directory structure and add common output directories to .gitignore.", + "order": 3, + "working_directory": "hardhat-example" + }, + { + "action": "Create .env file and .gitignore entry for private key", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with a PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key. For initial setup without a deployment, leaving PRIVATE_KEY empty is acceptable — the project will compile but deployment commands will fail until it is set. Do NOT ask for the key in chat.", + "order": 4, + "working_directory": "hardhat-example" + }, + { + "action": "Fetch and configure hardhat.config.ts", + "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file.", + "order": 5, + "working_directory": "hardhat-example" + } + ], + "supplementary_context": { + "description": "Load when the user is ready to deploy a contract or wants to verify a deployed contract.", + "pages": [ + { + "relevance": "Next step: deploy a basic Storage contract using the configured Hardhat project.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + }, + { + "relevance": "Deploy an ERC-20 token using the configured Hardhat environment.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", + "title": "Deploy an ERC-20 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" + } + ] + }, + "title": "Set Up Hardhat for Polkadot Hub (EVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Queries Polkadot Hub on-chain data via curl against the Substrate API Sidecar REST service. Covers account balance-info, asset-balances (including USDT/USDC), asset metadata, and block endpoints using Parity's public Sidecar instance. Use when you need account balances, asset info, or block data without writing SDK code. Also covers running a local Sidecar instance via npm or Docker for production use. Trigger phrases: 'query with Sidecar', 'Sidecar REST API', 'curl Polkadot balance', 'query chain data REST', 'Substrate API Sidecar'. No private key required — read-only operations only.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Sidecar is not running (only applies when using the local instance).", + "pattern": "ECONNREFUSED 127.0.0.1:8080", + "resolution": "Start Sidecar in a separate terminal: SAS_SUBSTRATE_URL=wss://polkadot-asset-hub-rpc.polkadot.io substrate-api-sidecar. Wait for 'SAS listening' before sending curl requests. If using the public endpoint, this error does not apply." + }, + { + "cause": "The Sidecar endpoint is temporarily unavailable or the URL is incorrect.", + "pattern": "curl: (6) Could not resolve host / Connection reset / 502 Bad Gateway", + "resolution": "Verify the base URL. If using the public endpoint, check https://status.polkadot.network for outages and retry after a few minutes. If using a local instance, verify the WebSocket endpoint URL and that the Sidecar process is running." + }, + { + "cause": "The queried account has no on-chain entry — it has never received any tokens.", + "pattern": "balance-info returns all-zero values for free/reserved/nonce", + "resolution": "Fund the account at https://faucet.polkadot.io/ and wait 1-2 minutes for confirmation. Re-query the endpoint." + }, + { + "cause": "The account does not hold the specified asset, or the asset ID is wrong.", + "pattern": "asset-balances returns empty items array", + "resolution": "Verify the asset ID. USDT on Polkadot Hub is asset ID 1984. The account must hold the asset for a balance to appear." + } + ], + "examples": [ + { + "actions": [ + "Use Parity's public Sidecar at https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io", + "Run curl against /accounts/{address}/balance-info", + "Divide the free field value by 10^10 to convert planck to DOT/PAS", + "Check the transferable field for the actual spendable balance" + ], + "result": "Account nonce and full balance breakdown (free, reserved, frozen, transferable) returned as JSON", + "scenario": "Common scenario: query account balance via REST without writing code", + "user_says": "Query the balance of a Polkadot Hub address using the REST API" + }, + { + "actions": [ + "Run curl against https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984", + "If items[] is non-empty, the account holds USDT (asset ID 1984)", + "Check the balance field in the response item (in smallest USDT unit, 6 decimals)" + ], + "result": "USDT balance for the account returned; empty items array if account holds no USDT", + "scenario": "Edge case: query USDT asset balance for an account", + "user_says": "How do I check if an account holds USDT on Polkadot Hub?" + } + ], + "id": "query-chain-data-sidecar-rest", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Parity public Sidecar endpoint: https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io (no setup required)", + "OR: WebSocket RPC endpoint for a Polkadot Hub node (if running Sidecar locally)" + ], + "runtime": [ + "curl (pre-installed on macOS and Linux; use WSL on Windows)", + "jq (optional, for formatted JSON output — install with: apt install jq or brew install jq)", + "Node.js v18+ and npm (only if running Sidecar locally)" + ] + }, + "primary_page": "chain-interactions/query-data/query-rest.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "chain-interactions/query-data/query-rest.md" + ], + "steps": [ + { + "action": "Choose your Sidecar endpoint", + "description": "You have two options:\n\n**Option A — Public endpoint (recommended for testing):** Use Parity's hosted Sidecar at `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io`. No setup required; skip to step 2.\n\n**Option B — Local Sidecar:** Run your own instance for production or high-frequency queries.\n\n```bash\n# Install globally\nnpm install -g @substrate/api-sidecar\n\n# Start with Polkadot Hub mainnet endpoint\nSAS_SUBSTRATE_URL=wss://polkadot-asset-hub-rpc.polkadot.io substrate-api-sidecar\n```\n\nWait for `SAS listening on http://127.0.0.1:8080/` before proceeding. If using the local instance, replace the base URL in all following steps with `http://127.0.0.1:8080`.", + "order": 1, + "working_directory": "." + }, + { + "action": "Query account balance", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/balance-info\" | jq ." + ], + "description": "Query the balance-info endpoint. Replace `INSERT_SS58_ADDRESS` with the target SS58 address. Response fields: `free` — spendable planck; `reserved` — locked/staked; `frozen` — frozen amounts; `transferable` — actual balance available to transfer (new field); `nonce` — transaction count. Divide raw planck values by 10^10 to convert to DOT/PAS. Omit `| jq .` if jq is not installed.", + "expected_output": "{\"at\":{\"hash\":\"0x...\",\"height\":\"12345\"},\"nonce\":\"0\",\"tokenSymbol\":\"DOT\",\"free\":\"...\",\"reserved\":\"...\",\"frozen\":\"...\",\"transferable\":\"...\"}", + "order": 2, + "working_directory": "." + }, + { + "action": "Query asset balances", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984\" | jq ." + ], + "description": "Query asset balances for asset ID 1984 (USDT) held by the account. Replace `INSERT_SS58_ADDRESS` with the target SS58 address. Replace `1984` with any registered asset ID (e.g., `1337` for USDC). To query multiple assets in one request, append additional `assets[]=` parameters: `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/accounts/INSERT_SS58_ADDRESS/asset-balances?assets[]=1984&assets[]=1337`. If the account holds no USDT, `items` will be an empty array.", + "expected_output": "{\"at\":{...},\"items\":[{\"assetId\":\"1984\",\"balance\":\"...\",\"isFrozen\":false,\"isSufficient\":false}]}", + "order": 3, + "working_directory": "." + }, + { + "action": "Query asset metadata", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/pallets/assets/storage/Metadata?keys[]=1984\" | jq ." + ], + "description": "Query metadata for asset ID 1984 (USDT). The `name` and `symbol` fields are returned as hex-encoded strings: `0x5465746865722055534454` decodes to 'Tether USD', `0x55534474` decodes to 'USDt'. Replace `1984` with any asset ID. For asset details (owner, supply, isSufficient), use `https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/pallets/assets/storage/Asset?keys[]=1984`.", + "expected_output": "{\"at\":{...},\"pallet\":\"assets\",\"storageItem\":\"Metadata\",\"keys\":[\"1984\"],\"value\":{\"deposit\":\"...\",\"name\":\"0x5465746865722055534454\",\"symbol\":\"0x55534474\",\"decimals\":\"6\",\"isFrozen\":false}}", + "order": 4, + "working_directory": "." + }, + { + "action": "Query block information", + "commands": [ + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/blocks/head\" | jq .", + "curl -s \"https://polkadot-asset-hub-public-sidecar.parity-chains.parity.io/blocks/INSERT_BLOCK_NUMBER\" | jq ." + ], + "description": "The first command fetches the latest finalized block. The second fetches a specific block by number — replace `INSERT_BLOCK_NUMBER` with any block number. The response includes block number, hash, parentHash, stateRoot, extrinsicsRoot, and extrinsics list.", + "expected_output": "{\"number\":\"12345\",\"hash\":\"0x...\",\"parentHash\":\"0x...\",\"extrinsics\":[...]}", + "order": 5, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user wants SDK-based querying as an alternative or needs to understand Sidecar response fields.", + "pages": [ + { + "relevance": "SDK-based alternative to Sidecar — query the same on-chain data using PAPI or Polkadot.js TypeScript code.", + "slug": "chain-interactions-query-data-query-sdks", + "title": "Query On-Chain State with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/query-data/query-sdks.md" + }, + { + "relevance": "Full Sidecar API reference listing all available endpoints, query parameters, and response schemas.", + "slug": "reference-tools-sidecar", + "title": "Sidecar REST API", + "url": "https://docs.polkadot.com/reference/tools/sidecar.md" + }, + { + "relevance": "SDK approach to querying the same account balance data returned by the Sidecar balance-info endpoint.", + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md" + } + ] + }, + "title": "Query On-Chain Data with Sidecar REST API", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Estimates the fee for a Polkadot Hub balance transfer using the PAPI TypeScript library's getEstimatedFees method. Use when you need to preview gas costs before submitting — for fee UIs, budget checks, or preflight validation. Returns estimated fee in planck and human-readable form, plus the total amount deducted (fee + transfer amount). Trigger phrases: 'estimate transaction fee', 'calculate fee Polkadot', 'getEstimatedFees PAPI', 'how much does a transaction cost', 'fee estimation Polkadot Hub'. No private key or testnet tokens required — fee estimation is read-only.", + "env_vars": [], + "error_patterns": [ + { + "cause": "PAPI type descriptors were not generated.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors'", + "resolution": "Run 'npx papi add polkadotTestNet -w wss://pas-rpc.stakeworld.io/assethub' in the fee-calculator directory." + }, + { + "cause": "Polkadot Hub TestNet endpoint is temporarily unavailable.", + "pattern": "WebSocket connection failed / ECONNREFUSED on papi add or script start", + "resolution": "Wait 2-5 minutes and retry. Try an alternative public endpoint if the issue persists." + }, + { + "cause": "Descriptor export name does not match the import, or polkadot-api version is below 1.0.0.", + "pattern": "TypeError: tx.getEstimatedFees is not a function", + "resolution": "Verify the import name matches the name used in 'npx papi add' (polkadotTestNet). Check version: npm list polkadot-api — version 1.0.0+ required." + }, + { + "cause": "Project is not configured as ESM.", + "pattern": "SyntaxError: Cannot use import statement in a module", + "resolution": "Run 'npm pkg set type=module' in the fee-calculator directory, then re-run." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'fee-calculator/' as an ESM Node.js project", + "Install polkadot-api and tsx", + "Generate descriptors with npx papi add polkadotTestNet", + "Fetch papi-fee-calculator.ts; substitute WS endpoint, sender address, recipient address", + "Run npx tsx papi-fee-calculator.ts" + ], + "result": "Estimated fee in DOT plus total deducted amount printed; user can budget before submitting the transfer", + "scenario": "Common scenario: preview fee before sending a transfer", + "user_says": "How much will it cost to send 1 DOT on Polkadot Hub?" + }, + { + "actions": [ + "Explain that Polkadot Hub TestNet has a base fee of ~1000 gwei — significantly higher than Ethereum mainnet", + "Note that PAS/DOT testnet fees do not reflect real economic cost", + "Get testnet tokens from https://faucet.polkadot.io/ if the sender balance is insufficient for actual submission" + ], + "result": "User understands TestNet fee mechanics and obtains testnet tokens if needed", + "scenario": "Edge case: fee appears unusually high compared to Ethereum", + "user_says": "The fee estimate seems high compared to what I expected" + } + ], + "id": "calculate-transaction-fees-papi", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for Polkadot Hub TestNet: wss://pas-rpc.stakeworld.io/assethub" + ], + "runtime": [ + "Node.js v18+ and npm or pnpm" + ] + }, + "primary_page": "chain-interactions/send-transactions/calculate-transaction-fees.md", + "project_structure": "fee-calculator/\n├── .papi/\n│ └── descriptors/\n├── papi-fee-calculator.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/send-transactions/calculate-transaction-fees", + "branch": "master", + "files": [ + { + "description": "Constructs a transfer_keep_alive tx and calls getEstimatedFees(); prints fee, transfer amount, and total in DOT units", + "path": "papi-fee-calculator.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/send-transactions/calculate-transaction-fees.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir fee-calculator && cd fee-calculator", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory 'fee-calculator' and initialize it as an ESM Node.js project. The type=module flag is required — polkadot-api uses ESM imports.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install polkadot-api", + "npm install --save-dev @types/node tsx typescript" + ], + "description": "Install polkadot-api (PAPI) and TypeScript tooling.", + "order": 2, + "working_directory": "fee-calculator" + }, + { + "action": "Generate PAPI type descriptors", + "commands": [ + "npx papi add polkadotTestNet -w wss://pas-rpc.stakeworld.io/assethub" + ], + "description": "Generate compile-time type descriptors for Polkadot Hub TestNet. Downloads chain metadata and produces the 'polkadotTestNet' export in @polkadot-api/descriptors. If the endpoint is unavailable, retry after a few minutes — the TestNet can be intermittently slow. Replace the -w URL if targeting a different network.", + "order": 3, + "working_directory": "fee-calculator" + }, + { + "action": "Fetch the fee estimation script", + "description": "Fetch the reference file and save it as 'papi-fee-calculator.ts'. Make these three substitutions: (1) Replace `INSERT_WS_ENDPOINT` with your WebSocket endpoint (e.g., `wss://pas-rpc.stakeworld.io/assethub`). (2) Replace `INSERT_ALICE_ADDRESS` with the sender's SS58 address. (3) Replace `INSERT_BOB_ADDRESS` with the recipient's SS58 address. Fee estimation does not sign or submit the transaction — any valid SS58 addresses work.", + "order": 4, + "reference_file": "papi-fee-calculator.ts", + "working_directory": "fee-calculator" + }, + { + "action": "Run the fee estimation script", + "commands": [ + "npx tsx papi-fee-calculator.ts" + ], + "description": "Execute the script. It constructs an unsigned Balances.transfer_keep_alive transaction for 1 DOT (10^10 planck) and calls getEstimatedFees to retrieve the fee estimate without submitting. The output shows fee, transfer amount, and total in DOT units. Fees on Polkadot Hub TestNet are higher than Ethereum due to the ~1000 gwei base fee.", + "expected_output": "Estimated fee: 0.001500 DOT\nTransaction amount: 1.000000 DOT\nTotal deducted: 1.001500 DOT", + "order": 5, + "working_directory": "fee-calculator" + } + ], + "supplementary_context": { + "description": "Load these pages when the user wants to submit the transaction after fee estimation or needs to understand fee mechanics.", + "pages": [ + { + "relevance": "How to sign and submit the same transaction whose fee was estimated — the natural next step.", + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + }, + { + "relevance": "Full PAPI reference including getPaymentInfo signatures and other typed transaction methods.", + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md" + } + ] + }, + "title": "Estimate Transaction Fees with PAPI", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Transfers assets cross-chain between Polkadot parachains using the ParaSpell XCM SDK with PAPI. Covers the full workflow: install the SDK, build an XCM transfer, dry-run for feasibility, verify existential deposit requirements, estimate fees, and submit against Paseo testnet. Use when you need to move tokens programmatically between parachains (e.g., Asset Hub to People Chain). Requires a funded Paseo testnet mnemonic. Trigger phrases: 'XCM transfer ParaSpell', 'cross-chain transfer Polkadot', 'transfer assets between parachains', 'send tokens XCM', 'ParaSpell SDK'.", + "env_vars": [ + { + "description": "Seed phrase (mnemonic or derivation path) for the sender account on Paseo Asset Hub. Must be funded with testnet PAS. Never commit to version control.", + "name": "SENDER_SEED", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Sender account has insufficient PAS to cover transfer amount plus XCM execution fees.", + "pattern": "Error: Insufficient funds / Inability to pay some fees", + "resolution": "Fund the sender on Paseo Asset Hub at https://faucet.polkadot.io/ (select Paseo > Asset Hub Paseo). Wait 2 minutes for confirmation." + }, + { + "cause": "Transfer amount is below the existential deposit on the destination chain, or recipient has no balance.", + "pattern": "Dry-run failed: destination existential deposit not met", + "resolution": "Increase the transfer AMOUNT above the destination chain's existential deposit, or ensure the recipient already has a balance on the destination chain." + }, + { + "cause": "Chain name does not match the ParaSpell supported chain list for the SDK version installed.", + "pattern": "Error: chain 'AssetHubPaseo' not found in ParaSpell", + "resolution": "Check the supported chains list for your @paraspell/sdk-pjs version. Chain names are case-sensitive and version-specific. Update SOURCE_CHAIN and DEST_CHAIN in the script accordingly." + }, + { + "cause": "Paseo TestNet instability — the TestNet can drop transactions under load.", + "pattern": "Transaction submitted but never finalizes", + "resolution": "Check if the transaction was included using subscan.io (paseo.subscan.io). If not included, re-run the script — nonce: -1 fetches the current nonce automatically." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'paraspell-xcm-transfer/' as an ESM project", + "Install @paraspell/sdk-pjs, @polkadot/api, keyring, dotenv", + "Create .env; ask user to fill MNEMONIC and DEST_ADDRESS without asking in chat", + "Create xcm-transfer.ts with the inline script", + "Verify sender has 2+ PAS at https://faucet.polkadot.io/", + "Run npx tsx xcm-transfer.ts" + ], + "result": "Dry-run passes, fee estimated, transaction finalized, PAS transferred cross-chain", + "scenario": "Common scenario: transfer PAS from Asset Hub Paseo to People Chain Paseo", + "user_says": "Transfer 1 PAS from Asset Hub to People Chain on Paseo testnet" + }, + { + "actions": [ + "Check the existential deposit for the destination chain using the ParaSpell SDK", + "Increase AMOUNT in the script above the existential deposit threshold", + "Re-run xcm-transfer.ts" + ], + "result": "Dry-run passes once the transfer amount covers the destination existential deposit", + "scenario": "Edge case: dry-run fails with existential deposit error", + "user_says": "The dry-run fails saying the destination existential deposit is not met" + } + ], + "id": "transfer-assets-parachains-paraspell", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Paseo Asset Hub (source): connected via @paraspell/sdk auto-routing", + "People Chain Paseo (destination): connected via @paraspell/sdk auto-routing" + ], + "runtime": [ + "Node.js v18+ and npm" + ], + "tokens": [ + "Paseo testnet PAS in the sender account on Paseo Asset Hub — get from https://faucet.polkadot.io/?parachain=1000 (Paseo Asset Hub faucet). Minimum 10 PAS recommended for transfer + fees." + ], + "wallet": [ + "SR25519 or Ed25519 seed phrase for the sender account on Paseo Asset Hub, funded with Paseo testnet PAS" + ] + }, + "primary_page": "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md", + "project_structure": "paraspell-transfer/\n├── index.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/send-transactions/interoperability/transfer-assets-parachains", + "branch": "master", + "files": [ + { + "description": "Complete ParaSpell XCM SDK transfer script covering dry-run, ED verification, fee estimation, and transaction submission", + "path": "index.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" + ], + "steps": [ + { + "action": "Initialize the project", + "commands": [ + "mkdir paraspell-transfer && cd paraspell-transfer", + "npm init -y" + ], + "description": "Create a new directory 'paraspell-transfer' and initialize it as a Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install --save-dev @types/node tsx typescript", + "npm install --save @paraspell/sdk polkadot-api @polkadot-labs/hdkd-helpers @polkadot-labs/hdkd" + ], + "description": "Install the ParaSpell SDK (PAPI variant), polkadot-api, @polkadot-labs/hdkd (key derivation), and TypeScript tooling.", + "order": 2, + "working_directory": "paraspell-transfer" + }, + { + "action": "Create the transfer script", + "description": "Fetch the reference file and save it as 'index.ts'. The script: (1) sets up PAPI client and connects to Paseo Asset Hub, (2) builds an XCM transfer of 10 PAS from Asset Hub to People Chain using Builder API, (3) dry-runs the transfer to confirm success before submitting, (4) verifies existential deposit requirements on the destination, (5) retrieves transfer info and fee estimates, (6) submits the transfer and waits for finalization. Replace INSERT_YOUR_SEED_PHRASE with your funded Paseo testnet seed phrase. For production, use an environment variable.", + "order": 3, + "reference_file": "index.ts", + "working_directory": "paraspell-transfer" + }, + { + "action": "Dry-run the transfer", + "commands": [ + "npx tsx index.ts" + ], + "description": "Run the script to execute the dry run first. Comment out functions selectively per the source page instructions: run dryRunTransfer() first, then verifyED(), then XcmTransferInfo(), then the final transfer() call. If dry-run fails, check the error before proceeding — common causes are insufficient balance or mismatched chain names.", + "expected_output": "Dry run successful: { success: true, ... }", + "order": 4, + "working_directory": "paraspell-transfer" + }, + { + "action": "Verify existential deposit and run fee estimation", + "commands": [ + "npx tsx index.ts" + ], + "description": "Comment out dryRunTransfer(), uncomment verifyED(), run. Then comment out verifyED(), uncomment XcmTransferInfo(), run to see fee estimates and account balances.", + "expected_output": "ED verification: { enough: true, ... }\nTransfer info: { xcmFee: ..., balance: ... }", + "order": 5, + "working_directory": "paraspell-transfer" + }, + { + "action": "Submit the transfer", + "commands": [ + "npx tsx index.ts" + ], + "description": "Comment out XcmTransferInfo(), uncomment transfer(), run. The transfer signs and submits the XCM transaction and waits for finalization. Check Paseo Asset Hub and People Chain accounts on Polkadot.js Apps to confirm the 10 PAS arrived.", + "expected_output": "Transfer submitted: { txHash: 0x... }\nFinalized: 0x...", + "order": 6, + "working_directory": "paraspell-transfer" + } + ], + "supplementary_context": { + "description": "Load these pages for background on XCM mechanics, fee estimation, or to debug a failing transfer.", + "pages": [ + { + "relevance": "Detailed XCM fee estimation covering local, delivery, and remote execution components.", + "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", + "title": "XCM Fee Estimation", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" + }, + { + "relevance": "Full ParaSpell SDK reference including supported chains, asset IDs, and Builder API documentation.", + "slug": "reference-tools-paraspell", + "title": "ParaSpell XCM SDK", + "url": "https://docs.polkadot.com/reference/tools/paraspell.md" + }, + { + "relevance": "How to debug a failing XCM transfer by replaying it with Chopsticks before submitting to TestNet.", + "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", + "title": "Replay and Dry Run XCMs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" + } + ] + }, + "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Forks Polkadot Hub state locally with Chopsticks and submits a DOT transfer transaction where fees are paid in USDT (asset ID 1984) rather than native DOT/PAS, using PAPI's ChargeAssetTxPayment signed extension. Uses Alice's dev account (preconfigured with USDT in the polkadot-asset-hub Chopsticks config). No faucet required. Trigger phrases: 'pay fees in USDT', 'alternative fee token Polkadot', 'ChargeAssetTxPayment', 'fee proxy Polkadot Hub', 'Chopsticks fee demo'. Also covers Polkadot.js and Subxt from the source page.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Chopsticks is not running.", + "pattern": "ECONNREFUSED ws://localhost:8000", + "resolution": "Start Chopsticks first: chopsticks -c polkadot-asset-hub. Wait for 'Listening on port 8000' before running the script or the papi add command." + }, + { + "cause": "PAPI type descriptors were not generated.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or assetHub is undefined", + "resolution": "Run: npx papi add assetHub -n polkadot_asset_hub. Then re-run the script." + }, + { + "cause": "The Chopsticks config for 'polkadot-asset-hub' does not seed Alice with USDT assets in this snapshot.", + "pattern": "Alice has insufficient USDT balance to pay fees", + "resolution": "Use Chopsticks' dev_setStorage RPC to seed Alice's USDT (asset ID 1984) balance in the forked state, or switch to the actual Polkadot Hub mainnet state which has Alice pre-funded." + }, + { + "cause": "The chain runtime version in the fork uses a different pallet name or configuration.", + "pattern": "Error: AssetTxPayment pallet not found / fee asset not supported", + "resolution": "Check available pallets by inspecting api.tx in a TypeScript IDE. The pallet may be named 'AssetConversionTxPayment' depending on the runtime version." + } + ], + "examples": [ + { + "actions": [ + "Start Chopsticks: chopsticks -c polkadot-asset-hub; wait for port 8000", + "Create 'fee-payment-tutorial/' with ESM Node.js init; install polkadot-api and hdkd", + "Generate PAPI descriptors: npx papi add assetHub -n polkadot_asset_hub", + "Fetch fee-payment-transaction.ts reference file; run npx ts-node fee-payment-transaction.ts" + ], + "result": "Transaction included in local fork; fees deducted from Alice's USDT balance via AssetTxPayment events", + "scenario": "Common scenario: demonstrate paying fees in USDT on a local Chopsticks fork", + "user_says": "Show me how to pay Polkadot Hub transaction fees in USDT instead of DOT" + }, + { + "actions": [ + "Use Chopsticks' dev_setStorage RPC to set Alice's USDT asset balance in the forked state", + "Re-run fee-payment-transaction.ts" + ], + "result": "Script succeeds once Alice's USDT balance is seeded", + "scenario": "Edge case: Alice has no USDT in the forked state", + "user_says": "The script fails saying Alice has no USDT balance" + } + ], + "id": "pay-fees-alternative-token", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Internet connection — Chopsticks downloads Polkadot Hub mainnet state on first run" + ], + "runtime": [ + "Node.js v18+ and npm (for PAPI/Polkadot.js)", + "Rust toolchain latest stable (for Subxt)", + "Chopsticks CLI installed globally: npm install -g @acala-network/chopsticks" + ] + }, + "primary_page": "chain-interactions/send-transactions/pay-fees-with-different-tokens.md", + "project_structure": "fee-payment-tutorial/\n├── .papi/\n│ └── descriptors/\n├── fee-payment-transaction.ts\n├── tsconfig.json\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/send-transactions/pay-fees-with-different-tokens", + "branch": "master", + "files": [ + { + "description": "PAPI TypeScript script that submits a DOT transfer with USDT fee payment using ChargeAssetTxPayment on a Chopsticks fork", + "path": "papi/fee-payment-transaction.ts" + }, + { + "description": "Polkadot.js JavaScript equivalent for the same fee-in-USDT pattern", + "path": "pjs/fee-payment-transaction.js" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/send-transactions/pay-fees-with-different-tokens.md" + ], + "steps": [ + { + "action": "Start the Chopsticks fork of Polkadot Hub", + "commands": [ + "chopsticks -c polkadot-asset-hub" + ], + "description": "Start Chopsticks using the built-in 'polkadot-asset-hub' config preset. This forks Polkadot Hub mainnet at ws://localhost:8000. The preset config seeds Alice's account with USDT assets (asset ID 1984). Keep this running in a dedicated terminal for all subsequent steps.", + "expected_output": "Listening on port 8000", + "order": 1, + "working_directory": "." + }, + { + "action": "Create project directory and install dependencies", + "commands": [ + "mkdir fee-payment-tutorial && cd fee-payment-tutorial", + "npm init -y", + "npm install --save-dev @types/node ts-node typescript", + "npm install --save @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers polkadot-api", + "npx tsc --init && npm pkg set type=module" + ], + "description": "Create 'fee-payment-tutorial' as an ESM Node.js project and install PAPI and hdkd libraries. For Polkadot.js, install @polkadot/api instead. For Subxt, create a Cargo project instead.", + "order": 2, + "working_directory": "." + }, + { + "action": "Generate PAPI type descriptors for the local fork", + "commands": [ + "npx papi add assetHub -n polkadot_asset_hub" + ], + "description": "Generate compile-time PAPI descriptors named 'assetHub' using the built-in 'polkadot_asset_hub' network ID. This generates types for the Polkadot Hub mainnet (same runtime as the Chopsticks fork).", + "order": 3, + "working_directory": "fee-payment-tutorial" + }, + { + "action": "Create the fee payment transaction script", + "description": "Fetch the reference file and save it as 'fee-payment-transaction.ts'. The script: (1) uses Alice's dev account (derived from '//Alice'), (2) connects to the Chopsticks fork at ws://localhost:8000, (3) creates a Balances.transfer_keep_alive transaction to Bob, (4) signs and submits with the 'asset' option specifying USDT via XCM location (PalletInstance(50), GeneralIndex(1984)), confirming fees are charged in USDT via AssetTxPayment events.", + "order": 4, + "reference_file": "papi/fee-payment-transaction.ts", + "working_directory": "fee-payment-tutorial" + }, + { + "action": "Run the fee payment script", + "commands": [ + "npx ts-node fee-payment-transaction.ts" + ], + "description": "Execute the script while Chopsticks is running. Key events to verify: 'AssetTxPayment' confirms fees paid in USDT; 'AssetConversion' shows USDT swapped for native fees; 'Balances' shows the transfer executed; 'System' shows success.", + "expected_output": "Transaction finalized. Events: AssetTxPayment, AssetConversion, Balances, System", + "order": 5, + "working_directory": "fee-payment-tutorial" + } + ], + "supplementary_context": { + "description": "Load these pages to understand fee mechanics, Chopsticks setup, or to compare with standard fee estimation.", + "pages": [ + { + "relevance": "Full Chopsticks reference including built-in config presets, RPC override methods, and mock-signature details.", + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md" + }, + { + "relevance": "How to estimate fees for a standard transaction — compare with the fee proxy approach to understand the difference.", + "slug": "chain-interactions-send-transactions-calculate-transaction-fees", + "title": "Calculate Transaction Fees", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/calculate-transaction-fees.md" + } + ] + }, + "title": "Pay Transaction Fees with an Alternative Token", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Runs a Polkadot system parachain RPC node (using People Chain as example) in archive or pruned mode via Docker or systemd. Covers chain spec download, optional snapshot download with rclone for faster sync, Docker image launch, systemd service configuration, and sync verification. A substitution table adapts steps for Bridge Hub, Coretime Chain, and other system parachains. Use when you need a self-hosted parachain RPC endpoint. Trigger phrases: 'run parachain node', 'People Chain RPC node', 'parachain RPC Docker', 'set up system parachain node', 'run parachain RPC systemd'. No tokens required.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The data volume has run out of disk space.", + "pattern": "Error: No space left on device / database write error", + "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=256). Check usage with: du -sh my-node-data/" + }, + { + "cause": "Another process is already using the required ports.", + "pattern": "EADDRINUSE port 9944 or 30333", + "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." + }, + { + "cause": "P2P ports 30333/30334 are blocked by firewall, or bootnodes are temporarily unavailable.", + "pattern": "Peers count stays at 0 after several minutes", + "resolution": "Open TCP ports 30333 and 30334: sudo ufw allow 30333/tcp && sudo ufw allow 30334/tcp. For cloud VMs, update the security group. If peers still do not connect after 10 minutes, check the Polkadot network status." + }, + { + "cause": "Node cannot find peers with required state, or the relay chain client is not syncing.", + "pattern": "Sync stalls at the same block for more than 30 minutes", + "resolution": "Restart the container or service. If stalling persists, download a fresh snapshot from https://snapshots.polkadot.io/ to skip the stalled sync range." + } + ], + "examples": [ + { + "actions": [ + "Pull parity/polkadot-parachain:stable", + "Create data directory /var/lib/people-chain", + "Run docker with --chain=people-polkadot, --state-pruning=1000, --blocks-pruning=1000, ports 30333 and 9944 exposed", + "Monitor sync with docker logs and system_syncState RPC", + "Verify with system_chain and system_health once currentBlock equals highestBlock" + ], + "result": "People Chain RPC node running, synced, serving requests on ws://localhost:9944", + "scenario": "Common scenario: run a pruned People Chain RPC node via Docker", + "user_says": "Run a People Chain RPC node on Polkadot using Docker" + }, + { + "actions": [ + "Check both parachain and relay chain sync progress separately in the logs", + "Verify peers > 0 via system_health and that port 30333 is open", + "Restart: docker restart people-chain-rpc", + "If stalling persists after restart, switch to --sync=warp" + ], + "result": "Node resumes syncing after restart or sync mode change", + "scenario": "Edge case: node stalls at the same block for 30+ minutes", + "user_says": "The node has been stuck at block 12345 for 30 minutes" + } + ], + "id": "run-parachain-rpc-node", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Open TCP ports: 30333 (parachain P2P), 30334 (relay chain P2P), 9944 (WebSocket RPC), 9933 (HTTP RPC), 9615 (Prometheus metrics, optional)", + "Public IP address and stable broadband connection (1 Gbps for production)" + ], + "runtime": [ + "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", + "rclone (optional but recommended for snapshot download): https://rclone.org/downloads/", + "Disk: ~200 GB SSD for pruned node; ~900 GB–1.2 TB NVMe SSD for archive node (varies by parachain)", + "RAM: minimum 64 GB; 128 GB recommended for high traffic", + "CPU: 8+ cores; 16+ cores for high traffic" + ] + }, + "primary_page": "node-infrastructure/run-a-node/parachain-rpc.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "node-infrastructure/run-a-node/parachain-rpc.md" + ], + "steps": [ + { + "action": "Choose deployment method and obtain chain spec", + "description": "Decide on deployment method: Docker (step 2) or systemd binary (step 3).\n\nDownload the chain specification for People Chain (or your target parachain):\n\n```bash\ncurl -L https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/cumulus/parachains/chain-specs/people-polkadot.json -o chain-spec.json\n```\n\nSystem parachain substitution table:\n| Parachain | Para ID | Chain Spec File | Snapshot Path |\n|---|---|---|---|\n| Bridge Hub | 1002 | bridge-hub-polkadot.json | polkadot-bridge-hub-paritydb-archive |\n| People Chain | 1004 | people-polkadot.json | polkadot-people-rocksdb-archive |\n| Coretime Chain | 1005 | coretime-polkadot.json | polkadot-coretime-rocksdb-archive |\n\nFor non-system parachains, check the parachain's documentation for chain spec files.", + "order": 1, + "working_directory": "." + }, + { + "action": "Download snapshots (optional but recommended)", + "commands": [ + "mkdir -p my-node-data/chains/people-polkadot/db", + "mkdir -p my-node-data/chains/polkadot/db", + "export SNAPSHOT_URL_PARACHAIN=\"https://snapshots.polkadot.io/polkadot-people-rocksdb-archive/INSERT_LATEST\"", + "rclone copyurl $SNAPSHOT_URL_PARACHAIN/files.txt files.txt && rclone copy --progress --transfers 20 --http-url $SNAPSHOT_URL_PARACHAIN --no-traverse --http-no-head --disable-http2 --inplace --no-gzip-encoding --size-only --retries 6 --retries-sleep 10s --files-from files.txt :http: my-node-data/chains/people-polkadot/db/ && rm files.txt", + "export SNAPSHOT_URL_RELAY=\"https://snapshots.polkadot.io/polkadot-rocksdb-prune/INSERT_LATEST\"", + "rclone copyurl $SNAPSHOT_URL_RELAY/files.txt files.txt && rclone copy --progress --transfers 20 --http-url $SNAPSHOT_URL_RELAY --no-traverse --http-no-head --disable-http2 --inplace --no-gzip-encoding --size-only --retries 6 --retries-sleep 10s --files-from files.txt :http: my-node-data/chains/polkadot/db/ && rm files.txt" + ], + "description": "Download pre-synced snapshots from https://snapshots.polkadot.io/ to cut initial sync time from days to hours. Replace INSERT_LATEST with the latest snapshot path from the snapshot provider. Use the parachain snapshot path from the substitution table in step 1. Skip this step if you prefer to sync from genesis.", + "order": 2, + "working_directory": "." + }, + { + "action": "Launch the node via Docker", + "commands": [ + "docker run -d --name people-chain-rpc --restart unless-stopped -p 9944:9944 -p 9933:9933 -p 9615:9615 -p 30334:30334 -p 30333:30333 -v $(pwd)/people-polkadot.json:/people-polkadot.json -v $(pwd)/my-node-data:/data parity/polkadot-parachain:stable --name=PeopleChainRPC --base-path=/data --chain=/people-polkadot.json --prometheus-external --prometheus-port 9615 --unsafe-rpc-external --rpc-port=9944 --rpc-cors=all --rpc-methods=safe --rpc-max-connections=1000 --state-pruning=1000 --blocks-pruning=256 -- --base-path=/data --chain=polkadot --state-pruning=256 --blocks-pruning=256 --rpc-port=0" + ], + "description": "Launch People Chain RPC node (pruned mode). Key args: --chain=/people-polkadot.json (chain spec path), --unsafe-rpc-external (enable external RPC), --rpc-methods=safe (only safe methods), --state-pruning=1000 --blocks-pruning=256 (pruned mode; change to 'archive' for archive mode), --rpc-max-connections=1000. The -- separator configures the embedded relay chain client with pruning. For archive mode, use --state-pruning=archive --blocks-pruning=archive. For systemd, create /etc/systemd/system/people-chain-rpc.service with the equivalent ExecStart args.", + "order": 3, + "working_directory": "." + }, + { + "action": "Create systemd service (binary method alternative)", + "description": "For systemd (skip if using Docker):\n\n1. Download binary: wget https://github.com/paritytech/polkadot-sdk/releases/download/{VERSION}/polkadot-parachain; chmod +x polkadot-parachain; sudo mv polkadot-parachain /usr/local/bin/\n\n2. Create dedicated user and data directory:\n```bash\nsudo useradd -r -s /bin/bash polkadot\nsudo mkdir -p /var/lib/people-chain-rpc\nsudo cp people-polkadot.json /var/lib/people-chain-rpc/\nsudo chown -R polkadot:polkadot /var/lib/people-chain-rpc\n```\n\n3. Create /etc/systemd/system/people-chain-rpc.service with ExecStart running polkadot-parachain with the same args as the Docker command above (adapted for systemd, using --rpc-external instead of --unsafe-rpc-external for production behind a proxy).\n\n4. Enable and start:\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable people-chain-rpc\nsudo systemctl start people-chain-rpc\n```", + "order": 4, + "working_directory": "." + }, + { + "action": "Monitor sync and verify the endpoint", + "commands": [ + "curl -H 'Content-Type: application/json' -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"system_syncState\", \"params\":[]}' http://localhost:9944", + "curl -H 'Content-Type: application/json' -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"system_health\", \"params\":[]}' http://localhost:9944" + ], + "description": "Check sync state: currentBlock equals highestBlock when fully synced. Check health: isSyncing should be false and peers > 0. For Docker logs: docker logs -f people-chain-rpc. For systemd logs: sudo journalctl -u people-chain-rpc -f. If peers is 0, verify ports 30333 and 30334 are open in your firewall.", + "expected_output": "{\"result\":{\"startingBlock\":0,\"currentBlock\":3394816,\"highestBlock\":3394816}}", + "order": 5, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to add TLS/WSS or run the Polkadot Hub RPC node specifically.", + "pages": [ + { + "relevance": "How to run an RPC node for Polkadot Hub specifically — different chain spec and optional Ethereum RPC adapter.", + "slug": "node-infrastructure-run-a-node-polkadot-hub-rpc", + "title": "Run an RPC Node for Polkadot Hub", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/polkadot-hub-rpc.md" + }, + { + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints.", + "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", + "title": "Set Up Secure WebSocket", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md" + }, + { + "relevance": "How to run a standalone Polkadot relay chain full node, separate from the embedded relay client.", + "slug": "node-infrastructure-run-a-node-relay-chain-full-node", + "title": "Set Up a Node", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/full-node.md" + } + ] + }, + "title": "Run a Parachain RPC Node", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Replays and dry-runs historical XCMs using Chopsticks multi-chain fork mode with PAPI TypeScript scripts. Covers building a local Wasm override for runtime logging, forking multiple chains via 'chopsticks xcm', extracting XCM call data from Subscan, submitting an XCM replay against the local fork with replay-xcm.ts, and simulating execution without state changes via dry-run-call.ts using DryRunApi.dry_run_call. Use when debugging a failing XCM, inspecting emitted events, or verifying fee coverage before resubmitting. Requires Rust build environment for Wasm compilation. Trigger phrases: 'replay XCM', 'dry-run XCM Chopsticks', 'debug XCM Polkadot', 'preview XCM execution', 'XCM dry run local fork'.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Chopsticks is not running or not yet ready.", + "pattern": "ECONNREFUSED ws://localhost:8000", + "resolution": "Start Chopsticks in a dedicated terminal: npx @acala-network/chopsticks xcm -r polkadot -p configs/polkadot-asset-hub-override.yaml -p acala. Wait for all three RPC endpoint announcements before running scripts." + }, + { + "cause": "PAPI type descriptors were not generated from the local fork.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or 'polkadotHub' export", + "resolution": "Ensure Chopsticks is running, then run: npx papi add polkadotHub -w ws://localhost:8000. Regenerate descriptors if the fork was restarted since the last generation." + }, + { + "cause": "The XCM message includes insufficient fees to cover execution on the destination chain.", + "pattern": "executionResult: { type: 'Err', value: { type: 'TooExpensive' } }", + "resolution": "Increase the fee amount in the BuyExecution instruction of the XCM message before re-submitting. Check the estimate-xcm-fees-teleport skill for how to calculate required fees." + }, + { + "cause": "Rust toolchain version mismatch or missing build dependencies.", + "pattern": "cargo build fails or polkadot-fellows/runtimes won't compile", + "resolution": "Ensure the Rust toolchain matches the toolchain-version in polkadot-fellows/runtimes (typically specified in rust-toolchain.toml). Run 'rustup update' and retry. The first build may take 30+ minutes." + }, + { + "cause": "The forked block is too old or the cached state (db.sqlite) is stale.", + "pattern": "Chopsticks fork diverges or shows 'Block not found'", + "resolution": "Delete db.sqlite and restart Chopsticks to re-fork from the configured block numbers. Verify the POLKADOT_HUB_BLOCK_NUMBER in .env is correct and the block predates the XCM you want to replay." + } + ], + "examples": [ + { + "actions": [ + "Create 'replay-xcm-tests/' and install all dependencies including Chopsticks", + "Create .env with block numbers from just before the failed XCM", + "Build Polkadot Hub Wasm from polkadot-fellows/runtimes (enables runtime logs)", + "Configure Chopsticks with wasm-override and runtime-log-level: 5", + "Start Chopsticks XCM fork in a dedicated terminal", + "Generate PAPI descriptors from local fork: npx papi add polkadotHub -w ws://localhost:8000", + "Locate the failed XCM on Subscan and copy the encoded call data hex", + "Fetch dry-run-call.ts; substitute the call data hex", + "Run npx tsx dry-run-call.ts; inspect executionResult for the failure reason" + ], + "result": "Dry-run reveals the failure reason (e.g., TooExpensive, Barrier blocked) and emitted events without spending tokens", + "scenario": "Common scenario: debug a failing XCM by dry-running it locally", + "user_says": "An XCM transfer I submitted failed. How can I debug it without spending more tokens?" + }, + { + "actions": [ + "Complete steps 1-6 to set up the Chopsticks multi-chain fork", + "Fetch replay-xcm.ts; substitute the call data hex", + "Run npx tsx replay-xcm.ts", + "Inspect the logged events from signSubmitAndWatch to trace the full XCM execution path" + ], + "result": "All emitted events (including XCM execution events on destination chain) logged to console", + "scenario": "Edge case: replaying the XCM against the local fork for full event tracing", + "user_says": "I need to see exactly which events fire when the XCM executes" + } + ], + "id": "replay-dry-run-xcm-chopsticks", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Internet access to fetch chain state for Chopsticks fork", + "Subscan or Polkadot.js Apps to locate and decode the XCM extrinsic call data" + ], + "runtime": [ + "Rust toolchain with cargo (to compile polkadot-fellows/runtimes for Wasm override)", + "Node.js v18+ and npm", + "npx (bundled with npm v5.2+)" + ] + }, + "primary_page": "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md", + "project_structure": "replay-xcm-tests/\n├── .env\n├── .papi/\n│ └── descriptors/\n├── configs/\n│ └── polkadot-asset-hub-override.yaml\n├── wasms/\n│ └── asset_hub_polkadot_runtime.compact.compressed.wasm\n├── dry-run-call.ts\n├── replay-xcm.ts\n├── tsconfig.json\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms", + "branch": "master", + "files": [ + { + "description": "Signs and submits an XCM transaction against a local Chopsticks fork using Alice's dev account; logs block inclusion and events", + "path": "replay-xcm.ts" + }, + { + "description": "Calls DryRunApi.dry_run_call to simulate XCM execution without state changes; uses XCM version 5 and logs the full dry-run result", + "path": "dry-run-call.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" + ], + "steps": [ + { + "action": "Create project directory and install dependencies", + "commands": [ + "mkdir -p replay-xcm-tests && cd replay-xcm-tests", + "npm init -y && npm pkg set type=module", + "npm install -g @acala-network/chopsticks@latest", + "npm install --save-dev typescript @types/node tsx", + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers", + "npx tsc --init" + ], + "description": "Create the working directory, initialize an ESM Node.js project, and install all dependencies. Chopsticks is installed globally to avoid version conflicts. polkadot-api, @polkadot-labs/hdkd, and @polkadot-labs/hdkd-helpers are used by the replay and dry-run scripts.", + "order": 1, + "working_directory": "." + }, + { + "action": "Create .env with block numbers for the fork", + "description": "Create a file named '.env' in replay-xcm-tests with the following content:\n\n```text\nPOLKADOT_BLOCK_NUMBER=26481107\nPOLKADOT_HUB_BLOCK_NUMBER=9079591\nACALA_BLOCK_NUMBER=8826385\n```\n\nReplace these block numbers with the heights just *before* the XCM you want to replay was sent. The block numbers anchor the Chopsticks fork to the exact chain state at the time of the original XCM execution. Using the block immediately before the XCM block ensures the fork starts from the correct pre-execution state.", + "order": 2, + "working_directory": "replay-xcm-tests" + }, + { + "action": "Build the Polkadot Hub Wasm override for runtime logging", + "commands": [ + "git clone git@github.com:polkadot-fellows/runtimes.git", + "cd runtimes && cargo build --release -p asset-hub-polkadot-runtime", + "mkdir -p ../wasms", + "cp target/release/wbuild/asset-hub-polkadot-runtime/asset_hub_polkadot_runtime.compact.compressed.wasm ../wasms/", + "cd .." + ], + "description": "Clone polkadot-fellows/runtimes and compile the Polkadot Hub runtime with the release profile. The compiled Wasm enables full runtime execution logs in Chopsticks, which are essential for tracing XCM execution flow. The cargo build may take 10-30 minutes on first run. Copy the resulting compressed Wasm to the wasms/ directory in replay-xcm-tests.", + "order": 3, + "working_directory": "replay-xcm-tests" + }, + { + "action": "Download and configure the Chopsticks config for Polkadot Hub", + "commands": [ + "mkdir -p configs", + "wget https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot-asset-hub.yml -O configs/polkadot-asset-hub-override.yaml" + ], + "description": "Download the Polkadot Hub Chopsticks config and add the Wasm override and runtime log level. Edit 'configs/polkadot-asset-hub-override.yaml' to add these lines:\n\n```yaml\nruntime-log-level: 5\nwasm-override: wasms/asset_hub_polkadot_runtime.compact.compressed.wasm\n```\n\nThe `runtime-log-level: 5` enables verbose runtime execution logs needed to trace XCM flow. The `wasm-override` points to the locally built Wasm from step 3.", + "order": 4, + "working_directory": "replay-xcm-tests" + }, + { + "action": "Start the multi-chain Chopsticks fork", + "commands": [ + "npx @acala-network/chopsticks xcm -r polkadot -p configs/polkadot-asset-hub-override.yaml -p acala" + ], + "description": "Start Chopsticks in XCM mode in a dedicated terminal. It forks Polkadot relay chain, Polkadot Hub (with your custom Wasm override), and Acala locally. The first run downloads several MB of chain state — subsequent runs use the cached state. Wait for all three RPC endpoint announcements before proceeding to step 6. Keep this terminal running throughout the remaining steps.", + "expected_output": "Polkadot Hub RPC on http://localhost:8000\nAcala RPC on http://localhost:8001\nPolkadot RPC on http://localhost:8002", + "interactive": true, + "order": 5, + "working_directory": "replay-xcm-tests" + }, + { + "action": "Generate PAPI type descriptors for the local fork", + "commands": [ + "npx papi add polkadotHub -w ws://localhost:8000" + ], + "description": "Generate compile-time PAPI type descriptors from the local Chopsticks Polkadot Hub fork. This produces the 'polkadotHub' export in @polkadot-api/descriptors, used by both replay-xcm.ts and dry-run-call.ts. Chopsticks must be running (step 5) for this command to succeed.", + "order": 6, + "working_directory": "replay-xcm-tests" + }, + { + "action": "Locate the XCM call data on Subscan", + "description": "This step is manual. Open Subscan for the source chain (e.g., https://assethub-polkadot.subscan.io). Navigate to the extrinsic that sent the XCM you want to replay. Decode the call data and copy the hex-encoded call payload (starts with '0x'). For the example XCM in the reference scripts, the call data is:\n\n```\n0x1f0803010100411f0300010100fc39fcf04a8071b7409823b7c82427ce67910c6ed80aa0e5093aff234624c8200304000002043205011f0092e81d790000000000\n```\n\nSave the hex string — you will substitute it in steps 8 and 9.", + "interactive": true, + "order": 7, + "working_directory": "." + }, + { + "action": "Fetch and run the XCM replay script", + "commands": [ + "npx tsx replay-xcm.ts" + ], + "description": "Fetch the reference file and save it as 'replay-xcm.ts'. In the file, replace the hardcoded call data hex string in `Binary.fromHex(...)` with the hex string you copied in step 7. The script uses Alice's dev account (DEV_PHRASE) as the signer — this works against the local Chopsticks fork because mock signatures are accepted. Run the script; it submits the XCM to the local fork and logs emitted events. Chopsticks must be running (step 5).", + "expected_output": "👀 Executing XCM: {...}\n📦 Included in block #...: 0x...\n📣 Event: ...", + "order": 8, + "reference_file": "replay-xcm.ts", + "working_directory": "replay-xcm-tests" + }, + { + "action": "Fetch and run the dry-run script", + "commands": [ + "npx tsx dry-run-call.ts" + ], + "description": "Fetch the reference file and save it as 'dry-run-call.ts'. Replace the hardcoded call data hex string in `Binary.fromHex(...)` with the same hex from step 7. The script calls DryRunApi.dry_run_call with XCM version 5 — this simulates execution without committing any state changes. Inspect the output for executionResult: a 'Success' type means the XCM would execute successfully; an 'Err' type reveals the failure reason (e.g., TooExpensive, Barrier blocked). Chopsticks must be running (step 5).", + "expected_output": "{ executionResult: { type: 'Success', value: {...} }, ... }", + "order": 9, + "reference_file": "dry-run-call.ts", + "working_directory": "replay-xcm-tests" + } + ], + "supplementary_context": { + "description": "Load these pages for XCM fee estimation, the ParaSpell transfer skill, or Chopsticks configuration reference.", + "pages": [ + { + "relevance": "How to estimate XCM fees before submission — helps diagnose BuyExecution failures in a dry-run.", + "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", + "title": "XCM Fee Estimation", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" + }, + { + "relevance": "Full Chopsticks reference including all config options, block forking, and RPC override methods.", + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md" + }, + { + "relevance": "How to submit a real XCM transfer between parachains — use after debugging with Chopsticks.", + "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", + "title": "Transfer Assets Between Parachains", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" + } + ] + }, + "title": "Replay and Dry-Run XCMs Using Chopsticks", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Estimates all three fee components for an XCM teleport from Polkadot Hub TestNet to Paseo People Chain: local execution fee (source chain), delivery fee (network transport), and remote execution fee (destination chain). Uses PAPI with @polkadot-labs/hdkd for key derivation and Chopsticks to fork both chains locally. No real tokens required. Trigger phrases: 'estimate XCM fees', 'XCM teleport fee', 'BuyExecution fee calculation', 'cross-chain fee estimate Polkadot', 'dry-run XCM fee'.", + "env_vars": [], + "error_patterns": [ + { + "cause": "One or both Chopsticks forks are not running.", + "pattern": "ECONNREFUSED ws://localhost:8000 or ws://localhost:8001", + "resolution": "Start each Chopsticks instance in a separate terminal. Run 'npx @acala-network/chopsticks --config=chopsticks-hub.yml' and 'npx @acala-network/chopsticks --config=chopsticks-people.yml'. Wait for 'Listening on port 8000/8001' before proceeding." + }, + { + "cause": "The chain runtime version does not implement the XcmPaymentApi runtime API at this method name.", + "pattern": "XcmPaymentApi method not found / query_delivery_fees undefined", + "resolution": "Check the source page for the correct API method names for your runtime version. The API surface changed between XCM V3 and V4. Verify the chain runtime version in the Chopsticks fork logs." + }, + { + "cause": "PAPI descriptors were not generated for one or both forks.", + "pattern": "Error: Cannot find module '@polkadot-api/descriptors' or hubFork/peopleFork undefined", + "resolution": "With both Chopsticks forks running, execute: npx papi add hubFork -w ws://localhost:8000 and npx papi add peopleFork -w ws://localhost:8001. Then re-run the script." + }, + { + "cause": "The XCM message structure passed to query_xcm_weight does not match the expected format for the chain runtime.", + "pattern": "Fee estimate returns 0 or undefined for remote execution fee", + "resolution": "Inspect the source page for the exact XCM instruction sequence expected by the destination chain. Verify that the MultiLocation structures use the correct XCM version (V3 vs V4) matching the chain runtime." + } + ], + "examples": [ + { + "actions": [ + "Create 'xcm-fee-estimate/' as ESM Node.js project and install Chopsticks and polkadot-api", + "Create chopsticks-hub.yml (port 8000) and chopsticks-people.yml (port 8001) with mock-signature-host: true", + "Start both Chopsticks forks in separate terminals", + "Generate PAPI descriptors: npx papi add hubFork and npx papi add peopleFork", + "Create estimate-xcm-fees.ts with the inline script; substitute sender and recipient addresses", + "Run npx tsx estimate-xcm-fees.ts and sum the three fee components" + ], + "result": "All three fee components printed in planck; sum used as BuyExecution fee budget for the teleport", + "scenario": "Common scenario: calculate the total fee budget needed for a PAS teleport to People Chain", + "user_says": "How much PAS do I need to include in the BuyExecution for a teleport from Polkadot Hub to People Chain?" + }, + { + "actions": [ + "Check the People Chain runtime version in Chopsticks logs", + "Consult the source page for the XcmPaymentApi method names for that runtime version", + "Update the API call in estimate-xcm-fees.ts to match the correct method signature" + ], + "result": "Remote execution fee estimated using the correct XcmPaymentApi method for the chain runtime", + "scenario": "Edge case: remote execution fee query fails with XcmPaymentApi not found", + "user_says": "The script errors with 'query_xcm_weight is not a function' on the People Chain fork" + } + ], + "id": "estimate-xcm-fees-teleport", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Internet connection to fork chain state from Polkadot Hub TestNet (paseo_asset_hub)", + "Internet connection to fork People Chain TestNet (wss://people-paseo.rpc.amforc.com)" + ], + "runtime": [ + "Node.js v18+ and npm", + "npx (bundled with npm v5.2+)", + "Chopsticks CLI installed globally: npm install -g @acala-network/chopsticks" + ] + }, + "primary_page": "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md", + "project_structure": "xcm-fee-estimation/\n├── .chopsticks/\n│ ├── paseo-asset-hub.yml\n│ └── paseo-people-chain.yml\n├── .papi/\n│ └── descriptors/\n├── teleport-polkadot-hub-to-people-chain.ts\n├── tsconfig.json\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/send-transactions/interoperability/estimate-xcm-fees", + "branch": "master", + "files": [ + { + "description": "Complete PAPI TypeScript script that estimates local, delivery, and remote XCM fees for a teleport from Polkadot Hub TestNet to People Chain using XCM V5", + "path": "teleport-polkadot-hub-to-people-chain.ts" + }, + { + "description": "Chopsticks configuration for forking Polkadot Hub TestNet on port 8000", + "path": "paseo-asset-hub.yml" + }, + { + "description": "Chopsticks configuration for forking Paseo People Chain on port 8001", + "path": "paseo-people-chain.yml" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" + ], + "steps": [ + { + "action": "Create project directory and install dependencies", + "commands": [ + "mkdir xcm-fee-estimation && cd xcm-fee-estimation", + "npm init -y", + "npm install --save-dev @types/node@^22.12.0 ts-node@^10.9.2 typescript@^5.7.3", + "npm install --save @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers polkadot-api", + "npx tsc --init" + ], + "description": "Create 'xcm-fee-estimation' as a Node.js project and install PAPI and hdkd libraries for key derivation, plus TypeScript tooling.", + "order": 1, + "working_directory": "." + }, + { + "action": "Generate PAPI type descriptors for both chains", + "commands": [ + "npx papi add polkadotHub -n paseo_asset_hub", + "npx papi add paseoPeopleChain -w wss://people-paseo.rpc.amforc.com" + ], + "description": "Generate typed descriptors for Polkadot Hub TestNet (named 'polkadotHub', using the built-in 'paseo_asset_hub' network ID) and People Chain TestNet (named 'paseoPeopleChain', using its WebSocket endpoint). Both must be generated before writing the script.", + "order": 2, + "working_directory": "xcm-fee-estimation" + }, + { + "action": "Create the fee estimation script", + "description": "Fetch the reference file and save it as 'teleport-polkadot-hub-to-people-chain.ts'. The script: (1) connects to both Polkadot Hub and People Chain via WebSocket, (2) constructs an XCM V5 teleport transaction, (3) estimates local execution fees on Polkadot Hub by computing XCM weight then converting weight to PAS, (4) dry-runs the XCM to capture forwarded messages and compute delivery fees, (5) estimates remote execution fees on People Chain by recomputing forwarded XCM weight and converting to PAS, (6) sums all components and prints a breakdown. Replace the sender and recipient SS58 addresses with actual addresses before running.", + "order": 3, + "reference_file": "teleport-polkadot-hub-to-people-chain.ts", + "working_directory": "xcm-fee-estimation" + }, + { + "action": "Create Chopsticks configuration files", + "commands": [ + "mkdir -p .chopsticks" + ], + "description": "Create a '.chopsticks' directory and add two config files:\n\nFile 1 — '.chopsticks/paseo-asset-hub.yml':\n```yaml\nendpoint: wss://asset-hub-paseo.dotters.network\nport: 8000\n```\n\nFile 2 — '.chopsticks/paseo-people-chain.yml':\n```yaml\nendpoint: wss://people-paseo.rpc.amforc.com\nport: 8001\n```\n\nThese fork Polkadot Hub on port 8000 and People Chain on port 8001. The reference configs are available at the reference_code base_path as paseo-asset-hub.yml and paseo-people-chain.yml.", + "order": 4, + "working_directory": "xcm-fee-estimation" + }, + { + "action": "Start both Chopsticks forks", + "commands": [ + "chopsticks --config=.chopsticks/paseo-people-chain.yml", + "chopsticks --config=.chopsticks/paseo-asset-hub.yml" + ], + "description": "Start each Chopsticks instance in a separate terminal. The first forks People Chain on port 8001; the second forks Polkadot Hub on port 8000. Both must be running before proceeding. The first run downloads several MB of state.", + "expected_output": "Listening on port 8001\nListening on port 8000", + "order": 5, + "working_directory": "xcm-fee-estimation" + }, + { + "action": "Run the fee estimation script", + "commands": [ + "npx ts-node teleport-polkadot-hub-to-people-chain.ts" + ], + "description": "Execute the script while both Chopsticks forks are running. The output shows all three fee components and their total. The sum is the total fee budget needed for the XCM BuyExecution instruction. If an XcmPaymentApi method is not available, check the source page for the correct API method names for your chain runtime version.", + "expected_output": "Local execution fee: ...\nDelivery fee: ...\nRemote execution fee: ...\nTotal fee: ...", + "order": 6, + "working_directory": "xcm-fee-estimation" + } + ], + "supplementary_context": { + "description": "Load these pages for XCM transfer execution, Chopsticks configuration, or debug techniques.", + "pages": [ + { + "relevance": "How to dry-run a full XCM message after fee estimation to verify it executes correctly before submitting.", + "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", + "title": "Replay and Dry Run XCMs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" + }, + { + "relevance": "How to submit the actual XCM teleport transfer once fees are estimated and verified.", + "slug": "chain-interactions-send-transactions-interoperability-transfer-assets-parachains", + "title": "Transfer Assets Between Parachains", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" + }, + { + "relevance": "Full Chopsticks reference including multi-chain fork setup, config options, and mock-signature details.", + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md" + } + ] + }, + "title": "Estimate XCM Fees for Asset Teleport", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the Polkadot SDK Parachain Template, compiles it in release mode, and runs a local development node to verify the build and environment. Use as the first step in the parachain launch workflow before registering on Paseo testnet or a local relay chain. Requires the Polkadot SDK build environment from the install-polkadot-sdk skill. The template provides a minimal parachain runtime with Aura consensus, FRAME pallets, and a pre-configured Cumulus integration. Trigger phrases: 'parachain template', 'set up parachain Polkadot SDK', 'clone parachain template', 'build parachain template', 'Polkadot SDK parachain starter'. First step in the three-page parachain launch series.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The wasm32-unknown-unknown target is not installed in the Rust toolchain.", + "pattern": "error[E0463]: can't find crate for 'std' / wasm32 target missing", + "resolution": "Run: rustup target add wasm32-unknown-unknown. If using a specific toolchain version, prefix with rustup target add wasm32-unknown-unknown --toolchain stable." + }, + { + "cause": "C/C++ build tools are not installed on the system.", + "pattern": "error: linking with 'cc' failed / linker not found", + "resolution": "On Ubuntu/Debian: sudo apt install build-essential clang. On macOS: xcode-select --install. Then re-run cargo build --release." + }, + { + "cause": "Insufficient RAM for parallel Rust compilation — cargo uses one thread per CPU core by default.", + "pattern": "Killed / process killed during compilation / OOM", + "resolution": "Reduce parallel compilation jobs: CARGO_BUILD_JOBS=2 cargo build --release. Consider adding swap space on Linux: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile." + }, + { + "cause": "Cargo.lock is out of sync with the current Polkadot SDK release pinned in Cargo.toml.", + "pattern": "error: package 'polkadot-sdk' not found / version mismatch in Cargo.lock", + "resolution": "Run cargo update to refresh the lock file to the latest compatible versions, then retry cargo build --release." + } + ], + "examples": [ + { + "actions": [ + "Confirm Rust toolchain with wasm32 target is installed (install-polkadot-sdk skill if not)", + "Clone polkadot-sdk-parachain-template from GitHub", + "Run cargo build --release in the cloned directory (allow 20-60 minutes)", + "Verify with ./target/release/parachain-template-node --version", + "Start in dev mode with ./target/release/parachain-template-node --dev to confirm it works" + ], + "result": "Template node built and running in dev mode; environment confirmed ready for the next parachain launch step", + "scenario": "Common scenario: first-time parachain developer setting up the template", + "user_says": "How do I get the Polkadot SDK Parachain Template running on my machine?" + }, + { + "actions": [ + "Set CARGO_BUILD_JOBS=2 to limit parallel compilation threads", + "Add at least 4 GB of swap space: sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile", + "Re-run CARGO_BUILD_JOBS=2 cargo build --release" + ], + "result": "Build completes successfully with reduced memory pressure from limited parallel jobs", + "scenario": "Edge case: build is killed due to out-of-memory on a low-spec machine", + "user_says": "The cargo build process gets killed partway through on my 4 GB RAM machine" + } + ], + "id": "set-up-parachain-template", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Rust toolchain with wasm32-unknown-unknown target — complete the install-polkadot-sdk skill first", + "Git", + "Disk: at least 5 GB free for the build artifacts", + "RAM: 8 GB minimum; 16 GB recommended for parallel compilation" + ] + }, + "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", + "reference_code": { + "base_path": "polkadot-docs/parachains/parachain-template", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-cookbook" + }, + "source_pages": [ + "parachains/launch-a-parachain/set-up-the-parachain-template.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK Parachain Template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git", + "cd polkadot-sdk-parachain-template" + ], + "description": "Clone the official Polkadot SDK Parachain Template repository. The template contains a minimal parachain runtime with Aura consensus, standard FRAME pallets, and Cumulus integration for relay chain connectivity. It includes both a node binary and a runtime Wasm blob.", + "order": 1, + "working_directory": "." + }, + { + "action": "Review the template directory structure", + "description": "Familiarize yourself with the key directories before building: 'node/' contains the node binary crate (CLI, RPC, service); 'runtime/' contains the parachain runtime (pallets, weights, Wasm); 'pallets/' contains a sample custom pallet. The 'Cargo.toml' at the root defines the workspace. No files need to be created or modified for the initial setup — the default configuration targets a local development network.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Build the parachain node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the parachain node and runtime in release mode. This step is time-consuming (20-60 minutes on first build) because it compiles the entire Polkadot SDK dependency tree. Subsequent builds use the Rust incremental cache and are significantly faster. If compilation fails with 'linker not found', install the build-essential package (Linux) or Xcode command line tools (macOS). On low-memory machines (less than 8 GB RAM), reduce parallel jobs: CARGO_BUILD_JOBS=2 cargo build --release.", + "expected_output": "Compiling parachain-template-node v0.1.0\n...\nFinished release [optimized] target(s)", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Verify the binary was produced", + "commands": [ + "./target/release/parachain-template-node --version" + ], + "description": "Confirm the binary exists and reports its version. If the command fails with 'No such file or directory', the build did not complete successfully — review the cargo build output for compilation errors.", + "expected_output": "parachain-template-node 0.1.0-...", + "order": 4, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Run the node in local development mode", + "commands": [ + "./target/release/parachain-template-node --dev" + ], + "description": "Start the node in development mode. In --dev mode the node runs as a single collator without a relay chain connection, using a local chain spec and instant block production. This verifies the binary works correctly. The node will not produce blocks in this mode without a connected relay chain — the 'Idle' output is expected. Press Ctrl+C to stop. The next step in the parachain launch series covers connecting to a relay chain.", + "expected_output": "Running in --dev mode\n...\nIdle (0 peers)", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template" + } + ], + "supplementary_context": { + "description": "Load these pages for the prerequisite SDK install, the next steps in the parachain launch series, or runtime concepts.", + "pages": [ + { + "relevance": "Prerequisite: install Rust, system dependencies, and the Polkadot SDK build environment before building the template.", + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md" + }, + { + "relevance": "Next step in the parachain launch series: acquire a para ID and slot on Paseo testnet after the template is built.", + "slug": "parachains-launch-a-parachain-deploy-to-polkadot", + "title": "Deploy on Polkadot", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/deploy-to-polkadot.md" + }, + { + "relevance": "Network endpoints and chain IDs for Paseo testnet and Polkadot mainnet parachain environments.", + "slug": "reference-parachains-networks", + "title": "Networks", + "url": "https://docs.polkadot.com/reference/parachains/networks.md" + } + ] + }, + "title": "Set Up the Polkadot SDK Parachain Template", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up a Polkadot Hub (Asset Hub) RPC node in archive or pruned mode via Docker or systemd, with an optional Ethereum-compatible JSON-RPC adapter (eth-rpc, part of pallet-revive) for EVM tooling. Covers chain spec download, rclone snapshot download for faster sync, Docker/systemd node setup, eth-rpc adapter configuration, and sync verification. Use when you need a self-hosted Polkadot Hub RPC endpoint for Substrate or Ethereum-compatible tools. Trigger phrases: 'run Polkadot Hub node', 'Asset Hub RPC node', 'Polkadot Hub Docker node', 'eth-rpc Polkadot Hub', 'Polkadot Hub archive node', 'pallet-revive eth-rpc'. No tokens required.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The data volume has run out of disk space.", + "pattern": "Error: No space left on device / database write error", + "resolution": "Increase the volume size, or switch from archive to pruned mode (--state-pruning=1000 --blocks-pruning=256). Check usage with: du -sh my-node-data/" + }, + { + "cause": "Another process is already using the required ports.", + "pattern": "EADDRINUSE port 9944 or 30333", + "resolution": "Change the port with --rpc-port=NEW_PORT for RPC and --port=NEW_P2P_PORT for P2P. Update firewall rules accordingly." + }, + { + "cause": "P2P ports 30333/30334 are blocked by firewall or bootnodes are temporarily unavailable.", + "pattern": "Peers count stays at 0 after several minutes", + "resolution": "Open TCP ports 30333 and 30334: sudo ufw allow 30333/tcp && sudo ufw allow 30334/tcp. For cloud VMs, update the security group. If peers do not connect after 10 minutes, check the Polkadot network status." + }, + { + "cause": "The Polkadot Hub node is not fully synced, or the RPC is not accessible at ws://127.0.0.1:9944.", + "pattern": "eth-rpc adapter fails to start / cannot connect to node", + "resolution": "Ensure the node is fully synced (currentBlock equals highestBlock). Verify the node has --unsafe-rpc-external enabled. Restart eth-rpc after confirming the node is synced." + }, + { + "cause": "The eth-rpc adapter is connected to the wrong chain or runtime.", + "pattern": "eth_chainId returns unexpected value", + "resolution": "Polkadot Hub (mainnet) does not have a fixed Ethereum chain ID in the same way as TestNet. For Paseo TestNet (chain ID: 420420417, hex: 0x18FF5B01), ensure the node runs --chain=asset-hub-paseo and verify eth_chainId returns 0x18FF5B01." + } + ], + "examples": [ + { + "actions": [ + "Download asset-hub-polkadot.json chain spec", + "Optionally download snapshots with rclone for faster sync", + "Run docker with --chain=/asset-hub-polkadot.json, --state-pruning=1000, --blocks-pruning=256, ports 30333, 30334, and 9944 exposed", + "Monitor sync with system_syncState; verify with system_health once synced" + ], + "result": "Polkadot Hub RPC node running, synced, serving requests on ws://localhost:9944", + "scenario": "Common scenario: run a pruned Polkadot Hub RPC node via Docker", + "user_says": "Run a Polkadot Hub RPC node using Docker" + }, + { + "actions": [ + "Verify the Polkadot Hub node is fully synced (currentBlock equals highestBlock)", + "Check eth-rpc adapter logs: docker logs -f eth-rpc", + "Confirm --node-rpc-url=ws://127.0.0.1:9944 matches the running node's RPC port", + "For Paseo TestNet, chain ID is 420420417 (0x18FF5B01) — verify eth_chainId returns this", + "Restart the eth-rpc adapter after the node is confirmed synced" + ], + "result": "MetaMask connects successfully with the correct Polkadot Hub chain ID", + "scenario": "Edge case: Ethereum tooling cannot connect via eth-rpc adapter", + "user_says": "MetaMask connects to port 8545 but shows the wrong chain ID or cannot connect" + } + ], + "id": "run-polkadot-hub-rpc-node", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Open TCP ports: 30333 (parachain P2P), 30334 (relay chain P2P), 9944 (WebSocket RPC), 9933 (HTTP RPC), 9615 (Prometheus), 8545 (Ethereum RPC if using eth-rpc adapter)", + "Public IP address and stable broadband connection (1 Gbps for production)" + ], + "runtime": [ + "Docker v24+ (for Docker method) OR a 64-bit Linux server with systemd (for binary method)", + "rclone (optional but recommended for snapshot download): https://rclone.org/downloads/", + "Disk: ~200 GB NVMe SSD for pruned node; ~1.2 TB NVMe SSD for archive node (~392 GB Asset Hub + ~822 GB relay chain pruned)", + "RAM: minimum 64 GB; 128 GB recommended for high traffic", + "CPU: 8+ cores; 16+ cores for high traffic" + ] + }, + "primary_page": "node-infrastructure/run-a-node/polkadot-hub-rpc.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "node-infrastructure/run-a-node/polkadot-hub-rpc.md" + ], + "steps": [ + { + "action": "Download chain specification", + "commands": [ + "curl -L https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/cumulus/parachains/chain-specs/asset-hub-polkadot.json -o asset-hub-polkadot.json" + ], + "description": "Download the official Polkadot Hub (Asset Hub) chain specification. This file defines network parameters, genesis state, and bootnodes.", + "order": 1, + "working_directory": "." + }, + { + "action": "Download snapshots (optional but recommended)", + "commands": [ + "mkdir -p my-node-data/chains/asset-hub-polkadot/db", + "mkdir -p my-node-data/chains/polkadot/db", + "export SNAPSHOT_URL_ASSET_HUB=\"https://snapshots.polkadot.io/polkadot-asset-hub-rocksdb-archive/INSERT_LATEST\"", + "rclone copyurl $SNAPSHOT_URL_ASSET_HUB/files.txt files.txt && rclone copy --progress --transfers 20 --http-url $SNAPSHOT_URL_ASSET_HUB --no-traverse --http-no-head --disable-http2 --inplace --no-gzip-encoding --size-only --retries 6 --retries-sleep 10s --files-from files.txt :http: my-node-data/chains/asset-hub-polkadot/db/ && rm files.txt", + "export SNAPSHOT_URL_RELAY=\"https://snapshots.polkadot.io/polkadot-rocksdb-prune/INSERT_LATEST\"", + "rclone copyurl $SNAPSHOT_URL_RELAY/files.txt files.txt && rclone copy --progress --transfers 20 --http-url $SNAPSHOT_URL_RELAY --no-traverse --http-no-head --disable-http2 --inplace --no-gzip-encoding --size-only --retries 6 --retries-sleep 10s --files-from files.txt :http: my-node-data/chains/polkadot/db/ && rm files.txt" + ], + "description": "Download pre-synced snapshots from https://snapshots.polkadot.io/ to cut initial sync time from days to hours. Replace INSERT_LATEST with the latest snapshot path. Archive node recommended for full eth-rpc compatibility. Skip if syncing from genesis.", + "order": 2, + "working_directory": "." + }, + { + "action": "Launch the Polkadot Hub node via Docker", + "commands": [ + "docker run -d --name polkadot-hub-rpc --restart unless-stopped -p 9944:9944 -p 9933:9933 -p 9615:9615 -p 30334:30334 -p 30333:30333 -v $(pwd)/asset-hub-polkadot.json:/asset-hub-polkadot.json -v $(pwd)/my-node-data:/data parity/polkadot-parachain:stable --name=PolkadotHubRPC --base-path=/data --chain=/asset-hub-polkadot.json --prometheus-external --prometheus-port 9615 --unsafe-rpc-external --rpc-port=9944 --rpc-cors=all --rpc-methods=safe --rpc-max-connections=1000 --state-pruning=archive --blocks-pruning=archive -- --base-path=/data --chain=polkadot --state-pruning=256 --blocks-pruning=256 --rpc-port=0" + ], + "description": "Launch Polkadot Hub RPC node (archive mode shown; for pruned, use --state-pruning=1000 --blocks-pruning=256). Key args: --unsafe-rpc-external enables external RPC, --rpc-methods=safe only allows safe methods, --rpc-max-connections=1000, the -- separator configures the embedded relay chain. For systemd, create /etc/systemd/system/polkadot-hub-rpc.service with equivalent ExecStart args (use --rpc-external instead of --unsafe-rpc-external behind a proxy).", + "order": 3, + "working_directory": "." + }, + { + "action": "Monitor sync and verify the endpoint", + "commands": [ + "curl -H 'Content-Type: application/json' -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"system_syncState\", \"params\":[]}' http://localhost:9944", + "curl -H 'Content-Type: application/json' -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"system_health\", \"params\":[]}' http://localhost:9944" + ], + "description": "Monitor sync: currentBlock equals highestBlock when fully synced. Verify health: isSyncing should be false and peers > 0. For Docker logs: docker logs -f polkadot-hub-rpc. For systemd logs: sudo journalctl -u polkadot-hub-rpc -f.", + "expected_output": "{\"result\":{\"startingBlock\":0,\"currentBlock\":3394816,\"highestBlock\":3394816}}", + "order": 4, + "working_directory": "." + }, + { + "action": "Run the Ethereum RPC adapter (optional)", + "commands": [ + "docker run -d --name eth-rpc --restart unless-stopped --network=host -v /var/lib/eth-rpc:/data paritypr/eth-rpc:master-1ea05e17 --node-rpc-url=ws://127.0.0.1:9944 --rpc-port=8545 --base-path=/data --unsafe-rpc-external --rpc-cors=all" + ], + "description": "Optional: Run the Ethereum RPC adapter (part of pallet-revive) to enable Ethereum JSON-RPC compatibility on port 8545 for MetaMask, Hardhat, Ethers.js, etc. Requires the Polkadot Hub node to be FULLY SYNCED first. Archive node recommended (--state-pruning=archive) for full historical query support. The --base-path flag persists the eth-rpc.db database across restarts. Check https://hub.docker.com/r/paritypr/eth-rpc/tags for the latest image tag (format: master-). For systemd, build from source: cargo build -p pallet-revive-eth-rpc --bin eth-rpc --release.", + "order": 5, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to add TLS/WSS, run a parachain RPC node, or understand the chain configuration.", + "pages": [ + { + "relevance": "How to run RPC nodes for other system parachains (People Chain, Bridge Hub, Coretime Chain) using the same polkadot-parachain binary.", + "slug": "node-infrastructure-run-a-node-parachain-rpc", + "title": "Run a Parachain RPC Node", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/parachain-rpc.md" + }, + { + "relevance": "How to add a WSS (TLS) reverse proxy in front of the RPC port using nginx — required for production endpoints.", + "slug": "node-infrastructure-run-a-node-relay-chain-secure-wss", + "title": "Set Up Secure WebSocket", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-node/relay-chain/secure-wss.md" + }, + { + "relevance": "Complete list of supported Ethereum JSON-RPC methods and example queries for verifying the eth-rpc adapter.", + "slug": "smart-contracts-for-eth-devs-json-rpc-apis", + "title": "JSON-RPC APIs", + "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/json-rpc-apis/" + } + ] + }, + "title": "Run an RPC Node for Polkadot Hub", + "version": "1.1.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs the three Polkadot validator binaries (polkadot, polkadot-prepare-worker, polkadot-execute-worker) needed to run a validator node, and prepares the server with NTP time synchronization and Landlock security. Covers four installation paths: GPG-verified curl download, APT package manager (Ubuntu/Debian), Docker image, or building from source. Use when provisioning a new validator server or upgrading binaries on an existing one. Trigger phrases: 'install Polkadot validator binaries', 'set up validator node', 'install polkadot-prepare-worker', 'configure validator server'. First step in the full validator onboarding flow before key management and bonding.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The downloaded binary was corrupted or tampered with during download.", + "pattern": "gpg: BAD signature", + "resolution": "Delete all downloaded files and repeat step 3 from a trusted network connection. Do not use any binary that fails GPG verification." + }, + { + "cause": "The binary was not moved to a directory in $PATH, or the directory is not in $PATH.", + "pattern": "polkadot: command not found after installation", + "resolution": "Run 'echo $PATH' and verify /usr/local/bin is listed. If not, run 'export PATH=$PATH:/usr/local/bin'. For persistence, add this export to /etc/environment or ~/.bashrc." + }, + { + "cause": "The three binaries were downloaded from different releases.", + "pattern": "version mismatch between polkadot and worker binaries", + "resolution": "Remove all three binaries and re-download all from the same release tag (e.g., polkadot-stable2512-2) from https://github.com/paritytech/polkadot-sdk/releases. All three must share the same version to interoperate." + }, + { + "cause": "The running kernel is older than 5.13 or was compiled without Landlock.", + "pattern": "Landlock not in dmesg output", + "resolution": "Upgrade the kernel: on Ubuntu, run 'sudo apt-get install linux-generic-hwe-22.04' then reboot. Verify with 'uname -r' that the new kernel is 5.16 or later. Alternatively, consult https://docs.kernel.org/userspace-api/landlock.html#kernel-support." + } + ], + "examples": [ + { + "actions": [ + "Run 'timedatectl' to check NTP sync; install ntp if not synced", + "Run 'dmesg | grep landlock' to verify Landlock is active", + "Download polkadot, polkadot-prepare-worker, and polkadot-execute-worker from release polkadot-stable2512-2", + "Verify each binary with GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", + "Move all three to /usr/local/bin and run --version on each to confirm matching versions" + ], + "result": "All three binaries installed at polkadot-stable2512-2, GPG verified, and accessible system-wide; ready for key management", + "scenario": "Common scenario: install on a fresh Ubuntu 22.04 server", + "user_says": "Install the Polkadot validator binaries on a new Ubuntu server" + }, + { + "actions": [ + "Import the Parity GPG key into /usr/share/keyrings/parity.gpg", + "Add the Parity APT repository to /etc/apt/sources.list.d/parity.list", + "Run 'apt update && apt install parity-keyring polkadot'", + "Verify with 'polkadot --version', 'polkadot-execute-worker --version', 'polkadot-prepare-worker --version'" + ], + "result": "Polkadot and both worker binaries installed via APT with automatic future upgrades via apt upgrade", + "scenario": "Edge case: APT install on a Debian-based server without curl", + "user_says": "Install Polkadot on Ubuntu using apt instead of downloading manually" + } + ], + "id": "set-up-polkadot-validator-node", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Linux server with kernel 5.16 or later (Ubuntu 22.04 LTS or newer recommended)", + "64-bit x86-64 or ARM64 architecture", + "Root or sudo access" + ] + }, + "primary_page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" + ], + "steps": [ + { + "action": "Verify or install NTP time synchronization", + "commands": [ + "timedatectl" + ], + "description": "Validators need accurate system time to avoid missing block authorship. Check NTP status with 'timedatectl'. If the output does not show 'System clock synchronized: yes', install and start NTP:\n\n sudo apt-get install ntp\n sudo ntpq -p\n\nThe 'ntpq -p' output should list one or more active peers with a '+' or '*' status prefix. Skipping NTP can cause the validator to miss blocks due to minor clock drift.", + "expected_output": "System clock synchronized: yes", + "order": 1, + "working_directory": "." + }, + { + "action": "Verify Landlock security is activated", + "commands": [ + "dmesg | grep landlock || journalctl -kg landlock" + ], + "description": "Landlock (Linux kernel 5.13+) is required by the Polkadot validator for sandboxing. Run the command above as root. If Landlock is active, you will see kernel log entries referencing 'landlock'. If there is no output, your kernel does not have Landlock enabled. Most modern Ubuntu/Debian kernels (5.16+) include it by default. If Landlock is absent, upgrade to a supported kernel or rebuild with Landlock enabled (see https://docs.kernel.org/userspace-api/landlock.html#kernel-support). Polkadot validators can run without Landlock but will operate without the sandboxing protection.", + "order": 2, + "working_directory": "." + }, + { + "action": "Install the Polkadot binaries via curl with GPG verification (recommended method)", + "commands": [ + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot.asc", + "gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE", + "gpg --verify polkadot.asc", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker.asc", + "gpg --verify polkadot-prepare-worker.asc", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker", + "curl -LO https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker.asc", + "gpg --verify polkadot-execute-worker.asc", + "chmod +x polkadot polkadot-prepare-worker polkadot-execute-worker", + "sudo mv polkadot polkadot-prepare-worker polkadot-execute-worker /usr/local/bin/" + ], + "description": "Download all three binaries for release polkadot-stable2512-2 and verify each with GPG. The release tag is polkadot-stable2512-2 — check https://github.com/paritytech/polkadot-sdk/releases for a newer stable release and replace all occurrences of 'polkadot-stable2512-2' in the commands if you want a different version. GPG key 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE is the ParityReleases key for current releases. A 'Good signature' message after each --verify confirms the binary is authentic. If GPG verification fails with 'BAD signature', discard the binary and re-download.\n\nALTERNATIVE — APT package manager (Ubuntu/Debian):\n gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE\n gpg --export 90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE > /usr/share/keyrings/parity.gpg\n echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list\n apt update && apt install parity-keyring polkadot\n\nALTERNATIVE — Docker:\n docker pull parity/polkadot:stable2512-2", + "order": 3, + "working_directory": "." + }, + { + "action": "Verify all three binaries are installed and versions match", + "commands": [ + "polkadot --version", + "polkadot-execute-worker --version", + "polkadot-prepare-worker --version" + ], + "description": "Run all three version commands and confirm the version strings are identical. If one binary is missing ('command not found'), ensure it was moved to /usr/local/bin/ in step 3 and that /usr/local/bin is in $PATH (echo $PATH). All three binaries must be in the same directory for the validator to function. If versions differ, re-download the mismatched binary from the same release tag used in step 3.", + "expected_output": "All three commands print the same version string, e.g. 'polkadot stable2512-2-...'", + "order": 4, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for key management, starting validation, or understanding validator requirements and operational tasks.", + "pages": [ + { + "relevance": "Minimum hardware and skill requirements before starting a validator — read before this skill.", + "slug": "node-infrastructure-run-a-validator-requirements", + "title": "Validator Requirements", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/requirements.md" + }, + { + "relevance": "Next step after binary installation: generating and registering session keys.", + "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-key-management", + "title": "Validator Key Management", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md" + }, + { + "relevance": "Syncing the node, bonding DOT, and activating the validator after binary setup.", + "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-start-validating", + "title": "Start Validating", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md" + }, + { + "relevance": "Prometheus and Grafana monitoring stack setup and security best practices for running validators.", + "slug": "node-infrastructure-run-a-validator-operational-tasks-general-management", + "title": "General Management", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/operational-tasks/general-management.md" + } + ] + }, + "title": "Set Up a Polkadot Validator Node", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The RPC endpoint URL is incorrect, the chain is temporarily unavailable, or the WSS endpoint requires authentication.", + "pattern": "Cannot connect to endpoint / WebSocket connection failed", + "resolution": "Verify the endpoint URL by connecting with wscat: wscat -c wss://your-endpoint. Try a public endpoint such as wss://polkadot-rpc.dwellir.com for Polkadot. Check the chain's status page or Discord for outages." + }, + { + "cause": "Global npm installation did not add the binary to PATH, or Chopsticks was installed locally without npx.", + "pattern": "chopsticks: command not found", + "resolution": "For global installs, run 'npm root -g' to find the global bin directory and ensure it is in $PATH. For local installs, use 'npx @acala-network/chopsticks' instead of 'chopsticks'." + }, + { + "cause": "Chopsticks uses the Smoldot light client which only supports the native Polkadot SDK API, not Ethereum JSON-RPC.", + "pattern": "MetaMask / Ethereum tooling cannot connect to the fork", + "resolution": "This is a known limitation. Chopsticks forks cannot be used with MetaMask or ethers.js. For EVM-compatible local testing, use a local Polkadot Hub development node instead." + }, + { + "cause": "The local fork has diverged from the live chain state, often because storage was manually modified before replay.", + "pattern": "Error replaying block: state root mismatch", + "resolution": "Restart Chopsticks without --db to use a fresh state (or delete the SQLite db file). Avoid modifying storage with dev_setStorage before running run-block." + } + ], + "examples": [ + { + "actions": [ + "Install Chopsticks 1.2.7 globally: npm i -g @acala-network/chopsticks@1.2.7", + "Download the Polkadot config from the Chopsticks repo: curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml", + "Start the fork: npx @acala-network/chopsticks --config=polkadot.yml", + "Wait for 'Running on port 8000', then connect via Polkadot.js API at ws://localhost:8000" + ], + "result": "Local Polkadot fork running at ws://localhost:8000; ready for transaction testing without spending real tokens", + "scenario": "Common scenario: fork Polkadot mainnet and inspect state", + "user_says": "Fork the Polkadot mainnet locally with Chopsticks so I can test transactions" + }, + { + "actions": [ + "Add an import-storage section to the YAML config overriding Alice's System.Account free balance to a large value", + "Restart the fork with the updated config", + "Alternatively, send a dev_setStorage WebSocket command after the fork is running to set Alice's balance" + ], + "result": "Alice's account has the specified token balance on the local fork; transactions that require funds now succeed", + "scenario": "Edge case: fork requires a funded test account", + "user_says": "I need Alice to have tokens on my forked chain for testing" + } + ], + "id": "set-up-chopsticks-fork", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Access to a WebSocket RPC endpoint for the chain to fork (e.g., wss://polkadot-rpc.dwellir.com)" + ], + "runtime": [ + "Node.js v18+", + "npm (included with Node.js) or yarn or pnpm" + ] + }, + "primary_page": "reference/tools/chopsticks.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/chopsticks.md" + ], + "steps": [ + { + "action": "Install Chopsticks globally", + "commands": [ + "npm i -g @acala-network/chopsticks@1.2.7" + ], + "description": "Install Chopsticks 1.2.7 globally. After installation, 'chopsticks' is available system-wide. For a project-local installation instead: mkdir my-chopsticks-project && cd my-chopsticks-project && npm init -y && npm i @acala-network/chopsticks@1.2.7. With local installation, run via 'npx @acala-network/chopsticks' instead of 'chopsticks' in subsequent steps.", + "expected_output": "added N packages", + "order": 1, + "working_directory": "." + }, + { + "action": "Create a fork configuration file", + "description": "Create a YAML configuration file (e.g., polkadot.yml) describing the chain to fork. Pre-built configs for common Polkadot SDK chains are available at https://github.com/AcalaNetwork/chopsticks/tree/master/configs — download one with:\n curl -sL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml -o polkadot.yml\n\n```yaml\nA minimal config for Polkadot looks like:\n endpoint: wss://polkadot-rpc.dwellir.com\n mock-signature-host: true\n block: 1000 # optional: fork from this block number\n\nThe 'import-storage' section (optional) lets you override storage values before the fork starts — useful for funding test accounts:\n import-storage:\n System:\n Account:\n - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # Alice\n - data:\n free: '100000000000000000'\n```\n\nReplace the endpoint with the WSS RPC URL for your target chain.", + "order": 2, + "working_directory": "." + }, + { + "action": "Start the chain fork", + "commands": [ + "npx @acala-network/chopsticks --config=polkadot.yml" + ], + "description": "Start the fork using the YAML config from step 2. Alternatively, fork without a config file using CLI flags:\n npx @acala-network/chopsticks --endpoint wss://polkadot-rpc.dwellir.com --block 100\n\nChoose the block number to fork from with --block (omit to use the chain head). Chopsticks caches state locally in an SQLite DB (set --db ./chopsticks.db to reuse between runs). When the fork is ready, you will see 'Running on port 8000'. The local fork is now accessible at ws://localhost:8000.", + "expected_output": "Running on port 8000", + "order": 3, + "working_directory": "." + }, + { + "action": "Interact with the fork", + "description": "Connect to the running fork at ws://localhost:8000.\n\n```typescript\nVia Polkadot.js API (TypeScript):\n import { ApiPromise, WsProvider } from '@polkadot/api';\n const api = await ApiPromise.create({ provider: new WsProvider('ws://localhost:8000') });\n const block = await api.rpc.chain.getBlock();\n console.log('Fork head:', block.block.header.number.toNumber());\n```\n\nVia Polkadot.js Apps UI: navigate to polkadot.js.org/apps, select network > Development > Custom, enter ws://localhost:8000, and click Switch.\n\nTo manipulate storage via the dev_setStorage WebSocket command (using wscat or a JS client):\n { \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"dev_setStorage\", \"params\": [{ \"System\": { \"Account\": [[\"0xALICE_KEY\", { \"data\": { \"free\": 1000000000000 } }]] } }] }", + "order": 4, + "working_directory": "." + }, + { + "action": "Replay a historical block (optional)", + "commands": [ + "npx @acala-network/chopsticks run-block --endpoint wss://polkadot-rpc.dwellir.com --output-path ./block-1000-output.json --block 1000" + ], + "description": "Replay block 1000 from Polkadot and save the state diff to a JSON file. Replace the endpoint and block number as needed. The output includes detailed storage changes and runtime logs. Add --html to generate an HTML visualization of the storage diff. This is useful for debugging why a specific transaction produced unexpected state changes.", + "order": 5, + "working_directory": "." + }, + { + "action": "Test XCM between multiple chains (optional)", + "commands": [ + "npx @acala-network/chopsticks xcm --r polkadot --p moonbeam --p astar" + ], + "description": "Fork multiple chains simultaneously to test XCM message flow. The --r flag specifies the relay chain config (by name or file path) and --p specifies parachain configs. After startup, each chain runs on a different port (printed in the output). Send XCM messages between the forked chains using the Polkadot.js API or dev_* WebSocket commands. Note: XCM testing requires config files for each chain; use pre-built configs from https://github.com/AcalaNetwork/chopsticks/tree/master/configs.", + "order": 6, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for parachain-specific fork tutorials, XCM debugging, or the fork-a-parachain cookbook tutorial.", + "pages": [ + { + "relevance": "Step-by-step cookbook tutorial for forking a parachain with Chopsticks — same tool, focused on the parachain testing workflow with a CI-backed example.", + "slug": "parachains-testing-fork-a-parachain", + "title": "Fork a Parachain Using Chopsticks", + "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md" + }, + { + "relevance": "Using Chopsticks to replay and dry-run XCM messages — advanced XCM debugging workflow.", + "slug": "chain-interactions-send-transactions-interoperability-debug-and-preview-xcms", + "title": "Replay and Dry Run XCMs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" + }, + { + "relevance": "Estimating XCM fees for teleports using Chopsticks local forks of Polkadot Hub and People Chain.", + "slug": "chain-interactions-send-transactions-interoperability-estimate-xcm-fees", + "title": "XCM Fee Estimation", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" + } + ] + }, + "title": "Set Up and Use Chopsticks for Chain Forking", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs and configures Moonwall, an end-to-end testing framework for Polkadot SDK-based blockchains. Covers global and local npm installation, interactive configuration initialization, moonwall.config.json structure, writing test suites with the describeSuite function, and running tests via the CLI. Use when you need a standardized test environment for a Substrate-based chain — local dev node, remote testnet, or Chopsticks fork. Trigger phrases: 'set up Moonwall', 'e2e tests Polkadot SDK', 'Moonwall testing framework', 'describeSuite Moonwall', 'install moonwall cli'. Version: 5.18.3.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Global npm installation did not add the binary to PATH.", + "pattern": "moonwall: command not found", + "resolution": "Run 'npm root -g' to find the global bin directory and verify it is in $PATH. Alternatively, use 'npx moonwall' for a local installation." + }, + { + "cause": "Moonwall was not installed or the local installation is missing.", + "pattern": "Cannot find module '@moonwall/cli'", + "resolution": "Run 'npm install @moonwall/cli@5.18.3' in the project directory. For global, run 'npm install -g @moonwall/cli@5.18.3'." + }, + { + "cause": "The node specified in the moonwall.config foundation is not running, or the endpoint URL is incorrect.", + "pattern": "Error: Cannot connect to endpoint ws://localhost:9944", + "resolution": "Start the node manually and verify it is listening on the configured port before running Moonwall. For 'dev' foundation, ensure the binPath points to an executable binary and the --dev flag is appropriate for your chain." + }, + { + "cause": "The test environment took too long to start or a test operation exceeded the global timeout.", + "pattern": "Timeout exceeded for test suite", + "resolution": "Increase the 'timeout' value in moonwall.config (e.g., from 30000 to 60000 ms). For slow chain operations like block finalization, add explicit wait-for-block assertions rather than fixed sleeps." + } + ], + "examples": [ + { + "actions": [ + "Install Moonwall 5.18.3 globally: npm install -g @moonwall/cli@5.18.3", + "Ask the user to run 'moonwall init' and complete the interactive wizard, choosing 'dev' foundation", + "Update moonwall.config with the correct node binPath and ws://localhost:9944 connection", + "Create a test file in ./tests using describeSuite with a foundationMethods of 'dev'", + "Run 'moonwall test default_env -c moonwall.config'" + ], + "result": "Moonwall launches the node binary, runs all test suites, and reports pass/fail results per test case", + "scenario": "Common scenario: run e2e tests against a local dev node", + "user_says": "Set up Moonwall to run e2e tests for my Polkadot SDK chain" + }, + { + "actions": [ + "Check the foundation type in moonwall.config — for 'dev', verify binPath is correct and the binary has execute permission", + "For 'read_only' or 'chopsticks', start the node or Chopsticks fork manually and confirm it is listening on the configured endpoint", + "Increase the connection timeout in moonwall.config if the node starts slowly" + ], + "result": "Node is reachable; Moonwall connects and runs tests", + "scenario": "Edge case: test environment fails to start", + "user_says": "Moonwall says it cannot connect to the node endpoint" + } + ], + "id": "set-up-e2e-testing-moonwall", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "A running Polkadot SDK node or Chopsticks fork for the test environment (e.g., ws://localhost:9944 or ws://localhost:8000)" + ], + "runtime": [ + "Node.js v20.10 or higher", + "npm, yarn, or pnpm" + ] + }, + "primary_page": "reference/tools/moonwall.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/moonwall.md" + ], + "steps": [ + { + "action": "Install Moonwall globally", + "commands": [ + "npm install -g @moonwall/cli@5.18.3" + ], + "description": "Install Moonwall 5.18.3 globally so the 'moonwall' command is available system-wide. For a project-local installation: mkdir my-moonwall-project && cd my-moonwall-project && npm init -y && npm install @moonwall/cli@5.18.3. With a local install, run Moonwall via 'npx moonwall' or add it to package.json scripts.", + "expected_output": "added N packages", + "order": 1, + "working_directory": "." + }, + { + "action": "Initialize the Moonwall configuration via the interactive wizard", + "commands": [ + "moonwall init" + ], + "description": "The 'moonwall init' command launches an interactive wizard to create your moonwall.config file. Delegate this step to the user — do not attempt to automate it. Ask the user to run 'moonwall init' in their terminal and answer the prompts:\n\n - label: a name for the test configuration (e.g., my-chain-tests)\n - global timeout: maximum time in ms for test execution (e.g., 30000)\n - environment name: name for the test environment (e.g., default_env)\n - network foundation: type of blockchain environment (e.g., 'dev' for a local binary node, 'chopsticks' for a fork)\n - tests directory: path for test files (e.g., ./tests)\n\nPress Enter to accept defaults or enter custom values. The wizard generates a moonwall.config file in the current directory. Ask the user to confirm when the wizard completes before proceeding.", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Review and customize moonwall.config.json", + "description": "Open the generated moonwall.config file and customize the environment settings. Key sections to update:\n\n - foundation.type: 'dev' (local binary), 'chopsticks' (fork), or 'read_only' (remote RPC)\n - For 'dev' foundation, set binPath to the path of your chain binary\n - connections[0].endpoint: the WebSocket URL of the node (e.g., ws://localhost:9944)\n - connections[0].type: the provider type (e.g., 'polkadotJs' for Polkadot.js API)\n\nExample configuration for testing a local dev node:\n {\n \"label\": \"my-chain-tests\",\n \"timeout\": 30000,\n \"environments\": [{\n \"name\": \"default_env\",\n \"foundation\": { \"type\": \"dev\", \"launchSpec\": [{ \"name\": \"node\", \"binPath\": \"./your-node-binary\", \"options\": [\"--dev\"] }] },\n \"testFileDir\": [\"./tests\"],\n \"connections\": [{ \"name\": \"api\", \"type\": \"polkadotJs\", \"endpoints\": [\"ws://localhost:9944\"] }]\n }]\n }", + "order": 3, + "working_directory": "." + }, + { + "action": "Write a test suite", + "description": "Create a test file in the tests directory (e.g., ./tests/balance.test.ts). Moonwall uses the describeSuite function to define test suites:\n\n```typescript\n import { describeSuite, expect } from '@moonwall/cli';\n\n describeSuite({\n id: 'D01',\n title: 'Balance transfer test',\n foundationMethods: 'dev',\n testCases: ({ it, context }) => {\n it('C100', 'Alice should have a balance', async () => {\n const api = context.polkadotJs();\n const accounts = await api.query.system.account('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');\n expect(accounts.data.free.toBigInt()).toBeGreaterThan(0n);\n });\n },\n });\n```\n\nEach test case requires a unique ID ('C100'), a title, and an async function receiving it (test runner) and context (provides the polkadotJs API). The API is initialized by Moonwall before tests run — do not create API connections manually.", + "order": 4, + "working_directory": "." + }, + { + "action": "Run the tests", + "commands": [ + "moonwall test default_env -c moonwall.config" + ], + "description": "Run the tests against the 'default_env' environment defined in moonwall.config. Replace 'default_env' with the environment name you chose in step 2. The test runner outputs:\n - Test suite execution status (pass/fail per suite)\n - Individual test case results\n - Execution time per test\n - Detailed error logs for failed tests\n\nIf the foundation type is 'dev', Moonwall launches the node binary automatically and tears it down after tests complete. If using 'read_only' or 'chopsticks', ensure the endpoint is reachable before running tests.", + "expected_output": "All test suites passed", + "order": 5, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for Chopsticks fork setup (to use as Moonwall's test environment) or for a local development node to test against.", + "pages": [ + { + "relevance": "Setting up a Chopsticks fork as the 'chopsticks' foundation type for Moonwall tests.", + "slug": "reference-tools-chopsticks", + "title": "Chopsticks", + "url": "https://docs.polkadot.com/reference/tools/chopsticks.md" + }, + { + "relevance": "How to run a local development node to use as the 'dev' foundation in Moonwall.", + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md" + } + ] + }, + "title": "Set Up End-to-End Testing with Moonwall", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Scaffolds a full-stack Polkadot Hub dApp: deploys a Solidity Storage contract via Hardhat Ignition to TestNet, then builds a Next.js 14 frontend with Viem wallet connect, read, and write components. Use when building a complete end-to-end smart contract application on Polkadot Hub from scratch. Trigger phrases: 'build a dapp polkadot', 'zero to hero tutorial', 'smart contract with frontend viem', 'deploy and connect next.js'. Covers dotenv private key security, 5000 gwei gas config, evmVersion cancun, Ignition recovery, and Next.js/Viem component wiring. Output: running dApp at localhost:3000.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Transaction gas price is below the 1000 gwei base fee on Polkadot Hub TestNet. Default Hardhat gas estimation produces values too low for this network.", + "pattern": "priority is too low", + "resolution": "Set gasPrice: 5000000000000 in the polkadotTestnet network block in hardhat.config.ts. Delete ignition/deployments/ if a prior attempt was made, then redeploy." + }, + { + "cause": "Ignition lost track of the transaction. The transaction may have succeeded or may be stuck in the mempool.", + "pattern": "IGN401 / Transaction dropped", + "resolution": "Check ignition/deployments//deployed_addresses.json. If the contract address is present, deployment succeeded — use that address. If absent: send a zero-value replacement transaction at the same nonce with gasPrice 2000000000000 (2x base fee), wait for it to mine, then delete ignition/deployments/ and redeploy." + }, + { + "cause": "OpenZeppelin v5.4.0+ uses the mcopy opcode (Cancun EVM upgrade). Solidity defaults to an older EVM version.", + "pattern": "invalid opcode: MCOPY", + "resolution": "Add evmVersion: 'cancun' to the Solidity compiler settings in hardhat.config.ts. Recompile with 'npx hardhat compile'." + }, + { + "cause": "Incorrect RPC URL or chain ID in hardhat.config.ts.", + "pattern": "Error: could not detect network", + "resolution": "Set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417 in the polkadotTestnet network block." + }, + { + "cause": "Frontend dependencies were not installed.", + "pattern": "Module not found: viem or wagmi", + "resolution": "Run 'npm install viem wagmi @tanstack/react-query' in the zero-to-hero-dapp/frontend directory." + } + ], + "examples": [ + { + "actions": [ + "Clone revm-hardhat-examples and navigate to zero-to-hero-dapp/storage-contract", + "Install dependencies and create .env with PRIVATE_KEY (user fills in)", + "Update hardhat.config.ts: dotenv, gasPrice 5000 gwei, evmVersion cancun, requiredConfirmations 1", + "Compile Storage.sol and deploy to polkadotTestnet via Ignition", + "Scaffold Next.js 14 frontend with create-next-app@14", + "Install viem, wagmi, react-query; update contract.ts with deployed address", + "Run npm run dev and test at localhost:3000" + ], + "result": "Storage contract deployed to TestNet; Next.js app at localhost:3000 with working wallet connect, read, and write contract interactions via MetaMask", + "scenario": "Common scenario: full dApp from scratch", + "user_says": "Build a smart contract dApp on Polkadot Hub with Viem and Next.js" + }, + { + "actions": [ + "Check ignition/deployments//deployed_addresses.json for a Storage address", + "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", + "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" + ], + "result": "Contract address recovered from deployment state or clean redeployment completed", + "scenario": "Edge case: Ignition reports transaction dropped but contract may have deployed", + "user_says": "Ignition says IGN401 but I don't know if the contract deployed" + } + ], + "id": "build-dapp-viem-nextjs", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22+", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for contract deployment gas" + ], + "wallet": [ + "MetaMask configured for Polkadot Hub TestNet (chainId 420420417)", + "0x-prefixed EVM private key for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/dapps/zero-to-hero.md", + "reference_code": { + "base_path": "zero-to-hero-dapp", + "branch": "master", + "files": [], + "repo": "polkadot-developers/revm-hardhat-examples" + }, + "source_pages": [ + "smart-contracts/cookbook/dapps/zero-to-hero.md" + ], + "steps": [ + { + "action": "Clone the reference repository", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git" + ], + "description": "Clone revm-hardhat-examples. This repo contains the complete zero-to-hero-dapp with both the Hardhat contract project and the Next.js frontend.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install contract project dependencies", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install Hardhat and contract dependencies, then add dotenv. dotenv replaces Hardhat's interactive vars system, which cannot be used in agent shells.", + "order": 2, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" + }, + { + "action": "Create .env file and protect it with .gitignore", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly, filling in PRIVATE_KEY=0x. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding.", + "order": 3, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" + }, + { + "action": "Update hardhat.config.ts for TestNet and security", + "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file.", + "order": 4, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" + }, + { + "action": "Compile the Storage contract", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile Storage.sol. If you see 'invalid opcode: MCOPY', verify evmVersion: 'cancun' is set in the Solidity compiler settings in hardhat.config.ts and recompile.", + "expected_output": "Compiled 1 Solidity file successfully", + "order": 5, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" + }, + { + "action": "Deploy the contract to Polkadot Hub TestNet", + "commands": [ + "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" + ], + "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy.", + "expected_output": "Storage deployed to: 0x...", + "order": 6, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" + }, + { + "action": "Scaffold the Next.js frontend", + "commands": [ + "echo 'n\\nn\\nn\\nn\\nn\\nn' | npx create-next-app@14 frontend --typescript --no-eslint --no-tailwind --no-src-dir --app" + ], + "description": "Scaffold Next.js 14 TypeScript app named 'frontend'. Piped 'n' answers handle any unexpected interactive prompts from create-next-app. If this fails due to prompt changes, run: npx create-next-app@14 frontend and answer No to all questions except TypeScript (Yes) and App Router (Yes).", + "order": 7, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp" + }, + { + "action": "Install frontend dependencies", + "commands": [ + "npm install viem wagmi @tanstack/react-query" + ], + "description": "Install viem for contract interaction, wagmi for React hooks, and React Query (required by wagmi v2).", + "order": 8, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend" + }, + { + "action": "Update contract address in contract config", + "description": "Open src/contracts/contract.ts (or the equivalent contract config file in the frontend). Replace the CONTRACT_ADDRESS placeholder or any hardcoded example address with the deployed contract address saved in step 6. Save the file.", + "order": 9, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend" + }, + { + "action": "Start the development server", + "commands": [ + "npm run dev" + ], + "description": "Start the Next.js dev server. Open http://localhost:3000 in a browser. Click 'Connect Wallet' and approve the MetaMask popup. Read the current stored value, then write a new value and confirm the MetaMask transaction. Verify the displayed value updates after the transaction is mined.", + "expected_output": "ready - started server on 0.0.0.0:3000", + "order": 10, + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend" + } + ], + "supplementary_context": { + "description": "Load these pages for Hardhat configuration reference, Viem library details, or network connection parameters.", + "pages": [ + { + "relevance": "Full Hardhat EVM configuration options for Polkadot Hub, including network settings and contract verification.", + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md" + }, + { + "relevance": "Viem library reference for chain config, wallet client, and contract interaction patterns on Polkadot Hub.", + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md" + }, + { + "relevance": "Network parameters (RPC URLs, chain IDs, WSS) for Polkadot Hub TestNet and Mainnet.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + } + ] + }, + "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Deploys a Solidity ERC-20 token contract to Polkadot Hub TestNet entirely through the Remix browser IDE with MetaMask wallet injection. Use when you prefer a no-CLI workflow or are demonstrating contract deployment to non-developers. Trigger phrases: 'deploy ERC-20 remix', 'erc20 token remix polkadot', 'deploy token browser IDE'. Covers fetching contract code from revm-hardhat-examples, Remix compile settings (evmVersion cancun for OpenZeppelin v5), MetaMask TestNet configuration, and minting tokens via the Remix UI. No local environment required beyond a browser and MetaMask.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Older Remix versions may not list 'cancun' in the EVM version dropdown.", + "pattern": "Remix compilation fails with 'Unknown key evmVersion cancun'", + "resolution": "Update to the latest Remix version by refreshing the page. The cancun option should appear in Advanced Configuration. Alternatively, use a custom EVM version by typing 'cancun' in the input field if available." + }, + { + "cause": "MetaMask is on a different network than Polkadot Hub TestNet.", + "pattern": "MetaMask shows wrong network after connecting to Remix", + "resolution": "In MetaMask, switch to 'Polkadot Hub TestNet' (chainId 420420417) before deploying. Confirm the Remix environment section shows the correct chain ID." + }, + { + "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", + "pattern": "Transaction fails with insufficient funds", + "resolution": "Visit https://faucet.polkadot.io/, enter your MetaMask address, and request testnet PAS. Wait for the tokens to arrive (usually under 1 minute), then retry deployment." + }, + { + "cause": "Contract constructor arguments may be missing or invalid, or the MetaMask account has insufficient funds.", + "pattern": "Remix shows 'Gas estimation failed'", + "resolution": "Verify the initialOwner address is filled in correctly in the constructor arguments. Check PAS balance in MetaMask and top up via faucet if needed." + } + ], + "examples": [ + { + "actions": [ + "Fetch MyToken.sol from revm-hardhat-examples and display to user", + "Open https://remix.ethereum.org, create MyToken.sol, paste contract", + "In Remix Solidity compiler, set version 0.8.20+ and EVM version 'cancun'", + "Add Polkadot Hub TestNet to MetaMask (chainId 420420417, RPC testnet URL)", + "Connect Injected Provider in Remix Deploy tab, deploy with initialOwner set to wallet address", + "Verify deployment and mint tokens via Remix UI" + ], + "result": "ERC-20 token deployed on Polkadot Hub TestNet; contract visible in Remix with working name, symbol, mint, and balanceOf functions", + "scenario": "Common scenario: deploy ERC-20 via Remix", + "user_says": "Deploy an ERC-20 token on Polkadot Hub using Remix IDE" + }, + { + "actions": [ + "Direct user to https://faucet.polkadot.io/ and ask them to enter their MetaMask address", + "Wait for confirmation that PAS tokens have arrived (check MetaMask balance)", + "Retry deployment from the Remix Deploy tab" + ], + "result": "Account funded with testnet PAS; deployment proceeds successfully", + "scenario": "Edge case: user has no testnet PAS tokens", + "user_says": "Deployment failed — gas fee transaction rejected" + } + ], + "id": "deploy-erc20-token-remix", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet (chainId 420420417) — configured in MetaMask" + ], + "runtime": [ + "A modern web browser (Chrome or Firefox recommended)" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required to pay deployment gas" + ], + "wallet": [ + "MetaMask browser extension installed and unlocked", + "An account funded with testnet PAS on Polkadot Hub TestNet (chainId 420420417, RPC: https://services.polkadothub-rpc.com/testnet)" + ] + }, + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md", + "reference_code": { + "base_path": "erc20-hardhat", + "branch": "master", + "files": [], + "repo": "polkadot-developers/revm-hardhat-examples" + }, + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md" + ], + "steps": [ + { + "action": "Fetch the ERC-20 contract source", + "description": "Fetch the OpenZeppelin ERC-20 contract source from the reference repository. The contract file is at: https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol\n\nRun: curl -sL https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol and display the contract source to the user. The user will paste this into Remix in the next step.", + "order": 1, + "working_directory": "." + }, + { + "action": "Open Remix IDE and create the contract file", + "description": "Navigate to https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), click the 'New File' icon and name it 'MyToken.sol'. Paste the full contract source fetched in step 1 into the editor. Save with Ctrl+S (or Cmd+S on Mac).", + "order": 2, + "working_directory": "." + }, + { + "action": "Configure the Solidity compiler in Remix", + "description": "Click the 'Solidity compiler' tab (shield icon) in the left sidebar. Set the following:\n- Compiler version: select 0.8.20 or higher (match the pragma in the contract source)\n- EVM version: select 'cancun' from the Advanced Configuration dropdown — required for OpenZeppelin v5.4.0+ (mcopy opcode)\nClick 'Compile MyToken.sol'. A green checkmark indicates success. If compilation fails with 'Unknown key', verify the EVM version is set to 'cancun'.", + "order": 3, + "working_directory": "." + }, + { + "action": "Add Polkadot Hub TestNet to MetaMask", + "description": "If MetaMask does not already have Polkadot Hub TestNet configured, add it manually:\n1. Open MetaMask and go to Settings > Networks > Add Network > Add a network manually.\n2. Enter: Network Name: Polkadot Hub TestNet; RPC URL: https://services.polkadothub-rpc.com/testnet; Chain ID: 420420417; Currency Symbol: PAS.\n3. Click Save and switch to the Polkadot Hub TestNet network.\n4. Confirm the account shown in MetaMask has PAS tokens. If not, visit https://faucet.polkadot.io/ to request testnet PAS.", + "order": 4, + "working_directory": "." + }, + { + "action": "Connect MetaMask to Remix and deploy", + "description": "In Remix, click the 'Deploy & run transactions' tab (Ethereum icon) in the left sidebar. Set:\n- Environment: select 'Injected Provider - MetaMask'\n- MetaMask will pop up asking to connect — click Connect and approve.\n- Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417).\n- In the Contract dropdown, select 'MyToken'.\n- Expand the Deploy section and fill in the constructor arguments: initialOwner = your MetaMask address.\n- Click the orange 'Deploy' button.\n- MetaMask will show a transaction confirmation — review the gas fee and click Confirm.\n- Wait for the transaction to be mined (10–30 seconds on TestNet). The deployed contract address appears in the 'Deployed Contracts' section below.", + "order": 5, + "working_directory": "." + }, + { + "action": "Verify the deployment and mint tokens", + "description": "In the 'Deployed Contracts' section in Remix, expand the MyToken contract entry. Verify the deployment:\n- Click 'name' to confirm it returns your token name.\n- Click 'symbol' to confirm the token symbol.\n- Click 'totalSupply' — should return 0 if the contract has no initial supply.\n\nTo mint tokens:\n1. Expand the 'mint' function.\n2. Enter: to = your MetaMask address, amount = 1000000000000000000 (1 token with 18 decimals).\n3. Click 'transact' and confirm in MetaMask.\n4. After mining, click 'balanceOf' and enter your address to verify the balance increased.", + "order": 6, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for ERC-20 Hardhat deployment (CLI alternative), network connection details, or wallet configuration.", + "pages": [ + { + "relevance": "CLI-based ERC-20 deployment using Hardhat — use when the user prefers a terminal workflow over Remix.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-erc20-erc20-hardhat", + "title": "Deploy an ERC-20 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" + }, + { + "relevance": "Network parameters for manually adding Polkadot Hub to MetaMask.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + }, + { + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask Injected Provider.", + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md" + } + ] + }, + "title": "Deploy an ERC-20 Token Using Remix IDE", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up a Node.js project, compiles a Solidity contract with solc, deploys to Polkadot Hub TestNet using Ethers.js v6, and interacts with the deployed contract via read and write script calls. Use when integrating Ethers.js into a new or existing Node.js project targeting Polkadot Hub. Trigger phrases: 'deploy with ethers.js polkadot', 'ethers.js contract deployment', 'use ethers polkadot hub', 'ethersjs testnet'. Covers dotenv credential setup, custom Polkadot Hub provider configuration, and the compile-deploy-interact workflow. Produces a deployed contract with verified read/write interaction.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "name": "PRIVATE_KEY", + "required": true + }, + { + "description": "HTTP RPC endpoint for Polkadot Hub TestNet. Set to https://services.polkadothub-rpc.com/testnet.", + "name": "RPC_URL", + "required": true + } + ], + "error_patterns": [ + { + "cause": "The deployer account does not have enough PAS to pay for deployment gas.", + "pattern": "Error: insufficient funds for intrinsic transaction cost", + "resolution": "Visit https://faucet.polkadot.io/, enter the deployer address, and request testnet PAS. Confirm balance in MetaMask or via eth_getBalance RPC call, then retry." + }, + { + "cause": "The RPC_URL in .env is incorrect or the endpoint is unreachable.", + "pattern": "Error: could not detect network", + "resolution": "Verify RPC_URL=https://services.polkadothub-rpc.com/testnet in .env. Test connectivity: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'" + }, + { + "cause": "Project is not configured as ESM.", + "pattern": "SyntaxError: Cannot use import statement in a module", + "resolution": "Run 'npm pkg set type=module' in the ethers-deploy directory to enable ES modules." + }, + { + "cause": "The gas price in the transaction is below the 1000 gwei TestNet base fee.", + "pattern": "Error: transaction underpriced", + "resolution": "Add an explicit gasPrice to the transaction options: { gasPrice: ethers.parseUnits('5000', 'gwei') }. Polkadot Hub TestNet has a base fee of 1000 gwei." + } + ], + "examples": [ + { + "actions": [ + "Create ethers-deploy ESM project and install ethers, dotenv, solc, tsx", + "Create .env with RPC_URL and empty PRIVATE_KEY (user fills in)", + "Fetch compile.ts, deploy.ts, interact.ts from polkadot-cookbook", + "Apply substitutions: dotenv import, process.env.RPC_URL, process.env.PRIVATE_KEY, chainId 420420417", + "Run npx tsx compile.ts to build ABI/bytecode", + "Run npx tsx deploy.ts and save deployed contract address", + "Run npx tsx interact.ts to verify read/write contract operations" + ], + "result": "Solidity contract compiled, deployed to Polkadot Hub TestNet, and verified with read/write operations via Ethers.js scripts", + "scenario": "Common scenario: deploy and interact with a Storage contract", + "user_says": "Deploy a contract to Polkadot Hub using Ethers.js" + }, + { + "actions": [ + "Open deploy.ts and add gasPrice override to the deployment transaction options", + "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", + "Retry with npx tsx deploy.ts" + ], + "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet", + "scenario": "Edge case: transaction underpriced error during deployment", + "user_says": "Deploy failed with transaction underpriced" + } + ], + "id": "deploy-contracts-ethers-js", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22+", + "npm" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" + ], + "wallet": [ + "0x-prefixed EVM private key for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/libraries/ethers-js.md", + "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/smart-contracts/libraries/ethers-js", + "branch": "master", + "files": [ + { + "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter.", + "path": "Storage.sol" + }, + { + "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts for Storage.sol", + "path": "compile.js" + }, + { + "description": "Deploys the compiled Storage contract to Polkadot Hub via ethers.js Wallet + ContractFactory", + "path": "deploy.js" + }, + { + "description": "Reads + writes the deployed Storage contract via ethers.js Contract.connect; calls the `storedNumber` getter and `setNumber(value)` setter.", + "path": "checkStorage.js" + }, + { + "description": "Initializes ethers.js JsonRpcProvider for Polkadot Hub TestNet", + "path": "connectToProvider.js" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "smart-contracts/libraries/ethers-js.md" + ], + "steps": [ + { + "action": "Create the project directory and initialize", + "commands": [ + "mkdir ethers-deploy && cd ethers-deploy", + "npm init -y", + "npm pkg set type=module" + ], + "description": "Create and initialize an ESM Node.js project. ESM is required for Ethers.js v6 imports.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install ethers dotenv", + "npm install --save-dev solc typescript tsx @types/node" + ], + "description": "Install ethers (v6), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling.", + "order": 2, + "working_directory": "ethers-deploy" + }, + { + "action": "Create .env file and .gitignore", + "commands": [ + "printf 'PRIVATE_KEY=\\nRPC_URL=https://services.polkadothub-rpc.com/testnet\\n' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create .env with RPC_URL pre-filled and empty PRIVATE_KEY. Stop and ask the user to edit .env directly by adding their 0x-prefixed private key as PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing.", + "order": 3, + "working_directory": "ethers-deploy" + }, + { + "action": "Fetch the provider connection helper", + "description": "Fetch the reference file `connectToProvider.js` and save it to the project root. Verify it points at the Polkadot Hub TestNet RPC URL `https://services.polkadothub-rpc.com/testnet`; if it uses a placeholder, replace `INSERT_RPC_URL` with the testnet URL.", + "order": 4, + "reference_file": "connectToProvider.js", + "working_directory": "ethers-deploy" + }, + { + "action": "Fetch the Storage.sol contract", + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed.", + "order": 5, + "reference_file": "Storage.sol", + "working_directory": "ethers-deploy" + }, + { + "action": "Fetch the compilation script", + "description": "Fetch the reference file `compile.js` and save it to the project root. The script invokes `solc` against `Storage.sol` and writes the ABI and bytecode to `artifacts/`.", + "order": 6, + "reference_file": "compile.js", + "working_directory": "ethers-deploy" + }, + { + "action": "Compile the Solidity contract", + "commands": [ + "node compile.js" + ], + "description": "Run the compile script to generate the contract ABI and bytecode. Confirm an `artifacts/` directory was created with `Storage.json` (or similar) before proceeding to deploy.", + "order": 7, + "working_directory": "ethers-deploy" + }, + { + "action": "Fetch the deployment script", + "description": "Fetch the reference file `deploy.js` and save it to the project root. Then apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line (or `require(\"dotenv\").config()` for CJS).\n(2) Replace `INSERT_RPC_URL` with `process.env.RPC_URL` (or hardcode the testnet URL).\n(3) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`.\n(4) Replace any hardcoded chain ID with `420420417`.", + "order": 8, + "reference_file": "deploy.js", + "working_directory": "ethers-deploy" + }, + { + "action": "Deploy the contract to Polkadot Hub TestNet", + "commands": [ + "node deploy.js" + ], + "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry.", + "order": 9, + "working_directory": "ethers-deploy" + }, + { + "action": "Fetch the interaction script and interact with the deployed contract", + "description": "Fetch the reference file `checkStorage.js` and save it as `checkStorage.js`. Then substitute `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step. Run it with `node checkStorage.js`. Verify the output shows successful read and write operations against the deployed contract.", + "order": 10, + "reference_file": "checkStorage.js", + "working_directory": "ethers-deploy" + } + ], + "supplementary_context": { + "description": "Load these pages for network details, wallet setup, or other library alternatives.", + "pages": [ + { + "relevance": "Network parameters (RPC URLs, chain IDs, WebSocket endpoints) for all Polkadot Hub environments.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + }, + { + "relevance": "Alternative to Ethers.js: Viem deployment pattern for Polkadot Hub (more type-safe, recommended for new projects).", + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md" + }, + { + "relevance": "Hardhat-based deployment workflow — use when the project needs a full Hardhat setup rather than lightweight scripts.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + } + ] + }, + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up a TypeScript project, compiles a Solidity contract, deploys to Polkadot Hub TestNet using Viem v2 walletClient.deployContract, and interacts with the deployed contract via publicClient and walletClient. Use when building a TypeScript project that needs direct Viem integration with Polkadot Hub without a Hardhat setup. Trigger phrases: 'deploy with viem polkadot', 'viem contract deployment', 'use viem polkadot hub', 'deploy viem typescript'. Covers custom Polkadot Hub chain definition, dotenv credential setup, and the compile-deploy-interact pattern. Companion to deploy-contracts-ethers-js.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Viem's default gas estimation produces a gas price below the 1000 gwei TestNet base fee.", + "pattern": "Error: fee cap too low / transaction underpriced", + "resolution": "Add an explicit gasPrice override to the deployContract call: { gasPrice: parseGwei('5000') }. Import parseGwei from 'viem'. Polkadot Hub TestNet requires at least 1000 gwei; use 5000 gwei (5x) as a safe margin." + }, + { + "cause": "The Viem chain definition's id does not match the connected network's chain ID.", + "pattern": "Error: Chain ID mismatch", + "resolution": "Verify the chain object has id: 420420417. Check with: const chainId = await publicClient.getChainId(); and confirm it returns 420420417." + }, + { + "cause": "Viem package not installed.", + "pattern": "Cannot find module 'viem'", + "resolution": "Run 'npm install viem' in the viem-deploy directory." + }, + { + "cause": "The deployer account does not have enough PAS for gas.", + "pattern": "Error: insufficient funds for intrinsic transaction cost", + "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS for the deployer address. Verify balance with: await publicClient.getBalance({ address: '0x...' })" + } + ], + "examples": [ + { + "actions": [ + "Create viem-deploy ESM project, install viem, dotenv, solc, tsx", + "Create .env with empty PRIVATE_KEY (user fills in)", + "Fetch chain.ts from polkadot-cookbook, verify chain ID 420420417 and TestNet RPC", + "Fetch deploy.ts, read.ts, write.ts; apply dotenv import and process.env.PRIVATE_KEY substitutions", + "Compile Solidity contract to ABI/bytecode artifacts", + "Run npx tsx deploy.ts and save deployed contract address", + "Update read.ts/write.ts with deployed address; run both to verify contract interaction" + ], + "result": "Contract deployed to Polkadot Hub TestNet; read and write operations confirmed via Viem scripts", + "scenario": "Common scenario: deploy and interact with a contract", + "user_says": "Deploy a smart contract to Polkadot Hub with Viem" + }, + { + "actions": [ + "Open deploy.ts and locate the walletClient.deployContract call", + "Add gasPrice: parseGwei('5000') to the transaction options object", + "Import parseGwei from 'viem' at the top of the file", + "Retry with npx tsx deploy.ts" + ], + "result": "Deployment succeeds with 5000 gwei gas price, above the 1000 gwei TestNet base fee", + "scenario": "Edge case: gas fee cap too low during deployment", + "user_says": "Deployment fails with fee cap too low error" + } + ], + "id": "deploy-contracts-viem", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22+", + "npm" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ — required for deployment gas" + ], + "wallet": [ + "0x-prefixed EVM private key for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/libraries/viem.md", + "project_structure": "viem-deploy/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── src/\n│ ├── chainConfig.ts\n│ ├── compile.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── .env\n├── .gitignore\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/smart-contracts/libraries/viem", + "branch": "master", + "files": [ + { + "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter.", + "path": "Storage.sol" + }, + { + "description": "Custom Polkadot Hub TestNet chain definition for viem (chain ID, RPC, native currency)", + "path": "chainConfig.ts" + }, + { + "description": "viem PublicClient construction against Polkadot Hub TestNet", + "path": "createClient.ts" + }, + { + "description": "viem WalletClient construction with PRIVATE_KEY from env", + "path": "createWallet.ts" + }, + { + "description": "Solidity compiler wrapper that produces ABI + bytecode artifacts", + "path": "compile.ts" + }, + { + "description": "Deploys the compiled Storage contract via viem WalletClient.deployContract", + "path": "deploy.ts" + }, + { + "description": "Reads + writes the deployed Storage contract via viem readContract/writeContract", + "path": "interact.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "smart-contracts/libraries/viem.md" + ], + "steps": [ + { + "action": "Create the project directory and initialize", + "commands": [ + "mkdir viem-deploy && cd viem-deploy", + "npm init -y && npm pkg set type=module", + "mkdir -p src contracts abis artifacts" + ], + "description": "Create and initialize an ESM Node.js project in a new `viem-deploy` directory. ESM is required for Viem v2. Pre-create the canonical layout directories (`src/`, `contracts/`, `abis/`, `artifacts/`) — the reference scripts assume this layout for relative paths.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install viem dotenv", + "npm install --save-dev solc typescript tsx @types/node" + ], + "description": "Install viem (v2), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling.", + "order": 2, + "working_directory": "viem-deploy" + }, + { + "action": "Create .env file and .gitignore", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' > .gitignore" + ], + "description": "Create .env with an empty PRIVATE_KEY placeholder. Stop and ask the user to edit .env directly by adding PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing.", + "order": 3, + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the chain configuration", + "description": "Fetch the reference file `chainConfig.ts` and save it as `src/chainConfig.ts`. Verify or substitute:\n(1) Chain ID is `420420417`.\n(2) RPC URL is `https://services.polkadothub-rpc.com/testnet`.\n(3) Replace any `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, `INSERT_CHAIN_NAME`, `INSERT_NETWORK_NAME`, `INSERT_CHAIN_DECIMALS`, `INSERT_CURRENCY_NAME`, `INSERT_CURRENCY_SYMBOL` placeholders.\n\n**Important:** the same set of placeholders also appears in `createClient.ts` (step 6) and `createWallet.ts` (step 7) — apply the same substitutions consistently in all three files.", + "order": 4, + "reference_file": "chainConfig.ts", + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the Storage.sol contract", + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed.", + "order": 5, + "reference_file": "Storage.sol", + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the public client setup", + "description": "Fetch the reference file `createClient.ts` and save it as `src/createClient.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (the file declares the chain locally and has the same 8 placeholders). Constructs a viem `PublicClient` against the Polkadot Hub TestNet chain definition; used for reads and tx receipts.", + "order": 6, + "reference_file": "createClient.ts", + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the wallet client setup", + "description": "Fetch the reference file `createWallet.ts` and save it as `src/createWallet.ts`. **Apply the same `INSERT_*` substitutions you applied to chainConfig.ts** (this file also declares the chain locally and has 9 placeholders — including a duplicate RPC URL for the `public.http` rpcUrls entry). Add `import \"dotenv/config\";` as the first line; the client reads `PRIVATE_KEY` from env.", + "order": 7, + "reference_file": "createWallet.ts", + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the compilation script", + "description": "Fetch the reference file `compile.ts` and save it as `src/compile.ts`. The script invokes `solc` against `contracts/Storage.sol` and writes ABI + bytecode artifacts to `abis/` and `artifacts/`.", + "order": 8, + "reference_file": "compile.ts", + "working_directory": "viem-deploy" + }, + { + "action": "Compile the Solidity contract", + "commands": [ + "npx tsx src/compile.ts" + ], + "description": "Run the compile script. Verify that `abis/Storage.json` and `artifacts/Storage.bin` (or equivalent) exist before deploying.", + "order": 9, + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the deployment script", + "description": "Fetch the reference file `deploy.ts` and save it as `src/deploy.ts`. Apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line.\n(2) Replace `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``.\n(3) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`.\n(4) Replace `INSERT_CHAIN_ID` with `420420417n` (BigInt).\n\nThe script reads from `../abis` and `../artifacts` relative to the script location, which resolves correctly when the script is in `src/`.", + "order": 10, + "reference_file": "deploy.ts", + "working_directory": "viem-deploy" + }, + { + "action": "Deploy the contract to Polkadot Hub TestNet", + "commands": [ + "npx tsx src/deploy.ts" + ], + "description": "Run the deployment script. Save the deployed contract address from the output.\n\nIf the transaction fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction fails with `fee cap too low` or similar gas errors: add a `gasPrice` override in `deploy.ts` (e.g. `gasPrice: parseGwei('5000')`).", + "order": 11, + "working_directory": "viem-deploy" + }, + { + "action": "Fetch the interaction script and interact with the deployed contract", + "description": "Fetch the reference file `interact.ts` and save it as `src/interact.ts`. Replace `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step and `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``. Run it with `npx tsx src/interact.ts`. Verify the value returned by reading `storedNumber` changes after calling `setNumber(value)`.", + "order": 12, + "reference_file": "interact.ts", + "working_directory": "viem-deploy" + } + ], + "supplementary_context": { + "description": "Load these pages for network details, Ethers.js comparison, or a full Hardhat-based workflow.", + "pages": [ + { + "relevance": "Network parameters for Polkadot Hub including chain IDs, RPC URLs, and WebSocket endpoints.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + }, + { + "relevance": "Ethers.js equivalent workflow — use when the user prefers Ethers.js v6 over Viem.", + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md" + }, + { + "relevance": "Full-stack dApp tutorial combining Viem with a Next.js frontend — the natural next step after deploying a contract with Viem.", + "slug": "smart-contracts-cookbook-dapps-zero-to-hero", + "title": "Zero to Hero Smart Contract DApp", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md" + } + ] + }, + "title": "Deploy Contracts to Polkadot Hub with Viem", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Creates a mock runtime module for FRAME pallet unit testing using construct_runtime! and derive_impl. Use when you need a self-contained test environment for a custom FRAME pallet before writing unit tests. Configures frame_system with test defaults, satisfies pallet Config traits, and provides genesis storage helpers for different initial states. Trigger phrases: 'mock runtime pallet', 'set up pallet testing', 'construct_runtime test', 'pallet test environment', 'derive_impl TestDefaultConfig'. Requires a completed FRAME pallet in pallets/pallet-custom. Do NOT use for full parachain runtime integration testing.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The mock.rs Config implementation is missing an associated type that the pallet's Config trait declares (e.g., WeightInfo).", + "pattern": "error[E0277]: the trait bound is not satisfied / missing associated type in Config impl", + "resolution": "Add the missing type to the impl pallet_custom::Config for Test block in mock.rs. Use a placeholder — for WeightInfo: type WeightInfo = ();" + }, + { + "cause": "An import path in mock.rs does not match the current crate layout.", + "pattern": "error[E0412]: cannot find type in scope / unresolved import", + "resolution": "Verify that import paths in mock.rs match your actual pallet crate name and module structure. Check Cargo.toml for the crate name." + }, + { + "cause": "A pallet in construct_runtime! has a misconfigured index or incompatible Config.", + "pattern": "error: proc macro `construct_runtime` panicked", + "resolution": "Verify the pallet name and module path match the actual module. Ensure all required Config associated types are implemented in the mock." + } + ], + "examples": [ + { + "actions": [ + "Create pallets/pallet-custom/src/mock.rs", + "Add #[cfg(test)] mod mock; to lib.rs after pub use pallet::*;", + "Fetch the complete mock.rs from the polkadot-cookbook reference and save it", + "Run cargo test --package pallet-custom --lib to confirm auto-generated tests pass" + ], + "result": "Two auto-generated tests pass: construct_runtime integrity test and genesis config build test. The mock runtime is ready for unit test authoring.", + "scenario": "Common scenario: create a test environment for the custom counter pallet", + "user_says": "Set up a mock runtime so I can write unit tests for my counter pallet" + }, + { + "actions": [ + "Open src/mock.rs", + "Add type WeightInfo = (); inside the impl pallet_custom::Config for Test block", + "Re-run cargo test --package pallet-custom --lib" + ], + "result": "Compilation succeeds with the () placeholder WeightInfo, appropriate for test environments.", + "scenario": "Edge case: pallet Config has additional associated types not in the reference mock.rs", + "user_says": "Compilation fails with 'missing associated type WeightInfo in impl pallet_custom::Config for Test'" + } + ], + "id": "set-up-pallet-mock-runtime", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [], + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain (stable, via rustup)", + "Cargo", + "Completed the Make a Custom Pallet guide — the counter pallet must exist in pallets/pallet-custom" + ], + "tokens": [], + "wallet": [] + }, + "primary_page": "parachains/customize-runtime/pallet-development/mock-runtime.md", + "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n └── mock.rs", + "reference_code": { + "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/mock-runtime", + "branch": "master", + "files": [ + { + "description": "Complete mock runtime: construct_runtime! with System + CustomPallet, frame_system TestDefaultConfig, pallet Config impl, and three genesis storage helpers (default, counter init, interactions init)", + "path": "mock.rs" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "parachains/customize-runtime/pallet-development/mock-runtime.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK parachain template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the parachain template node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Create mock.rs and add module declaration to lib.rs", + "commands": [ + "touch mock.rs" + ], + "description": "Create the empty mock.rs file. Then open src/lib.rs and insert the following two lines immediately after the 'pub use pallet::*;' line:\n\n```rust\n#[cfg(test)]\nmod mock;\n```\n\nThe #[cfg(test)] attribute ensures this module is only compiled when running tests, keeping it out of production builds.", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Fetch and save the complete mock.rs", + "description": "Fetch the reference file and save it as 'mock.rs', replacing the empty placeholder. The file must contain: (1) imports including construct_runtime!, frame_system, and IdentityLookup; (2) Test runtime type and Block type alias via construct_runtime!; (3) #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] for System with AccountId = u64, Lookup = IdentityLookup, Block = Block; (4) impl pallet_custom::Config for Test with type RuntimeEvent = RuntimeEvent and type CounterMaxValue = ConstU32<1000>; (5) new_test_ext() genesis helper; (6) new_test_ext_with_counter(val: u32) helper; (7) new_test_ext_with_interactions() helper accepting counter value and Vec of (AccountId, u32) interactions.", + "order": 4, + "reference_file": "mock.rs", + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Verify mock runtime compiles", + "commands": [ + "cargo test --package pallet-custom --lib" + ], + "description": "Compile the test code including the new mock module. Two auto-generated tests will run: the construct_runtime integrity test and the genesis config build test. Both must pass. If compilation fails with 'missing field' errors, add the missing associated type to the impl pallet_custom::Config for Test block in mock.rs using a sensible default (e.g., type WeightInfo = ();).", + "expected_output": "test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok\ntest mock::test_genesis_config_builds ... ok", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom" + } + ], + "supplementary_context": { + "description": "Load when continuing pallet development after mock runtime setup.", + "pages": [ + { + "relevance": "Next step: write comprehensive unit tests using the mock runtime.", + "slug": "parachains-customize-runtime-pallet-development-pallet-testing", + "title": "Unit Test Pallets", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/pallet-testing.md" + }, + { + "relevance": "Prerequisite: the custom counter pallet that this mock runtime is written for.", + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md" + } + ] + }, + "title": "Set Up a Mock Runtime for Pallet Unit Testing", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Writes a complete tests.rs module for a FRAME pallet using assert_ok!, assert_noop!, and System::assert_last_event macros. Use after completing the mock runtime guide to verify pallet logic in complete isolation. Covers all critical test categories: dispatchable success paths, arithmetic guard errors, root-only origin checks, event data correctness, user-interaction tracking, and genesis state loading. Trigger phrases: 'unit test FRAME pallet', 'test pallet logic', 'assert_noop assert_ok', 'pallet test suite', 'test dispatchable'. Requires completed create-a-pallet and set-up-pallet-mock-runtime guides. Do NOT use for integration or end-to-end testing of a full parachain.", + "env_vars": [], + "error_patterns": [ + { + "cause": "System::set_block_number(1) was not called before the dispatchable. Events are suppressed on block 0.", + "pattern": "left: [], right: [EventRecord { ... }] / assertion failed on event", + "resolution": "Add System::set_block_number(1); as the first line inside the execute_with closure for any test that checks events." + }, + { + "cause": "The dispatchable modified storage before returning an error, violating the no-op contract.", + "pattern": "error: assert_noop! failed — storage was modified", + "resolution": "This is a pallet bug: all preconditions must be checked before any storage mutations. Fix the pallet extrinsic, not the test." + }, + { + "cause": "A dispatchable wrapped in assert_ok! returned an error. Usually wrong origin or incorrect genesis state.", + "pattern": "thread 'tests::...' panicked at 'called Result::unwrap() on Err'", + "resolution": "Check that the genesis helper (new_test_ext vs new_test_ext_with_counter) matches expected initial state, and the origin type matches what the extrinsic requires." + } + ], + "examples": [ + { + "actions": [ + "Create pallets/pallet-custom/src/tests.rs", + "Add #[cfg(test)] mod tests; to lib.rs", + "Write imports: use crate::{mock::*, Error, Event}; plus FRAME macros", + "Write increment_works, set_counter_value_works, decrement_works with System::set_block_number(1) for event checks", + "Write error tests: overflow, underflow, root-only access control", + "Write genesis_config_works and interaction tracking tests", + "Run cargo test --package pallet-custom — expect 15 passing tests" + ], + "result": "All 15 tests pass including 2 auto-generated mock tests.", + "scenario": "Common scenario: write a complete test suite for the counter pallet", + "user_says": "Write unit tests for my custom counter pallet covering increment, decrement, errors, and genesis config" + }, + { + "actions": [ + "Open the failing test in tests.rs", + "Add System::set_block_number(1); as the first line inside the execute_with closure", + "Re-run cargo test --package pallet-custom" + ], + "result": "The event test passes because events are recorded starting from block 1.", + "scenario": "Edge case: event assertion fails with empty event list", + "user_says": "My event test fails: 'left: [], right: [EventRecord {...}]'" + } + ], + "id": "unit-test-frame-pallet", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [], + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain (stable, via rustup)", + "Cargo", + "Completed the Make a Custom Pallet guide — counter pallet in pallets/pallet-custom", + "Completed the Mock Your Runtime guide — mock.rs must exist in pallets/pallet-custom/src" + ], + "tokens": [], + "wallet": [] + }, + "primary_page": "parachains/customize-runtime/pallet-development/pallet-testing.md", + "reference_code": { + "base_path": "polkadot-docs/parachains/customize-runtime/pallet-development/pallet-testing", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-cookbook" + }, + "source_pages": [ + "parachains/customize-runtime/pallet-development/pallet-testing.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK parachain template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the parachain template node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Create tests.rs and add module declaration to lib.rs", + "commands": [ + "touch tests.rs" + ], + "description": "Create the empty tests.rs file. Then open src/lib.rs and add the following two lines immediately after the mock module declaration:\n\n```rust\n#[cfg(test)]\nmod tests;\n```\n\nThe module declarations in lib.rs should now read:\n#[cfg(test)] mod mock;\n#[cfg(test)] mod tests;", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Write test module imports", + "description": "Open src/tests.rs and write these imports at the top:\n\n```rust\nuse crate::{mock::*, Error, Event};\nuse frame::deps::frame_support::{assert_noop, assert_ok};\nuse frame::deps::sp_runtime::DispatchError;\n```\n\nThese bring in: all mock runtime items (Test, new_test_ext*, RuntimeOrigin, System, CustomPallet, CounterValue, UserInteractions), the pallet Error and Event enums, FRAME assertion macros, and DispatchError for origin checks.", + "order": 4, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Write basic operation and event emission tests", + "description": "Append these tests to src/tests.rs. Call System::set_block_number(1) inside execute_with before any event assertion — events are suppressed on block 0:\n\n```rust\n#[test]\nfn increment_works() — uses new_test_ext(), signs with account 1, increments by 50, asserts CounterValue is 50 and CounterIncremented event with new_value:50/who:1/amount:50, asserts UserInteractions for account 1 is 1.\n\n#[test]\nfn set_counter_value_works() — uses new_test_ext(), sets block 1, calls set_counter_value(RuntimeOrigin::root(), 100), asserts CounterValue is 100 and CounterValueSet { new_value: 100 } event.\n\n#[test]\nfn decrement_works() — uses new_test_ext_with_counter(100), signs with account 2, decrements by 30, asserts CounterValue is 70 and CounterDecremented event.\n```", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Write error condition and access control tests", + "description": "Append these tests to src/tests.rs:\n\n```rust\n#[test]\nfn increment_fails_on_overflow() — uses new_test_ext_with_counter(u32::MAX), asserts assert_noop!(increment(signed(1), 1), Error::::Overflow). assert_noop! verifies both the error AND that no storage was modified.\n\n#[test]\nfn decrement_fails_on_underflow() — uses new_test_ext_with_counter(10), asserts noop on decrement(signed(1), 11) with Error::Underflow.\n\n#[test]\nfn set_counter_value_requires_root() — uses new_test_ext(), asserts noop on set_counter_value(signed(alice), 100) with DispatchError::BadOrigin, then asserts ok with RuntimeOrigin::root().\n\n#[test]\nfn increment_respects_max_value() — uses new_test_ext_with_counter(950), asserts noop on increment(signed(1), 51) with Error::CounterMaxValueExceeded, then asserts ok on increment of 50.\n```", + "order": 6, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Write genesis configuration and interaction tracking tests", + "description": "Append these final tests to src/tests.rs:\n\n```rust\n#[test]\nfn genesis_config_works() — uses new_test_ext_with_interactions(42, vec![(1, 5), (2, 10)]), asserts CounterValue is 42, UserInteractions for account 1 is 5, for account 2 is 10.\n\n#[test]\nfn increment_tracks_multiple_interactions() — calls increment three times on same account, asserts UserInteractions count is 3.\n\n#[test]\nfn different_users_tracked_separately() — account 1 increments twice (total interactions: 2), account 2 decrements once (interactions: 1); asserts each account tracked independently.\n```", + "order": 7, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Run the complete test suite", + "commands": [ + "cargo test --package pallet-custom" + ], + "description": "Run all tests. Expected output shows 15 passing tests: 13 pallet tests plus 2 auto-generated mock tests. If fewer appear, verify every function has #[test]. If an event test fails with 'left: []', confirm System::set_block_number(1) is the first call inside the execute_with closure.", + "expected_output": "test result: ok. 15 passed; 0 failed; 0 ignored", + "order": 8, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom" + } + ], + "supplementary_context": { + "description": "Load when extending pallet testing or proceeding to benchmarking.", + "pages": [ + { + "relevance": "Next step: benchmark extrinsics and generate production weight files.", + "slug": "parachains-customize-runtime-pallet-development-benchmark-pallet", + "title": "Benchmark Your Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/benchmark-pallet.md" + }, + { + "relevance": "Prerequisite: the mock runtime providing new_test_ext helpers and the Test runtime type.", + "slug": "parachains-customize-runtime-pallet-development-mock-runtime", + "title": "Mock Your Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md" + } + ] + }, + "title": "Write Unit Tests for a FRAME Pallet", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Implements full FRAME pallet benchmarking: adds benchmarking.rs with #[benchmark] functions, defines WeightInfo trait, wires it into pallet Config, configures Cargo.toml features, builds release WASM, installs frame-omni-bencher, downloads the weight template, and generates weights.rs. Use when a custom pallet needs accurate extrinsic weights before production deployment. Trigger phrases: 'benchmark pallet', 'frame-omni-bencher', 'generate weight file', 'WeightInfo', 'pallet weights production'. Requires a working parachain template with the custom pallet integrated. Do NOT use for estimating gas on EVM contracts.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The weights.rs uses frame_support types with the wrong import path for the SDK version.", + "pattern": "error[E0412]: cannot find type Weight / use of undeclared crate", + "resolution": "Use frame::prelude::Weight rather than frame_support::weights::Weight for SDK versions that use the unified frame crate." + }, + { + "cause": "The pallet was not registered in define_benchmarks! in runtime/src/benchmarks.rs, or the pallet name is misspelled.", + "pattern": "Error: No benchmarks found for pallet_custom", + "resolution": "Add [pallet_custom, CustomPallet] to define_benchmarks! — use underscore form for the crate name (pallet_custom not pallet-custom) and the correct runtime type alias." + }, + { + "cause": "The pallet's runtime-benchmarks feature was not added to the runtime's feature cascade in runtime/Cargo.toml.", + "pattern": "could not compile parachain-template-runtime / runtime-benchmarks feature error", + "resolution": "Add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks list in runtime/Cargo.toml." + } + ], + "examples": [ + { + "actions": [ + "Create benchmarking.rs with #[benchmark] functions for all three extrinsics", + "Create placeholder weights.rs with WeightInfo trait and () impl", + "Add WeightInfo to pallet Config and update #[pallet::weight] attributes in lib.rs", + "Update pallet Cargo.toml with runtime-benchmarks feature", + "Wire into runtime: mock.rs WeightInfo=(), runtime Cargo.toml, runtime Config, define_benchmarks!", + "Run cargo test -p pallet-custom --features runtime-benchmarks to verify benchmark compilation", + "cargo build --release --features runtime-benchmarks to build benchmark WASM", + "Install frame-omni-bencher, download frame-weight-template.hbs", + "Run frame-omni-bencher to generate weights.rs", + "Update runtime Config to use pallet_custom::weights::SubstrateWeight" + ], + "result": "A measured weights.rs replaces the placeholder; runtime uses accurate production weights.", + "scenario": "Common scenario: benchmark the counter pallet and generate a production weight file", + "user_says": "Benchmark my counter pallet and generate weights.rs for production use" + }, + { + "actions": [ + "Open runtime/src/benchmarks.rs", + "Verify define_benchmarks! includes [pallet_custom, CustomPallet]", + "If missing, add the entry with underscore crate name and correct runtime type alias", + "Rebuild with --features runtime-benchmarks and re-run frame-omni-bencher" + ], + "result": "Benchmarks are discovered and weights.rs is generated successfully.", + "scenario": "Edge case: frame-omni-bencher reports 'No benchmarks found'", + "user_says": "frame-omni-bencher says No benchmarks found for pallet_custom" + } + ], + "id": "benchmark-frame-pallet", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [], + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with wasm32-unknown-unknown target", + "Cargo", + "The custom counter pallet in pallets/pallet-custom with mock runtime set up (from mock-runtime guide)" + ], + "tokens": [], + "wallet": [] + }, + "primary_page": "parachains/customize-runtime/pallet-development/benchmark-pallet.md", + "reference_code": { + "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/benchmark-pallet", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "parachains/customize-runtime/pallet-development/benchmark-pallet.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK parachain template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the parachain template node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Create benchmarking.rs with benchmark functions", + "commands": [ + "touch benchmarking.rs" + ], + "description": "Create pallets/pallet-custom/src/benchmarking.rs with:\n\n```rust\n#![cfg(feature = \"runtime-benchmarks\")]\nuse super::*;\nuse frame::deps::frame_benchmarking::v2::*;\nuse frame::benchmarking::prelude::RawOrigin;\n\n#[benchmarks]\nmod benchmarks {\n use super::*;\n\n #[benchmark]\n fn set_counter_value() {\n let new_value: u32 = 100;\n #[extrinsic_call]\n _(RawOrigin::Root, new_value);\n assert_eq!(CounterValue::::get(), new_value);\n }\n\n #[benchmark]\n fn increment() {\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 50;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), amount);\n assert_eq!(UserInteractions::::get(caller), 1);\n }\n\n #[benchmark]\n fn decrement() {\n CounterValue::::put(100);\n let caller: T::AccountId = whitelisted_caller();\n let amount: u32 = 30;\n #[extrinsic_call]\n _(RawOrigin::Signed(caller.clone()), amount);\n assert_eq!(CounterValue::::get(), 70);\n }\n```\n\n impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);\n}\n\nFor a different pallet, update function names and assertions accordingly.", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Define WeightInfo trait in weights.rs", + "commands": [ + "touch weights.rs" + ], + "description": "Create pallets/pallet-custom/src/weights.rs with a WeightInfo trait and () placeholder implementation:\n\n```rust\npub trait WeightInfo {\n fn set_counter_value() -> frame::prelude::Weight;\n fn increment() -> frame::prelude::Weight;\n fn decrement() -> frame::prelude::Weight;\n}\n\nimpl WeightInfo for () {\n fn set_counter_value() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(10_000, 0) }\n fn increment() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n fn decrement() -> frame::prelude::Weight { frame::prelude::Weight::from_parts(15_000, 0) }\n}\n```\n\nThe () impl is used in tests; benchmark-generated values will replace this file in the final step.", + "order": 4, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Wire WeightInfo into pallet Config and extrinsic annotations", + "description": "Edit src/lib.rs with four changes:\n1. At the top add: pub mod weights; (expose weights module)\n2. After 'pub use pallet::*;' add: #[cfg(feature = \"runtime-benchmarks\")] mod benchmarking;\n3. In #[pallet::config] trait add: type WeightInfo: weights::WeightInfo;\n4. Replace hardcoded weight attributes on each extrinsic:\n - #[pallet::weight(T::WeightInfo::set_counter_value())]\n - #[pallet::weight(T::WeightInfo::increment())]\n - #[pallet::weight(T::WeightInfo::decrement())]", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom/src" + }, + { + "action": "Update pallet Cargo.toml with runtime-benchmarks feature", + "description": "Open pallets/pallet-custom/Cargo.toml and add the runtime-benchmarks feature under [features]:\n\n```toml\n[features]\ndefault = [\"std\"]\nruntime-benchmarks = [\n \"frame/runtime-benchmarks\",\n]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame/std\",\n]\n```", + "order": 6, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom" + }, + { + "action": "Update mock.rs, runtime Cargo.toml, runtime config, and benchmarks.rs", + "description": "Four integration edits required:\n1. src/mock.rs: add type WeightInfo = (); to impl pallet_custom::Config for Test.\n2. runtime/Cargo.toml: add 'pallet-custom/runtime-benchmarks' to the runtime-benchmarks feature list.\n3. runtime/src/configs/mod.rs: add type WeightInfo = (); to impl pallet_custom::Config for Runtime (placeholder).\n4. runtime/src/benchmarks.rs: add [pallet_custom, CustomPallet] inside the define_benchmarks! macro.", + "order": 7, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Test benchmark compilation", + "commands": [ + "cargo test -p pallet-custom --features runtime-benchmarks" + ], + "description": "Run benchmark unit tests generated by impl_benchmark_test_suite!. All three benchmark functions must pass before building the full runtime. These tests verify the benchmark code compiles and the assertions hold.", + "expected_output": "test benchmarking::benchmarks::bench_set_counter_value ... ok", + "order": 8, + "working_directory": "polkadot-sdk-parachain-template/pallets/pallet-custom" + }, + { + "action": "Build the release runtime with benchmarks enabled", + "commands": [ + "cargo build --release --features runtime-benchmarks" + ], + "description": "Compile the full runtime in release mode. Produces the WASM at target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm. Build may take 15-30 minutes. This WASM is only for benchmarking — do not use for production deployment.", + "expected_output": "Finished release [optimized] target(s)", + "order": 9, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Install frame-omni-bencher", + "commands": [ + "cargo install frame-omni-bencher --locked" + ], + "description": "Install via cargo, or download a pre-built binary from the Polkadot SDK releases page. Replace VERSION with the SDK version tag used by the parachain template (e.g., polkadot-stable2412):\nmacOS: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher-aarch64-apple-darwin && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/\nUbuntu: curl -L -o frame-omni-bencher https://github.com/paritytech/polkadot-sdk/releases/download/VERSION/frame-omni-bencher && chmod +x frame-omni-bencher && sudo mv frame-omni-bencher /usr/local/bin/", + "expected_output": "Installed package `frame-omni-bencher`", + "order": 10, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Create the weight Handlebars template file", + "description": "Create the file `./pallets/pallet-custom/frame-weight-template.hbs` with the following content. This is the canonical Substrate weight template (a Handlebars template used by `frame-omni-bencher` to render the generated `weights.rs`). Save this exact content — needed in step 10:\n\n```handlebars\n{{header}}\n//! Autogenerated weights for `{{pallet}}`\n//!\n//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}\n//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`\n//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`\n//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`\n//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`\n\n// Executed Command:\n{{#each args as |arg|}}\n// {{arg}}\n{{/each}}\n\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#![allow(unused_parens)]\n#![allow(unused_imports)]\n#![allow(missing_docs)]\n\nuse frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};\nuse core::marker::PhantomData;\n\n/// Weight functions needed for `{{pallet}}`.\npub trait WeightInfo {\n\t{{#each benchmarks as |benchmark|}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{c.name}}: u32, {{/each~}}\n\t) -> Weight;\n\t{{/each}}\n}\n\n/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.\npub struct SubstrateWeight(PhantomData);\n{{#if (or (eq pallet \"frame_system\") (eq pallet \"frame_system_extensions\"))}}\nimpl WeightInfo for SubstrateWeight {\n{{else}}\nimpl WeightInfo for SubstrateWeight {\n{{/if}}\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n\n// For backwards compatibility and tests.\nimpl WeightInfo for () {\n\t{{#each benchmarks as |benchmark|}}\n\t{{#each benchmark.comments as |comment|}}\n\t/// {{comment}}\n\t{{/each}}\n\t{{#each benchmark.component_ranges as |range|}}\n\t/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.\n\t{{/each}}\n\tfn {{benchmark.name~}}\n\t(\n\t\t{{~#each benchmark.components as |c| ~}}\n\t\t{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}\n\t) -> Weight {\n\t\t// Proof Size summary in bytes:\n\t\t// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`\n\t\t// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.\n\t\tWeight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})\n\t\t\t{{#each benchmark.component_weight as |cw|}}\n\t\t\t// Standard Error: {{underscore cw.error}}\n\t\t\t.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_reads \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_reads as |cr|}}\n\t\t\t.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#if (ne benchmark.base_writes \"0\")}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))\n\t\t\t{{/if}}\n\t\t\t{{#each benchmark.component_writes as |cw|}}\n\t\t\t.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))\n\t\t\t{{/each}}\n\t\t\t{{#each benchmark.component_calculated_proof_size as |cp|}}\n\t\t\t.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))\n\t\t\t{{/each}}\n\t}\n\t{{/each}}\n}\n```\n\n**Note on versioning:** This template content is taken from `paritytech/polkadot-sdk` at tag `polkadot-stable2412`. The template format is stable across SDK versions — you should not need to update it for routine SDK bumps. If `frame-omni-bencher` reports a template-version mismatch (rare, only on major SDK breaking changes), fetch the latest from `https://github.com/paritytech/polkadot-sdk/blob/master/substrate/.maintain/frame-weight-template.hbs` and update this skill.", + "order": 11, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Execute benchmarks and generate weights.rs", + "commands": [ + "frame-omni-bencher v1 benchmark pallet --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm --pallet pallet_custom --extrinsic \"\" --template ./pallets/pallet-custom/frame-weight-template.hbs --output ./pallets/pallet-custom/src/weights.rs" + ], + "description": "Run benchmarks against the WASM runtime and generate production weights.rs. This overwrites the placeholder weights.rs from step 2. The output contains a SubstrateWeight struct implementing WeightInfo with actual measured values. For more precise measurements, add: --steps 50 --repeat 20", + "expected_output": "Wrote weight file to ./pallets/pallet-custom/src/weights.rs", + "order": 12, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Update runtime config to use generated weights", + "description": "Open runtime/src/configs/mod.rs and update the pallet Config to use measured weights instead of ():\n\n```rust\nimpl pallet_custom::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type CounterMaxValue = ConstU32<1000>;\n type WeightInfo = pallet_custom::weights::SubstrateWeight;\n}\n```\n\nThen rebuild for production without the benchmarks flag:\ncargo build --release", + "order": 13, + "working_directory": "polkadot-sdk-parachain-template" + } + ], + "supplementary_context": { + "description": "Load for context on pallet development prerequisites or parachain template.", + "pages": [ + { + "relevance": "The custom counter pallet being benchmarked.", + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md" + }, + { + "relevance": "The parachain template runtime structure required for benchmark integration.", + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md" + } + ] + }, + "title": "Benchmark a FRAME Pallet and Generate Weight Files", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Missing system build dependencies such as clang, cmake, or protobuf-compiler.", + "pattern": "error: linker not found / error while loading shared libraries / could not compile", + "resolution": "Complete the Install Polkadot SDK Dependencies guide. On Ubuntu: sudo apt install -y cmake clang libclang-dev protobuf-compiler. On macOS: brew install cmake protobuf." + }, + { + "cause": "The revive-dev-node is not running when eth-rpc starts, or it crashed before the adapter connected.", + "pattern": "Connection refused at localhost:8545 / eth-rpc exits immediately", + "resolution": "Ensure revive-dev-node --dev is running in a separate terminal and producing blocks before starting eth-rpc. The adapter connects via WebSocket to the node." + }, + { + "cause": "An Ethereum tool is calling a JSON-RPC method not implemented by the ETH-RPC adapter (e.g., anvil_* or debug_*).", + "pattern": "Method not found / eth_method not supported", + "resolution": "The local node supports a subset of the Ethereum JSON-RPC API. Use Polkadot Hub TestNet for full API compatibility. Check the Polkadot EVM differences documentation for supported methods." + } + ], + "examples": [ + { + "actions": [ + "Clone polkadot-sdk from GitHub", + "Build revive-dev-node with cargo build --release in polkadot-sdk/", + "Build eth-rpc adapter with cargo build --release", + "Start revive-dev-node --dev in terminal 1", + "Start eth-rpc --dev in terminal 2", + "Confirm ETH-RPC is listening at http://localhost:8545" + ], + "result": "Local node produces blocks; ETH-RPC adapter accepts standard Ethereum JSON-RPC at localhost:8545.", + "scenario": "Common scenario: start a local dev environment for smart contract development", + "user_says": "Set up a local Polkadot node for testing my smart contracts" + }, + { + "actions": [ + "Cancel the build with Ctrl+C", + "Check free disk space (df -h) — at least 20 GB required", + "Check free RAM — at least 8 GB recommended", + "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" + ], + "result": "Build completes more slowly with reduced CPU and memory usage.", + "scenario": "Edge case: cargo build takes excessively long or runs out of memory", + "user_says": "The build has been running for over an hour and my machine is unresponsive" + } + ], + "id": "set-up-local-dev-node", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [], + "runtime": [ + "Rust toolchain (stable + nightly via rustup) with wasm32-unknown-unknown target", + "Polkadot SDK build dependencies — complete the Install Polkadot SDK Dependencies guide first (Rust, clang, cmake, protobuf-compiler)", + "Git", + "At least 20 GB free disk space for the release build" + ], + "tokens": [], + "wallet": [] + }, + "primary_page": "smart-contracts/dev-environments/local-dev-node.md", + "reference_code": { + "base_path": "polkadot-docs/smart-contracts/local-dev-node", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-cookbook" + }, + "source_pages": [ + "smart-contracts/dev-environments/local-dev-node.md" + ], + "steps": [ + { + "action": "Clone the polkadot-sdk repository", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk.git", + "cd polkadot-sdk" + ], + "description": "Clone the official polkadot-sdk repository. The clone downloads several GB of source code. The repository contains both the revive-dev-node implementation and the ETH-RPC adapter.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the revive-dev-node binary", + "commands": [ + "cargo build -p revive-dev-node --bin revive-dev-node --release" + ], + "description": "Compile the Revive Dev node in release mode. Release builds are optimized but take significantly longer (up to 30 minutes). The binary will be at polkadot-sdk/target/release/revive-dev-node. If out of memory, limit parallelism by appending: -- -j2", + "expected_output": "Finished release [optimized] target(s)", + "order": 2, + "working_directory": "polkadot-sdk" + }, + { + "action": "Build the ETH-RPC adapter binary", + "commands": [ + "cargo build -p pallet-revive-eth-rpc --bin eth-rpc --release" + ], + "description": "Compile the ETH-RPC adapter. This translates Ethereum JSON-RPC calls into Substrate requests. Binary at polkadot-sdk/target/release/eth-rpc.", + "expected_output": "Finished release [optimized] target(s)", + "order": 3, + "working_directory": "polkadot-sdk" + }, + { + "action": "Start the Revive Dev node", + "commands": [ + "./target/release/revive-dev-node --dev" + ], + "description": "Start the local development node with --dev. This initializes a blockchain with pallet-revive for smart contracts and pre-funded development accounts. The node immediately produces blocks. Leave this terminal running — open a new terminal for step 5. For debug logging, prepend: RUST_LOG=\"error,evm=debug,sc_rpc_server=info,runtime::revive=debug\"", + "interactive": true, + "order": 4, + "working_directory": "polkadot-sdk" + }, + { + "action": "Start the ETH-RPC adapter in a new terminal", + "commands": [ + "./target/release/eth-rpc --dev" + ], + "description": "Open a new terminal, navigate to the polkadot-sdk directory, and start the adapter with --dev. It connects to the local node and exposes Ethereum JSON-RPC at http://localhost:8545. Success is indicated by log lines showing the adapter is ready. For debug logging, prepend: RUST_LOG=\"info,eth-rpc=debug\"\n\nVerify the environment is ready by connecting Hardhat, Remix, or MetaMask to http://localhost:8545.", + "interactive": true, + "order": 5, + "working_directory": "polkadot-sdk" + } + ], + "supplementary_context": { + "description": "Load for guidance on connecting Ethereum tools or deploying contracts.", + "pages": [ + { + "relevance": "Prerequisite: installing Rust and Polkadot SDK build dependencies.", + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md" + }, + { + "relevance": "Configure Hardhat to connect to http://localhost:8545 for local testing.", + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md" + } + ] + }, + "title": "Set Up a Local Development Node", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up a lightweight Dedot TypeScript client to read chain state and send transactions on any Polkadot SDK blockchain. Use when you need a modern, tree-shakable alternative to Polkadot.js for querying storage, subscribing to events, calling runtime APIs, or submitting signed extrinsics. Trigger phrases: 'use Dedot', 'query chain with Dedot', 'Dedot client', 'interact with chain TypeScript'. Output: account balance, runtime constants, and submitted transaction hash. No testnet tokens required for read-only steps.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Dependencies not installed or node_modules missing.", + "pattern": "Error: Cannot find package 'dedot'", + "resolution": "Run 'npm install dedot' in the dedot-example directory." + }, + { + "cause": "Project is not configured as ESM.", + "pattern": "SyntaxError: Cannot use import statement in a module", + "resolution": "Run 'npm pkg set type=module' in the project directory." + }, + { + "cause": "Wrong WebSocket endpoint URL or the node is unreachable.", + "pattern": "WebSocket connection failed / ECONNREFUSED", + "resolution": "Verify the WebSocket URL is correct and the node is online. For Polkadot mainnet use wss://rpc.polkadot.io. For Polkadot Hub TestNet use wss://asset-hub-paseo.dotters.network." + }, + { + "cause": "The queried account does not exist on chain (never had a balance).", + "pattern": "TypeError: Cannot read properties of undefined (reading 'free')", + "resolution": "An account with no on-chain history returns a default AccountInfo with all balances at 0. Check that the ADDRESS constant is a valid SS58 address for the target chain. Use a funded account or a known validator address for initial testing." + } + ], + "examples": [ + { + "actions": [ + "Scaffold 'dedot-example/' as an ESM Node.js project", + "Install dedot, @dedot/chaintypes, and tsx", + "Create main.ts with DedotClient connected to wss://rpc.polkadot.io", + "Query client.query.system.account for the target address", + "Run npx tsx main.ts and read the printed free balance" + ], + "result": "Free balance of the queried address printed to console in planck units.", + "scenario": "Common scenario: query account balance", + "user_says": "Use Dedot to check the balance of a Polkadot account" + }, + { + "actions": [ + "Generate a ChainApi interface for the custom chain: npx dedot chaintypes -w wss://your-node.example.com", + "Import the generated ChainApi and pass it as the type parameter to DedotClient.new()", + "Access custom pallet storage via client.query..()" + ], + "result": "Type-safe access to custom pallet storage with IntelliSense for the target chain's specific APIs.", + "scenario": "Edge case: targeting a custom Polkadot SDK chain", + "user_says": "Connect Dedot to my parachain and query a custom pallet" + } + ], + "id": "interact-with-chain-dedot", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.polkadot.io for Polkadot mainnet)" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "primary_page": "reference/tools/dedot.md", + "project_structure": "dedot-example/\n├── main.ts\n├── send-tx.ts\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/reference/tools/dedot", + "branch": "master", + "files": [ + { + "description": "DedotClient initialization via WsProvider against a Polkadot Hub WebSocket endpoint, typed against PolkadotApi from @dedot/chaintypes", + "path": "client-initialization-via-ws.ts" + }, + { + "description": "Signs and submits a Balances.transfer extrinsic using a @polkadot/keyring keypair; demonstrates the full signTx flow", + "path": "sign-and-send-tx-with-keyring.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "reference/tools/dedot.md" + ], + "steps": [ + { + "action": "Initialize an ESM Node.js project", + "commands": [ + "mkdir dedot-example && cd dedot-example", + "npm init -y && npm pkg set type=module" + ], + "description": "Create a new directory named 'dedot-example' and initialize it as an ESM Node.js project. The 'type=module' setting is required because Dedot uses ES module imports.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Dedot and TypeScript tooling", + "commands": [ + "npm install dedot", + "npm install -D @dedot/chaintypes tsx typescript" + ], + "description": "Install the 'dedot' runtime package, '@dedot/chaintypes' for per-chain type definitions and IntelliSense, and 'tsx' plus 'typescript' as dev tooling. The '@dedot/chaintypes' package enables auto-completion for chain-specific pallets and storage entries.", + "order": 2, + "working_directory": "dedot-example" + }, + { + "action": "Fetch the DedotClient initialization script", + "description": "Fetch the reference file `client-initialization-via-ws.ts` and save it as `main.ts`. Then make these substitutions:\n(1) Replace `INSERT_ADDRESS` with the SS58 address you'll query.\n(2) Replace `wss://rpc.polkadot.io` with the target chain's WebSocket endpoint if needed.\n\nThe script connects via `WsProvider` and uses `PolkadotApi` (from `@dedot/chaintypes`) as the ChainApi interface for type safety. If you target a different chain, replace `PolkadotApi` with the appropriate ChainApi (e.g. `KusamaApi`) or omit it to fall back to `SubstrateApi`.", + "order": 3, + "reference_file": "client-initialization-via-ws.ts", + "working_directory": "dedot-example" + }, + { + "action": "Append the balance query and disconnect", + "description": "Open `main.ts` and append the following block immediately before the end of `main()` (or wherever the connection is established) so the script queries account balance and cleanly disconnects:\n\n```typescript\nconst balance = await client.query.system.account('INSERT_ADDRESS');\nconsole.log('Free balance:', balance.data.free);\nawait client.disconnect();\n```\n\nReplace `INSERT_ADDRESS` with the same SS58 address you used in the previous step.", + "order": 4, + "working_directory": "dedot-example" + }, + { + "action": "Run the query script", + "commands": [ + "npx tsx main.ts" + ], + "description": "Execute main.ts. The output prints the free balance of the queried address in the chain's smallest unit. A zero balance is valid for unfunded accounts — it will not throw an error.", + "expected_output": "Free balance: ", + "order": 5, + "working_directory": "dedot-example" + }, + { + "action": "Sign and send a transaction", + "description": "Fetch the reference file `sign-and-send-tx-with-keyring.ts` and save it as `send-tx.ts`. The pattern uses `@polkadot/keyring` for signing. Make these substitutions:\n(1) Replace `INSERT_DEST_ADDRESS` with the recipient SS58 address.\n(2) Replace `2_000_000_000_000n` with the desired amount in planck (1 DOT = 10^10 planck).\n\nThen install the signer dependency:\n\n```bash\nnpm install @polkadot/keyring @polkadot/util-crypto\n```\n\nRun with `npx tsx send-tx.ts`. The script will print the transaction hash on inclusion.", + "order": 6, + "reference_file": "sign-and-send-tx-with-keyring.ts", + "working_directory": "dedot-example" + } + ], + "supplementary_context": { + "description": "Load these pages when the user asks about connecting to specific chains or using alternative clients.", + "pages": [ + { + "relevance": "Multi-SDK tutorial showing how to query account balance using PAPI, Polkadot.js, Dedot, and other clients side-by-side.", + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md" + }, + { + "relevance": "How to construct and submit signed transactions using multiple SDKs including Dedot.", + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + } + ] + }, + "title": "Interact with Polkadot Chains Using Dedot", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs the polkadot-omni-node pre-built binary and runs a full node for any compatible Polkadot parachain using an external chain spec file. Use when you need to run a parachain node without maintaining a custom node codebase, or when testing a runtime against a live network. Trigger phrases: 'run a parachain node', 'polkadot-omni-node', 'spin up a parachain full node', 'start omni node'. Output: syncing parachain node with WebSocket RPC at ws://localhost:9944. Requires a chain spec JSON file for the target parachain.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Binary not on PATH or installation failed.", + "pattern": "polkadot-omni-node: command not found", + "resolution": "Verify the binary was moved to /usr/local/bin/ and that directory is in $PATH. Run 'which polkadot-omni-node' to check. If missing, repeat the installation step." + }, + { + "cause": "The parachain runtime does not implement all APIs required by polkadot-omni-node (e.g., GetParachainInfo, AuraApi, or required pallets).", + "pattern": "Error: The chain spec is not compatible / missing required runtime API", + "resolution": "Check the runtime compatibility requirements at docs.polkadot.com/reference/tools/omninode/. The runtime must implement GetParachainInfo, AuraApi, and include the System, ParachainSystem, Aura, and ParachainInfo pallets. See the parachain template at github.com/paritytech/polkadot-sdk-parachain-template for a reference implementation." + }, + { + "cause": "Network connectivity issue, firewall blocking P2P ports, or incorrect chain spec.", + "pattern": "No peers found / node stuck at block 0", + "resolution": "Ensure outbound TCP on ports 30333/30334 is not blocked. Verify the chain spec corresponds to an active network. Check the Polkadot Discord for any network outages." + } + ], + "examples": [ + { + "actions": [ + "Download the latest polkadot-omni-node binary from the Polkadot SDK releases page", + "Verify installation with polkadot-omni-node --version", + "Download the Asset Hub chain spec JSON from paritytech.github.io/chainspecs/", + "Save as chain_spec.json and run: polkadot-omni-node --chain ./chain_spec.json --sync warp" + ], + "result": "Syncing Asset Hub node with WebSocket RPC accessible at ws://localhost:9944.", + "scenario": "Common scenario: run an Asset Hub full node", + "user_says": "Use polkadot-omni-node to run an Asset Hub node" + }, + { + "actions": [ + "Install Rust via rustup and system dependencies per parachains/install-polkadot-sdk/", + "Check crates.io for the latest polkadot-omni-node version", + "Run: cargo install --locked polkadot-omni-node@INSERT_VERSION", + "Verify: polkadot-omni-node --version" + ], + "result": "polkadot-omni-node installed from source and verified on a non-standard platform.", + "scenario": "Edge case: build from source when no pre-built binary is available for the platform", + "user_says": "I'm on a non-standard Linux architecture — install polkadot-omni-node from source" + } + ], + "id": "run-parachain-node-omni-node", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Internet access to download the binary and to sync the parachain" + ], + "runtime": [ + "macOS (Apple Silicon or Intel) or Linux (Ubuntu/Debian) for the pre-built binary path", + "Rust and Cargo (via rustup) if building from source — see docs.polkadot.com/parachains/install-polkadot-sdk/ for system dependencies" + ] + }, + "primary_page": "reference/tools/omninode.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/omninode.md" + ], + "steps": [ + { + "action": "Install polkadot-omni-node", + "description": "Download the pre-built binary for your platform from the Polkadot SDK releases page: https://github.com/paritytech/polkadot-sdk/releases\n\nFind the latest stable release tag (e.g., polkadot-stable2409-2 or similar). Look for the asset named:\n- macOS Apple Silicon: polkadot-omni-node-aarch64-apple-darwin\n- macOS Intel: polkadot-omni-node-x86_64-apple-darwin\n- Linux x86_64: polkadot-omni-node (no suffix)\n\nReplace INSERT_RELEASE_TAG with the full tag name (e.g., polkadot-stable2409-2):\n\nFor macOS:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node-aarch64-apple-darwin\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nFor Ubuntu/Linux:\n```\ncurl -L -o polkadot-omni-node https://github.com/paritytech/polkadot-sdk/releases/download/INSERT_RELEASE_TAG/polkadot-omni-node\nchmod +x polkadot-omni-node\nsudo mv polkadot-omni-node /usr/local/bin/\n```\n\nAlternatively, install from source (requires Rust — see prerequisites). Replace INSERT_VERSION with the crate version listed on https://crates.io/crates/polkadot-omni-node:\n```\ncargo install --locked polkadot-omni-node@INSERT_VERSION\n```", + "order": 1, + "working_directory": "." + }, + { + "action": "Verify the installation", + "commands": [ + "polkadot-omni-node --version" + ], + "description": "Confirm the binary is installed and accessible from PATH. You should see the installed version number. If the command is not found, ensure /usr/local/bin is in your PATH, or use the full path to the binary.", + "expected_output": "polkadot-omni-node ", + "order": 2, + "working_directory": "." + }, + { + "action": "Obtain a chain specification", + "description": "The polkadot-omni-node requires a chain spec JSON file. The official source is https://github.com/paritytech/chainspecs or the browsable UI at https://paritytech.github.io/chainspecs/\n\n1. Visit https://paritytech.github.io/chainspecs/ and find the parachain you want to run.\n2. Click the chain spec entry to open the JSON.\n3. Save the full JSON content locally as a file, e.g., 'chain_spec.json'.\n\nNote the exact file path — you will pass it to --chain in the next step. For testing with a custom parachain, you can export the chain spec from a Polkadot SDK node using 'polkadot-omni-node build-spec' or use chain-spec-builder from the Polkadot SDK.", + "order": 3, + "working_directory": "." + }, + { + "action": "Run a parachain full node", + "description": "Launch the node with warp sync enabled. Replace './chain_spec.json' with the actual path to your saved chain spec file:\n\nTo see all available flags before running:\n```\npolkadot-omni-node --help\n```\n\nTo start the node:\n```\npolkadot-omni-node --chain ./chain_spec.json --sync warp\n```\n\nThe --chain flag points to the chain spec; --sync warp enables fast catch-up to the latest finalized state (historical blocks sync in the background).\n\nYou will see log lines like 'Syncing' and 'Imported #NNNN' as the node connects to peers and downloads state. The node exposes a WebSocket RPC endpoint at ws://localhost:9944 by default.\n\nTo stop the node, press Ctrl+C. For long-running production use, configure a systemd service or a process manager.", + "order": 4, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for context on parachain runtimes, chain specs, or building from source.", + "pages": [ + { + "relevance": "How to build a parachain template runtime that is compatible with polkadot-omni-node.", + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md" + }, + { + "relevance": "Install Rust and system dependencies needed if building polkadot-omni-node from source.", + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md" + } + ] + }, + "title": "Run a Parachain Node with Polkadot Omni Node", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up the Python Substrate Interface library to connect to any Polkadot SDK blockchain, query on-chain storage (account balance, nonce), and submit signed extrinsics. Use when building Python scripts, data pipelines, or backend services that need to read or write chain state without a JavaScript runtime. Trigger phrases: 'Python Substrate Interface', 'py-substrate-interface', 'query Polkadot with Python', 'Substrate Python', 'substrate-interface pip'. Output: printed account balance details and transaction receipt with extrinsic and block hash.", + "env_vars": [ + { + "description": "The 12- or 24-word mnemonic phrase for the signing account. Required only for transaction submission steps. Stop and ask the user to add this to a .env file — do not ask for it in chat.", + "name": "MNEMONIC", + "required": false + } + ], + "error_patterns": [ + { + "cause": "Library not installed or virtual environment not activated.", + "pattern": "ModuleNotFoundError: No module named 'substrateinterface'", + "resolution": "Ensure the virtual environment is activated (run 'source bin/activate' on macOS/Linux or '.\\Scripts\\activate' on Windows), then run 'pip install substrate-interface'." + }, + { + "cause": "Wrong WebSocket URL or the node is not running.", + "pattern": "ConnectionError / WebSocket connection refused", + "resolution": "Verify the URL in SubstrateInterface(url=...). For a local node ensure it's running with --rpc-external or uses the default port 9944. For public endpoints, test with wss://rpc.polkadot.io." + }, + { + "cause": "The address passed to the storage query is not a valid SS58-encoded address.", + "pattern": "ValueError: Invalid address format", + "resolution": "Verify the account address is in SS58 format and matches the network's prefix. For Polkadot mainnet, addresses start with '1'; for testnet (Paseo) addresses start with a different prefix. Use polkadot.js.org/apps to validate the address." + }, + { + "cause": "The sender account does not have enough balance to pay the transfer amount plus transaction fees.", + "pattern": "ExtrinsicFailed: Token.FundsUnavailable", + "resolution": "Check the account balance with read_state.py before submitting. Ensure the 'value' parameter in call_params covers both the transfer amount and transaction fees." + } + ], + "examples": [ + { + "actions": [ + "Create virtual environment and install substrate-interface", + "Create connect.py with SubstrateInterface(url='wss://rpc.polkadot.io') and verify connection", + "Create read_state.py with substrate.query(module='System', storage_function='Account', params=[ADDRESS])", + "Print free_balance, reserved, and nonce from account_info.value" + ], + "result": "Account balance details printed with free balance in planck.", + "scenario": "Common scenario: query account balance from Python", + "user_says": "Use Python to check the balance of a Polkadot account" + }, + { + "actions": [ + "Check receipt.is_success — if False, print receipt.error_message for the specific error", + "If FundsUnavailable: verify sender balance covers transfer amount plus fees using read_state.py", + "If BadOrigin: verify the keypair matches the sender address in the call_params 'dest' field" + ], + "result": "Root cause identified from receipt.error_message; transaction corrected and resubmitted.", + "scenario": "Edge case: transaction fails with ExtrinsicFailed", + "user_says": "My Python transfer script runs but the extrinsic failed" + } + ], + "id": "interact-polkadot-node-py-substrate", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for the target Polkadot SDK node (e.g., wss://rpc.polkadot.io for Polkadot mainnet, or ws://127.0.0.1:9944 for a local node)" + ], + "runtime": [ + "Python 3.7+", + "pip" + ] + }, + "primary_page": "reference/tools/py-substrate-interface.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/py-substrate-interface.md" + ], + "steps": [ + { + "action": "Create a virtual environment and install the library", + "commands": [ + "python3 -m venv py-substrate-example && cd py-substrate-example", + "source bin/activate", + "pip install substrate-interface" + ], + "description": "Create an isolated virtual environment named 'py-substrate-example' and install the substrate-interface library. On Windows, replace 'source bin/activate' with '.\\Scripts\\activate'. After activation, the prompt shows '(py-substrate-example)'. All subsequent commands must be run in this activated environment.", + "order": 1, + "working_directory": "." + }, + { + "action": "Create the connection script", + "description": "Create 'connect.py' with the following content. Replace 'INSERT_WS_URL' with the WebSocket endpoint for your target node (e.g., 'wss://rpc.polkadot.io' for Polkadot mainnet or 'ws://127.0.0.1:9944' for a local dev node).\n\nFetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/substrate_interface.py -o connect.py\n\nThen replace INSERT_WS_URL in connect.py with the target endpoint. Run to verify the connection:\npython3 connect.py\n\nExpected output: 'Connected to chain: Polkadot' (or your target chain name). If the chain name is wrong or absent, the URL may be incorrect.", + "order": 2, + "working_directory": "py-substrate-example" + }, + { + "action": "Query account balance", + "description": "Create 'read_state.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/read_state.py -o read_state.py\n\nSubstitutions required:\n- At the top of the file, add the SubstrateInterface connection (same as connect.py, replacing INSERT_WS_URL with the endpoint).\n- Replace 'INSERT_ADDRESS' with the SS58 address to query.\n\nRun:\npython3 read_state.py\n\nExpected output:\n Account Details:\n - Free Balance: \n - Reserved: \n - Nonce: \n\nThe values are in planck (smallest unit). Divide by 10^10 for DOT values on Polkadot mainnet.", + "order": 3, + "working_directory": "py-substrate-example" + }, + { + "action": "Submit a balance transfer", + "description": "Create '.env' file:\nMNEMONIC=\n\nAdd '.env' to .gitignore:\necho '.env' >> .gitignore\n\nStop and ask the user to fill in their mnemonic phrase in the .env file. Do NOT ask for the mnemonic phrase in chat. Wait for confirmation before continuing.\n\nInstall dotenv:\npip install python-dotenv\n\nCreate 'send_tx.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/send_tx.py -o send_tx.py\n\nAt the top of send_tx.py, add:\n```python\nimport os\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom substrateinterface import SubstrateInterface, Keypair\n\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\nkeypair = Keypair.create_from_mnemonic(os.environ['MNEMONIC'])\n```\n\nThen add the template content below. Substitutions in send_tx.py:\n- Replace INSERT_WS_URL with the WebSocket endpoint.\n- Replace 'INSERT_ADDRESS' (in call_params 'dest') with the recipient SS58 address.\n- Replace 'INSERT_VALUE' with the transfer amount in planck (e.g., 1000000000000 for 0.1 DOT).\n\nRun:\npython3 send_tx.py\n\nExpected output:\n Transaction successful:\n - Extrinsic Hash: 0x...\n - Block Hash: 0x...", + "order": 4, + "working_directory": "py-substrate-example" + } + ], + "supplementary_context": { + "description": "Load these pages for context on chain interaction patterns or the Python SDK documentation.", + "pages": [ + { + "relevance": "Multi-SDK account query tutorial including a Python Substrate Interface example alongside PAPI and Polkadot.js.", + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md" + }, + { + "relevance": "How to construct and submit transactions using multiple SDK clients including Python Substrate Interface.", + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + } + ] + }, + "title": "Interact with a Polkadot Node Using Python Substrate Interface", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Scaffolds a Rust project with the Subxt library to interact with any Polkadot SDK blockchain using compile-time type safety. Use when building Rust services, CLI tools, or backend applications that need to query chain state, call runtime APIs, or submit transactions without an external RPC framework. Trigger phrases: 'Subxt', 'Rust Polkadot client', 'interact with Polkadot in Rust', 'subxt-cli metadata', 'type-safe Polkadot Rust'. Output: existential deposit constant, account info, and confirmed balance transfer event.", + "env_vars": [ + { + "description": "12- or 24-word BIP39 mnemonic for the signing account. Required for the transaction submission step. Stop and ask the user to add this to a .env file — do not ask for it in chat.", + "name": "SECRET_PHRASE", + "required": false + } + ], + "error_patterns": [ + { + "cause": "The generated types from #[subxt::subxt] changed after re-downloading metadata from a different chain or after a runtime upgrade.", + "pattern": "error[E0425]: cannot find value / type in scope — after changing metadata", + "resolution": "Re-download metadata (step 4) and rebuild. If querying a different network than the metadata was originally fetched for, the type paths (polkadot::storage(), polkadot::constants(), etc.) may differ — check the Subxt docs for the new paths." + }, + { + "cause": "INSERT_NODE_URL was not replaced or the URL format is incorrect.", + "pattern": "Error: Rpc error: target url is not valid", + "resolution": "Ensure NODE_URL in subxt.rs is a full WebSocket URL starting with ws:// or wss:// (e.g., wss://rpc.polkadot.io). HTTP URLs are not supported." + }, + { + "cause": "INSERT_SECRET_PHRASE was not replaced or is malformed.", + "pattern": "invalid phrase: mnemonic / BIP39 error", + "resolution": "Verify the mnemonic is 12 or 24 BIP39 words separated by spaces. Load it from the SECRET_PHRASE env variable rather than hardcoding. Ensure there are no leading/trailing spaces in the .env value." + }, + { + "cause": "Insufficient balance, wrong destination address, or node RPC error.", + "pattern": "Module 'Balances' / ExtrinsicFailed", + "resolution": "Check that the sender account has enough balance to cover the transfer amount plus transaction fees. Verify INSERT_DEST_ADDRESS is a valid SS58 address for the target network." + } + ], + "examples": [ + { + "actions": [ + "Create Rust project and install subxt-cli", + "Add subxt, subxt-signer, tokio dependencies to Cargo.toml", + "Fetch chain metadata: subxt metadata --url wss://rpc.polkadot.io > polkadot_metadata.scale", + "Create subxt.rs with #[subxt::subxt] macro pointing to polkadot_metadata.scale", + "Replace INSERT_NODE_URL and INSERT_ADDRESS, then run cargo run" + ], + "result": "Existential deposit constant and account info struct printed to stdout.", + "scenario": "Common scenario: query chain constants and account balance", + "user_says": "Use Subxt in Rust to read the existential deposit and an account's balance" + }, + { + "actions": [ + "Re-fetch metadata: subxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale", + "Run cargo build and review compile errors for changed type paths", + "Update pallet names or storage entry paths in subxt.rs to match the new metadata" + ], + "result": "Code updated to match post-upgrade runtime types and successfully compiles and runs.", + "scenario": "Edge case: runtime upgrade changes generated types", + "user_says": "My Subxt code broke after the chain did a runtime upgrade" + } + ], + "id": "interact-polkadot-node-subxt", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Rust and Cargo installed via rustup (https://rustup.rs/)", + "A Rust project directory (cargo new my_project)" + ] + }, + "primary_page": "reference/tools/subxt.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/subxt.md" + ], + "steps": [ + { + "action": "Create a Rust project", + "commands": [ + "cargo new subxt-example && cd subxt-example" + ], + "description": "Create a new Rust binary project named 'subxt-example'. All subsequent steps run from within this directory.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install the subxt-cli tool", + "description": "Install the subxt-cli command-line tool used to download chain metadata. Check https://crates.io/crates/subxt-cli for the latest version and replace INSERT_SUBXT_CLI_VERSION below:\n\ncargo install subxt-cli@INSERT_SUBXT_CLI_VERSION\n\nAs of the current reference files, the version is 0.50.0, so the command would be:\ncargo install subxt-cli@0.50.0\n\nThis installs the 'subxt' binary globally. Installation may take several minutes as it compiles from source.", + "order": 2, + "working_directory": "subxt-example" + }, + { + "action": "Add Subxt dependencies to Cargo.toml", + "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is.", + "order": 3, + "working_directory": "subxt-example" + }, + { + "action": "Download chain metadata", + "description": "Use subxt-cli to fetch the chain's runtime metadata. Replace INSERT_NODE_URL with the WebSocket URL of the node (e.g., wss://rpc.polkadot.io for Polkadot mainnet):\n\nsubxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale\n\nThis saves the SCALE-encoded metadata to 'polkadot_metadata.scale' in the project root. The #[subxt::subxt] macro references this file at compile time. If the command hangs, verify the node URL is reachable and the node is running.", + "order": 4, + "working_directory": "subxt-example" + }, + { + "action": "Create the main Rust source file", + "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation.", + "order": 5, + "working_directory": "subxt-example" + }, + { + "action": "Build and run", + "commands": [ + "cargo run" + ], + "description": "Build and run the project. The first build will take several minutes as dependencies compile. Subsequent builds are faster. The program: (1) reads the existential deposit constant, (2) fetches account info for INSERT_ADDRESS, (3) submits a balance transfer and waits for finalization. If the transfer step fails with an error, check that the account has sufficient balance (at least INSERT_AMOUNT + gas fees) and that INSERT_SECRET_PHRASE belongs to the sender account.", + "expected_output": "Existential deposit: \nAccount info: \nBalance transfer successful: ", + "order": 6, + "working_directory": "subxt-example" + } + ], + "supplementary_context": { + "description": "Load these pages for context on chain interaction patterns or the Subxt API surface.", + "pages": [ + { + "relevance": "Multi-SDK account query tutorial including Subxt alongside PAPI, Polkadot.js, and Python Substrate Interface.", + "slug": "chain-interactions-accounts-query-accounts", + "title": "Query Account Information with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/accounts/query-accounts.md" + }, + { + "relevance": "How to sign and submit transactions with multiple SDK clients — context for the Subxt transaction pattern.", + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + } + ] + }, + "title": "Interact with a Polkadot Node Using Subxt (Rust)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Scaffolds a Hardhat project manually, installs OpenZeppelin contracts and dotenv, creates a MyNFT.sol ERC-721 contract and Ignition deployment module, compiles with Cancun EVM version, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when deploying an ERC-721 NFT to Polkadot Hub. Requires testnet PAS tokens and a funded EVM wallet. Trigger phrases: 'deploy NFT Polkadot', 'ERC-721 Hardhat', 'deploy non-fungible token', 'mint NFT Polkadot Hub'. Uses OpenZeppelin Contracts v5.4.0+ requiring evmVersion cancun. Do NOT use for ERC-20 tokens; use deploy-erc20-token-hardhat instead.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode requiring Cancun EVM version.", + "pattern": "CompilationError: Invalid opcode: MCOPY", + "resolution": "In hardhat.config.ts set evmVersion: 'cancun' inside solidity.settings, then run 'npx hardhat compile' again." + }, + { + "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." + }, + { + "cause": "Ignition lost track of deployment due to missing gasPrice or requiredConfirmations: 0.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." + }, + { + "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + } + ], + "examples": [ + { + "actions": [ + "Create project directory 'hardhat-nft-deployment' and run npm init -y", + "Install hardhat, OpenZeppelin contracts, and dotenv", + "Create hardhat.config.ts with dotenv, evmVersion: cancun, gasPrice 5000 gwei, requiredConfirmations: 1", + "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Create contracts/MyNFT.sol and ignition/modules/MyNFT.ts (replacing INSERT_OWNER_ADDRESS)", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet; delegate confirmation to user" + ], + "result": "ERC-721 NFT contract deployed; contract address printed to console", + "scenario": "Common scenario: deploy a new ERC-721 NFT contract", + "user_says": "Deploy an ERC-721 NFT contract to Polkadot Hub TestNet" + }, + { + "actions": [ + "Open hardhat.config.ts", + "Set solidity to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", + "Run npx hardhat compile again" + ], + "result": "Compilation succeeds with Cancun EVM version", + "scenario": "Edge case: compilation fails with MCOPY opcode error", + "user_says": "Compilation fails with 'Invalid opcode: MCOPY'" + } + ], + "id": "deploy-erc721-nft-hardhat", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "runtime": [ + "Node.js v22.13.1 or later", + "npm" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" + ], + "steps": [ + { + "action": "Create the project directory and initialize npm", + "commands": [ + "mkdir hardhat-nft-deployment", + "cd hardhat-nft-deployment", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Hardhat, OpenZeppelin, and dotenv", + "commands": [ + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox", + "npm install @openzeppelin/contracts dotenv" + ], + "description": "Install Hardhat v2 toolchain, OpenZeppelin contracts (v5.4.0+), and dotenv for secure private key handling.", + "order": 2, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Create the Hardhat configuration file", + "description": "Create a file named 'hardhat.config.ts' with the following content:\n\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\nimport '@nomicfoundation/hardhat-toolbox';\nimport 'dotenv/config';\n\nconst config: HardhatUserConfig = {\n solidity: {\n version: '0.8.28',\n settings: { evmVersion: 'cancun' }\n },\n networks: {\n polkadotTestnet: {\n url: 'https://services.polkadothub-rpc.com/testnet',\n chainId: 420420417,\n accounts: [process.env.PRIVATE_KEY as string],\n gasPrice: 5000000000000\n }\n },\n ignition: { requiredConfirmations: 1 }\n};\n\nexport default config;\n```\n\nKey points: (1) 'dotenv/config' must be the first functional import. (2) evmVersion: 'cancun' is required for OpenZeppelin v5.4.0+ (mcopy opcode). (3) gasPrice: 5000000000000 (5000 gwei) prevents 'priority is too low' errors on TestNet. (4) requiredConfirmations: 1 prevents Ignition from misreading pending transactions as dropped.", + "order": 3, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Create .env file and .gitignore entry", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding.", + "order": 4, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Create project directories", + "commands": [ + "mkdir -p contracts ignition/modules" + ], + "description": "Create the contracts and ignition/modules directories that Hardhat expects.", + "order": 5, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Create the MyNFT.sol contract", + "description": "Create 'contracts/MyNFT.sol' with the following OpenZeppelin ERC-721 contract.Use the inline content below:\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```", + "order": 6, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Create the Ignition deployment module", + "description": "Create 'ignition/modules/MyNFT.ts'. Replace INSERT_OWNER_ADDRESS with the Ethereum address that should own the NFT contract (typically the address derived from your PRIVATE_KEY):\n\n```typescript\nimport { buildModule } from '@nomicfoundation/hardhat-ignition/modules';\n\nconst MyNFTModule = buildModule('MyNFTModule', (m) => {\n const myNFT = m.contract('MyNFT', ['INSERT_OWNER_ADDRESS']);\n return { myNFT };\n});\n\nexport default MyNFTModule;\n```", + "order": 7, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Compile the contract", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile MyNFT.sol. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in hardhat.config.ts and recompile.", + "expected_output": "Compiled 1 Solidity file successfully", + "order": 8, + "working_directory": "hardhat-nft-deployment" + }, + { + "action": "Deploy the NFT contract to Polkadot Hub TestNet", + "commands": [ + "npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet" + ], + "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success.", + "expected_output": "MyNFTModule#MyNFT - 0x...", + "interactive": true, + "order": 9, + "working_directory": "hardhat-nft-deployment" + } + ], + "supplementary_context": { + "description": "Load when the user asks about minting NFTs, building a dApp on the deployed contract, or ERC-721 concepts.", + "pages": [ + { + "relevance": "Build a full dApp (Next.js + Viem) on top of a deployed smart contract on Polkadot Hub.", + "slug": "smart-contracts-cookbook-dapps-zero-to-hero", + "title": "Zero to Hero Smart Contract DApp", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/dapps/zero-to-hero.md" + }, + { + "relevance": "Reference for full Hardhat EVM environment setup and network configuration for Polkadot Hub.", + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md" + } + ] + }, + "title": "Deploy an ERC-721 NFT Using Hardhat", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", + "env_vars": [], + "error_patterns": [ + { + "cause": "MetaMask is connected to a different network than Polkadot Hub TestNet.", + "pattern": "MetaMask: Wrong network / chain ID mismatch", + "resolution": "Switch to or add Polkadot Hub TestNet in MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet." + }, + { + "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", + "pattern": "insufficient funds for gas", + "resolution": "Get testnet PAS tokens from https://faucet.polkadot.io/ for the connected account, then retry." + }, + { + "cause": "Contract code syntax error or Solidity version mismatch.", + "pattern": "Compilation errors in Remix", + "resolution": "Verify the contract code was pasted correctly. In the Solidity Compiler tab, ensure the compiler version is 0.8.22 or later." + } + ], + "examples": [ + { + "actions": [ + "Open https://remix.ethereum.org and create contracts/MyNFT.sol with the ERC-721 code", + "Compile via the Solidity Compiler plugin", + "Connect MetaMask with Injected Provider to Polkadot Hub TestNet", + "Enter owner address as constructor argument and click Deploy", + "Confirm transaction in MetaMask" + ], + "result": "ERC-721 NFT contract deployed; contract address shown in Remix Deployed Contracts panel", + "scenario": "Common scenario: deploy ERC-721 NFT via Remix IDE", + "user_says": "Deploy an ERC-721 NFT using Remix" + }, + { + "actions": [ + "Direct user to https://wizard.openzeppelin.com/polkadot to generate a custom ERC-721 contract", + "Paste the generated code into Remix as contracts/MyNFT.sol", + "Proceed with the standard compile and deploy steps" + ], + "result": "Custom ERC-721 contract with user-specified extensions compiled and deployed via Remix", + "scenario": "Edge case: user wants a custom NFT with additional features", + "user_says": "I want to customize my NFT with a supply cap or royalties" + } + ], + "id": "deploy-erc721-nft-remix", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet (chain ID 420420417)" + ], + "tokens": [ + "Testnet PAS tokens for gas — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask browser extension installed and configured for Polkadot Hub TestNet", + "Testnet account funded with PAS tokens" + ] + }, + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" + ], + "steps": [ + { + "action": "Open Remix IDE and create the contract file", + "description": "Delegate to the user: (1) Navigate to https://remix.ethereum.org in your web browser. (2) Under the 'contracts' folder, click 'Create new file' and name it 'MyNFT.sol'. (3) Paste the following ERC-721 contract code:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```\n\nWait for the user to confirm the file has been created and the code pasted.", + "interactive": true, + "order": 1, + "working_directory": "." + }, + { + "action": "Compile the contract in Remix", + "description": "Delegate to the user: (1) Click the 'Solidity Compiler' icon in the left panel. (2) Click the 'Compile MyNFT.sol' button. Success: the compiler icon shows a green checkmark. If errors appear, check that the contract code was pasted correctly and the Solidity version is 0.8.22+.", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: (1) Click 'Deploy & Run Transactions' in the left panel. (2) In the 'Environment' dropdown, select 'Injected Provider - MetaMask'. (3) Approve the MetaMask connection popup. (4) Confirm MetaMask is on Polkadot Hub TestNet (chain ID 420420417). If not, add it via MetaMask Settings > Networks with RPC https://services.polkadothub-rpc.com/testnet. Confirm the funded account is visible.", + "interactive": true, + "order": 3, + "working_directory": "." + }, + { + "action": "Deploy the NFT contract", + "description": "Delegate to the user: (1) Ensure 'MyNFT' is selected in the Contract dropdown. (2) Expand the Deploy section and enter the constructor argument: the owner address (typically your MetaMask wallet address). (3) Click 'Deploy'. (4) Confirm the transaction in MetaMask. Once deployed, the contract appears in 'Deployed Contracts'. Record the contract address.", + "interactive": true, + "order": 4, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load when the user asks about connecting Remix to Polkadot Hub or wants CLI-based NFT deployment.", + "pages": [ + { + "relevance": "How to connect Remix IDE to Polkadot Hub and configure MetaMask for the TestNet.", + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md" + }, + { + "relevance": "Alternative CLI-based NFT deployment using Hardhat — better for production or CI/CD workflows.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-nft-nft-hardhat", + "title": "Deploy an ERC-721 Using Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" + } + ] + }, + "title": "Deploy an ERC-721 NFT Using Remix IDE", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer account. Loaded via 'source .env' into the shell session. Must be funded with testnet PAS. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Foundry was installed but PATH was not updated for the current shell session.", + "pattern": "foundryup: command not found / forge: command not found", + "resolution": "Run 'source ~/.bashrc' (or '~/.zshrc' on macOS) or start a new terminal." + }, + { + "cause": "Using the Foundry stable release instead of nightly. Stable does not include Polkadot chain definitions.", + "pattern": "Error: Chain 'polkadot-testnet' not found / unknown chain", + "resolution": "Install the nightly build: run 'foundryup --version nightly'." + }, + { + "cause": "Account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", + "pattern": "Error: Priority is too low / insufficient funds", + "resolution": "Get testnet PAS from https://faucet.polkadot.io/. The Foundry nightly build handles gas pricing automatically for Polkadot chains." + }, + { + "cause": "forge test runs against Anvil (Ethereum semantics), not Polkadot Hub. Existential deposit and gas model differ.", + "pattern": "forge test: tests pass locally but fail on TestNet", + "resolution": "Use forge test only for unit testing contract logic. For Polkadot-specific behavior, test against a local dev node or TestNet." + } + ], + "examples": [ + { + "actions": [ + "Install Foundry nightly: curl -L https://foundry.paradigm.xyz | bash && foundryup --version nightly", + "Run forge init my-foundry-project", + "Configure foundry.toml with Polkadot TestNet verifier settings", + "Create .env with PRIVATE_KEY; ask user to fill in and run source .env", + "Run forge build to compile", + "Run forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url ... --private-key $PRIVATE_KEY --broadcast", + "Verify: forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet" + ], + "result": "Contract compiled, deployed, and verified on Polkadot Hub TestNet", + "scenario": "Common scenario: install Foundry and deploy a contract", + "user_says": "Set up Foundry and deploy a contract to Polkadot Hub TestNet" + }, + { + "actions": [ + "Run 'forge --version' to check build type", + "If not nightly, run 'foundryup --version nightly' to upgrade", + "Retry the forge create command" + ], + "result": "Foundry upgraded to nightly; polkadot-testnet chain recognized and deployment succeeds", + "scenario": "Edge case: stable Foundry installed instead of nightly", + "user_says": "I get 'unknown chain polkadot-testnet' when running forge create" + } + ], + "id": "set-up-foundry-polkadot-hub", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet (chain ID 420420417) — supported natively by Foundry nightly as --chain polkadot-testnet" + ], + "runtime": [ + "Unix-based OS (Linux or macOS) or Windows WSL", + "Git installed", + "curl installed" + ], + "tokens": [ + "Testnet PAS tokens for deployment — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/dev-environments/foundry.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/dev-environments/foundry.md" + ], + "steps": [ + { + "action": "Install Foundry nightly build", + "commands": [ + "curl -L https://foundry.paradigm.xyz | bash", + "foundryup --version nightly" + ], + "description": "Install foundryup then the Foundry nightly build. Nightly is required — stable does not include native Polkadot chain support. If 'foundryup' is not found after the first command, run 'source ~/.bashrc' (or ~/.zshrc) or start a new terminal. Verify with 'forge --version'.", + "expected_output": "forge Version: (nightly build version string)", + "order": 1, + "working_directory": "." + }, + { + "action": "Initialize a new Foundry project", + "commands": [ + "forge init my-foundry-project", + "cd my-foundry-project" + ], + "description": "Create a new Foundry project with src/ (contracts), script/ (deployment scripts), test/ (Solidity tests), lib/ (dependencies), and foundry.toml. A sample Counter.sol is placed in src/.", + "order": 2, + "working_directory": "." + }, + { + "action": "Configure foundry.toml for Polkadot Hub TestNet", + "description": "Replace the contents of 'foundry.toml' with the following. Choose Blockscout (no API key) or Routescan:\n\nBlockscout:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"\", url = \"https://blockscout-testnet.polkadot.io/api?\" }\n```\n\nRoutescan:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"verifyContract\", url = \"https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan\" }\n```\n\nWith this config, use --chain polkadot-testnet in commands without specifying the RPC URL explicitly.", + "order": 3, + "working_directory": "my-foundry-project" + }, + { + "action": "Create .env file and load private key", + "commands": [ + "printf 'PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env with their 0x-prefixed private key for a testnet-funded account. Do NOT ask for the private key in chat. After confirmation, instruct the user to run 'source .env' in their terminal to load the variable into their shell session.", + "order": 4, + "working_directory": "my-foundry-project" + }, + { + "action": "Compile contracts", + "commands": [ + "forge build" + ], + "description": "Compile all Solidity contracts in src/. Artifacts output to out/. The sample Counter.sol should compile without errors.", + "expected_output": "Compiler run successful!", + "order": 5, + "working_directory": "my-foundry-project" + }, + { + "action": "Deploy a contract to Polkadot Hub TestNet", + "commands": [ + "forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url https://services.polkadothub-rpc.com/testnet --private-key $PRIVATE_KEY --broadcast" + ], + "description": "Deploy the Counter contract to Polkadot Hub TestNet. Replace 'src/Counter.sol:Counter' with your contract path and name if deploying a custom contract. Save the 'Deployed to: 0x...' address — needed for verification and interaction.", + "expected_output": "Deployed to: 0x...", + "order": 6, + "working_directory": "my-foundry-project" + }, + { + "action": "Verify the contract on the block explorer", + "description": "Replace INSERT_CONTRACT_ADDRESS with the address from step 6.\n\nBlockscout:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet\n```\n\nRoutescan:\n```bash\nforge verify-contract INSERT_CONTRACT_ADDRESS src/Counter.sol:Counter --verifier-url 'https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan' --etherscan-api-key 'verifyContract' --chain polkadot-testnet\n```\n\nExpected output: 'Response: OK' and a URL to the verified contract.", + "order": 7, + "working_directory": "my-foundry-project" + }, + { + "action": "Interact with the contract using Cast", + "description": "Replace INSERT_CONTRACT_ADDRESS and INSERT_ACCOUNT_ADDRESS with actual values.\n\nRead from contract:\n```bash\ncast call INSERT_CONTRACT_ADDRESS 'number()(uint256)' --chain polkadot-testnet\n```\n\nWrite to contract:\n```bash\ncast send INSERT_CONTRACT_ADDRESS 'setNumber(uint256)' 42 --chain polkadot-testnet --private-key $PRIVATE_KEY\n```\n\nCheck balance:\n```bash\ncast balance INSERT_ACCOUNT_ADDRESS --chain polkadot-testnet\n```", + "order": 8, + "working_directory": "my-foundry-project" + } + ], + "supplementary_context": { + "description": "Load when the user wants to understand Polkadot Hub EVM differences or compare Foundry with Hardhat.", + "pages": [ + { + "relevance": "Explains differences between Polkadot Hub EVM and standard Ethereum including gas model and existential deposit.", + "slug": "smart-contracts-for-eth-devs-evm-vs-pvm", + "title": "EVM vs PVM", + "url": "https://docs.polkadot.com/smart-contracts/for-eth-devs/evm-vs-pvm.md" + }, + { + "relevance": "Network details for Polkadot Hub TestNet and MainNet RPC endpoints and chain IDs.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + } + ] + }, + "title": "Use Foundry with Polkadot Hub", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs Zombienet (executable, Nix, or Docker), downloads polkadot and polkadot-parachain binaries, writes a TOML network config defining relay chain validators and parachain collators, spawns the network via the native provider, writes .zndsl test files using Zombienet's DSL, and runs the test suite. Use when spawning and testing ephemeral Polkadot SDK networks locally. Trigger phrases: 'Zombienet', 'spawn local parachain', 'test parachain network', 'local relay chain Zombienet', 'zndsl test'. Requires polkadot and polkadot-parachain binaries.", + "env_vars": [], + "error_patterns": [ + { + "cause": "polkadot or polkadot-parachain binaries are not in PATH.", + "pattern": "polkadot: binary not found / command not found", + "resolution": "Run 'zombienet setup polkadot polkadot-parachain' to download the binaries, then add them to PATH: export PATH=$PATH:." + }, + { + "cause": "macOS Gatekeeper is blocking the Zombienet executable.", + "pattern": "macOS: cannot be opened because it is from an unidentified developer", + "resolution": "Run: xattr -d com.apple.quarantine zombienet" + }, + { + "cause": "Binary download was slow or the chain spec file is missing or invalid.", + "pattern": "Timeout: network did not start within timeout seconds", + "resolution": "Increase the timeout value in the [settings] section of network.toml. Verify chain_spec_path exists and is valid." + }, + { + "cause": "Parachain onboarding takes time or the collator failed to start.", + "pattern": "Test FAIL: parachain X is not registered within N seconds", + "resolution": "Increase the 'within N seconds' timeout in the .zndsl assertion. Review Zombienet logs for collator startup errors." + } + ], + "examples": [ + { + "actions": [ + "Install Zombienet executable and run zombienet setup polkadot polkadot-parachain", + "Download paseo-asset-hub.json chain spec if using Polkadot Hub parachain", + "Create network.toml with relay chain (alice, bob) and parachain (id=1000)", + "Run zombienet spawn network.toml --provider native in a separate terminal", + "Create network-test.zndsl with health and parachain assertions", + "Run zombienet test network-test.zndsl --provider native" + ], + "result": "Local network spawns successfully; all test assertions PASS", + "scenario": "Common scenario: spawn and test a relay+parachain network", + "user_says": "Spawn a local Zombienet network and run tests" + }, + { + "actions": [ + "Remove the [[parachains]] section from network.toml", + "Remove parachain assertions from network-test.zndsl", + "Run zombienet spawn network.toml --provider native", + "Run zombienet test network-test.zndsl --provider native" + ], + "result": "Relay chain with alice and bob validators spawns; health assertions pass", + "scenario": "Edge case: test relay chain only without a parachain", + "user_says": "Spawn just a relay chain network for testing consensus behavior" + } + ], + "id": "spawn-test-network-zombienet", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Unix-based OS (Linux or macOS) or Windows WSL", + "polkadot and polkadot-parachain binaries — downloaded via: zombienet setup polkadot polkadot-parachain" + ] + }, + "primary_page": "reference/tools/zombienet.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/zombienet.md" + ], + "steps": [ + { + "action": "Install Zombienet", + "commands": [ + "curl -L https://github.com/paritytech/zombienet/releases/latest/download/zombienet-linux-x64 -o zombienet", + "chmod +x zombienet", + "mv zombienet /usr/local/bin/zombienet", + "zombienet version" + ], + "description": "Download the Zombienet executable for Linux x64. For macOS replace 'zombienet-linux-x64' with 'zombienet-macos'. For macOS Gatekeeper issues run: xattr -d com.apple.quarantine zombienet. The 'zombienet version' command confirms successful installation. Docker alternative: docker run -it --rm -v $(pwd):/home/nonroot/zombie-net/host-current-files paritytech/zombienet", + "order": 1, + "working_directory": "." + }, + { + "action": "Download required node binaries", + "commands": [ + "zombienet setup polkadot polkadot-parachain" + ], + "description": "Download the polkadot and polkadot-parachain binaries for the native provider. Zombienet downloads these to a local path and adds them to PATH for the session. Verify availability: 'polkadot --version' and 'polkadot-parachain --version'.", + "order": 2, + "working_directory": "." + }, + { + "action": "Create a network configuration file", + "description": "Create 'network.toml' with a basic network configuration. This defines a relay chain with two validators and a parachain with one collator. If using the Polkadot Hub TestNet chain spec first download it:\n```bash\nwget https://paseo-r2.zondax.ch/chain-specs/paseo-asset-hub.json\n```\n\nThen create network.toml:\n```toml\n[settings]\ntimeout = 1000\n\n[relaychain]\nchain = \"paseo\"\ndefault_command = \"polkadot\"\n\n [[relaychain.nodes]]\n name = \"alice\"\n validator = true\n\n [[relaychain.nodes]]\n name = \"bob\"\n validator = true\n\n[[parachains]]\nid = 1000\nchain_spec_path = \"./paseo-asset-hub.json\"\n\n [parachains.collator]\n name = \"collator-01\"\n command = \"polkadot-parachain\"\n```\n\nAdjust id, chain_spec_path, and command fields to match your target network. For relay-only tests, omit the [[parachains]] section.", + "order": 3, + "working_directory": "." + }, + { + "action": "Spawn the network", + "commands": [ + "zombienet spawn network.toml --provider native" + ], + "description": "Spawn the local network using the native provider. Zombienet will: (1) locate or download binaries, (2) generate chain specifications, (3) start relay chain validators, (4) register and start parachain collators, (5) display RPC endpoints. The first relay chain node is typically accessible at ws://127.0.0.1:9944. Run this in a separate terminal — the network persists until you stop it with Ctrl+C.", + "expected_output": "Network launched\nRPC endpoints:", + "order": 4, + "working_directory": "." + }, + { + "action": "Write a test file", + "description": "Create 'network-test.zndsl' to test the spawned network:\n\n```\nDescription: Basic network health test\nNetwork: ./network.toml\nCreds: config\n\nalice: is up\nbob: is up\nalice: parachain 1000 is registered within 200 seconds\nalice: parachain 1000 block height is at least 10 within 300 seconds\n```\n\nCommon assertion types: node health ('alice: is up'), parachain registration, block height, metrics ('alice: reports node_roles is 4'), and log patterns ('alice: log line matches glob \"Imported #1\" within 10 seconds'). Remove parachain assertions if your network has no parachain.", + "order": 5, + "working_directory": "." + }, + { + "action": "Run the test suite", + "commands": [ + "zombienet test network-test.zndsl --provider native" + ], + "description": "Execute the test suite against the running network. Each assertion is evaluated in order; Zombienet reports PASS or FAIL. Exit code 0 = all pass, 1 = any failure. Ensure the network from step 4 is still running before executing tests.", + "expected_output": "PASS", + "order": 6, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load when the user wants more Zombienet configuration options or wants to build and deploy the parachain template first.", + "pages": [ + { + "relevance": "Full tutorial for spawning a local parachain test network including building the parachain template binary.", + "slug": "parachains-testing-run-a-parachain-network", + "title": "Run a Parachain Network", + "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md" + }, + { + "relevance": "Alternative testing approach using Chopsticks to fork a live parachain for isolated state testing.", + "slug": "parachains-testing-fork-a-parachain", + "title": "Fork a Parachain Using Chopsticks", + "url": "https://docs.polkadot.com/parachains/testing/fork-a-parachain.md" + } + ] + }, + "title": "Spawn and Test a Parachain Network with Zombienet", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Sets up a Node.js project with Web3.js and solc, creates a Storage Solidity contract, configures a provider for Polkadot Hub TestNet, compiles to EVM bytecode, deploys, and interacts via read/write scripts. Use when integrating Polkadot Hub with Web3.js. Trigger phrases: 'Web3.js Polkadot', 'deploy contract Web3', 'web3js smart contract Polkadot'. NOTE: Web3.js has been sunset — for new projects use deploy-contracts-ethers-js or deploy-contracts-viem instead. All code files require substituting INSERT_* placeholders before execution.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the deployer account. Used in deploy.js and updateStorage.js. Must be funded with testnet PAS. Never commit to version control.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Deployer account has insufficient PAS or gas price is below the TestNet 1000 gwei base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Get testnet PAS from https://faucet.polkadot.io/ and verify the funded account is used in deploy.js." + }, + { + "cause": "Web3.js dependency was not installed.", + "pattern": "Error: Cannot find module 'web3'", + "resolution": "Run 'npm install web3' in the web3js-project directory." + }, + { + "cause": "Compile step was skipped or output files are not in expected locations.", + "pattern": "Error reading ABI or bytecode file / file not found", + "resolution": "Run 'node scripts/compile.js' first to generate abis/Storage.json and artifacts/Storage.bin." + }, + { + "cause": "Wrong RPC URL or TestNet is temporarily unavailable.", + "pattern": "Error: cannot connect to provider / network timeout", + "resolution": "Verify the RPC URL is 'https://services.polkadothub-rpc.com/testnet'. Check Polkadot status pages for TestNet outages." + } + ], + "examples": [ + { + "actions": [ + "Create web3js-project, npm init -y, install web3 and solc", + "Create contracts/Storage.sol with the storage contract", + "Create and run scripts/connectToProvider.js (replace INSERT_RPC_URL, INSERT_CHAIN_ID, INSERT_CHAIN_NAME)", + "Create and run scripts/compile.js to generate ABI and bytecode", + "Create scripts/deploy.js, replace INSERT_PRIVATE_KEY with process.env.PRIVATE_KEY, run it", + "Create scripts/updateStorage.js, replace INSERT_PRIVATE_KEY and INSERT_CONTRACT_ADDRESS, run it" + ], + "result": "Storage contract deployed; read and write interactions confirmed", + "scenario": "Common scenario: deploy a Storage contract and interact with it", + "user_says": "Deploy a smart contract to Polkadot Hub using Web3.js" + }, + { + "actions": [ + "Note that Web3.js has been sunset", + "Recommend Ethers.js (deploy-contracts-ethers-js) or viem (deploy-contracts-viem) as actively maintained alternatives" + ], + "result": "User directed to deploy-contracts-ethers-js or deploy-contracts-viem for modern contract interaction", + "scenario": "Edge case: user wants a modern library instead of Web3.js", + "user_says": "Web3.js is sunset, what should I use instead?" + } + ], + "id": "deploy-interact-contracts-web3js", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417)" + ], + "runtime": [ + "Node.js v22.13.1 or later", + "npm v6.13.4 or later" + ], + "tokens": [ + "Testnet PAS tokens for deployment gas — get from https://faucet.polkadot.io/ (TestNet base fee: 1000 gwei)" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/libraries/web3-js.md", + "project_structure": "web3js-project/\n├── abis/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ ├── deploy.js\n│ └── updateStorage.js\n├── contract-address.json\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/smart-contracts/libraries/web3-js", + "branch": "master", + "files": [ + { + "description": "Simple Solidity Storage contract: public `storedNumber` state variable + `setNumber(uint256 _newNumber)` setter.", + "path": "Storage.sol" + }, + { + "description": "Web3.js provider connection to Polkadot Hub TestNet", + "path": "connectToProvider.js" + }, + { + "description": "Solidity compiler wrapper using solc; produces ABI + bytecode artifacts", + "path": "compile.js" + }, + { + "description": "Deploys the compiled Storage contract via Web3.js Contract.deploy().send()", + "path": "deploy.js" + }, + { + "description": "Calls `setNumber(value)` on the deployed Storage contract via Web3.js, then reads `storedNumber` to confirm.", + "path": "updateStorage.js" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "smart-contracts/libraries/web3-js.md" + ], + "steps": [ + { + "action": "Create project directory and initialize npm", + "commands": [ + "mkdir web3js-project", + "cd web3js-project", + "npm init -y" + ], + "description": "Create a new directory and initialize a Node.js project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Web3.js and solc", + "commands": [ + "npm install web3", + "npm install --save-dev solc" + ], + "description": "Install the Web3.js library for blockchain interaction and solc for compiling Solidity contracts to EVM bytecode.", + "order": 2, + "working_directory": "web3js-project" + }, + { + "action": "Create project directories and the Storage contract", + "commands": [ + "mkdir -p contracts scripts abis artifacts" + ], + "description": "Create the expected directory structure (contracts/, scripts/, abis/, artifacts/), then fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. The contract has a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter:\n\n```solidity\n// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.9;\n\ncontract Storage {\n uint256 public storedNumber;\n function setNumber(uint256 _newNumber) public { storedNumber = _newNumber; }\n}\n```", + "order": 3, + "reference_file": "Storage.sol", + "working_directory": "web3js-project" + }, + { + "action": "Create the provider connection script and verify connectivity", + "description": "Fetch the reference file `connectToProvider.js` and save it as `scripts/connectToProvider.js`. Then make these substitutions:\n(1) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`\n(2) Replace `INSERT_CHAIN_ID` with `420420417`\n(3) Replace `INSERT_CHAIN_NAME` with `polkadot-hub-testnet`\n\nThen verify: `node scripts/connectToProvider.js`\nExpected output: `Connected to chain ID: 420420417`", + "order": 4, + "reference_file": "connectToProvider.js", + "working_directory": "web3js-project" + }, + { + "action": "Create the compile script and compile the contract", + "commands": [ + "node scripts/compile.js" + ], + "description": "Fetch the reference file `compile.js` and save it as `scripts/compile.js`. The script reads `contracts/Storage.sol`, compiles it with solc, saves ABI to `abis/Storage.json` and bytecode to `artifacts/Storage.bin`. Then run it to compile.", + "expected_output": "ABI and bytecode saved", + "order": 5, + "reference_file": "compile.js", + "working_directory": "web3js-project" + }, + { + "action": "Create the deployment script and deploy the contract", + "description": "Fetch the reference file `deploy.js` and save it as `scripts/deploy.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY` (do NOT hardcode the key)\n(2) Ensure the RPC URL points to `https://services.polkadothub-rpc.com/testnet`\n\nSet `PRIVATE_KEY` in your environment: `export PRIVATE_KEY=0x...` (never commit this). Then deploy:\n\n```bash\nnode scripts/deploy.js\n```\n\nExpected: contract address saved in `contract-address.json`. Record this address.", + "order": 6, + "reference_file": "deploy.js", + "working_directory": "web3js-project" + }, + { + "action": "Create the interaction script and interact with the contract", + "description": "Fetch the reference file `updateStorage.js` and save it as `scripts/updateStorage.js`. Then substitute:\n(1) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`\n(2) Replace `INSERT_CONTRACT_ADDRESS` with the address from step 6, or ensure `contract-address.json` is read automatically\n(3) Verify the ABI references `abis/Storage.json`\n\nThen run: `node scripts/updateStorage.js`\nExpected: current stored value displayed, then updated to 1.", + "order": 7, + "reference_file": "updateStorage.js", + "working_directory": "web3js-project" + } + ], + "supplementary_context": { + "description": "Load when the user wants a modern alternative to Web3.js or asks about contract interaction options.", + "pages": [ + { + "relevance": "Recommended modern alternative to Web3.js for deploying and interacting with contracts on Polkadot Hub.", + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md" + }, + { + "relevance": "TypeScript-native alternative library for Polkadot Hub contract interactions.", + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md" + } + ] + }, + "title": "Deploy and Interact with Smart Contracts Using Web3.js", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Integrates an existing Polkadot SDK pallet into a local parachain runtime. Covers adding the pallet crate to Cargo.toml, implementing the pallet's Config trait in runtime/src/lib.rs, registering the pallet in the construct_runtime! macro, compiling the runtime, generating a chain spec, and running the parachain locally to verify the pallet is active. Use when extending a Polkadot SDK parachain template with standard SDK pallets (pallet-utility, pallet-multisig, pallet-proxy, etc.). Trigger phrases: 'add pallet to runtime', 'integrate pallet parachain', 'pallet-utility runtime', 'extend parachain runtime', 'construct_runtime pallet'. Prerequisite: working parachain template dev environment (see set-up-the-parachain-template). Final GUI verification uses Polkadot.js Apps.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The impl pallet_utility::Config for Runtime block is missing or has a typo in runtime/src/lib.rs.", + "pattern": "error[E0277]: the trait `pallet_utility::Config` is not implemented for `Runtime`", + "resolution": "Ensure the impl block is present and all required associated types are specified. Run cargo check -p runtime to see the full list of missing types. Each missing type will be listed as 'required by this bound in pallet_utility::Config'." + }, + { + "cause": "pallet-utility was not added to runtime/Cargo.toml, or it was added but Cargo has not re-resolved dependencies.", + "pattern": "error[E0432]: unresolved import `pallet_utility`", + "resolution": "Add pallet-utility to runtime/Cargo.toml [dependencies] with the correct version and default-features = false. Run cargo build again — Cargo will fetch and compile the new dependency." + }, + { + "cause": "The pallet was registered in construct_runtime! but with the wrong name, or the node is running an old binary without the updated runtime.", + "pattern": "Utility pallet does not appear in Polkadot.js Apps extrinsics", + "resolution": "Stop the node, rebuild with cargo build --release, regenerate the chain spec, and restart. Verify the construct_runtime! entry name exactly matches what you expect to see in the UI." + } + ], + "examples": [ + { + "actions": [ + "Check the existing Polkadot SDK version in runtime/Cargo.toml", + "Add pallet-utility dependency with matching version and default-features = false", + "Add pallet-utility/std to the std feature list", + "Implement pallet_utility::Config for Runtime in runtime/src/lib.rs with all required associated types", + "Add Utility: pallet_utility entry to construct_runtime!", + "Run cargo build --release", + "Generate chain spec and start node with --dev --tmp", + "Verify 'utility' appears in Polkadot.js Apps extrinsics" + ], + "result": "pallet-utility integrated into the runtime; utility.batch, utility.batchAll, and utility.dispatchAs are callable on-chain", + "scenario": "Common scenario: add pallet-utility to a parachain runtime", + "user_says": "Add pallet-utility to my parachain runtime" + }, + { + "actions": [ + "Read the full compiler error — each missing type is listed with 'required by this bound'", + "Add each missing associated type to the impl block", + "Refer to the pallet's source (src/lib.rs) or docs for the expected type definitions", + "Re-run cargo build --release" + ], + "result": "All required Config associated types satisfied; compilation succeeds", + "scenario": "Edge case: compilation fails with missing associated type in Config", + "user_says": "I get a compiler error about missing associated types in my Config impl" + } + ], + "id": "add-existing-pallet-to-runtime", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "No external network required — all compilation and testing is local" + ], + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with the wasm32-unknown-unknown target (`rustup target add wasm32-unknown-unknown`)", + "Polkadot SDK system dependencies installed (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" + ] + }, + "primary_page": "parachains/customize-runtime/add-existing-pallets.md", + "reference_code": { + "base_path": "", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-cookbook" + }, + "source_pages": [ + "parachains/customize-runtime/add-existing-pallets.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK parachain template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the parachain template node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Identify the pallet version to use", + "commands": [ + "grep 'polkadot-sdk' runtime/Cargo.toml | head -5" + ], + "description": "Check the existing Polkadot SDK version in your runtime's Cargo.toml. All new pallets must use the same version to avoid dependency conflicts. Look for entries like polkadot-sdk = { version = \"0.X.0\", ... } or per-crate pins. Note the version — you will use it in step 2. The docs use pallet-utility as the example pallet; substitute any SDK pallet name.", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Add the pallet dependency to runtime/Cargo.toml", + "description": "Open runtime/Cargo.toml and add pallet-utility (or your target pallet) to the [dependencies] section. Use the same version and features pattern as existing pallets:\n\n```toml\n[dependencies]\n...\npallet-utility = { version = \"\", default-features = false }\n```\n\nAlso add pallet-utility to the std features list so it compiles correctly for native (non-WASM) targets:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-utility/std\",\n]\n```\n\nReplace with the version you identified in step 1. After editing, run: cargo check -p runtime 2>&1 | head -20 — the output should not show dependency resolution errors.", + "order": 4, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Implement the pallet's Config trait in runtime/src/lib.rs", + "description": "Open runtime/src/lib.rs. Add the Config implementation for the pallet. For pallet-utility, add:\n\n```rust\nimpl pallet_utility::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n type PalletsOrigin = OriginCaller;\n type WeightInfo = pallet_utility::weights::SubstrateWeight;\n}\n```\n\nFor other pallets, refer to the pallet's documentation or source for required Config fields. Each required associated type must be specified — the Rust compiler will list missing types if any are omitted. Place this impl block near other pallet Config impls for consistency.", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Register the pallet in construct_runtime!", + "description": "Find the construct_runtime! macro invocation in runtime/src/lib.rs (typically near the bottom of the file). Add the new pallet entry following the existing pattern:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic\n {\n // ... existing pallets ...\n Utility: pallet_utility,\n }\n);\n```\n\nThe name before the colon (Utility) is the on-chain pallet name used in storage keys and extrinsic prefixes — choose carefully as it cannot be changed without a migration. Save the file.", + "order": 6, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Compile the runtime", + "commands": [ + "cargo build --release 2>&1 | tail -20" + ], + "description": "Build the full parachain node (includes the runtime WASM blob). This typically takes 5-15 minutes on first build. A successful build ends with: Compiling node-template vX.X.X ... Finished release [optimized] target(s). If compilation fails with 'the trait `Config` for `pallet_utility` is not satisfied', check that all required associated types are implemented in step 3.", + "order": 7, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Generate a fresh chain spec and start the node locally", + "commands": [ + "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/local-spec.json", + "./target/release/parachain-template-node --dev --tmp --chain /tmp/local-spec.json" + ], + "description": "Generate a development chain spec and start the node in --dev mode. The --tmp flag stores chain data in a temporary directory. Once the node is running and producing blocks (look for: Imported #1 in logs), open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics. Verify that 'utility' appears in the pallet dropdown as a callable extrinsic module.", + "order": 8, + "working_directory": "polkadot-sdk-parachain-template" + } + ], + "supplementary_context": { + "description": "Load these pages for parachain template setup or for adding multiple pallet instances.", + "pages": [ + { + "relevance": "Rust toolchain and system dependencies required before building any parachain runtime.", + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md" + }, + { + "relevance": "Prerequisite: setting up the parachain template development environment.", + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md" + }, + { + "relevance": "Next step: adding multiple instances of the same pallet (e.g., two pallet-collective instances).", + "slug": "parachains-customize-runtime-add-pallet-instances", + "title": "Add Multiple Pallet Instances", + "url": "https://docs.polkadot.com/parachains/customize-runtime/add-pallet-instances.md" + }, + { + "relevance": "Building a custom FRAME pallet from scratch and integrating it into the runtime.", + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md" + } + ] + }, + "title": "Add an Existing Pallet to a Parachain Runtime", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Configures two independent instances of an instantiable Polkadot SDK pallet within a single parachain runtime. Uses pallet-collective as the example to demonstrate adding TechnicalCommittee (Instance1) and Council (Instance2) with separate membership and voting parameters. Covers identifying instantiable pallets, adding per-instance Cargo dependencies, implementing Config(Instance1) and Config(Instance2) traits, registering both instances in construct_runtime!, compiling, and verifying instance independence. Use when a runtime needs two separate governance bodies, two token pools, or any other scenario requiring two isolated instances of the same pallet logic. Trigger phrases: 'multiple pallet instances', 'two collective instances', 'pallet-collective council technical committee', 'instantiable pallet runtime'. Prerequisite: working parachain template (see add-existing-pallet-to-runtime).", + "env_vars": [], + "error_patterns": [ + { + "cause": "Two impl blocks are targeting the same instance type — usually because both use the default instance () instead of Instance1/Instance2.", + "pattern": "error[E0119]: conflicting implementations of trait `pallet_collective::Config`", + "resolution": "Ensure each impl block explicitly names a different instance: impl pallet_collective::Config and impl pallet_collective::Config. Type aliases (type TechnicalCommitteeInstance = pallet_collective::Instance1;) help clarify intent." + }, + { + "cause": "The pallet does not support instantiation — it lacks the I: 'static = () generic in its Config trait.", + "pattern": "error: the pallet does not implement the Instance trait", + "resolution": "Only instantiable pallets (those with a second generic parameter) can be used this way. Check the pallet source or use a different pallet that supports multiple instances." + }, + { + "cause": "One construct_runtime! entry is missing or has a typo.", + "pattern": "Only one instance visible in Polkadot.js Apps", + "resolution": "Verify both entries are present in construct_runtime! with distinct names and correct instance specifiers: TechnicalCommittee: pallet_collective:: and Council: pallet_collective::. Rebuild and restart the node." + } + ], + "examples": [ + { + "actions": [ + "Verify pallet-collective has the I: 'static = () generic — confirm it is instantiable", + "Add pallet-collective to runtime/Cargo.toml with default-features = false and add to std features", + "Implement pallet_collective::Config as TechnicalCommitteeInstance with committee-appropriate parameters", + "Implement pallet_collective::Config as CouncilInstance with council-appropriate parameters", + "Add TechnicalCommittee: pallet_collective:: and Council: pallet_collective:: to construct_runtime!", + "Run cargo build --release", + "Start node in --dev mode and verify both 'technicalCommittee' and 'council' appear in Polkadot.js Apps" + ], + "result": "Two independent pallet-collective instances registered — TechnicalCommittee and Council operate with independent storage, membership, and voting parameters", + "scenario": "Common scenario: add TechnicalCommittee and Council as two pallet-collective instances", + "user_says": "Add a TechnicalCommittee and a Council to my parachain runtime using pallet-collective" + }, + { + "actions": [ + "Check construct_runtime! for both TechnicalCommittee and Council entries", + "Verify each entry uses the correct instance specifier: :: and ::", + "Rebuild with cargo build --release", + "Regenerate chain spec and restart node" + ], + "result": "Both collective instances visible and callable in Polkadot.js Apps", + "scenario": "Edge case: runtime compiles but only one instance shows in the UI", + "user_says": "My runtime compiled but I only see one collective in Polkadot.js Apps" + } + ], + "id": "configure-multiple-pallet-instances", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "No external network required — local development only" + ], + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with wasm32-unknown-unknown target", + "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)", + "Familiarity with adding a single pallet to a runtime (see add-existing-pallet-to-runtime)" + ] + }, + "primary_page": "parachains/customize-runtime/add-pallet-instances.md", + "reference_code": { + "base_path": "", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-cookbook" + }, + "source_pages": [ + "parachains/customize-runtime/add-pallet-instances.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK parachain template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the parachain template node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Verify pallet supports instantiation", + "description": "Not all pallets support multiple instances. An instantiable pallet uses a second generic type parameter (the Instance) in its Config trait definition, e.g.: `pub trait Config: frame_system::Config`. Check the pallet's source or documentation for this pattern. pallet-collective and pallet-membership are standard examples. If the pallet does not have the Instance generic, it cannot be instantiated multiple times.", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Add pallet dependencies to runtime/Cargo.toml", + "description": "Open runtime/Cargo.toml. A single pallet crate covers all instances — add it once:\n\n```toml\n[dependencies]\n...\npallet-collective = { version = \"\", default-features = false }\npallet-membership = { version = \"\", default-features = false }\n```\n\nReplace `` with the version matching your other SDK pallets (check existing entries). Add both to the std feature list:\n\n```toml\n[features]\nstd = [\n ...\n \"pallet-collective/std\",\n \"pallet-membership/std\",\n]\n```\n\nVerify: `cargo check -p runtime 2>&1 | head -20`", + "order": 4, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Implement Config for TechnicalCommittee", + "description": "Open runtime/src/lib.rs. Add the first instance's Config implementation. For pallet-collective as TechnicalCommittee:\n\n```rust\ntype TechnicalCommitteeInstance = pallet_collective::Instance1;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<5>;\n type MaxProposals = ConstU32<100>;\n type MaxMembers = ConstU32<100>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nSubstitute `ConstU32` values with the desired governance parameters.", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Implement Config for Council", + "description": "Add the second instance's Config implementation immediately after the first:\n\n```rust\ntype CouncilInstance = pallet_collective::Instance2;\n\nimpl pallet_collective::Config for Runtime {\n type RuntimeOrigin = RuntimeOrigin;\n type Proposal = RuntimeCall;\n type RuntimeEvent = RuntimeEvent;\n type MotionDuration = ConstU32<7>;\n type MaxProposals = ConstU32<50>;\n type MaxMembers = ConstU32<20>;\n type DefaultVote = pallet_collective::PrimeDefaultVote;\n type WeightInfo = pallet_collective::weights::SubstrateWeight;\n type SetMembersOrigin = EnsureRoot;\n type MaxProposalWeight = BlockWeights;\n}\n```\n\nThe instances share the same pallet code but maintain completely independent storage — `MotionDuration`, `MaxProposals`, and `MaxMembers` can differ per instance.", + "order": 6, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Register both instances in construct_runtime!", + "description": "Find `construct_runtime!` in runtime/src/lib.rs and add both instances with unique names:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n TechnicalCommittee: pallet_collective::,\n Council: pallet_collective::,\n }\n);\n```\n\nThe names `TechnicalCommittee` and `Council` become the on-chain pallet identifiers for storage and extrinsics — choose carefully as renaming requires a migration. The `::` and `::` syntax tells the macro which Instance type alias to bind to each pallet entry.", + "order": 7, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Compile the runtime", + "commands": [ + "cargo build --release 2>&1 | tail -20" + ], + "description": "Build the parachain node. Compilation succeeds when both instance Config impls are complete and construct_runtime! entries are correct. Expected output: Finished release [optimized] target(s). If you see 'conflicting implementations of trait Config', check that each instance type alias (TechnicalCommitteeInstance, CouncilInstance) is distinct.", + "order": 8, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Verify instance independence", + "commands": [ + "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/instances-spec.json", + "./target/release/parachain-template-node --dev --tmp --chain /tmp/instances-spec.json" + ], + "description": "Generate a fresh dev chain spec and start the node. Open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944. Navigate to Developer > Extrinsics — verify both 'technicalCommittee' and 'council' appear as separate callable modules. Navigate to Developer > Chain State and confirm both pallet storage namespaces exist independently.", + "order": 9, + "working_directory": "polkadot-sdk-parachain-template" + } + ], + "supplementary_context": { + "description": "Load these pages for single pallet integration or custom pallet development.", + "pages": [ + { + "relevance": "Prerequisite: adding a single pallet to a runtime — covers the Cargo, Config, and construct_runtime! basics.", + "slug": "parachains-customize-runtime-add-existing-pallets", + "title": "Add an Existing Pallet to the Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md" + }, + { + "relevance": "Building a custom pallet from scratch — next step after mastering pallet integration.", + "slug": "parachains-customize-runtime-pallet-development-create-a-pallet", + "title": "Create a Custom Pallet", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet.md" + }, + { + "relevance": "Rust toolchain and build dependency installation.", + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md" + } + ] + }, + "title": "Configure Multiple Instances of a Pallet in a Runtime", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides through building a custom FRAME pallet from scratch using the Polkadot SDK. Uses a counter pallet (increment and decrement extrinsics with bounded storage) as the example to demonstrate all core FRAME patterns: pallet directory layout, Cargo.toml configuration, #[pallet::config], #[pallet::storage], #[pallet::event], #[pallet::error], #[pallet::call], and genesis config. After implementing the pallet, integrates it into the parachain runtime (Cargo dependency, Config impl, construct_runtime!), compiles, and runs locally. Use when building any new runtime module with on-chain state and extrinsics. Trigger phrases: 'create FRAME pallet', 'custom pallet from scratch', 'write a pallet', 'FRAME storage events extrinsics', 'build counter pallet'. Prerequisites: Polkadot SDK installed, parachain template set up. Verification step uses Polkadot.js Apps GUI.", + "env_vars": [], + "error_patterns": [ + { + "cause": "ConstU32 is not imported in runtime/src/lib.rs.", + "pattern": "error[E0412]: cannot find type `ConstU32` in this scope", + "resolution": "Add to the use statement at the top of runtime/src/lib.rs: use frame_support::traits::ConstU32; (or verify it is already imported via frame_support::prelude::*). The parachain template runtime usually imports it — search for existing ConstU32 usages to confirm." + }, + { + "cause": "The impl pallet_counter::Config for Runtime block is missing or has a missing associated type.", + "pattern": "error[E0277]: the trait `pallet_counter::Config` is not implemented", + "resolution": "Ensure the impl block is in runtime/src/lib.rs and all required types are specified. The Rust compiler lists each missing associated type — add them one by one." + }, + { + "cause": "The path in runtime/Cargo.toml does not match the actual pallet directory location.", + "pattern": "error: failed to select a version for `pallet-counter` / could not find Cargo.toml", + "resolution": "Verify the relative path: from runtime/, pallets/counter/ should be at ../pallets/counter. Run ls ../pallets/counter/Cargo.toml from inside the runtime/ directory to confirm the path exists." + }, + { + "cause": "The counter has reached MaxValue (100 by default).", + "pattern": "CounterOverflow error when submitting increment", + "resolution": "Decrement first to free up space, or increase MaxValue in the runtime Config (type MaxValue = ConstU32<1000>;) and rebuild. This behavior is by design — the error confirms the overflow protection is working." + } + ], + "examples": [ + { + "actions": [ + "Create pallets/counter/src/ directory and lib.rs file", + "Write Cargo.toml with frame-support, frame-system, parity-scale-codec, and scale-info dependencies", + "Add pallet-counter to workspace root Cargo.toml members", + "Implement the pallet in lib.rs: Config trait, StorageValue, Event enum, Error enum, and two call extrinsics (increment/decrement) with overflow/underflow protection", + "Add pallet-counter path dependency to runtime/Cargo.toml", + "Implement pallet_counter::Config for Runtime with MaxValue = ConstU32<100>", + "Add Counter: pallet_counter to construct_runtime!", + "Run cargo build --release", + "Start node in --dev mode; submit increment extrinsic via Polkadot.js Apps; verify CounterValue storage" + ], + "result": "Custom counter pallet deployed locally; increment and decrement extrinsics callable on-chain; CounterValue storage updates correctly with overflow protection", + "scenario": "Common scenario: build a counter pallet with increment and decrement extrinsics", + "user_says": "Create a custom FRAME pallet from scratch" + }, + { + "actions": [ + "Add a #[pallet::genesis_config] struct and #[pallet::genesis_build] impl to the pallet's lib.rs to accept an initial_value", + "In the runtime's chain_spec.rs (or equivalent), include Counter: CounterConfig { initial_value: 10 } in the genesis state", + "Rebuild and regenerate the chain spec to include the genesis config" + ], + "result": "Node starts with CounterValue pre-set to 10; visible in Developer > Chain State > counter.counterValue immediately after genesis", + "scenario": "Edge case: adding a genesis config to set an initial counter value", + "user_says": "How do I set the initial counter value to 10 at genesis?" + } + ], + "id": "create-frame-pallet", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "No external network required — all work is local" + ], + "runtime": [ + "Git (steps 1-2 clone and build the polkadot-sdk-parachain-template inline; see also the standalone set-up-parachain-template skill at https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md)", + "Disk: at least 5 GB free for build artifacts", + "Rust toolchain with wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown)", + "Polkadot SDK system dependencies (see https://docs.polkadot.com/parachains/install-polkadot-sdk.md)" + ] + }, + "primary_page": "parachains/customize-runtime/pallet-development/create-a-pallet.md", + "reference_code": { + "base_path": "", + "branch": "master", + "files": [], + "repo": "polkadot-developers/polkadot-cookbook" + }, + "source_pages": [ + "parachains/customize-runtime/pallet-development/create-a-pallet.md" + ], + "steps": [ + { + "action": "Clone the Polkadot SDK parachain template", + "commands": [ + "git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git" + ], + "description": "Clone the canonical parachain template. All subsequent steps assume `polkadot-sdk-parachain-template/` exists at the project root. The full standalone walkthrough is the `set-up-parachain-template` skill.", + "order": 1, + "working_directory": "." + }, + { + "action": "Build the parachain template node in release mode", + "commands": [ + "cargo build --release" + ], + "description": "Compile the template once so the runtime crate, pallet-template, and dependencies are resolved before pallet work begins. First build can take 10-20 minutes.", + "order": 2, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Create the pallet directory and Cargo.toml", + "commands": [ + "mkdir -p pallets/counter/src", + "touch pallets/counter/src/lib.rs" + ], + "description": "Create the pallet directory structure inside the parachain template workspace. The pallet name (counter) becomes the crate name. Create pallets/counter/Cargo.toml with the following content, replacing with the version matching your other workspace pallets:\n\n```toml\n[package]\nname = \"pallet-counter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ncodec = { package = \"parity-scale-codec\", version = \"3\", default-features = false, features = [\"derive\"] }\nscale-info = { version = \"2\", default-features = false, features = [\"derive\"] }\nframe-support = { version = \"\", default-features = false }\nframe-system = { version = \"\", default-features = false }\nframe-benchmarking = { version = \"\", default-features = false, optional = true }\n\n[features]\ndefault = [\"std\"]\nstd = [\n \"codec/std\",\n \"scale-info/std\",\n \"frame-support/std\",\n \"frame-system/std\",\n]\nruntime-benchmarks = [\"frame-benchmarking/runtime-benchmarks\"]\n```\n\nAlso add pallet-counter to the workspace's root Cargo.toml members list:\nmembers = [\n ...\n \"pallets/counter\",\n]", + "order": 3, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Implement the pallet in pallets/counter/src/lib.rs", + "description": "Write the pallet implementation in pallets/counter/src/lib.rs. A minimal counter pallet contains:\n\n#![cfg_attr(not(feature = \"std\"), no_std)]\n\npub use pallet::*;\n\n```rust\n#[frame_support::pallet]\npub mod pallet {\n use frame_support::pallet_prelude::*;\n use frame_system::pallet_prelude::*;\n\n #[pallet::pallet]\n pub struct Pallet(_);\n\n #[pallet::config]\n pub trait Config: frame_system::Config {\n type RuntimeEvent: From> + IsType<::RuntimeEvent>;\n #[pallet::constant]\n type MaxValue: Get;\n }\n```\n\n #[pallet::storage]\n pub type CounterValue = StorageValue<_, u32, ValueQuery>;\n\n```rust\n #[pallet::event]\n #[pallet::generate_deposit(pub(super) fn deposit_event)]\n pub enum Event {\n CounterIncremented { new_value: u32 },\n CounterDecremented { new_value: u32 },\n }\n\n #[pallet::error]\n pub enum Error {\n CounterOverflow,\n CounterUnderflow,\n }\n\n #[pallet::call]\n impl Pallet {\n #[pallet::call_index(0)]\n #[pallet::weight(10_000)]\n pub fn increment(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_add(1).ok_or(Error::::CounterOverflow)?;\n ensure!(new_val <= T::MaxValue::get(), Error::::CounterOverflow);\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterIncremented { new_value: new_val });\n Ok(())\n }\n\n #[pallet::call_index(1)]\n #[pallet::weight(10_000)]\n pub fn decrement(origin: OriginFor) -> DispatchResult {\n ensure_signed(origin)?;\n let val = CounterValue::::get();\n let new_val = val.checked_sub(1).ok_or(Error::::CounterUnderflow)?;\n CounterValue::::put(new_val);\n Self::deposit_event(Event::CounterDecremented { new_value: new_val });\n Ok(())\n }\n }\n}\n```\n\nThe MaxValue constant bounds the counter to prevent overflow. checked_add/checked_sub replace panicking arithmetic.", + "order": 4, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Add pallet-counter to runtime/Cargo.toml", + "description": "Open runtime/Cargo.toml and add pallet-counter as a workspace dependency:\n\n```toml\n[dependencies]\n...\npallet-counter = { path = \"../pallets/counter\", default-features = false }\n\nAdd to the std feature list:\n[features]\nstd = [\n ...\n \"pallet-counter/std\",\n]\n```\n\nNote: path = \"../pallets/counter\" is a relative path from the runtime/ directory to the pallet directory.", + "order": 5, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Implement pallet_counter::Config in runtime/src/lib.rs", + "description": "Open runtime/src/lib.rs. Add the Config implementation for the counter pallet:\n\n```rust\nimpl pallet_counter::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type MaxValue = ConstU32<100>;\n}\n```\n\nConstU32<100> sets the MaxValue constant to 100 — adjust as needed. ConstU32 is from frame_support::traits and is already imported in the parachain template runtime.", + "order": 6, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Register the pallet in construct_runtime!", + "description": "Find construct_runtime! in runtime/src/lib.rs and add the counter pallet:\n\n```rust\nconstruct_runtime!(\n pub enum Runtime where ... {\n // ... existing pallets ...\n Counter: pallet_counter,\n }\n);\n```\n\nThe name Counter is the on-chain pallet identifier. Save the file.", + "order": 7, + "working_directory": "polkadot-sdk-parachain-template" + }, + { + "action": "Compile and run locally", + "commands": [ + "cargo build --release 2>&1 | tail -20", + "./target/release/parachain-template-node build-spec --disable-default-bootnode --chain local > /tmp/counter-spec.json", + "./target/release/parachain-template-node --dev --tmp --chain /tmp/counter-spec.json" + ], + "description": "Build the entire parachain node including the new pallet. Expected: Finished release [optimized]. Then generate a dev chain spec and start the node. Once running, open Polkadot.js Apps at https://polkadot.js.org/apps/?rpc=ws://localhost:9944 and navigate to Developer > Extrinsics — 'counter' should appear with increment and decrement callables. Submit an increment extrinsic signed with Alice (//Alice) and verify the CounterValue storage changes via Developer > Chain State > counter.counterValue.", + "order": 8, + "working_directory": "polkadot-sdk-parachain-template" + } + ], + "supplementary_context": { + "description": "Load these pages for runtime setup prerequisites or next steps in pallet development.", + "pages": [ + { + "relevance": "Rust and system dependencies required before building any FRAME pallet.", + "slug": "parachains-install-polkadot-sdk", + "title": "Install Polkadot SDK", + "url": "https://docs.polkadot.com/parachains/install-polkadot-sdk.md" + }, + { + "relevance": "Setting up the parachain template workspace where the custom pallet lives.", + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md" + }, + { + "relevance": "Adding an existing SDK pallet to a runtime — prerequisite concepts for runtime integration.", + "slug": "parachains-customize-runtime-add-existing-pallets", + "title": "Add an Existing Pallet to the Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/add-existing-pallets.md" + }, + { + "relevance": "Next step: setting up a mock runtime for unit testing the custom pallet.", + "slug": "parachains-customize-runtime-pallet-development-mock-runtime", + "title": "Mock Your Runtime", + "url": "https://docs.polkadot.com/parachains/customize-runtime/pallet-development/mock-runtime.md" + } + ] + }, + "title": "Create a Custom FRAME Pallet", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Provides three methods for fetching Polkadot SDK runtime metadata: curl JSON-RPC (state_getMetadata, returns raw hex bytes), subxt CLI (human-readable JSON, ideal for inspection), and Polkadot.js Apps RPC UI (interactive). Use when building client libraries, inspecting pallet structure, or verifying runtime composition after an upgrade. Trigger phrases: 'get runtime metadata', 'state_getMetadata', 'inspect pallet calls', 'fetch chain metadata', 'subxt metadata', 'what pallets are on chain'. No private key or wallet required. The metadata output describes all pallets, storage items, calls, events, errors, and constants in the current runtime.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The RPC endpoint URL is incorrect, the node is not running, or the endpoint does not accept HTTP connections.", + "pattern": "curl returns connection refused or timeout", + "resolution": "Verify the RPC endpoint URL. For local nodes confirm the node is running and listening on port 9944. Some nodes only accept WebSocket connections — use subxt (Method B) with ws:// or wss:// instead of curl." + }, + { + "cause": "The WebSocket URL is incorrect, the node is not running, or the subxt-cli version is incompatible with the node's metadata version.", + "pattern": "subxt metadata fails with 'failed to fetch metadata'", + "resolution": "Verify the WebSocket URL (wss:// for TLS, ws:// for local). Confirm the node is running. Update subxt-cli: cargo install subxt-cli --force." + }, + { + "cause": "The node connected to is a different chain than expected, or the genesis has not been customized.", + "pattern": "Metadata output shows unexpected pallets or an empty pallets array", + "resolution": "Verify the RPC endpoint points to the intended chain. For local dev nodes ensure the chain spec matches the expected runtime." + } + ], + "examples": [ + { + "actions": [ + "Run: subxt metadata --url wss://rpc.polkadot.io --format json > spec.json", + "Open spec.json and locate the pallets array", + "Find a specific pallet by name (e.g. Balances) and follow its calls type index to read available calls" + ], + "result": "spec.json contains the complete runtime metadata in human-readable JSON; pallets, calls, storage items, events, and types are all inspectable.", + "scenario": "Common scenario: inspect pallet list and call signatures of Polkadot mainnet", + "user_says": "Get the runtime metadata from the Polkadot mainnet node" + }, + { + "actions": [ + "Verify the local node is running (polkadot-omni-node --dev or similar)", + "Run: subxt metadata --url ws://127.0.0.1:9944 --format json > local-spec.json", + "Or use curl: curl -H \"Content-Type: application/json\" -d '{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"state_getMetadata\"}' http://127.0.0.1:9944" + ], + "result": "Metadata from the local dev node retrieved; reflects the exact runtime version currently running locally.", + "scenario": "Edge case: inspect metadata from a local dev node", + "user_says": "How do I get the metadata from my local dev node at ws://127.0.0.1:9944?" + } + ], + "id": "retrieve-runtime-metadata", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "An accessible RPC endpoint for the target Polkadot SDK node (e.g. https://rpc.polkadot.io for mainnet, or http://127.0.0.1:9944 for a local dev node)" + ], + "runtime": [ + "curl (Method A — available by default on macOS and most Linux distros)", + "subxt CLI (Method B — install with: cargo install subxt-cli)" + ] + }, + "primary_page": "reference/parachains/chain-data.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/parachains/chain-data.md" + ], + "steps": [ + { + "action": "(Method A) Fetch metadata via curl JSON-RPC", + "commands": [ + "curl -H \"Content-Type: application/json\" -d '{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\"}' https://rpc.polkadot.io" + ], + "description": "Call the state_getMetadata RPC method using curl. Replace https://rpc.polkadot.io with your target node's HTTP RPC endpoint. For a local node use http://127.0.0.1:9944. The response is a hex-encoded SCALE-encoded byte string — not human-readable. Use Method B or C if you need human-readable JSON.", + "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":\"0x6d657461...\",\"id\":1}", + "order": 1, + "working_directory": "." + }, + { + "action": "(Method B) Fetch metadata as human-readable JSON using subxt CLI", + "commands": [ + "subxt metadata --url wss://rpc.polkadot.io --format json > spec.json" + ], + "description": "Install the subxt CLI if not already installed: cargo install subxt-cli. Run subxt metadata to fetch and decode the metadata to JSON. Replace wss://rpc.polkadot.io with your node's WebSocket endpoint (wss:// for remote, ws:// for local). For a local dev node: subxt metadata --url ws://127.0.0.1:9944 --format json > spec.json. You can also explore metadata interactively at https://paritytech.github.io/subxt-explorer/.", + "expected_output": "spec.json created containing the runtime metadata in human-readable JSON format", + "order": 2, + "working_directory": "." + }, + { + "action": "(Method C) Retrieve metadata via Polkadot.js Apps RPC UI", + "description": "Navigate to https://polkadot.js.org/apps/#/rpc and connect to your target node. Click the Developer dropdown and select RPC Calls. 1. Select state as the endpoint. 2. Select getMetadata(at) as the method. 3. Click Submit RPC Call. The metadata is returned in JSON format in the UI. This is the most interactive option but is not scriptable.", + "order": 3, + "working_directory": "." + }, + { + "action": "Interpret the metadata output", + "description": "The metadata JSON contains three top-level sections: types (SCALE type registry indexed by u32 identifiers), pallets (array of pallet descriptors with storage, calls, events, errors, and constants for each pallet), and extrinsic (transaction version and signed extensions list). To find calls for a specific pallet: locate the pallet by name in the pallets array, follow the calls type index integer to the types section, then read the variant definitions. Example: find pallets[name='Sudo'].calls.type → find types[id=that u32].def.variant.variants for the available sudo calls. Note: type identifiers change between runtime versions — do not hardcode them.", + "order": 4, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for context on how metadata is used in client applications or SDK-level interactions.", + "pages": [ + { + "relevance": "Subxt library usage for generating typed client code from runtime metadata — the primary consumer of metadata in Rust applications.", + "slug": "reference-tools-subxt", + "title": "Subxt Rust API", + "url": "https://docs.polkadot.com/reference/tools/subxt.md" + }, + { + "relevance": "SCALE encoding details that explain why the raw curl metadata output is hex-encoded bytes rather than readable JSON.", + "slug": "reference-parachains-data-encoding", + "title": "Data Encoding", + "url": "https://docs.polkadot.com/reference/parachains/data-encoding.md" + } + ] + }, + "title": "Retrieve Polkadot Runtime Metadata", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Installs @polkadot/api and demonstrates creating a WsProvider connection, reading chain constants and on-chain storage, and submitting a signed balance transfer. Use when maintaining or migrating legacy TypeScript/JavaScript projects that already depend on @polkadot/api. Warning: this library is in maintenance mode — new projects should use Polkadot API (PAPI) or Dedot. Trigger phrases: 'polkadot.js api', '@polkadot/api', 'polkadot javascript sdk', 'use polkadot js'. Outcome: TypeScript script that connects to a node, reads account state, and sends a signed transaction.", + "env_vars": [ + { + "description": "WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet).", + "name": "WS_ENDPOINT", + "required": true + }, + { + "description": "12 or 24-word seed phrase for the signing account. Required only for the transaction-sending step.", + "name": "MNEMONIC", + "required": false + } + ], + "error_patterns": [ + { + "cause": "The WS_ENDPOINT URL is unreachable or the protocol is wrong.", + "pattern": "Error: WebSocket is not open / ECONNREFUSED", + "resolution": "Verify WS_ENDPOINT in .env starts with wss:// (not https://). Check the node is online. For Polkadot Hub TestNet use wss://services.polkadothub-rpc.com/testnet." + }, + { + "cause": "The project is not configured as an ESM module.", + "pattern": "SyntaxError: Cannot use import statement in a module", + "resolution": "Run 'npm pkg set type=module' in the polkadot-api-demo directory, then retry." + }, + { + "cause": "Dependencies not installed.", + "pattern": "Error: Cannot find module '@polkadot/api'", + "resolution": "Run 'npm install @polkadot/api @polkadot/keyring dotenv' in the polkadot-api-demo directory." + }, + { + "cause": "The sender account has insufficient funds to pay transaction fees.", + "pattern": "RpcError: 1010: Invalid Transaction: Inability to pay some fees", + "resolution": "Fund the sender account with tokens. For Polkadot Hub TestNet use the faucet at https://faucet.polkadot.io/." + } + ], + "examples": [ + { + "actions": [ + "Initialize ESM Node.js project and install @polkadot/api", + "Create .env with WS_ENDPOINT set to the Polkadot Hub TestNet WebSocket URL", + "Create query.ts with ApiPromise.create() and api.query.system.account()", + "Run npx tsx query.ts and read account balance output" + ], + "result": "Account free/reserved/frozen balances printed to console; chain name and existential deposit shown", + "scenario": "Common scenario: query account balance", + "user_says": "Use @polkadot/api to check an account balance on Polkadot Hub" + }, + { + "actions": [ + "Surface maintenance-mode warning: @polkadot/api is in maintenance mode", + "Recommend PAPI (reference-tools-papi) or Dedot (reference-tools-dedot) for new projects", + "If user confirms they want @polkadot/api anyway, proceed with installation steps" + ], + "result": "User informed of deprecation status; skill proceeds with @polkadot/api if that is the deliberate choice", + "scenario": "Edge case: user is starting a new project", + "user_says": "I want to build a new Polkadot app with polkadot.js api" + } + ], + "id": "use-polkadot-js-api", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "WebSocket RPC endpoint for the target chain (e.g., wss://rpc.ibp.network/polkadot for Polkadot mainnet)" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ] + }, + "primary_page": "reference/tools/polkadot-js-api.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "reference/tools/polkadot-js-api.md" + ], + "steps": [ + { + "action": "Initialize ESM Node.js project and install @polkadot/api", + "commands": [ + "mkdir polkadot-api-demo && cd polkadot-api-demo", + "npm init -y && npm pkg set type=module", + "npm install @polkadot/api @polkadot/keyring dotenv" + ], + "description": "Create a new directory named polkadot-api-demo and initialize it as an ESM Node.js project. The type=module flag is required because @polkadot packages use ESM-only imports. Install dotenv for secure credential loading.", + "order": 1, + "working_directory": "." + }, + { + "action": "Create .env file and add to .gitignore", + "commands": [ + "printf 'WS_ENDPOINT=\\nMNEMONIC=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with empty placeholders and exclude it from git. Stop and ask the user to edit the .env file directly — do NOT ask for the mnemonic or endpoint in chat. Set WS_ENDPOINT to the WebSocket URL for the target chain (e.g., wss://services.polkadothub-rpc.com/testnet for Polkadot Hub TestNet, or wss://rpc.ibp.network/polkadot for mainnet). Set MNEMONIC if you plan to send transactions. Wait for confirmation before proceeding.", + "order": 2, + "working_directory": "polkadot-api-demo" + }, + { + "action": "Create the chain-query script", + "description": "Create a file named query.ts with the following content. Replace INSERT_ADDRESS with the SS58-encoded address to query (e.g., an address from the create-polkadot-account skill output):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const chain = await api.rpc.system.chain();\n const existentialDeposit = api.consts.balances.existentialDeposit;\n console.log(`Connected to: ${chain}`);\n console.log(`Existential deposit: ${existentialDeposit.toHuman()}`);\n const account = await api.query.system.account('INSERT_ADDRESS');\n console.log('Account data:', account.toHuman());\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);", + "order": 3, + "working_directory": "polkadot-api-demo" + }, + { + "action": "Run the query script", + "commands": [ + "npx tsx query.ts" + ], + "expected_output": "Connected to: Polkadot (or your target chain name)\nExistential deposit: 10 mDOT (or equivalent)\nAccount data: { nonce: ..., data: { free: ..., reserved: ... } }", + "order": 4, + "working_directory": "polkadot-api-demo" + }, + { + "action": "Create and run the transaction script", + "description": "Create a file named transfer.ts. Only proceed if MNEMONIC is set in .env. Replace INSERT_RECIPIENT with the destination SS58 address and INSERT_AMOUNT with the transfer amount in planck units (e.g., 1000000000000 = 1 DOT with 10 decimals, or 1000000000000000000 = 1 PAS with 18 decimals):\n\n```typescript\nimport 'dotenv/config';\nimport { ApiPromise, WsProvider, Keyring } from '@polkadot/api';\n\nasync function main() {\n const provider = new WsProvider(process.env.WS_ENDPOINT!);\n const api = await ApiPromise.create({ provider });\n const keyring = new Keyring({ type: 'sr25519' });\n const sender = keyring.addFromUri(process.env.MNEMONIC!);\n console.log(`Sending from: ${sender.address}`);\n const txHash = await api.tx.balances\n .transferKeepAlive('INSERT_RECIPIENT', INSERT_AMOUNT)\n .signAndSend(sender);\n console.log(`Transaction hash: ${txHash}`);\n await api.disconnect();\n}\n```\n\nmain().catch(console.error);\n\nThen run: npx tsx transfer.ts", + "order": 5, + "working_directory": "polkadot-api-demo" + } + ], + "supplementary_context": { + "description": "Load these pages when the user asks about API alternatives, migration paths, or needs deeper context on chain interactions.", + "pages": [ + { + "relevance": "Polkadot API (PAPI) — the recommended replacement for @polkadot/api in new projects.", + "slug": "reference-tools-papi", + "title": "Polkadot-API", + "url": "https://docs.polkadot.com/reference/tools/papi.md" + }, + { + "relevance": "Dedot — modern lightweight alternative to @polkadot/api with better TypeScript inference.", + "slug": "reference-tools-dedot", + "title": "Dedot", + "url": "https://docs.polkadot.com/reference/tools/dedot.md" + }, + { + "relevance": "Multi-SDK transaction guide covering PAPI, Polkadot.js, Dedot, and Subxt variants.", + "slug": "chain-interactions-send-transactions-with-sdks", + "title": "Send Transactions with SDKs", + "url": "https://docs.polkadot.com/chain-interactions/send-transactions/with-sdks.md" + } + ] + }, + "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", + "env_vars": [], + "error_patterns": [ + { + "cause": "MetaMask connection was rejected or MetaMask is not installed.", + "pattern": "MetaMask: User denied account access / MetaMask not connected", + "resolution": "Click the MetaMask extension icon and ensure it is unlocked. Re-select 'Injected Provider - MetaMask' in the Remix Environment dropdown and approve the connection prompt." + }, + { + "cause": "The connected MetaMask account has no testnet PAS tokens.", + "pattern": "Transaction failed: insufficient funds for gas", + "resolution": "Visit https://faucet.polkadot.io/ to obtain testnet PAS tokens. Ensure the faucet is sending to the correct address shown in MetaMask." + }, + { + "cause": "MetaMask is connected to a different network instead of Polkadot Hub TestNet.", + "pattern": "Wrong network in MetaMask / chain ID mismatch", + "resolution": "Open MetaMask and switch to 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add it." + }, + { + "cause": "The Remix compiler version does not match the pragma in Storage.sol.", + "pattern": "Solidity compile error: pragma mismatch", + "resolution": "In the Solidity Compiler tab, select a compiler version compatible with the pragma at the top of the contract file." + } + ], + "examples": [ + { + "actions": [ + "Verify MetaMask is on Polkadot Hub TestNet and account has PAS tokens", + "Open remix.ethereum.org and locate contracts/1_Storage.sol", + "Compile with Solidity Compiler tab", + "Select Injected Provider - MetaMask in Deploy tab and click Deploy", + "Confirm MetaMask transaction and wait for mining" + ], + "result": "Storage contract deployed; appears in Remix Deployed Contracts panel with callable store/retrieve functions", + "scenario": "Common scenario: first contract deployment", + "user_says": "Deploy a smart contract on Polkadot Hub using Remix IDE" + }, + { + "actions": [ + "Direct user to https://faucet.polkadot.io/ to request testnet PAS tokens", + "Ask user to confirm the faucet sent tokens to the correct address", + "Once confirmed, retry the Deploy step in Remix" + ], + "result": "User obtains testnet PAS tokens and deployment proceeds successfully", + "scenario": "Edge case: no testnet tokens", + "user_says": "I try to deploy but MetaMask says insufficient funds" + } + ], + "id": "deploy-basic-contract-remix", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", + "Chain ID: 420420417" + ], + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "tokens": [ + "Testnet PAS tokens from https://faucet.polkadot.io/ (rate limit: check faucet page)" + ], + "wallet": [ + "MetaMask browser extension installed and configured for Polkadot Hub TestNet", + "Wallet funded with testnet PAS tokens" + ] + }, + "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" + ], + "steps": [ + { + "action": "Verify MetaMask is connected to Polkadot Hub TestNet", + "description": "Instruct the user: Open MetaMask and confirm the selected network is 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add the network. Also confirm the account balance shows testnet PAS tokens. If the balance is zero, visit https://faucet.polkadot.io/ to request tokens. Wait for the user to confirm both conditions before proceeding.", + "order": 1, + "working_directory": "." + }, + { + "action": "Open Remix IDE and locate Storage.sol", + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed.", + "order": 2, + "working_directory": "." + }, + { + "action": "Compile Storage.sol in Remix", + "description": "Instruct the user: Click the Solidity Compiler icon (second icon in the left sidebar). Confirm the compiler version is 0.8.x or higher. Click the blue 'Compile 1_Storage.sol' button. Wait for the green checkmark indicating successful compilation. If there are errors, verify the compiler version matches the pragma in the file.", + "expected_output": "Green checkmark on the Solidity Compiler sidebar icon; no errors shown.", + "order": 3, + "working_directory": "." + }, + { + "action": "Deploy to Polkadot Hub TestNet via MetaMask", + "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", + "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel.", + "order": 4, + "working_directory": "." + }, + { + "action": "Interact with the deployed Storage contract", + "description": "Instruct the user: In the 'Deployed Contracts' panel, expand the deployed Storage contract. To write a value: enter a number in the field next to the 'store' button and click 'store'. Confirm the MetaMask transaction. To read the stored value: click the 'retrieve' button (no transaction needed). The return value shows in the Remix console below.", + "expected_output": "retrieve() returns the number stored in the previous store() call.", + "order": 5, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to set up MetaMask, obtain tokens, or move to a CLI-based deployment workflow.", + "pages": [ + { + "relevance": "How to configure MetaMask or Talisman for Polkadot Hub TestNet.", + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md" + }, + { + "relevance": "How to obtain testnet PAS tokens for deployment transactions.", + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md" + }, + { + "relevance": "CLI-based alternative: deploy the same Storage contract using Hardhat and a local toolchain.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", + "title": "Deploy a Basic Contract with Hardhat", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + } + ] + }, + "title": "Deploy a Basic Smart Contract with Remix IDE", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", + "env_vars": [], + "error_patterns": [ + { + "cause": "MetaMask extension is not installed or is disabled in the browser.", + "pattern": "MetaMask not detected / No injected Web3 provider", + "resolution": "Install the MetaMask browser extension from https://metamask.io and reload Remix. Ensure the extension is enabled for the remix.ethereum.org domain." + }, + { + "cause": "MetaMask is connected to a different chain.", + "pattern": "Wrong network shown in Remix (chain ID is not 420420417)", + "resolution": "Open MetaMask and manually switch to Polkadot Hub TestNet. If Polkadot Hub TestNet is not in the list, follow the connect-wallet-polkadot-hub skill to add it." + }, + { + "cause": "The injected provider selection was not saved or MetaMask was not connected.", + "pattern": "Remix Deploy panel shows 'JavaScript VM' after trying to switch", + "resolution": "Click the Environment dropdown again and re-select 'Injected Provider - MetaMask'. Approve the MetaMask connection popup if it appears." + } + ], + "examples": [ + { + "actions": [ + "Open https://remix.ethereum.org", + "Click Deploy and Run Transactions tab", + "Select Injected Provider - MetaMask in the Environment dropdown", + "Approve the MetaMask connection popup", + "Verify account address and chain ID 420420417 appear in the Deploy panel" + ], + "result": "Remix IDE is connected to Polkadot Hub TestNet via MetaMask and ready for contract deployment", + "scenario": "Common scenario: connect Remix before deployment", + "user_says": "Connect Remix IDE to Polkadot Hub so I can deploy a contract" + }, + { + "actions": [ + "Ask user to open MetaMask and switch to Polkadot Hub TestNet", + "If network not present, direct to connect-wallet-polkadot-hub skill", + "Confirm Remix updates to show chain ID 420420417 automatically" + ], + "result": "Remix shows correct network after MetaMask network switch", + "scenario": "Edge case: MetaMask on wrong network", + "user_says": "Remix shows the wrong chain ID after connecting" + } + ], + "id": "connect-remix-polkadot", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed and unlocked with Polkadot Hub TestNet configured (chain ID 420420417)" + ] + }, + "primary_page": "smart-contracts/dev-environments/remix.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/dev-environments/remix.md" + ], + "steps": [ + { + "action": "Open Remix IDE", + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. Wait for the IDE to fully load (the file explorer appears on the left). Ensure MetaMask is unlocked and set to Polkadot Hub TestNet before proceeding.", + "order": 1, + "working_directory": "." + }, + { + "action": "Navigate to the Deploy and Run Transactions tab", + "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left.", + "order": 2, + "working_directory": "." + }, + { + "action": "Select Injected Provider - MetaMask", + "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step.", + "order": 3, + "working_directory": "." + }, + { + "action": "Verify the connection", + "description": "Instruct the user: Confirm the following in the Deploy panel: 1) The Account field shows your MetaMask wallet address. 2) The network indicator shows chain ID 420420417 (Polkadot Hub TestNet). If the chain ID shows a different value, switch networks in MetaMask to Polkadot Hub TestNet and the Remix panel will update automatically.", + "expected_output": "Remix Deploy panel shows MetaMask account address and Polkadot Hub TestNet (chain ID 420420417).", + "order": 4, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for wallet setup or when the user wants to deploy a contract after connecting.", + "pages": [ + { + "relevance": "How to add Polkadot Hub TestNet to MetaMask before using Remix.", + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md" + }, + { + "relevance": "Next step after connecting Remix: deploy the default Storage.sol contract.", + "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-remix", + "title": "Deploy a Basic Contract with Remix IDE", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" + } + ] + }, + "title": "Connect Remix IDE to Polkadot Hub", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides the user through adding Polkadot Hub TestNet (RPC, chain ID 420420417, PAS token) to MetaMask or Talisman and switching the active network. All steps are browser extension GUI interactions. Use before deploying smart contracts, using Remix IDE with Polkadot Hub, or obtaining testnet tokens. Trigger phrases: 'connect metamask polkadot hub', 'add polkadot network metamask', 'set up wallet polkadot testnet', 'configure talisman polkadot hub'. Outcome: wallet shows Polkadot Hub TestNet as active network and is ready for on-chain interactions.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The RPC URL was entered incorrectly or the TestNet endpoint is temporarily unavailable.", + "pattern": "Could not fetch chain ID / Invalid RPC URL", + "resolution": "Verify the RPC URL is exactly: https://services.polkadothub-rpc.com/testnet (no trailing slash). Check the Polkadot Hub TestNet status page or Discord for outages." + }, + { + "cause": "A different endpoint was used that returns a different chain ID.", + "pattern": "Chain ID mismatch: expected 420420417", + "resolution": "Delete the incorrectly configured network from MetaMask and re-add it using the exact RPC URL and chain ID 420420417." + }, + { + "cause": "Account not funded with testnet tokens.", + "pattern": "Network added but balance shows 0 PAS", + "resolution": "Visit https://faucet.polkadot.io/, paste your address, select Polkadot Hub TestNet, and request tokens." + } + ], + "examples": [ + { + "actions": [ + "Open MetaMask, click network selector, click Add network, then Add a network manually", + "Enter network name, RPC URL (https://services.polkadothub-rpc.com/testnet), chain ID 420420417, symbol PAS", + "Save and switch to Polkadot Hub TestNet", + "Visit https://faucet.polkadot.io/ and request testnet PAS tokens" + ], + "result": "MetaMask shows Polkadot Hub TestNet as active network with a non-zero PAS balance", + "scenario": "Common scenario: add Polkadot Hub TestNet to MetaMask", + "user_says": "Add Polkadot Hub TestNet to MetaMask and get some test tokens" + }, + { + "actions": [ + "Verify the RPC URL is exactly https://services.polkadothub-rpc.com/testnet with no trailing slash", + "Check whether the Polkadot Hub TestNet is experiencing downtime", + "Retry adding the network once the endpoint is confirmed reachable" + ], + "result": "Network added successfully once the correct RPC URL is verified and the endpoint is reachable", + "scenario": "Edge case: RPC URL rejected by MetaMask", + "user_says": "MetaMask says it could not fetch the chain ID when I enter the RPC URL" + } + ], + "id": "connect-wallet-polkadot-hub", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "wallet": [ + "MetaMask installed (https://metamask.io) OR Talisman installed (https://talisman.xyz)" + ] + }, + "primary_page": "smart-contracts/integrations/wallets.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/integrations/wallets.md" + ], + "steps": [ + { + "action": "Open MetaMask network settings", + "description": "Instruct the user: Click the MetaMask extension icon in the browser toolbar to open it. Click the network selector at the top of the MetaMask popup (shows 'Ethereum Mainnet' by default). Click 'Add network' at the bottom of the network list. On the Add Network page, click 'Add a network manually'.", + "order": 1, + "working_directory": "." + }, + { + "action": "Enter Polkadot Hub TestNet network details", + "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added.", + "order": 2, + "working_directory": "." + }, + { + "action": "Switch to Polkadot Hub TestNet", + "description": "Instruct the user: In MetaMask, click the network selector again. Select 'Polkadot Hub TestNet' from the list. The header now shows 'Polkadot Hub TestNet' and the balance shows PAS.", + "expected_output": "MetaMask header displays 'Polkadot Hub TestNet'; account balance shows PAS balance.", + "order": 3, + "working_directory": "." + }, + { + "action": "Obtain testnet PAS tokens if needed", + "description": "If the PAS balance is 0, instruct the user: Visit https://faucet.polkadot.io/ in the browser. Paste the MetaMask account address (click the address in MetaMask to copy it). Select 'Polkadot Hub TestNet' in the faucet network dropdown. Click 'Get tokens'. Tokens typically arrive within 30 seconds.", + "expected_output": "MetaMask PAS balance shows a non-zero amount.", + "order": 4, + "working_directory": "." + }, + { + "action": "Alternative: Add Polkadot Hub TestNet to Talisman", + "description": "If the user prefers Talisman instead of MetaMask, instruct them: Open the Talisman extension and click the settings icon. Go to Networks and Tokens, then Add Network. Enter the same network details as step 2: RPC URL: https://services.polkadothub-rpc.com/testnet, Chain ID: 420420417, Symbol: PAS. Save and switch to the Polkadot Hub TestNet network in Talisman.", + "order": 5, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs network reference data or wants to proceed to contract deployment after wallet setup.", + "pages": [ + { + "relevance": "Full network connection reference with RPC URLs, chain IDs, and WSS endpoints for all Polkadot Hub environments.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + }, + { + "relevance": "Step-by-step guide to obtaining testnet PAS tokens from the Polkadot faucet.", + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md" + }, + { + "relevance": "How to connect Remix IDE to Polkadot Hub using the configured MetaMask wallet.", + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md" + } + ] + }, + "title": "Connect a Wallet to Polkadot Hub", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Scaffolds a Next.js 15 application with Wagmi v3 and viem, configures the Polkadot Hub TestNet chain, and implements wallet connection, block-number polling (useBlockNumber), and Storage contract read/write (useReadContract + useWriteContract). Use when building EVM dApps that target Polkadot Hub using the Wagmi React hooks library. Key capabilities: project scaffold with create-next-app, WagmiProvider setup, custom chain config for Polkadot Hub TestNet (chain ID 420420417), wallet connector UI, contract interaction hooks. Trigger phrases: 'wagmi polkadot', 'next.js wagmi dapp', 'wagmi contract interaction', 'useReadContract polkadot', 'wagmi v3 tutorial'. All wallet and browser steps are delegated to the user. No reference cookbook repo — all code is inlined in the steps.", + "env_vars": [], + "error_patterns": [ + { + "cause": "WagmiProvider not wrapping the component tree, or connectors not configured.", + "pattern": "Error: No connectors found / useConnect: No connector available", + "resolution": "Ensure the Providers wrapper (WagmiProvider + QueryClientProvider) wraps the entire app in layout.tsx and that the config in wagmi.ts includes the injected() connector." + }, + { + "cause": "The wallet is connected to a different chain than Polkadot Hub TestNet.", + "pattern": "ChainMismatchError: Chain ID 420420417 not supported", + "resolution": "In MetaMask, switch to the Polkadot Hub TestNet network (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill to add the network if it is not present." + }, + { + "cause": "useBlockNumber returned undefined before the first block is fetched.", + "pattern": "TypeError: Cannot read properties of undefined (reading 'toString') on blockNumber", + "resolution": "The component handles this with the null-coalescing operator (blockNumber?.toString() ?? 'Loading...'). This is expected on initial render and resolves automatically." + }, + { + "cause": "Incompatible @tanstack/react-query version installed.", + "pattern": "Error: wagmi requires react-query v5 or higher", + "resolution": "Run: npm install @tanstack/react-query@latest to ensure v5+ is installed." + } + ], + "examples": [ + { + "actions": [ + "Scaffold wagmi-dapp with create-next-app@15 non-interactively", + "Install wagmi, viem@2.x, and @tanstack/react-query", + "Create wagmi.ts with polkadotHubTestnet chain definition and createConfig", + "Wrap the app with WagmiProvider and QueryClientProvider in providers.tsx", + "Add ConnectWallet, BlockNumber, StorageRead, StorageWrite components", + "Start dev server and delegate browser testing to the user" + ], + "result": "Next.js app at localhost:3000 with wallet connection and live block number display", + "scenario": "Common scenario: scaffold a Wagmi dApp and connect to Polkadot Hub TestNet", + "user_says": "Help me build a Next.js app with Wagmi that connects to Polkadot Hub" + }, + { + "actions": [ + "Replace CONTRACT_ADDRESS in StorageRead.tsx with the user's deployed contract address", + "Replace STORAGE_ABI with the ERC-20 ABI fragment for the desired function (e.g. balanceOf)", + "Update functionName and args accordingly", + "Keep chainId: polkadotHubTestnet.id to ensure the call targets the correct network" + ], + "result": "useReadContract returns the result from the user's deployed contract on Polkadot Hub TestNet", + "scenario": "Edge case: user wants to call their own deployed contract instead of the pre-deployed Storage", + "user_says": "I deployed my own ERC-20 token — how do I call it with Wagmi useReadContract?" + } + ], + "id": "use-wagmi-polkadot-hub", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "runtime": [ + "Node.js v18+", + "npm or pnpm" + ], + "tokens": [ + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask or any EIP-1193 wallet installed in the browser" + ] + }, + "primary_page": "smart-contracts/libraries/wagmi.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/libraries/wagmi.md" + ], + "steps": [ + { + "action": "Scaffold the Next.js project non-interactively", + "commands": [ + "npx create-next-app@15 wagmi-dapp --ts --no-eslint --no-tailwind --no-src-dir --app --use-npm --skip-install", + "cd wagmi-dapp", + "npm install" + ], + "description": "Scaffold a Next.js 15 project using CLI flags (NOT piped input — `create-next-app@15+` ignores piped stdin and will hang waiting for keyboard input). The flags answer every interactive prompt: `--ts` (TypeScript), `--no-eslint`, `--no-tailwind`, `--no-src-dir` (files at project root, NOT in src/), `--app` (App Router), `--use-npm` (force npm — skip the package-manager prompt), `--skip-install` (defer install until after we cd in). Then `cd wagmi-dapp` and run `npm install`. The remaining steps assume the no-src-dir layout (so `app/`, `components/`, `wagmi.ts` all live at the project root).", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Wagmi v3 and peer dependencies", + "commands": [ + "npm install wagmi viem@2.x @tanstack/react-query" + ], + "description": "Install wagmi (React hooks for Ethereum), viem v2 (low-level EVM transport), and @tanstack/react-query (required peer dependency for Wagmi v3 data-fetching hooks).", + "order": 2, + "working_directory": "wagmi-dapp" + }, + { + "action": "Create the Wagmi config file with Polkadot Hub chain", + "description": "Create a new file at src/wagmi.ts (or wagmi.ts in the project root if using the src/ layout). Paste the following content exactly:\n\n```typescript\nimport { createConfig, http } from 'wagmi';\nimport { defineChain } from 'viem';\nimport { injected } from 'wagmi/connectors';\n\nexport const polkadotHubTestnet = defineChain({\n id: 420420417,\n name: 'Polkadot Hub TestNet',\n nativeCurrency: { name: 'PAS', symbol: 'PAS', decimals: 18 },\n rpcUrls: {\n default: { http: ['https://services.polkadothub-rpc.com/testnet'] },\n },\n});\n\nexport const config = createConfig({\n chains: [polkadotHubTestnet],\n connectors: [injected()],\n transports: {\n [polkadotHubTestnet.id]: http(),\n },\n});\n```\n\nThis configures Wagmi to use Polkadot Hub TestNet as the only chain with the injected (MetaMask) connector.", + "order": 3, + "working_directory": "wagmi-dapp" + }, + { + "action": "Wrap the app with WagmiProvider and QueryClientProvider", + "description": "Open src/app/layout.tsx (or pages/_app.tsx if using pages router). Add a client-side providers wrapper. Create src/app/providers.tsx with:\n\n```typescript\n'use client';\nimport { WagmiProvider } from 'wagmi';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { config } from '../wagmi';\n\nconst queryClient = new QueryClient();\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n return (\n \n \n {children}\n \n \n );\n}\n```\n\nThen wrap the body in src/app/layout.tsx with: import { Providers } from './providers'; and wrap {children} with Providers.", + "order": 4, + "working_directory": "wagmi-dapp" + }, + { + "action": "Add wallet connection component", + "description": "Create src/components/ConnectWallet.tsx with the following content:\n\n```typescript\n'use client';\nimport { useAccount, useConnect, useDisconnect } from 'wagmi';\n\nexport function ConnectWallet() {\n const { address, isConnected } = useAccount();\n const { connect, connectors } = useConnect();\n const { disconnect } = useDisconnect();\n\n if (isConnected) {\n return (\n
\n

Connected: {address}

\n \n
\n );\n }\n\n return (\n
\n {connectors.map((connector) => (\n \n ))}\n
\n );\n}\n```\n\nThis renders a connect/disconnect button using the injected (MetaMask) connector.", + "order": 5, + "working_directory": "wagmi-dapp" + }, + { + "action": "Add block number query using useBlockNumber", + "description": "Create src/components/BlockNumber.tsx with:\n\n```typescript\n'use client';\nimport { useBlockNumber } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nexport function BlockNumber() {\n const { data: blockNumber } = useBlockNumber({\n chainId: polkadotHubTestnet.id,\n watch: true,\n });\n return

Current block: {blockNumber?.toString() ?? 'Loading...'}

;\n}\n```\n\nThe watch: true option polls for new blocks and updates the component automatically.", + "order": 6, + "working_directory": "wagmi-dapp" + }, + { + "action": "Add Storage contract read interaction using useReadContract", + "description": "Create `src/components/StorageRead.tsx` to read the current value from a Storage contract deployed on Polkadot Hub TestNet. This step uses the publicly-deployed Storage contract at `0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3` (originally deployed for the zero-to-hero dApp tutorial). Its ABI uses `store(uint256)` and `retrieve()`, which matches the inline ABI below. If you'd rather use your own contract, deploy via `deploy-basic-contract-hardhat` and replace the address; ensure the deployed contract exposes `retrieve()` (or update the ABI to match your contract's getter):\n\n```typescript\n'use client';\nimport { useReadContract } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\n\nconst STORAGE_ABI = [\n {\n inputs: [],\n name: 'retrieve',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageRead() {\n const { data, isLoading } = useReadContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'retrieve',\n chainId: polkadotHubTestnet.id,\n });\n\n return (\n

Stored value: {isLoading ? 'Loading...' : data?.toString() ?? 'N/A'}

\n );\n}\n```\n\nThe `as \\`0x${string}\\`` cast satisfies viem/wagmi's strict address type — without it, the literal string would type-check as `string` and cause a build failure on the `useReadContract({ address: ... })` call.", + "order": 7, + "working_directory": "wagmi-dapp" + }, + { + "action": "Add Storage contract write interaction using useWriteContract", + "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from .", + "order": 8, + "working_directory": "wagmi-dapp" + }, + { + "action": "Assemble components in the main page and start the dev server", + "commands": [ + "npm run dev" + ], + "description": "Open src/app/page.tsx and replace its content with:\n\n```typescript\nimport { ConnectWallet } from '../components/ConnectWallet';\nimport { BlockNumber } from '../components/BlockNumber';\nimport { StorageRead } from '../components/StorageRead';\nimport { StorageWrite } from '../components/StorageWrite';\n\nexport default function Home() {\n return (\n
\n

Wagmi Polkadot Hub dApp

\n \n \n \n \n
\n );\n}\n```\n\nThen run npm run dev to start the Next.js dev server on http://localhost:3000.", + "expected_output": "Next.js dev server running at http://localhost:3000", + "order": 9, + "working_directory": "wagmi-dapp" + }, + { + "action": "Connect wallet and test contract interactions in the browser", + "description": "Delegate to the user: Open http://localhost:3000 in the browser. Click 'Connect MetaMask' to connect your wallet. Verify that the current block number updates automatically. Verify that the 'Stored value' field displays the current value from the Storage contract. Enter a number in the input field and click 'Store'. MetaMask will prompt for transaction confirmation — approve it. After confirmation, the stored value should update to the new number.", + "expected_output": "Wallet connected, block number displayed, Storage contract read/write working", + "interactive": true, + "order": 10, + "working_directory": "wagmi-dapp" + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs MetaMask setup, faucet tokens, or wants to use viem directly instead of Wagmi.", + "pages": [ + { + "relevance": "Lower-level viem library for direct contract calls without React hooks — useful when Wagmi is not needed.", + "slug": "smart-contracts-libraries-viem", + "title": "viem for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/viem.md" + }, + { + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required before the wallet connection step works.", + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md" + }, + { + "relevance": "Full Wagmi source page with the pre-deployed Storage contract address and complete code snippets.", + "slug": "smart-contracts-libraries-wagmi", + "title": "Wagmi for Polkadot Hub Smart Contracts", + "url": "https://docs.polkadot.com/smart-contracts/libraries/wagmi.md" + } + ] + }, + "title": "Build a Wagmi dApp Connected to Polkadot Hub", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", + "env_vars": [ + { + "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", + "name": "PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "The RPC URL is unreachable or the TestNet is down.", + "pattern": "AssertionError: Failed to connect to RPC endpoint", + "resolution": "Verify the URL https://services.polkadothub-rpc.com/testnet is correct and reachable. Check Polkadot Hub TestNet status." + }, + { + "cause": "The deployer account has no PAS tokens.", + "pattern": "ValueError: insufficient funds for gas * price + value", + "resolution": "Visit https://faucet.polkadot.io/ and request testnet PAS tokens for your account address." + }, + { + "cause": "The .env file is missing or PRIVATE_KEY is not defined in it.", + "pattern": "KeyError: 'PRIVATE_KEY'", + "resolution": "Create a .env file in the web3py-contracts directory with the line: PRIVATE_KEY=0xYOUR_KEY. Ensure python-dotenv is installed and load_dotenv() is called before accessing os.environ['PRIVATE_KEY']." + }, + { + "cause": "Solidity pragma version mismatch.", + "pattern": "SolcError: Source file requires different compiler version", + "resolution": "Ensure install_solc('0.8.20') is called in compile.py before compile_standard. The py-solc-x library downloads the compiler if not already cached." + } + ], + "examples": [ + { + "actions": [ + "Create web3py-contracts/ directory and Python virtualenv", + "Install web3, py-solc-x, python-dotenv via pip", + "Instruct user to add PRIVATE_KEY to .env file directly (not in chat)", + "Create Storage.sol, compile.py, deploy.py, interact.py from inline code", + "Run python compile.py then python deploy.py", + "Run python interact.py to verify store/retrieve" + ], + "result": "Contract deployed and verified on Polkadot Hub TestNet; stored value updated to 42", + "scenario": "Common scenario: deploy a Storage contract to Polkadot Hub TestNet using Python", + "user_says": "Deploy a smart contract to Polkadot Hub using Python and Web3.py" + }, + { + "actions": [ + "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", + "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", + "Update deploy.py: change compiled['contracts']['YourFile.sol']['YourContract'] to match", + "Update interact.py: change the ABI extraction path and function calls to match the user's contract ABI" + ], + "result": "Custom contract compiled and deployed; interact.py calls the contract's specific functions", + "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", + "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?" + } + ], + "id": "deploy-interact-contracts-web3py", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet — RPC https://services.polkadothub-rpc.com/testnet, chain ID 420420417" + ], + "runtime": [ + "Python 3.9+ with pip", + "Solidity compiler (installed automatically by py-solc-x)" + ], + "tokens": [ + "Small PAS balance for deployment gas — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (PRIVATE_KEY stored in .env — never enter in chat)" + ] + }, + "primary_page": "smart-contracts/libraries/web3-py.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/libraries/web3-py.md" + ], + "steps": [ + { + "action": "Create project directory and Python virtual environment", + "commands": [ + "mkdir web3py-contracts && cd web3py-contracts", + "python3 -m venv venv", + "source venv/bin/activate" + ], + "description": "Create a new directory named 'web3py-contracts', create a Python virtual environment inside it, and activate it. On Windows use 'venv\\Scripts\\activate' instead.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install Python dependencies", + "commands": [ + "pip install web3 py-solc-x python-dotenv" + ], + "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary.", + "order": 2, + "working_directory": "web3py-contracts" + }, + { + "action": "Create the .env file with PRIVATE_KEY", + "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore", + "order": 3, + "working_directory": "web3py-contracts" + }, + { + "action": "Create the Storage.sol Solidity contract", + "description": "Create a file named 'Storage.sol' with the following content:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ncontract Storage {\n uint256 private storedNumber;\n\n function store(uint256 num) public {\n storedNumber = num;\n }\n\n function retrieve() public view returns (uint256) {\n return storedNumber;\n }\n}\n```\n\nThis is a simple Storage contract with a store(uint256) write function and a retrieve() read function.", + "order": 4, + "working_directory": "web3py-contracts" + }, + { + "action": "Create compile.py to compile the Solidity contract", + "commands": [ + "python compile.py" + ], + "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", + "expected_output": "Compilation successful — compiled.json written.", + "order": 5, + "working_directory": "web3py-contracts" + }, + { + "action": "Create deploy.py to deploy the contract", + "commands": [ + "python deploy.py" + ], + "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", + "expected_output": "Deploying from account: 0x...\nContract deployed at: 0x...\nAddress saved to contract_address.txt", + "order": 6, + "working_directory": "web3py-contracts" + }, + { + "action": "Create interact.py to call store and retrieve", + "commands": [ + "python interact.py" + ], + "description": "Create a file named 'interact.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\naccount = w3.eth.account.from_key(PRIVATE_KEY)\n\nwith open('contract_address.txt') as f:\n contract_address = f.read().strip()\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\nabi = compiled['contracts']['Storage.sol']['Storage']['abi']\n\ncontract = w3.eth.contract(address=contract_address, abi=abi)\n\n# Read current value\nvalue = contract.functions.retrieve().call()\nprint(f'Current stored value: {value}')\n\n# Write new value\nnonce = w3.eth.get_transaction_count(account.address)\ntx = contract.functions.store(42).build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\nprint(f'store(42) tx: {receipt.transactionHash.hex()}')\n\n# Verify\nvalue = contract.functions.retrieve().call()\nprint(f'Updated stored value: {value}')\n```", + "expected_output": "Current stored value: 0\nstore(42) tx: 0x...\nUpdated stored value: 42", + "order": 7, + "working_directory": "web3py-contracts" + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs to understand the Polkadot Hub network params or wants to use JavaScript/TypeScript instead of Python.", + "pages": [ + { + "relevance": "Full Web3.py source page with complete code snippets and additional contract examples.", + "slug": "smart-contracts-libraries-web3-py", + "title": "Web3.py", + "url": "https://docs.polkadot.com/smart-contracts/libraries/web3-py.md" + }, + { + "relevance": "Ethers.js library — recommended JavaScript alternative to the sunset Web3.js library.", + "slug": "smart-contracts-libraries-ethers-js", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js.md" + }, + { + "relevance": "Full network connection reference with RPC URL, chain ID, and WSS endpoints for Polkadot Hub.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + } + ] + }, + "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The IERC20.sol file was not compiled successfully before clicking At Address.", + "pattern": "Remix: 'At Address' button disabled or no output", + "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." + }, + { + "cause": "MetaMask is still on Ethereum Mainnet or another network.", + "pattern": "MetaMask: Wrong network — expected Custom (420420417)", + "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." + }, + { + "cause": "Attempting to transfer more PAS than the account holds.", + "pattern": "Transaction reverted: insufficient balance", + "resolution": "Call balanceOf first to confirm the available balance, then use an amount in wei that does not exceed it." + } + ], + "examples": [ + { + "actions": [ + "Load source page to find the ERC-20 precompile address", + "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", + "Create IERC20.sol in Remix with the standard ERC-20 interface and compile it", + "Set Environment to Injected Provider - MetaMask in Deploy and Run tab", + "Click At Address with the precompile address", + "Expand balanceOf, enter the MetaMask address, and click call" + ], + "result": "uint256 PAS balance in wei displayed in Remix console", + "scenario": "Common scenario: check PAS balance via ERC-20 precompile in Remix", + "user_says": "How do I call balanceOf on the ERC-20 precompile in Remix?" + }, + { + "actions": [ + "Load source page to get the ERC-20 precompile address", + "In the Solidity contract, import or define the IERC20 interface", + "Store the precompile address as a constant: IERC20 constant PAS_ERC20 = IERC20(PRECOMPILE_ADDRESS)", + "Call PAS_ERC20.balanceOf(msg.sender) or PAS_ERC20.transfer(to, amount) as needed", + "Deploy the wrapper contract via Remix with MetaMask connected to Polkadot Hub TestNet" + ], + "result": "Solidity contract calls the ERC-20 precompile to read balances or transfer native PAS tokens", + "scenario": "Edge case: user wants to call the precompile from a Solidity contract", + "user_says": "How do I use the ERC-20 precompile from inside my Solidity contract?" + } + ], + "id": "interact-erc20-precompile-polkadot-hub", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "tokens": [ + "PAS balance in MetaMask for transfer/approve operations — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ] + }, + "primary_page": "smart-contracts/precompiles/erc20.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/precompiles/erc20.md" + ], + "steps": [ + { + "action": "Find the ERC-20 precompile address from the source page", + "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token.", + "interactive": true, + "order": 1, + "working_directory": "." + }, + { + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to the Polkadot Hub TestNet network (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet). If the network is not added yet, use the add-polkadot-hub-testnet-metamask skill to add it. Verify your PAS balance is non-zero before proceeding.", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Open Remix IDE and create the ERC-20 interface file", + "description": "Delegate to the user: Open https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), create a new file named 'IERC20.sol'. Paste the standard ERC-20 interface into the file:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function balanceOf(address account) external view returns (uint256);\n function transfer(address to, uint256 amount) external returns (bool);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n```\n\nClick the Solidity Compiler tab (shield icon), set the compiler version to 0.8.20, and click 'Compile IERC20.sol'.", + "interactive": true, + "order": 3, + "working_directory": "." + }, + { + "action": "Connect Remix to MetaMask and load the precompile using At Address", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below.", + "interactive": true, + "order": 4, + "working_directory": "." + }, + { + "action": "Call balanceOf to check PAS token balance", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", + "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18", + "interactive": true, + "order": 5, + "working_directory": "." + }, + { + "action": "Call approve and transfer functions", + "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", + "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console", + "interactive": true, + "order": 6, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs the precompile address, wants to add MetaMask, or wants to call the precompile from a contract.", + "pages": [ + { + "relevance": "Full ERC-20 precompile source page with the exact precompile address, complete ABI, and additional usage examples.", + "slug": "smart-contracts-precompiles-erc20", + "title": "Interact with the ERC20 Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md" + }, + { + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite for this skill.", + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md" + }, + { + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider.", + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md" + } + ] + }, + "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", + "env_vars": [], + "error_patterns": [ + { + "cause": "IStorage.sol was not compiled, or the wrong contract is selected in the dropdown.", + "pattern": "Remix: At Address button grayed out", + "resolution": "Compile IStorage.sol first via the Solidity Compiler tab, then return to Deploy and Run. Ensure 'IStorage' is selected in the Contract dropdown before clicking At Address." + }, + { + "cause": "The key used in getBytes does not match the key used in setBytes, or the setBytes transaction did not confirm.", + "pattern": "getBytes returns 0x (empty bytes)", + "resolution": "Ensure the bytes32 key in getBytes is identical to the one used in setBytes. Check Remix console for the setBytes transaction hash to confirm it was mined." + }, + { + "cause": "Gas estimate too low for setBytes with a large value.", + "pattern": "MetaMask: Transaction failed with out of gas", + "resolution": "In MetaMask, increase the gas limit manually before confirming the setBytes transaction." + } + ], + "examples": [ + { + "actions": [ + "Load source page to get the Storage precompile address", + "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", + "Create IStorage.sol in Remix with setBytes/getBytes interface and compile it", + "Set Environment to Injected Provider - MetaMask, select IStorage, click At Address with the precompile address", + "Call setBytes with a bytes32 key (hex-padded) and hex-encoded bytes value", + "Call getBytes with the same key to verify the stored value" + ], + "result": "Value stored on-chain via setBytes; getBytes returns the same hex-encoded bytes", + "scenario": "Common scenario: store and retrieve bytes using the Storage precompile", + "user_says": "How do I use the Storage precompile in Remix to store a value?" + }, + { + "actions": [ + "Load source page to get the precompile address", + "In the user's contract, import or define the IStorage interface", + "Store the precompile address as a constant: IStorage constant STORE = IStorage(PRECOMPILE_ADDRESS)", + "Call STORE.setBytes(key, value) and STORE.getBytes(key) in contract functions", + "Deploy the wrapper contract in Remix targeting Polkadot Hub TestNet" + ], + "result": "User's contract reads and writes key-value data through the Storage precompile", + "scenario": "Edge case: user wants to use the Storage precompile from within a Solidity contract", + "user_says": "Can I call the Storage precompile from inside my own contract?" + } + ], + "id": "interact-storage-precompile-remix", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "tokens": [ + "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ] + }, + "primary_page": "smart-contracts/precompiles/storage.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/precompiles/storage.md" + ], + "steps": [ + { + "action": "Find the Storage precompile address from the source page", + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix.", + "interactive": true, + "order": 1, + "working_directory": "." + }, + { + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance.", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Create the Storage precompile interface in Remix", + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'IStorage.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IStorage {\n function setBytes(bytes32 key, bytes memory value) external;\n function getBytes(bytes32 key) external view returns (bytes memory);\n}\n```\n\nGo to the Solidity Compiler tab, set the version to 0.8.20, and click 'Compile IStorage.sol'. Confirm there are no errors.", + "interactive": true, + "order": 3, + "working_directory": "." + }, + { + "action": "Connect Remix to MetaMask and load the precompile using At Address", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection popup. Verify the network displays 'Custom (420420417) network'. Select 'IStorage' in the Contract dropdown. Paste the Storage precompile address (from step 1) into the 'At Address' input. Click 'At Address'. An IStorage instance should appear in the Deployed Contracts section.", + "interactive": true, + "order": 4, + "working_directory": "." + }, + { + "action": "Call setBytes to store a value", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", + "expected_output": "Transaction confirmed; tx hash shown in Remix console", + "interactive": true, + "order": 5, + "working_directory": "." + }, + { + "action": "Call getBytes to retrieve the stored value", + "description": "Delegate to the user: In the IStorage instance, expand 'getBytes'. Enter the same bytes32 key used in setBytes. Click 'call' (blue button). The output displays the stored bytes value in hex. To decode: the hex 0x68656c6c6f decodes to 'hello' in UTF-8.", + "expected_output": "bytes output matching the hex value stored in setBytes", + "interactive": true, + "order": 6, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs the precompile address, wants to call the precompile from Solidity, or needs Remix connection help.", + "pages": [ + { + "relevance": "Full Storage precompile source page with the exact precompile address, full ABI, and key encoding examples.", + "slug": "smart-contracts-precompiles-storage", + "title": "Interact with the Storage Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/storage.md" + }, + { + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider.", + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md" + }, + { + "relevance": "How to add Polkadot Hub TestNet to MetaMask — required prerequisite.", + "slug": "smart-contracts-integrations-wallets", + "title": "Wallets for Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md" + } + ] + }, + "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The sig or pubKey bytes are not the correct length or format for sr25519.", + "pattern": "Remix: call reverts on verifySignature with invalid inputs", + "resolution": "sr25519 signatures are 64 bytes (128 hex chars with 0x prefix = 130 chars total). Public keys are 32 bytes (64 hex chars). Use the subkey tool to generate a valid signature: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'. Ensure all inputs are hex-encoded with 0x prefix." + }, + { + "cause": "Incorrect precompile address entered, or MetaMask connected to the wrong network.", + "pattern": "At Address: no contract at address", + "resolution": "Re-load the source page to get the correct System precompile address. Verify MetaMask shows chain ID 420420417." + }, + { + "cause": "The interface in ISystem.sol may not match the exact ABI exposed by the precompile.", + "pattern": "ISystem.sol compilation error: function not found in interface", + "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/system.md for the authoritative ABI and update the interface accordingly." + } + ], + "examples": [ + { + "actions": [ + "Load source page to get the System precompile address", + "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", + "Create ISystem.sol in Remix with the BLAKE2 and other function signatures, then compile", + "Set Environment to Injected Provider - MetaMask, select ISystem, click At Address with the precompile address", + "Expand blake2b, enter hex-encoded input data (e.g. 0x68656c6c6f for 'hello'), click call", + "Record the BLAKE2b hash output" + ], + "result": "BLAKE2b hash of the input bytes returned as hex in the Remix console", + "scenario": "Common scenario: compute a BLAKE2b hash using the System precompile in Remix", + "user_says": "How do I use the System precompile to hash data with BLAKE2b in Remix?" + }, + { + "actions": [ + "Generate a test signature using subkey: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'", + "Note the 64-byte signature hex and the 32-byte Alice public key hex", + "In Remix, expand verifySignature on the ISystem instance", + "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", + "Click call and confirm the result is true" + ], + "result": "bool true returned — sr25519 signature verified on-chain via the System precompile", + "scenario": "Edge case: user wants to verify an sr25519 signature from a Polkadot account", + "user_says": "Can I verify a Polkadot sr25519 signature on-chain using the System precompile?" + } + ], + "id": "interact-system-precompile-remix", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "tokens": [ + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ] + }, + "primary_page": "smart-contracts/precompiles/system.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/precompiles/system.md" + ], + "steps": [ + { + "action": "Find the System precompile address from the source page", + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix.", + "interactive": true, + "order": 1, + "working_directory": "." + }, + { + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance.", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Create the System precompile interface in Remix", + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'.", + "interactive": true, + "order": 3, + "working_directory": "." + }, + { + "action": "Connect Remix to MetaMask and load the precompile using At Address", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection. Verify the network shows 'Custom (420420417) network'. Select 'ISystem' in the Contract dropdown. Paste the System precompile address (from step 1) into 'At Address'. Click 'At Address'. An ISystem instance should appear in the Deployed Contracts section.", + "interactive": true, + "order": 4, + "working_directory": "." + }, + { + "action": "Call blake2b to hash data", + "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", + "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data", + "interactive": true, + "order": 5, + "working_directory": "." + }, + { + "action": "Call verifySignature for sr25519 verification", + "description": "Delegate to the user: Expand the 'verifySignature' function. To use this function you need three values:\n\n- INSERT_SIGNATURE: Replace with a real sr25519 signature as hex bytes (64 bytes, 128 hex chars). Generate a signature using a Polkadot.js signer or subkey tool: subkey sign --scheme sr25519 --suri '//Alice' --message 'your_message'\n\n- INSERT_PUBLIC_KEY: Replace with the sr25519 public key (32 bytes, 64 hex chars) corresponding to the signer.\n\n- message: The message bytes that were signed (hex-encoded).\n\nEnter the three values in the 'sig', 'pubKey', and 'message' fields respectively, then click 'call'. The function returns true if the signature is valid, false otherwise.", + "expected_output": "bool: true if the sr25519 signature is valid for the given pubKey and message", + "interactive": true, + "order": 6, + "working_directory": "." + }, + { + "action": "Call accountExists to check account presence", + "description": "Delegate to the user: Expand the 'accountExists' function. Enter an Ethereum-format address (0x...) in the 'account' field. Click 'call'. The function returns true if the account exists on-chain (i.e. has been funded or deployed), false if it has never received any funds or activity.", + "expected_output": "bool: true if the account exists on Polkadot Hub TestNet, false otherwise", + "interactive": true, + "order": 7, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages when the user needs the precompile address, wants to use sr25519 signing tools, or needs Remix setup help.", + "pages": [ + { + "relevance": "Full System precompile source page with the exact precompile address, authoritative ABI, and all function signatures.", + "slug": "smart-contracts-precompiles-system", + "title": "Interact with the System Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/system.md" + }, + { + "relevance": "How to connect Remix IDE to Polkadot Hub via MetaMask injected provider.", + "slug": "smart-contracts-dev-environments-remix", + "title": "Use the Remix IDE on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/remix.md" + }, + { + "relevance": "ERC-20 precompile — often used alongside the System precompile in contract interactions.", + "slug": "smart-contracts-precompiles-erc20", + "title": "Interact with the ERC20 Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md" + } + ] + }, + "title": "Interact with the System Precompile on Polkadot Hub via Remix", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Guides you through creating an IXcm Solidity interface in Remix, loading the XCM precompile, and calling weighMessage, execute, and send functions. Use when you need to test XCM message dispatch from a smart contract, estimate XCM execution weight, or send cross-chain messages from Polkadot Hub via Solidity. All interaction is browser-based using Remix IDE with MetaMask. Trigger phrases: 'XCM precompile', 'send XCM from contract', 'weighMessage', 'xcm execute', 'cross-chain from Solidity', 'IXcm interface'. Output: XCM weight estimates and on-chain transaction receipts.", + "env_vars": [], + "error_patterns": [ + { + "cause": "SCALE-encoded XCM bytes are malformed or the weight limit is too low.", + "pattern": "Remix: transaction reverted on execute or send", + "resolution": "Verify the encoding against the example on the source page. Use weighMessage first to obtain the correct maxWeight. Ensure the correct precompile address is used for TestNet." + }, + { + "cause": "Incorrect precompile address entered, or MetaMask is connected to the wrong network.", + "pattern": "At Address: no contract at address", + "resolution": "Reload the source page to get the authoritative XCM precompile address. Verify MetaMask shows chain ID 420420417." + }, + { + "cause": "The interface definition does not match the ABI the precompile exposes.", + "pattern": "IXcm.sol compilation error: function not found", + "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md for the authoritative ABI and update IXcm.sol accordingly." + }, + { + "cause": "PAS balance too low to cover gas on Polkadot Hub TestNet (base fee: 1000 gwei).", + "pattern": "MetaMask: insufficient funds for gas", + "resolution": "Obtain PAS tokens from https://faucet.polkadot.io/. Ensure the account has at least 0.01 PAS before submitting state-changing calls." + } + ], + "examples": [ + { + "actions": [ + "Load XCM precompile address and IXcm ABI from source page", + "Create and compile IXcm.sol in Remix", + "Load precompile via At Address", + "Call weighMessage with encoded destination and message bytes", + "Read the returned weight estimate" + ], + "result": "Weight estimate (refTime and proofSize) indicating the execution cost on the destination chain", + "scenario": "Common scenario: estimate XCM execution fee before sending", + "user_says": "How much will it cost to send an XCM message from my contract on Polkadot Hub?" + }, + { + "actions": [ + "Compare SCALE-encoded message bytes against the source page example", + "Call weighMessage first to confirm the message decodes correctly", + "Check that maxWeight is at least the refTime returned by weighMessage", + "If revert persists, reload IXcm.sol ABI from source page to confirm interface matches precompile" + ], + "result": "Root cause identified (malformed encoding or wrong interface); corrected message executes successfully", + "scenario": "Edge case: execute call reverts due to malformed SCALE encoding", + "user_says": "My XCM execute call keeps reverting in Remix" + } + ], + "id": "interact-xcm-precompile-remix", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet — chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet" + ], + "runtime": [ + "Modern web browser (Chrome or Firefox recommended)" + ], + "tokens": [ + "Small PAS balance for write transactions — obtain from https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" + ] + }, + "primary_page": "smart-contracts/precompiles/xcm.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/precompiles/xcm.md" + ], + "steps": [ + { + "action": "Find the XCM precompile address and IXcm ABI from the source page", + "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI.", + "interactive": true, + "order": 1, + "working_directory": "." + }, + { + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance for gas fees.", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Create the IXcm interface in Remix and compile it", + "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors.", + "interactive": true, + "order": 3, + "working_directory": "." + }, + { + "action": "Load the XCM precompile using At Address in Remix", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section.", + "interactive": true, + "order": 4, + "working_directory": "." + }, + { + "action": "Call weighMessage to estimate XCM execution cost", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", + "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost", + "interactive": true, + "order": 5, + "working_directory": "." + }, + { + "action": "Call execute to run an XCM message locally", + "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", + "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs", + "interactive": true, + "order": 6, + "working_directory": "." + }, + { + "action": "Call send to dispatch an XCM to another chain", + "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", + "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs", + "interactive": true, + "order": 7, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for XCM encoding details, precompile addresses, and cross-chain messaging concepts.", + "pages": [ + { + "relevance": "Authoritative XCM precompile address, IXcm interface definition, and SCALE-encoded message examples.", + "slug": "smart-contracts-precompiles-xcm", + "title": "Interact with the XCM Precompile", + "url": "https://docs.polkadot.com/smart-contracts/precompiles/xcm.md" + }, + { + "relevance": "XCM tooling for encoding, debugging, and monitoring message delivery.", + "slug": "reference-tools-xcm-tools", + "title": "XCM Tools", + "url": "https://docs.polkadot.com/reference/tools/xcm-tools.md" + }, + { + "relevance": "Polkadot Hub TestNet network details and RPC endpoints.", + "slug": "smart-contracts-connect", + "title": "Connect to Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/connect.md" + } + ] + }, + "title": "Interact with the XCM Precompile via Remix IDE", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Covers the end-to-end parachain launch workflow on Paseo TestNet: obtain PAS tokens, reserve a para ID via Polkadot.js Apps, generate collator keys with subkey (Docker), build plain and raw chain specs with chain-spec-builder, export genesis wasm and state with polkadot-omni-node, register a parathread on-chain, generate a node key, start the collator, and insert session keys via RPC. Use this skill after completing the set-up-parachain-template skill. Trigger phrases: 'deploy parachain', 'register parachain Paseo', 'launch parachain TestNet', 'reserve para ID', 'parachain registration'. Part of the three-page launch series: set-up-the-parachain-template to deploy-to-polkadot to obtain-coretime.", + "env_vars": [], + "error_patterns": [ + { + "cause": "The parachain was not built in release mode or the Wasm path is incorrect.", + "pattern": "chain-spec-builder: cannot find runtime Wasm file", + "resolution": "Run 'cargo build --release' in the parachain-template directory. The Wasm artifact is at: target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm" + }, + { + "cause": "The para ID was not properly reserved, or the registration account differs from the manager.", + "pattern": "Parathread registration fails: 'ParaAlreadyExists' or 'NotRegistrar'", + "resolution": "Verify that PARA_ID was successfully reserved in step 2 using the correct account. Use the same account for the registration in step 7." + }, + { + "cause": "Parathread registration has not finalized on-chain, or chain_spec_raw.json has the wrong para_id.", + "pattern": "Collator log: 'Relay chain does not contain our parachain'", + "resolution": "Verify the Parathreads tab shows 'Onboarding' for PARA_ID. Confirm chain_spec_raw.json contains the correct para_id. Registration takes up to one epoch (600 blocks on Paseo)." + }, + { + "cause": "The collator was started without '--rpc-methods=Unsafe'.", + "pattern": "author_insertKey: 'Method not found'", + "resolution": "Stop the collator, add '--rpc-methods=Unsafe' to the startup command, and restart. Never expose unsafe RPC methods to external network interfaces." + } + ], + "examples": [ + { + "actions": [ + "Get PAS tokens from faucet", + "Reserve a para ID via Polkadot.js Apps", + "Generate Aura (SR25519) and GRANDPA (ED25519) collator keys with subkey Docker", + "Build plain chain spec, embed keys, build raw chain spec, export genesis data", + "Register parathread on-chain via Polkadot.js Apps", + "Generate node key, start collator, insert session keys via RPC" + ], + "result": "Parachain registered on Paseo TestNet with status 'Onboarding'; collator is running and syncing", + "scenario": "Common scenario: first deployment to Paseo after building the template", + "user_says": "I built the parachain template — how do I deploy it to Paseo TestNet?" + }, + { + "actions": [ + "Verify port 30333 is open on the server firewall", + "Confirm the startup command includes the '-- --chain paseo' relay chain section", + "Add a known Paseo bootnode with '--bootnodes /dns/...' to the startup flags", + "Wait 10 minutes for peer discovery — initial warp sync connection can be slow" + ], + "result": "Collator finds relay chain peers and begins syncing; block production starts after parathread onboards", + "scenario": "Edge case: collator shows 0 peers after launch", + "user_says": "My collator started but shows 0 peers and is not syncing the relay chain" + } + ], + "id": "deploy-parachain-to-polkadot-testnet", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Paseo TestNet — wss://rpc.ibp.network/paseo", + "Server with TCP port 30333 open for P2P collator connections" + ], + "runtime": [ + "Polkadot SDK parachain template built in release mode (see set-up-parachain-template skill)", + "Rust and Cargo installed", + "Docker installed (for subkey key generation)", + "polkadot-omni-node binary: cargo install polkadot-omni-node", + "chain-spec-builder binary: cargo install chain-spec-builder" + ], + "tokens": [ + "PAS tokens from https://faucet.polkadot.io/ — required for para ID reservation and parathread registration fees" + ], + "wallet": [ + "Polkadot.js browser extension (https://polkadot.js.org/extension/) with a PAS-funded account" + ] + }, + "primary_page": "parachains/launch-a-parachain/deploy-to-polkadot.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "parachains/launch-a-parachain/deploy-to-polkadot.md" + ], + "steps": [ + { + "action": "Obtain PAS tokens from the faucet", + "description": "Delegate to the user: Open https://faucet.polkadot.io/, select the 'Paseo' network, paste your SS58 account address, and submit. The faucet delivers 500 PAS per request. Verify the balance in Polkadot.js Apps before proceeding.", + "interactive": true, + "order": 1, + "working_directory": "." + }, + { + "action": "Reserve a para ID on Paseo TestNet", + "description": "Delegate to the user: Open https://polkadot.js.org/apps/ and connect to Paseo TestNet (wss://rpc.ibp.network/paseo). Navigate to Network > Parachains > Parathreads. Click '+ Parathread'. Select your PAS-funded account as manager and submit. After finalization, the UI shows your assigned para ID — record it as PARA_ID (needed in all subsequent steps).", + "expected_output": "Para ID assigned and visible in the Parathreads tab", + "interactive": true, + "order": 2, + "working_directory": "." + }, + { + "action": "Generate collator session keys using subkey (Docker)", + "commands": [ + "docker run --rm -it parity/subkey generate --scheme sr25519", + "docker run --rm -it parity/subkey generate --scheme ed25519" + ], + "description": "Run both commands. The first produces an SR25519 key (used for Aura block production); the second produces an ED25519 key (used for GRANDPA finality). Record both seed phrases and Account IDs securely. Never share the seed phrases. Save SR25519 Account ID as AURA_KEY and ED25519 Account ID as GRANDPA_KEY.", + "order": 3, + "working_directory": "." + }, + { + "action": "Build the plain chain spec", + "commands": [ + "chain-spec-builder create --relay-chain paseo --para-id INSERT_PARA_ID --runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm named-preset development" + ], + "description": "Replace INSERT_PARA_ID with the value of PARA_ID from step 2 (e.g., 2000). This produces 'chain_spec.json'. Then open 'chain_spec.json' and make these edits: (1) Set the 'id' field to a unique string (e.g., 'my-parachain-paseo'). (2) Insert AURA_KEY and GRANDPA_KEY (from step 3) into the 'aura' and 'grandpa' genesis sections. (3) Confirm 'para_id' equals PARA_ID. Save the file.", + "order": 4, + "working_directory": "parachain-template" + }, + { + "action": "Build the raw chain spec", + "commands": [ + "polkadot-omni-node build-spec --chain chain_spec.json --raw > chain_spec_raw.json" + ], + "description": "Converts the human-readable chain spec to SCALE-encoded raw format. Verify 'chain_spec_raw.json' was created and is non-empty.", + "expected_output": "chain_spec_raw.json created (non-empty JSON file)", + "order": 5, + "working_directory": "parachain-template" + }, + { + "action": "Export genesis wasm and state", + "commands": [ + "polkadot-omni-node export-genesis-wasm --chain chain_spec_raw.json genesis_wasm", + "polkadot-omni-node export-genesis-state --chain chain_spec_raw.json genesis_state" + ], + "description": "Produces 'genesis_wasm' (runtime Wasm bytecode) and 'genesis_state' (initial chain state). Both files are required for the on-chain registration in step 7.", + "order": 6, + "working_directory": "parachain-template" + }, + { + "action": "Register the parathread on-chain", + "description": "Delegate to the user: In Polkadot.js Apps (connected to Paseo), navigate to Network > Parachains > Parathreads. Find your reserved PARA_ID and click 'Register'. In the modal, upload 'genesis_wasm' for 'Genesis WASM' and 'genesis_state' for 'Initial state'. Submit and sign. After finalization, the status changes from 'Reserved' to 'Onboarding'. Registration takes effect at the next epoch (up to 600 blocks on Paseo).", + "expected_output": "Parathread status changes to 'Onboarding' in the Parathreads tab", + "interactive": true, + "order": 7, + "working_directory": "." + }, + { + "action": "Generate a P2P node key for the collator", + "commands": [ + "polkadot-omni-node generate-node-key --file node-key.dat" + ], + "description": "Writes a P2P node key to 'node-key.dat' and prints the peer ID to stdout. Save the peer ID as NODE_PEER_ID. Keep 'node-key.dat' secure — losing it changes the peer ID on next restart.", + "order": 8, + "working_directory": "parachain-template" + }, + { + "action": "Start the collator node", + "commands": [ + "./target/release/parachain-template-node --collator --chain chain_spec_raw.json --node-key-file node-key.dat --port 30333 --rpc-port 9944 --rpc-methods=Unsafe -- --chain paseo --sync warp" + ], + "description": "The '--' separator splits collator arguments from the embedded relay chain (Paseo) arguments. '--sync warp' enables fast warp sync for Paseo. The collator first syncs the relay chain (several minutes). Wait for log messages containing 'Parachain synced' before inserting session keys in step 10.", + "order": 9, + "working_directory": "parachain-template" + }, + { + "action": "Insert session keys into the collator keystore", + "commands": [ + "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"aura\",\"INSERT_AURA_SEED_PHRASE\",\"INSERT_AURA_PUBLIC_KEY_HEX\"],\"id\":1}'", + "curl -s -X POST http://localhost:9944 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"author_insertKey\",\"params\":[\"gran\",\"INSERT_GRANDPA_SEED_PHRASE\",\"INSERT_GRANDPA_PUBLIC_KEY_HEX\"],\"id\":1}'" + ], + "description": "Make these substitutions before running: INSERT_AURA_SEED_PHRASE -> SR25519 seed phrase from step 3; INSERT_AURA_PUBLIC_KEY_HEX -> SR25519 Account ID (0x-prefixed) from step 3; INSERT_GRANDPA_SEED_PHRASE -> ED25519 seed phrase from step 3; INSERT_GRANDPA_PUBLIC_KEY_HEX -> ED25519 Account ID (0x-prefixed) from step 3. Each successful command returns: {\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}. Do NOT include these commands in chat history — they contain seed phrases. Run them directly in the server terminal.", + "expected_output": "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1} for each key insertion", + "order": 10, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for template setup, coretime, and local test network guidance.", + "pages": [ + { + "relevance": "Required prerequisite: build the parachain template locally before deploying to TestNet.", + "slug": "parachains-launch-a-parachain-set-up-the-parachain-template", + "title": "Set Up the Polkadot SDK Parachain Template", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/set-up-the-parachain-template.md" + }, + { + "relevance": "Next step: obtain on-demand or bulk coretime to activate parachain block production.", + "slug": "parachains-launch-a-parachain-obtain-coretime", + "title": "Obtain Coretime", + "url": "https://docs.polkadot.com/parachains/launch-a-parachain/obtain-coretime.md" + }, + { + "relevance": "Test the parachain locally with Zombienet before deploying to TestNet.", + "slug": "parachains-testing-run-a-parachain-network", + "title": "Run a Parachain Network", + "url": "https://docs.polkadot.com/parachains/testing/run-a-parachain-network.md" + } + ] + }, + "title": "Deploy a Parachain to the Polkadot TestNet (Paseo)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Incorrect chain ID or RPC URL entered in MetaMask network settings.", + "pattern": "MetaMask: 'Invalid chain ID' or cannot connect to network", + "resolution": "Verify the chain ID is exactly 420420417 (decimal) and the RPC URL is https://services.polkadothub-rpc.com/testnet. Remove and re-add the network if the error persists." + }, + { + "cause": "The address already received tokens within the 24-hour rate limit window.", + "pattern": "Faucet rate limit: 'Already requested recently'", + "resolution": "Wait 24 hours, or use a different development wallet address." + }, + { + "cause": "TestNet may be under maintenance, or the public RPC endpoint URL has changed.", + "pattern": "RPC endpoint returns errors or is unreachable", + "resolution": "Check https://docs.polkadot.com/smart-contracts/connect/ for the latest endpoint URLs. The WSS alternative is wss://services.polkadothub-rpc.com/testnet." + } + ], + "examples": [ + { + "actions": [ + "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", + "Request PAS tokens from https://faucet.polkadot.io/", + "Verify funded balance in MetaMask" + ], + "result": "MetaMask connected to Polkadot Hub TestNet with funded PAS balance, ready for smart contract deployment", + "scenario": "Common scenario: set up development environment from scratch", + "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?" + }, + { + "actions": [ + "Reference the MainNet network parameters from the source page", + "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" + ], + "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage", + "scenario": "Edge case: looking up MainNet connection parameters", + "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?" + } + ], + "id": "connect-polkadot-hub-testnet", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Internet access to Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", + "Internet access to the faucet: https://faucet.polkadot.io/" + ], + "wallet": [ + "MetaMask browser extension installed (https://metamask.io/download/)" + ] + }, + "primary_page": "smart-contracts/connect.md", + "reference_code": { + "base_path": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/connect.md" + ], + "steps": [ + { + "action": "Add Polkadot Hub TestNet to MetaMask", + "description": "In MetaMask, open Settings > Networks > Add a network > Add a network manually. Enter these parameters: Network name: 'Polkadot Hub TestNet'; New RPC URL: 'https://services.polkadothub-rpc.com/testnet'; Chain ID: '420420417'; Currency symbol: 'PAS'. For the Block Explorer URL, check https://docs.polkadot.com/smart-contracts/connect/ for the current explorer URL, as it may change. Click Save and switch MetaMask to this network. This step must be delegated to the user.", + "order": 1, + "working_directory": "." + }, + { + "action": "Request PAS test tokens from the faucet", + "description": "Navigate to https://faucet.polkadot.io/. Select 'Polkadot Hub TestNet', paste your 0x-prefixed EVM address, and click the request button. The faucet credits PAS tokens within seconds. Rate limit: approximately 500 PAS per 24 hours per address. This step must be delegated to the user.", + "order": 2, + "working_directory": "." + }, + { + "action": "Verify MetaMask balance", + "description": "In MetaMask, confirm the PAS balance is non-zero on the Polkadot Hub TestNet network. To verify programmatically: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"YOUR_ADDRESS\",\"latest\"],\"id\":1}'. Replace YOUR_ADDRESS with the 0x-prefixed address. A non-zero hex result confirms the account is funded.", + "order": 3, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load these pages for development framework configuration that uses the TestNet endpoints established here.", + "pages": [ + { + "relevance": "Hardhat network configuration using the TestNet RPC URL and chain ID provided by this skill.", + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md" + }, + { + "relevance": "Detailed faucet workflow for obtaining PAS test tokens.", + "slug": "smart-contracts-faucet", + "title": "Get Tokens from the Official Faucet", + "url": "https://docs.polkadot.com/smart-contracts/faucet.md" + } + ] + }, + "title": "Connect to Polkadot Hub and Get Test Tokens", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Stores a file on the Polkadot Bulletin Chain using PAPI TypeScript and retrieves it by CID via the IPFS gateway. Use when you need decentralized IPFS-compatible file storage for dApp assets, NFT metadata, or files under 8 MiB. Requires a Polkadot account authorized via the Bulletin Chain Console UI faucet (no programmatic self-authorization). Trigger phrases: 'store file Bulletin Chain', 'PAPI TransactionStorage store', 'Polkadot decentralized storage', 'Bulletin Chain IPFS'.", + "env_vars": [ + { + "description": "12-word mnemonic of the authorized Polkadot account. Do NOT ask user to paste mnemonic in chat — they must edit .env directly.", + "name": "MNEMONIC", + "required": true + } + ], + "error_patterns": [ + { + "cause": "The signing account has no active authorization on the Bulletin Chain, or it has expired.", + "pattern": "Error: account has no authorization", + "resolution": "Re-authorize via the Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/ before retrying." + }, + { + "cause": "File exceeds the ~8 MiB per-transaction size limit.", + "pattern": "file too large / data exceeds per-transaction limit", + "resolution": "Split into chunks under 8 MiB and submit multiple store transactions, recording (block, index) per chunk." + }, + { + "cause": ".env not populated or dotenv not loaded.", + "pattern": "MNEMONIC is undefined / Cannot derive key from undefined", + "resolution": "Verify .env contains MNEMONIC= and that `import 'dotenv/config';` is the first line of store-data.ts." + }, + { + "cause": "Network issue or RPC temporarily unavailable.", + "pattern": "WebSocket connection failed / ECONNREFUSED wss://paseo-bulletin-rpc.polkadot.io", + "resolution": "Check internet connectivity and retry after a few minutes. Monitor the Polkadot Discord for outage announcements." + } + ], + "examples": [ + { + "actions": [ + "Authorize Polkadot account via Console UI faucet", + "Set up Node.js ESM project and install polkadot-api + hdkd + dotenv", + "Run npx papi add bulletin to generate chain descriptors", + "Create .env with MNEMONIC, modify store-data.ts to use process.env.MNEMONIC", + "Set FILE_PATH to the image path, run npx tsx store-data.ts", + "Receive CID for use in NFT metadata" + ], + "result": "File stored on-chain; CID like bafk2bzacea6wlxy... retrievable at https://paseo-ipfs.polkadot.io/ipfs/", + "scenario": "Common scenario: store an image for an NFT collection", + "user_says": "Store my NFT artwork on the Polkadot Bulletin Chain and get its IPFS CID" + }, + { + "actions": [ + "Retrieve the (block, index) pair from when the data was stored", + "Confirm expiration block has not yet passed (check Bulletin Chain Explorer)", + "Call TransactionStorage.renew({block, index}) using PAPI with the same authorized signer", + "Record new (block, index) from the Renewed event for future renewals" + ], + "result": "Retention timer reset; original CID remains valid for another full retention period", + "scenario": "Edge case: stored data approaches expiry", + "user_says": "My stored data is about to expire — how do I renew it?" + } + ], + "id": "store-retrieve-data-bulletin-chain", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Bulletin Chain TestNet WebSocket: wss://paseo-bulletin-rpc.polkadot.io" + ], + "runtime": [ + "Node.js v18 or later", + "npm" + ], + "tokens": [ + "Bulletin Chain storage authorization (not DOT/PAS) — obtained from Console UI faucet at https://paritytech.github.io/polkadot-bulletin-chain/. The authorize_account extrinsic requires Root origin; self-authorization is not available programmatically." + ], + "wallet": [ + "A Polkadot account (SS58) with active Bulletin Chain authorization, plus its 12-word mnemonic for signing" + ] + }, + "primary_page": "chain-interactions/store-data/bulletin-chain.md", + "project_structure": "bulletin-store-example/\n├── .papi/\n│ └── (generated Bulletin Chain descriptors)\n├── store-data.ts\n├── .env\n├── .gitignore\n├── node_modules/\n└── package.json", + "reference_code": { + "base_path": ".snippets/code/chain-interactions/store-data/bulletin-chain", + "branch": "master", + "files": [ + { + "description": "PAPI TypeScript script that connects to Bulletin Chain, reads a local file, and submits it via TransactionStorage.store. Uses DEV_PHRASE by default — replace with process.env.MNEMONIC as instructed in step 6.", + "path": "store-data.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" + }, + "source_pages": [ + "chain-interactions/store-data/bulletin-chain.md" + ], + "steps": [ + { + "action": "Authorize account via Bulletin Chain Console UI", + "description": "This step requires manual browser interaction — delegate to the user.\n\n1. Open https://paritytech.github.io/polkadot-bulletin-chain/ and click Connect to link a wallet (Polkadot.js, Talisman, SubWallet, or Fearless).\n2. Go to Faucet > Storage Faucet tab.\n3. Under Authorize Account, enter the number of Transactions and Bytes needed, click Authorize Account, and approve in the wallet extension.\n4. Verify authorization on the Accounts tab — note the expiration block number.\n\nNote: Authorization expires at a block. Once expired, data cannot be renewed without re-authorizing.", + "order": 1, + "working_directory": "." + }, + { + "action": "Create and initialize the project", + "commands": [ + "mkdir bulletin-store-example", + "cd bulletin-store-example", + "npm init -y", + "npm pkg set type=module" + ], + "description": "Create a new Node.js ESM project. The ESM module type is required for polkadot-api and hdkd packages.", + "order": 2, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install polkadot-api @polkadot-labs/hdkd @polkadot-labs/hdkd-helpers multiformats" + ], + "description": "Install polkadot-api (typed chain client), hdkd and hdkd-helpers (key derivation), and multiformats (CID decoding).", + "order": 3, + "working_directory": "bulletin-store-example" + }, + { + "action": "Add Bulletin Chain metadata", + "commands": [ + "npx papi add bulletin -w wss://paseo-bulletin-rpc.polkadot.io" + ], + "description": "Connect to the Bulletin Chain RPC, download chain metadata, and generate typed descriptors in .papi/. These descriptors provide compile-time type safety for all pallet interactions.", + "expected_output": "Descriptors generated successfully", + "order": 4, + "working_directory": "bulletin-store-example" + }, + { + "action": "Create .env with mnemonic placeholder", + "commands": [ + "printf 'MNEMONIC=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty MNEMONIC placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in MNEMONIC with the 12-word mnemonic of the authorized account. Do NOT ask for the mnemonic in chat. Wait for user confirmation before proceeding.", + "order": 5, + "working_directory": "bulletin-store-example" + }, + { + "action": "Create store-data.ts", + "description": "Fetch the reference file and save as store-data.ts. The script uses a dev phrase by default. Modify to use your authorized account's mnemonic: replace the DEV_PHRASE usage with your actual mnemonic. Replace 'INSERT_IMAGE_PATH' with the path to the file you want to store (ask the user for this path before running). Save the file.", + "order": 6, + "reference_file": "store-data.ts", + "working_directory": "bulletin-store-example" + }, + { + "action": "Run the store script", + "commands": [ + "npx tsx store-data.ts" + ], + "description": "Run the script to submit the file to the Bulletin Chain. On success it prints the block hash, transaction index, CID, and an IPFS gateway URL. Save the CID and (block, index) pair — needed to retrieve or renew the stored data.", + "expected_output": "Image stored successfully! CID: bafk2bzace...", + "order": 7, + "working_directory": "bulletin-store-example" + } + ], + "supplementary_context": { + "description": "Load when the user asks about Bulletin Chain architecture, data retention, retrieval methods (P2P vs IPFS gateway), or the renewal workflow.", + "pages": [ + { + "relevance": "Technical reference for TransactionStorage pallet extrinsics, storage size limits, retention period, retrieval methods (IPFS gateway, P2P Helia), and renewal mechanics.", + "slug": "reference-polkadot-hub-data-storage", + "title": "Data Storage", + "url": "https://docs.polkadot.com/reference/polkadot-hub/data-storage.md" + } + ] + }, + "title": "Store and Retrieve Data on the Bulletin Chain", + "version": "1.0.1", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the polkavm-hardhat-examples repo, compiles Uniswap V2 Factory and Pair contracts to the Polkadot Virtual Machine (PVM) using the Hardhat Polkadot plugin and resolc compiler, and deploys to Polkadot Hub TestNet. Use when deploying a DEX AMM factory on Polkadot Hub with PVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 PVM', 'UniswapV2Factory PVM Polkadot', 'deploy AMM Polkadot Hub PVM', 'polkavm-hardhat-examples uniswap'.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the account deploying to Polkadot Hub TestNet (passetHub network). Must be funded with testnet PAS. Do NOT ask user to paste key in chat — they must edit .env directly.", + "name": "AH_PRIV_KEY", + "required": true + }, + { + "description": "0x-prefixed EVM private key for local testing. For local-only use, Alice's dev key (0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133) is the default in the config. Not required for TestNet deployment.", + "name": "LOCAL_PRIV_KEY", + "required": false + } + ], + "error_patterns": [ + { + "cause": "Account not funded with testnet PAS, or network base fee (1000 gwei on Polkadot Hub TestNet) exceeds the transaction gas price.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Get testnet tokens at https://faucet.polkadot.io/. If gas issues persist, add gasPrice: 5000000000000 (5000 gwei) to the passetHub network block in hardhat.config.js." + }, + { + "cause": ".env file not populated or not loaded by dotenv.", + "pattern": "Error: AH_PRIV_KEY is undefined / invalid private key", + "resolution": "Verify .env exists in uniswap-v2-polkadot/ and contains AH_PRIV_KEY=0x. The config uses require('dotenv').config() automatically." + }, + { + "cause": "Using wrong --network flag; documentation shows polkadotHubTestNet but the config defines passetHub.", + "pattern": "Error: network passetHub not found / unknown network", + "resolution": "Use --network passetHub instead of --network polkadotHubTestNet." + }, + { + "cause": "The resolc compiler is not installed or not accessible.", + "pattern": "Compilation error: resolc not found / PVM compiler unavailable", + "resolution": "Run npm install to reinstall @parity/hardhat-polkadot and its bundled resolc compiler. Verify package.json includes the plugin." + } + ], + "examples": [ + { + "actions": [ + "Clone polkavm-hardhat-examples and cd uniswap-v2-polkadot", + "Run npm install", + "Create .env with AH_PRIV_KEY for testnet deployment", + "Run npx hardhat compile to compile to PVM bytecode", + "Run npx hardhat run scripts/deploy.js --network passetHub", + "Save deployed Factory and Pair addresses" + ], + "result": "UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair deployed to Polkadot Hub TestNet with PVM bytecode", + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using PVM", + "user_says": "Deploy Uniswap V2 on Polkadot Hub using PVM" + }, + { + "actions": [ + "Confirm account has testnet PAS at https://faucet.polkadot.io/", + "If gas price issue, add gasPrice: 5000000000000 to passetHub config in hardhat.config.js", + "Retry deployment with npx hardhat run scripts/deploy.js --network passetHub" + ], + "result": "Deployment succeeds after funding the account and optionally raising gas price", + "scenario": "Edge case: deployment fails with insufficient funds", + "user_says": "My Uniswap V2 deployment is failing with insufficient funds" + } + ], + "id": "deploy-uniswap-v2-core-pvm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://testnet-passet-hub-eth-rpc.polkadot.io (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v16 or later", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account (AH_PRIV_KEY)" + ] + }, + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md", + "project_structure": "polkavm-hardhat-examples/\n└── uniswap-v2-polkadot/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── scripts/\n │ └── deploy.js\n ├── test/\n ├── .env\n ├── .gitignore\n ├── hardhat.config.js\n └── package.json", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" + ], + "steps": [ + { + "action": "Clone the polkavm-hardhat-examples repository", + "commands": [ + "git clone https://github.com/polkadot-developers/polkavm-hardhat-examples.git", + "cd polkavm-hardhat-examples/uniswap-v2-polkadot" + ], + "description": "Clone the repository and navigate to the Uniswap V2 project. The hardhat.config.js already uses dotenv (require('dotenv').config()) and process.env variables — no Hardhat vars conversion needed.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install" + ], + "description": "Install all project dependencies including @parity/hardhat-polkadot and the resolc PVM compiler.", + "order": 2, + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot" + }, + { + "action": "Create .env with private key placeholders", + "commands": [ + "printf 'AH_PRIV_KEY=\\nLOCAL_PRIV_KEY=\\n' > .env" + ], + "description": "Create .env with empty placeholders. The .gitignore already includes .env. Stop here and ask the user to edit .env directly — fill in AH_PRIV_KEY with the 0x-prefixed private key of the account that will deploy to Polkadot Hub TestNet. Do NOT ask for the key in chat. For LOCAL_PRIV_KEY, Alice's dev key is the default in hardhat.config.js for local testing. Wait for confirmation before proceeding.", + "order": 3, + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot" + }, + { + "action": "Compile the contracts", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile the Uniswap V2 contracts to PVM bytecode using the Hardhat Polkadot plugin and the resolc compiler. Compiled artifacts (ABI and PVM bytecode) appear in the artifacts/ directory.", + "expected_output": "Compiled 1 Solidity file successfully", + "order": 4, + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot" + }, + { + "action": "Deploy to Polkadot Hub TestNet", + "commands": [ + "npx hardhat run scripts/deploy.js --network passetHub" + ], + "description": "Deploy UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair to Polkadot Hub TestNet via the passetHub network config. The script outputs the deployed addresses — save them, especially the Factory address, which is needed for creating liquidity pairs.\n\nNote: The documentation shows `--network polkadotHubTestNet` but the actual network name in hardhat.config.js is `passetHub`.", + "expected_output": "Factory deployed to : 0x...", + "order": 5, + "working_directory": "polkavm-hardhat-examples/uniswap-v2-polkadot" + } + ], + "supplementary_context": { + "description": "Load when the user asks about PVM vs EVM differences, local development setup, or testing the deployed contracts.", + "pages": [ + { + "relevance": "Set up a local Polkadot development node and ETH-RPC adapter to test the Uniswap V2 PVM contracts locally before deploying to TestNet.", + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md" + }, + { + "relevance": "Configure the @parity/hardhat-polkadot plugin and resolc compiler for PVM smart contract development.", + "slug": "smart-contracts-dev-environments-hardhat-polkadot", + "title": "Set Up Hardhat with the Polkadot Plugin", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat-polkadot.md" + } + ] + }, + "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Core contracts (Factory and Pair) with standard Hardhat and TypeScript, converts Hardhat vars to dotenv for agent-compatible key management, and deploys to Polkadot Hub TestNet via the EVM execution path. Use when deploying a DEX AMM factory on Polkadot Hub using standard EVM bytecode. Requires testnet PAS tokens. Trigger phrases: 'deploy Uniswap V2 EVM Polkadot', 'UniswapV2Factory EVM Polkadot Hub', 'deploy AMM Polkadot REVM'.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "name": "TESTNET_PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." + }, + { + "cause": "hardhat.config.ts still uses vars.get() instead of dotenv.", + "pattern": "Error: vars is not defined / Cannot read properties of undefined", + "resolution": "Ensure `import 'dotenv/config';` is the first line of hardhat.config.ts and replace vars.get('TESTNET_PRIVATE_KEY') with process.env.TESTNET_PRIVATE_KEY." + }, + { + "cause": ".env not populated or not in the correct directory.", + "pattern": "TESTNET_PRIVATE_KEY is undefined", + "resolution": "Verify .env exists in uniswap-v2-core-hardhat/ and contains TESTNET_PRIVATE_KEY=0x." + }, + { + "cause": "Ignition lost track of deployment — missing gasPrice or requiredConfirmations: 0.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1 in config, then redeploy." + } + ], + "examples": [ + { + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", + "Run npx hardhat compile", + "Run npx hardhat run scripts/deploy.ts --network polkadotTestnet", + "Save Factory, TokenA, TokenB, and Pair addresses" + ], + "result": "UniswapV2Factory, two test ERC-20 tokens, and a trading pair deployed to Polkadot Hub TestNet via EVM", + "scenario": "Common scenario: deploy Uniswap V2 Core to Polkadot Hub TestNet using EVM", + "user_says": "Deploy Uniswap V2 on Polkadot Hub using EVM Hardhat" + }, + { + "actions": [ + "Verify gasPrice: 5000000000000 is set in polkadotTestnet network config", + "Delete ignition/deployments/ directory", + "Confirm TESTNET_PRIVATE_KEY account still has PAS balance", + "Redeploy with npx hardhat run scripts/deploy.ts --network polkadotTestnet" + ], + "result": "Clean deployment after resolving gas configuration and clearing stale Ignition state", + "scenario": "Edge case: deployment stuck with IGN401 Transaction dropped", + "user_says": "Ignition says my V2 Core deployment transaction was dropped" + } + ], + "id": "deploy-uniswap-v2-core-evm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md", + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2ERC20.sol\n │ ├── UniswapV2Factory.sol\n │ └── UniswapV2Pair.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" + ], + "steps": [ + { + "action": "Clone the revm-hardhat-examples repository", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout b0a8627059a9d9cb759682310219557550186bc4", + "cd uniswap-v2-core-hardhat" + ], + "description": "Clone the repository, check out the pinned commit (tested configuration), and navigate to the Uniswap V2 Core project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies, then additionally install dotenv. dotenv is required to replace Hardhat's interactive vars system with a .env-based private key approach usable in agent shells.", + "order": 2, + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat" + }, + { + "action": "Create .env with private key placeholder", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key for the testnet deployer account. Do NOT ask for the key in chat. Wait for confirmation before proceeding.", + "order": 3, + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat" + }, + { + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee) to prevent 'Priority is too low' errors.\n5. Confirm `ignition.requiredConfirmations` is 1 (not 0) — zero-confirmation only works on local dev nodes with instant finality.\nSave the file.", + "order": 4, + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat" + }, + { + "action": "Compile the contracts", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile UniswapV2ERC20, UniswapV2Factory, and UniswapV2Pair (Solidity 0.5.16). Artifacts (ABI and EVM bytecode) appear in the artifacts/ directory.", + "expected_output": "Compiled 3 Solidity files successfully", + "order": 5, + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat" + }, + { + "action": "Deploy to Polkadot Hub TestNet", + "commands": [ + "npx hardhat run scripts/deploy.ts --network polkadotTestnet" + ], + "description": "Deploy UniswapV2Factory, two test ERC-20 tokens, and create a trading pair. The script outputs all deployed addresses. Save the Factory address — needed for creating additional pairs and for the Periphery (Router) deployment.", + "expected_output": "UniswapV2Factory deployed to: 0x...", + "order": 6, + "working_directory": "revm-hardhat-examples/uniswap-v2-core-hardhat" + } + ], + "supplementary_context": { + "description": "Load when the user asks about the V2 Periphery Router deployment, local testing, or Hardhat configuration for Polkadot.", + "pages": [ + { + "relevance": "Next step: deploy the Uniswap V2 Router (WETH9, Router02) on top of the deployed V2 Core.", + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-periphery-v2", + "title": "Uniswap V2 Periphery with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" + }, + { + "relevance": "Set up a local Polkadot development node to run the test suite against a local node before TestNet deployment.", + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md" + } + ] + }, + "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V2 Periphery contracts (WETH9, Router02) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when adding the Router layer (liquidity, swaps, slippage protection) on top of Uniswap V2 Core. V2 Core Solidity dependency is a local file reference — no pre-deployed core required. Trigger phrases: 'deploy Uniswap V2 Router EVM Polkadot', 'UniswapV2Router02 Polkadot Hub', 'Uniswap periphery Polkadot REVM'.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "name": "TESTNET_PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Fund the account at https://faucet.polkadot.io/. Ensure gasPrice: 5000000000000 is set in the polkadotTestnet block of hardhat.config.ts." + }, + { + "cause": "The local V2 Core Solidity dependency (../../uniswap-v2-core-hardhat) is not accessible. Likely cloning only the periphery subdirectory or checking out different commits for each.", + "pattern": "Error: Cannot find module '@uniswap/v2-core' / ENOENT uniswap-v2-core-hardhat", + "resolution": "Ensure the full revm-hardhat-examples repo is cloned and both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ are at the same commit. Run npm install from uniswap-v2-periphery-hardhat/." + }, + { + "cause": "hardhat.config.ts still uses Hardhat vars.get() instead of dotenv.", + "pattern": "vars is not defined / Cannot read properties of undefined", + "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." + }, + { + "cause": "Ignition lost transaction state — missing gasPrice or requiredConfirmations: 0.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and requiredConfirmations: 1, then redeploy." + } + ], + "examples": [ + { + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v2-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy with --network polkadotTestnet, confirm when prompted", + "Save WETH9, Factory, and Router02 addresses" + ], + "result": "WETH9, UniswapV2Factory, and UniswapV2Router02 deployed to Polkadot Hub TestNet", + "scenario": "Common scenario: deploy Uniswap V2 Periphery Router to Polkadot Hub TestNet", + "user_says": "Deploy the Uniswap V2 Router on Polkadot Hub EVM" + }, + { + "actions": [ + "Verify the full repo is cloned (both uniswap-v2-core-hardhat/ and uniswap-v2-periphery-hardhat/ must exist)", + "Ensure git checkout used the same commit for the whole repo (a871364...)", + "Re-run npm install from uniswap-v2-periphery-hardhat/ — the local file reference resolves to ../uniswap-v2-core-hardhat/" + ], + "result": "npm install succeeds and @uniswap/v2-core is resolved from the sibling directory", + "scenario": "Edge case: npm install fails with missing V2 Core module", + "user_says": "npm install is failing because it cannot find @uniswap/v2-core" + } + ], + "id": "deploy-uniswap-v2-periphery-evm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md", + "project_structure": "revm-hardhat-examples/\n└── uniswap-v2-periphery-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── UniswapV2Router01.sol\n │ └── UniswapV2Router02.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV2Router02.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" + ], + "steps": [ + { + "action": "Clone the revm-hardhat-examples repository", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout a871364c8f4da052855b5c8ee4ed6b89fd182cb1", + "cd uniswap-v2-periphery-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V2 Periphery project. The sibling directory uniswap-v2-core-hardhat/ (at the same commit) is automatically used for the local V2 Core Solidity dependency.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies including the local V2 Core reference (resolved automatically from ../uniswap-v2-core-hardhat/). Then install dotenv to replace Hardhat's interactive vars system.", + "order": 2, + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat" + }, + { + "action": "Create .env with private key placeholder", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude it from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding.", + "order": 3, + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat" + }, + { + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet 1000 gwei base fee).\n5. Confirm `ignition.requiredConfirmations` is 1.\nSave the file.", + "order": 4, + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat" + }, + { + "action": "Compile the contracts", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile the Uniswap V2 Periphery contracts using a multi-compiler setup (Solidity 0.5.16 for V2 Core dependency and 0.6.6 for Router contracts). Artifacts appear in the artifacts/ directory.", + "expected_output": "Compiled 5 Solidity files successfully", + "order": 5, + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat" + }, + { + "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV2Router02.ts --network polkadotTestnet" + ], + "description": "Deploy WETH9, UniswapV2Factory, and UniswapV2Router02 using Hardhat Ignition. Ignition will prompt to confirm the target network name and chain ID — delegate this confirmation to the user. After confirmation, contracts are deployed in two batches (Factory+WETH9 in parallel, then Router02). Save all three deployed addresses.", + "expected_output": "UniswapV2Router02Module#UniswapV2Router02 deployed at 0x...", + "interactive": true, + "order": 6, + "working_directory": "revm-hardhat-examples/uniswap-v2-periphery-hardhat" + } + ], + "supplementary_context": { + "description": "Load when the user asks about the V2 Core prerequisite, testing the Router, or V3 upgrades.", + "pages": [ + { + "relevance": "V2 Core tutorial covering Factory and Pair deployment — the Periphery Router builds on top of the same Core contracts included as a local dependency.", + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v2-core-v2", + "title": "Uniswap V2 Core with EVM on Polkadot", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" + }, + { + "relevance": "Set up a local development node to run the test suite against localNode before TestNet deployment.", + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md" + } + ] + }, + "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Core contracts (UniswapV3Factory) with Hardhat, converts Hardhat vars to dotenv, and deploys to Polkadot Hub TestNet via Hardhat Ignition. V3 config requires bytecodeHash set to none to keep Factory under the 24KB EIP-170 contract size limit. Use when deploying a concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 EVM Polkadot', 'UniswapV3Factory Polkadot Hub', 'concentrated liquidity Polkadot REVM'.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "name": "TESTNET_PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "bytecodeHash is not set to 'none', causing UniswapV3Factory to exceed the EIP-170 24KB limit.", + "pattern": "Error: contract code too large / Contract creation code storage out of gas", + "resolution": "In hardhat.config.ts under the Solidity compiler settings, ensure metadata: { bytecodeHash: 'none' } is present. Recompile after the fix." + }, + { + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." + }, + { + "cause": "hardhat.config.ts still uses Hardhat vars.get().", + "pattern": "vars is not defined / Cannot read properties of undefined", + "resolution": "Add `import 'dotenv/config';` as first line and replace vars.get with process.env.TESTNET_PRIVATE_KEY." + }, + { + "cause": "Ignition lost transaction state.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "resolution": "Delete ignition/deployments/, verify gas config (gasPrice: 5000000000000, requiredConfirmations: 1), and redeploy." + } + ], + "examples": [ + { + "actions": [ + "Clone revm-hardhat-examples, checkout pinned commit, cd uniswap-v3-core-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY", + "Convert hardhat.config.ts: replace vars.get with process.env, add gasPrice, preserve bytecodeHash: 'none'", + "Run npx hardhat compile", + "Run npx hardhat ignition deploy UniswapV3Factory.ts --network polkadotTestnet, confirm when prompted", + "Save deployed UniswapV3Factory address" + ], + "result": "UniswapV3Factory deployed to Polkadot Hub TestNet — entry point for creating V3 concentrated-liquidity pools", + "scenario": "Common scenario: deploy Uniswap V3 Core to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V3 on Polkadot Hub EVM" + }, + { + "actions": [ + "Check hardhat.config.ts Solidity compiler settings for the existence of metadata: { bytecodeHash: 'none' }", + "If missing, add it under settings: { optimizer: {...}, metadata: { bytecodeHash: 'none' } }", + "Recompile with npx hardhat compile" + ], + "result": "Compilation succeeds after setting bytecodeHash: 'none' to exclude metadata hash and keep contract under the 24KB EIP-170 limit", + "scenario": "Edge case: compilation fails with contract too large", + "user_says": "Hardhat compilation fails saying UniswapV3Factory is too large" + } + ], + "id": "deploy-uniswap-v3-core-evm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md", + "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-core-hardhat/\n ├── contracts/\n │ ├── interfaces/\n │ ├── libraries/\n │ ├── test/\n │ ├── NoDelegateCall.sol\n │ ├── UniswapV3Factory.sol\n │ ├── UniswapV3Pool.sol\n │ └── UniswapV3PoolDeployer.sol\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Factory.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n └── package.json", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + ], + "steps": [ + { + "action": "Clone the revm-hardhat-examples repository", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 3ff28ae44c4ab041a96953f49d0e2dae0408f28f", + "cd uniswap-v3-core-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Core project.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies then add dotenv to replace Hardhat's interactive vars system with .env-based private key management.", + "order": 2, + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat" + }, + { + "action": "Create .env with private key placeholder", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding.", + "order": 3, + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat" + }, + { + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from 'hardhat/config';` to `import { HardhatUserConfig } from 'hardhat/config';`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has('TESTNET_PRIVATE_KEY') ? [vars.get('TESTNET_PRIVATE_KEY')] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei).\n5. Confirm `ignition.requiredConfirmations` is 1.\n6. Preserve the existing `bytecodeHash: 'none'` in the Solidity compiler settings — this is required to keep UniswapV3Factory under the EIP-170 24KB contract size limit.\nSave the file.", + "order": 4, + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat" + }, + { + "action": "Compile the contracts", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile UniswapV3Factory, UniswapV3Pool, UniswapV3PoolDeployer, NoDelegateCall, and math libraries (Solidity 0.7.6). The bytecodeHash: 'none' setting in the Solidity config is critical — without it, UniswapV3Factory exceeds the EIP-170 24KB contract size limit.", + "expected_output": "Compiled 5 Solidity files successfully", + "order": 5, + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat" + }, + { + "action": "Deploy to Polkadot Hub TestNet via Hardhat Ignition", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Factory.ts --network polkadotTestnet" + ], + "description": "Deploy UniswapV3Factory using Hardhat Ignition. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save the deployed Factory address — it is the entry point for creating V3 pools and is needed for the Periphery (SwapRouter, NonfungiblePositionManager) deployment.", + "expected_output": "UniswapV3FactoryModule#UniswapV3Factory deployed at 0x...", + "interactive": true, + "order": 6, + "working_directory": "revm-hardhat-examples/uniswap-v3-core-hardhat" + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 Periphery (SwapRouter, NFPM) deployment, local testing, or concentrated liquidity mechanics.", + "pages": [ + { + "relevance": "Next step: deploy SwapRouter and NonfungiblePositionManager on top of the deployed V3 Factory.", + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-periphery-v3", + "title": "Uniswap V3 Periphery with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + }, + { + "relevance": "Set up a local development node to run the 187-test V3 suite before TestNet deployment.", + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md" + } + ] + }, + "title": "Deploy Uniswap V3 Core on Polkadot Hub (EVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" + }, + { + "chain_role": "isolated", + "description": "Clones the revm-hardhat-examples repo, compiles unmodified Uniswap V3 Periphery contracts (SwapRouter, NonfungiblePositionManager) with Hardhat, converts Hardhat vars to dotenv, and deploys all four contracts (UniswapV3Factory, WETH9, SwapRouter, NonfungiblePositionManager) to Polkadot Hub TestNet in a single Hardhat Ignition run. The V3 Core contracts are resolved automatically from a local sibling package reference. Use when building a full-stack concentrated-liquidity DEX on Polkadot Hub. Trigger phrases: 'deploy Uniswap V3 Periphery Polkadot', 'SwapRouter NonfungiblePositionManager Polkadot Hub', 'Uniswap V3 full deployment EVM Polkadot'.", + "env_vars": [ + { + "description": "0x-prefixed EVM private key for the account deploying contracts to Polkadot Hub TestNet. Must be funded with testnet PAS. Do NOT ask user to paste key in chat — edit .env directly.", + "name": "TESTNET_PRIVATE_KEY", + "required": true + } + ], + "error_patterns": [ + { + "cause": "Account underfunded or gasPrice below the 1000 gwei TestNet base fee.", + "pattern": "Error: insufficient funds / Priority is too low", + "resolution": "Fund the account at https://faucet.polkadot.io/ and ensure gasPrice: 5000000000000 is set in the polkadotTestnet config." + }, + { + "cause": "hardhat.config.ts still uses Hardhat vars.get() or vars.has().", + "pattern": "vars is not defined / Cannot read properties of undefined (reading 'has')", + "resolution": "Add `import 'dotenv/config';` as the first line and replace `vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`." + }, + { + "cause": "Ignition lost track of a pending transaction due to TestNet latency or gas underpricing.", + "pattern": "IGN401: Transaction dropped / Transaction Already Imported", + "resolution": "Delete `ignition/deployments/`, verify gasPrice is 5000000000000 and requiredConfirmations is 1, then redeploy. If the deployment receipt exists in deployed_addresses.json but code is missing at the address, wait and verify via eth_getCode before retrying." + }, + { + "cause": "bytecodeHash is not set to 'none', causing the compiled Pool bytecode to differ from the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol.", + "pattern": "POOL_INIT_CODE_HASH mismatch / pool address computation incorrect", + "resolution": "In hardhat.config.ts under Solidity compiler settings, ensure `metadata: { bytecodeHash: 'none' }` is present. Recompile after the fix." + }, + { + "cause": "The hardhat network is missing allowUnlimitedContractSize: true, causing Periphery contracts that exceed 24KB to fail during local testing.", + "pattern": "Error: cannot estimate gas / contract deployment failed during testing", + "resolution": "In hardhat.config.ts, ensure `networks.hardhat.allowUnlimitedContractSize = true` is set. This flag only applies to the in-process Hardhat network used for testing; it is not needed for TestNet deployment." + } + ], + "examples": [ + { + "actions": [ + "Clone revm-hardhat-examples, check out pinned commit, cd uniswap-v3-periphery-hardhat", + "Run npm install && npm install dotenv", + "Create .env with TESTNET_PRIVATE_KEY placeholder, ask user to fill it in", + "Convert hardhat.config.ts: add dotenv import, replace vars.get with process.env, add gasPrice: 5000000000000", + "Run npx hardhat compile (expects 39 Solidity files compiled successfully)", + "Run npx hardhat ignition deploy UniswapV3Periphery.ts --network polkadotTestnet, confirm when prompted", + "Save deployed addresses for UniswapV3Factory, WETH9, SwapRouter, and NonfungiblePositionManager" + ], + "result": "All four Uniswap V3 Periphery contracts deployed to Polkadot Hub TestNet — SwapRouter for token swaps and NonfungiblePositionManager for concentrated liquidity LP positions", + "scenario": "Common scenario: deploy all Uniswap V3 Periphery contracts to Polkadot Hub TestNet", + "user_says": "Deploy Uniswap V3 SwapRouter and NonfungiblePositionManager to Polkadot Hub" + }, + { + "actions": [ + "Do not retry at the same gas price — that will fail again with the same error", + "Check ignition/deployments/ and deployed_addresses.json for any partial state", + "Verify the contract is not already deployed: run eth_getCode at any addresses in deployed_addresses.json", + "If not deployed, delete the ignition/deployments/ directory", + "Confirm gasPrice: 5000000000000 and requiredConfirmations: 1 are set in hardhat.config.ts", + "Re-run: npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" + ], + "result": "Deployment completes cleanly after removing stale Ignition state and confirming gas configuration", + "scenario": "Edge case: deployment fails with IGN401 or Transaction Already Imported", + "user_says": "Ignition reports the transaction was dropped and retrying gives 'Transaction Already Imported'" + } + ], + "id": "deploy-uniswap-v3-periphery-evm", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (chain ID 420420417, base fee 1000 gwei)" + ], + "runtime": [ + "Node.js v22 or later", + "npm", + "Git" + ], + "tokens": [ + "Testnet PAS tokens for gas fees — get from https://faucet.polkadot.io/" + ], + "wallet": [ + "An EVM private key (0x-prefixed) for a funded testnet account" + ] + }, + "primary_page": "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md", + "project_structure": "revm-hardhat-examples/\n└── uniswap-v3-periphery-hardhat/\n ├── contracts/\n │ ├── SwapRouter.sol\n │ ├── NonfungiblePositionManager.sol\n │ ├── NonfungibleTokenPositionDescriptor.sol\n │ ├── base/\n │ ├── interfaces/\n │ ├── lens/\n │ ├── libraries/\n │ └── test/\n ├── ignition/\n │ └── modules/\n │ └── UniswapV3Periphery.ts\n ├── scripts/\n │ └── deploy.ts\n ├── test/\n ├── artifacts/\n ├── hardhat.config.ts\n ├── .env\n ├── package.json\n └── tsconfig.json", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "steps": [ + { + "action": "Clone the revm-hardhat-examples repository", + "commands": [ + "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git", + "cd revm-hardhat-examples", + "git checkout 96696ad15c3cf01b9168a71ad5114f27c34a8726", + "cd uniswap-v3-periphery-hardhat" + ], + "description": "Clone the repository, check out the pinned commit, and navigate to the Uniswap V3 Periphery project. The monorepo layout is critical: the Periphery project depends on V3 Core contracts through a local file reference (`\"@uniswap/v3-core\": \"file:../uniswap-v3-core-hardhat\"`), so both sibling directories must be present.", + "order": 1, + "working_directory": "." + }, + { + "action": "Install dependencies", + "commands": [ + "npm install", + "npm install dotenv" + ], + "description": "Install project dependencies. `npm install` resolves the local `@uniswap/v3-core` sibling package automatically — no separate step is needed. Then add `dotenv` to replace Hardhat's interactive vars system with .env-based private key management.", + "order": 2, + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat" + }, + { + "action": "Create .env with private key placeholder", + "commands": [ + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", + "echo '.env' >> .gitignore" + ], + "description": "Create .env with an empty TESTNET_PRIVATE_KEY placeholder and exclude from git. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with the 0x-prefixed EVM private key. Do NOT ask for the key in chat. Wait for confirmation before proceeding.", + "order": 3, + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat" + }, + { + "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", + "description": "Modify hardhat.config.ts with all of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. Change `import { HardhatUserConfig, vars } from \"hardhat/config\";` to `import { HardhatUserConfig } from \"hardhat/config\";`.\n3. In the polkadotTestnet network block, replace `accounts: vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []` with `accounts: process.env.TESTNET_PRIVATE_KEY ? [process.env.TESTNET_PRIVATE_KEY] : []`.\n4. In the polkadotTestnet network block, add `gasPrice: 5000000000000,` (5000 gwei — 5x the TestNet base fee).\n5. Confirm `ignition.requiredConfirmations` is 1 (already set).\n6. Preserve `bytecodeHash: \"none\"` in the Solidity compiler settings — required so the compiled UniswapV3Pool bytecode hash matches the hardcoded POOL_INIT_CODE_HASH in PoolAddress.sol, enabling correct CREATE2 pool address derivation.\n7. Preserve `allowUnlimitedContractSize: true` for the hardhat network — several Periphery contracts exceed the 24KB EIP-170 limit and require this for local testing.\nSave the file.", + "order": 4, + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat" + }, + { + "action": "Compile the contracts", + "commands": [ + "npx hardhat compile" + ], + "description": "Compile SwapRouter, NonfungiblePositionManager, and all supporting periphery contracts (Solidity 0.7.6). The `bytecodeHash: 'none'` setting ensures the Pool bytecode hash matches PoolAddress.sol's hardcoded constant, which is required for CREATE2 pool address computation to work correctly during swaps and LP operations.", + "expected_output": "Compiled 39 Solidity files successfully", + "order": 5, + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat" + }, + { + "action": "Deploy all contracts to Polkadot Hub TestNet via Hardhat Ignition", + "commands": [ + "npx hardhat ignition deploy ./ignition/modules/UniswapV3Periphery.ts --network polkadotTestnet" + ], + "description": "Deploy all four contracts using Hardhat Ignition. The module deploys UniswapV3Factory and WETH9 in the first batch (in parallel), then SwapRouter and NonfungiblePositionManager once their dependencies are ready. Ignition prompts to confirm the network name and chain ID — delegate this confirmation to the user. Save all four deployed addresses: they are needed for any downstream swap or LP interaction.", + "expected_output": "UniswapV3PeripheryModule#UniswapV3Factory deployed at 0x...\nUniswapV3PeripheryModule#WETH9 deployed at 0x...\nUniswapV3PeripheryModule#SwapRouter deployed at 0x...\nUniswapV3PeripheryModule#NonfungiblePositionManager deployed at 0x...", + "interactive": true, + "order": 6, + "working_directory": "revm-hardhat-examples/uniswap-v3-periphery-hardhat" + } + ], + "supplementary_context": { + "description": "Load when the user asks about V3 Core as a prerequisite, testing against a local development node, or the Hardhat environment setup.", + "pages": [ + { + "relevance": "The V3 Core contracts (UniswapV3Factory, UniswapV3Pool) are resolved automatically via local npm reference — no separate Core tutorial step required, but this page explains the Factory/Pool architecture the Periphery builds on.", + "slug": "smart-contracts-cookbook-eth-dapps-uniswap-v3-core-v3", + "title": "Uniswap V3 Core with EVM on Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" + }, + { + "relevance": "Required for running the 39-test suite (SwapRouter and NonfungiblePositionManager tests) against a local Polkadot node via `npx hardhat test --network localNode` before TestNet deployment.", + "slug": "smart-contracts-dev-environments-local-dev-node", + "title": "Local Development Node", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node.md" + }, + { + "relevance": "Reference for Hardhat network configuration, Ignition deployment options, and gas settings specific to Polkadot Hub.", + "slug": "smart-contracts-dev-environments-hardhat", + "title": "Use Hardhat with Polkadot Hub", + "url": "https://docs.polkadot.com/smart-contracts/dev-environments/hardhat.md" + } + ] + }, + "title": "Deploy Uniswap V3 Periphery on Polkadot Hub (EVM)", + "version": "1.0.0", + "workflow_pattern": "sequential" } + ] } diff --git a/skill_candidates.json b/skill_candidates.json index 413d440e4..ea8abc98f 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -1,20 +1,18 @@ { - "schema_version": "2", - "generated": "2026-05-15T16:00:00Z", - "project_id": "polkadot-docs", - "scoring_rubric_version": "1.0", "candidates": [ { - "skill_id": "create-polkadot-account", - "title": "Create a Polkadot Account Programmatically", - "source_pages": [ - "chain-interactions/accounts/create-account.md" - ], + "built_at": "2026-04-21T17:00:00Z", "category": "tutorial", - "priority": "high", - "status": "built", "notes": "Complete, multi-language tutorial (TypeScript, Python, Rust) covering account key generation. Fully self-contained with cookbook-tested code. All three language variants could become one skill or three; one skill covering the PAPI/TypeScript path is recommended.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -27,28 +25,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-04-21T17:00:00Z" - }, - { - "skill_id": "query-account-info-sdks", - "title": "Query Account Information with SDKs", + "skill_id": "create-polkadot-account", "source_pages": [ - "chain-interactions/accounts/query-accounts.md" + "chain-interactions/accounts/create-account.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Create a Polkadot Account Programmatically" + }, + { + "built_at": "2026-04-21T17:00:00Z", + "category": "tutorial", "notes": "Multi-SDK tutorial covering PAPI, Polkadot.js, Dedot, Python Substrate Interface, and Subxt. Each SDK variant is self-contained. Cookbook-backed with CI badges. Recommend one skill targeting the PAPI variant as the primary path.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -61,29 +59,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-04-21T17:00:00Z" - }, - { - "skill_id": "query-chain-data-sidecar-rest", - "title": "Query On-Chain State with Sidecar REST API", + "skill_id": "query-account-info-sdks", "source_pages": [ - "chain-interactions/query-data/query-rest.md" + "chain-interactions/accounts/query-accounts.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Query Account Information with SDKs" + }, + { "built_at": "2026-04-21T18:30:00Z", + "category": "tutorial", "notes": "Covers querying account balances, asset metadata, and block info via curl against the Sidecar REST API. P3 absent (no files to create, only curl commands). Reference tables for response fields contribute to reference score but procedural score still meets tutorial threshold. Has cookbook CI badge (K4).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 8 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -96,27 +93,28 @@ "K4", "S1", "S2" - ], - "category_scores": { - "procedural": 15, - "reference": 8, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 - } - }, - { - "skill_id": "query-chain-state-sdks", - "title": "Query On-Chain State with SDKs", + ] + }, + "skill_id": "query-chain-data-sidecar-rest", "source_pages": [ - "chain-interactions/query-data/query-sdks.md" + "chain-interactions/query-data/query-rest.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Query On-Chain State with Sidecar REST API" + }, + { + "built_at": "2026-04-21T17:00:00Z", + "category": "tutorial", "notes": "Multi-SDK tutorial (PAPI, Polkadot.js, Dedot, Python Substrate Interface, Subxt) for querying account balance and asset info. Cookbook-backed. Recommend primary skill targeting PAPI TypeScript path.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -129,28 +127,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-04-21T17:00:00Z" - }, - { - "skill_id": "call-runtime-apis-sdks", - "title": "Call Runtime APIs with SDKs", + "skill_id": "query-chain-state-sdks", "source_pages": [ - "chain-interactions/query-data/runtime-api-calls.md" + "chain-interactions/query-data/query-sdks.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Query On-Chain State with SDKs" + }, + { + "built_at": "2026-04-21T17:00:00Z", + "category": "tutorial", "notes": "Multi-SDK tutorial demonstrating AccountNonceApi and Metadata runtime API calls. Reference API table at the bottom is supplementary. Cookbook-backed with CI badge.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 5 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -164,29 +162,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 5, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-04-21T17:00:00Z" - }, - { - "skill_id": "calculate-transaction-fees-papi", - "title": "Calculate Transaction Fees Programmatically", + "skill_id": "call-runtime-apis-sdks", "source_pages": [ - "chain-interactions/send-transactions/calculate-transaction-fees.md" + "chain-interactions/query-data/runtime-api-calls.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Call Runtime APIs with SDKs" + }, + { "built_at": "2026-04-21T18:30:00Z", + "category": "tutorial", "notes": "Covers fee estimation using PAPI and Polkadot.js SDK. Includes a GUI section via Polkadot.js Apps (S2 notes this adds non-deterministic path) but the primary SDK workflow is fully programmatic. No cookbook badge (K4 absent).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -198,28 +195,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 - } - }, - { - "skill_id": "replay-dry-run-xcm-chopsticks", - "title": "Replay and Dry-Run XCMs Using Chopsticks", + ] + }, + "skill_id": "calculate-transaction-fees-papi", "source_pages": [ - "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" + "chain-interactions/send-transactions/calculate-transaction-fees.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Calculate Transaction Fees Programmatically" + }, + { "built_at": "2026-04-21T19:00:00Z", + "category": "tutorial", "notes": "Advanced tutorial covering XCM replay and dry-run via Chopsticks local fork. Requires building a Wasm binary and using Subscan to locate call data (partial manual step reduces S2). No cookbook badge. Specialized debugging skill for XCM developers.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 19, "signals": [ "P1", "P2", @@ -230,28 +227,28 @@ "K3", "S1", "S2" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 19 - } - }, - { - "skill_id": "estimate-xcm-fees-teleport", - "title": "Estimate XCM Fees for Asset Teleport", + ] + }, + "skill_id": "replay-dry-run-xcm-chopsticks", "source_pages": [ - "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" + "chain-interactions/send-transactions/interoperability/debug-and-preview-xcms.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Replay and Dry-Run XCMs Using Chopsticks" + }, + { "built_at": "2026-04-21T19:00:00Z", + "category": "tutorial", "notes": "Tutorial for estimating local execution, delivery, and remote execution fees for XCM teleports between Polkadot Hub TestNet and People Chain. Uses Chopsticks for forked chains. Code is presented in segments then assembled; full code collapsible available. No cookbook badge.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 19, "signals": [ "P1", "P2", @@ -262,27 +259,28 @@ "K3", "S1", "S2" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 19 - } - }, - { - "skill_id": "bridge-eth-to-polkadot-snowbridge", - "title": "Bridge Assets from Ethereum to Polkadot via Snowbridge", + ] + }, + "skill_id": "estimate-xcm-fees-teleport", "source_pages": [ - "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md" + "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md" ], + "status": "built", + "title": "Estimate XCM Fees for Asset Teleport" + }, + { + "blocked_reason": "Manual test (2026-05-05) showed the skill cannot run as written. The skill installs @paraspell/sdk (PAPI/viem variant) but Ethereum->Polkadot Snowbridge transfers are only implemented in @paraspell/sdk-pjs. Even sdk-pjs's EvmBuilder hardcodes origin to mainnet chains ('Ethereum' | 'Moonbeam' | 'Moonriver' | 'Darwinia'); no Sepolia/testnet origin exists. The inline bridge.ts calls APIs (.address, .buildEthTransfer with ethSigner) that don't exist in the current SDK. End-to-end testing requires real mainnet WETH + ETH for gas + rewriting the script against sdk-pjs. ParaSpell's Snowbridge surface is in-flux (per upstream docs, PJS-only until migration to PAPI/viem completes). Revisit when the SDK stabilizes.", "category": "tutorial", - "priority": "medium", - "status": "blocked", "notes": "Covers bridging ERC-20 tokens (WETH) from Ethereum mainnet to Polkadot Hub using ParaSpell SDK and Snowbridge. K1 absent — requires real mainnet WETH tokens which an agent cannot autonomously obtain. S2 absent — bridge relay takes ~30 minutes with variable external state. Priority reduced to medium.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 11, "signals": [ "P1", "P2", @@ -291,29 +289,28 @@ "K2", "K3", "S1" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 11 + ] }, - "blocked_reason": "Manual test (2026-05-05) showed the skill cannot run as written. The skill installs @paraspell/sdk (PAPI/viem variant) but Ethereum->Polkadot Snowbridge transfers are only implemented in @paraspell/sdk-pjs. Even sdk-pjs's EvmBuilder hardcodes origin to mainnet chains ('Ethereum' | 'Moonbeam' | 'Moonriver' | 'Darwinia'); no Sepolia/testnet origin exists. The inline bridge.ts calls APIs (.address, .buildEthTransfer with ethSigner) that don't exist in the current SDK. End-to-end testing requires real mainnet WETH + ETH for gas + rewriting the script against sdk-pjs. ParaSpell's Snowbridge surface is in-flux (per upstream docs, PJS-only until migration to PAPI/viem completes). Revisit when the SDK stabilizes." - }, - { - "skill_id": "transfer-assets-parachains-paraspell", - "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK", + "skill_id": "bridge-eth-to-polkadot-snowbridge", "source_pages": [ - "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" + "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md" ], - "category": "tutorial", - "priority": "high", - "status": "built", + "status": "blocked", + "title": "Bridge Assets from Ethereum to Polkadot via Snowbridge" + }, + { "built_at": "2026-04-21T18:30:00Z", + "category": "tutorial", "notes": "Covers full XCM transfer workflow using ParaSpell SDK: build transaction, dry-run, verify existential deposit, get fee estimates, and submit. Uses Paseo testnet with faucet tokens. Self-contained. No cookbook badge.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -325,28 +322,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 - } - }, - { - "skill_id": "pay-fees-alternative-token", - "title": "Pay Transaction Fees with an Alternative Token", + ] + }, + "skill_id": "transfer-assets-parachains-paraspell", "source_pages": [ - "chain-interactions/send-transactions/pay-fees-with-different-tokens.md" + "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK" + }, + { "built_at": "2026-04-21T18:30:00Z", + "category": "tutorial", "notes": "Demonstrates paying DOT transfer fees in USDT using PAPI, Polkadot.js, or Subxt against a Chopsticks local fork (Alice dev account — no real tokens needed). Self-contained. No cookbook badge.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -358,27 +355,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 - } - }, - { - "skill_id": "send-transactions-sdks", - "title": "Send Transactions with SDKs", + ] + }, + "skill_id": "pay-fees-alternative-token", "source_pages": [ - "chain-interactions/send-transactions/with-sdks.md" + "chain-interactions/send-transactions/pay-fees-with-different-tokens.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Pay Transaction Fees with an Alternative Token" + }, + { + "built_at": "2026-04-21T17:00:00Z", + "category": "tutorial", "notes": "Multi-SDK tutorial for constructing, signing, and submitting balance transfers (PAPI, Polkadot.js, Dedot, Python Substrate Interface, Subxt). Cookbook-backed. Fundamental SDK skill and likely prerequisite for other chain-interaction skills.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -391,28 +389,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-04-21T17:00:00Z" - }, - { - "skill_id": "convert-assets-asset-hub", - "title": "Convert Assets on Asset Hub via AMM", + "skill_id": "send-transactions-sdks", "source_pages": [ - "chain-interactions/token-operations/convert-assets.md" + "chain-interactions/send-transactions/with-sdks.md" ], + "status": "built", + "title": "Send Transactions with SDKs" + }, + { + "blocked_reason": "Depends on an AMM; replicating locally is tricky and Paseo coverage is limited or absent.", "category": "tutorial", - "priority": "medium", - "status": "blocked", "notes": "Covers creating liquidity pools, adding/removing liquidity, and swapping assets using the AssetConversion pallet via Polkadot.js Apps UI. P3 absent (no source files to create); workflow is GUI-dependent (S1 absent). K2 absent. Reaches tutorial threshold via P1+P2+P4=15 with P4 present. Priority medium due to GUI-only execution and absence of programmatic code.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 11, "signals": [ "P1", "P2", @@ -421,28 +419,28 @@ "K1", "K3", "S2" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 11 + ] }, - "blocked_reason": "Depends on an AMM; replicating locally is tricky and Paseo coverage is limited or absent." - }, - { - "skill_id": "register-foreign-asset-via-xcm-polkadot-hub", - "title": "Register a Foreign Asset on Polkadot Hub", + "skill_id": "convert-assets-asset-hub", "source_pages": [ - "chain-interactions/token-operations/register-foreign-asset.md" + "chain-interactions/token-operations/convert-assets.md" ], - "category": "tutorial", - "priority": "medium", "status": "blocked", + "title": "Convert Assets on Asset Hub via AMM" + }, + { + "blocked_reason": "Requires the skill to detect and provision a local asset hub plus parachain network; current generation can't orchestrate that prerequisite.", + "category": "tutorial", "notes": "Walks through registering a foreign asset via two-step XCM: generate encoded call data on Polkadot Hub, then dispatch via source parachain. Uses Polkadot.js Apps UI (GUI-dependent, S1 absent). P3 absent. Reaches tutorial threshold via P1+P2+P4=15. Priority medium due to GUI-only workflow.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 11, "signals": [ "P1", "P2", @@ -451,28 +449,28 @@ "K1", "K3", "S2" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 11 + ] }, - "blocked_reason": "Requires the skill to detect and provision a local asset hub plus parachain network; current generation can't orchestrate that prerequisite." - }, - { - "skill_id": "register-local-asset-polkadot-hub", - "title": "Register a Local Asset on Polkadot Hub", + "skill_id": "register-foreign-asset-via-xcm-polkadot-hub", "source_pages": [ - "chain-interactions/token-operations/register-local-asset.md" + "chain-interactions/token-operations/register-foreign-asset.md" ], - "category": "tutorial", - "priority": "medium", "status": "blocked", + "title": "Register a Foreign Asset on Polkadot Hub" + }, + { + "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) — prerequisite provisioning is outside current skill capability.", + "category": "tutorial", "notes": "Step-by-step guide for creating a local (integer-ID) asset on Polkadot Hub via the Network > Assets UI in Polkadot.js Apps. GUI-dependent throughout (S1 absent). P3 absent. Reaches tutorial threshold via P1+P2+P4=15. Priority medium due to GUI-only execution.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 11, "signals": [ "P1", "P2", @@ -481,29 +479,28 @@ "K1", "K3", "S2" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 11 + ] }, - "blocked_reason": "Requires the skill to set up a parachain with the asset pallet (or use Paseo) — prerequisite provisioning is outside current skill capability." - }, - { - "skill_id": "install-polkadot-sdk", - "title": "Install the Polkadot SDK", + "skill_id": "register-local-asset-polkadot-hub", "source_pages": [ - "parachains/install-polkadot-sdk.md" + "chain-interactions/token-operations/register-local-asset.md" ], - "category": "tutorial", - "priority": "high", - "status": "built", + "status": "blocked", + "title": "Register a Local Asset on Polkadot Hub" + }, + { "built_at": "2026-04-21T18:00:00Z", + "category": "tutorial", "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch — covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -515,28 +512,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 - } - }, - { - "skill_id": "set-up-parachain-template", - "title": "Set Up the Polkadot SDK Parachain Template", + ] + }, + "skill_id": "install-polkadot-sdk", "source_pages": [ - "parachains/launch-a-parachain/set-up-the-parachain-template.md" + "parachains/install-polkadot-sdk.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Install the Polkadot SDK" + }, + { "built_at": "2026-04-21T19:00:00Z", + "category": "tutorial", "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent — directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 — requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 20, "signals": [ "P1", "P2", @@ -548,27 +545,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 20 - } - }, - { - "skill_id": "run-collator-system-parachain", - "title": "Run a Block-Producing Collator for a System Parachain", + ] + }, + "skill_id": "set-up-parachain-template", "source_pages": [ - "node-infrastructure/run-a-collator.md" + "parachains/launch-a-parachain/set-up-the-parachain-template.md" ], + "status": "built", + "title": "Set Up the Polkadot SDK Parachain Template" + }, + { + "blocked_reason": "Impractical prereq: system parachains are operated by Parity/W3F, not external developers.", "category": "tutorial", - "priority": "high", - "status": "blocked", "notes": "Covers full collator setup: install binary (Docker or systemd), generate node key, obtain chain spec, run collator, generate session keys, and register on-chain. Registration requires governance approval for system parachains (S2 absent for that step). Core node setup is deterministic. Matches node/validator setup skill shape.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 19, "signals": [ "P1", "P2", @@ -579,29 +577,28 @@ "K3", "S1", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 19 + ] }, - "blocked_reason": "Impractical prereq: system parachains are operated by Parity/W3F, not external developers." - }, - { - "skill_id": "run-parachain-rpc-node", - "title": "Run a Parachain RPC Node", + "skill_id": "run-collator-system-parachain", "source_pages": [ - "node-infrastructure/run-a-node/parachain-rpc.md" + "node-infrastructure/run-a-collator.md" ], - "category": "tutorial", - "priority": "high", - "status": "built", + "status": "blocked", + "title": "Run a Block-Producing Collator for a System Parachain" + }, + { "built_at": "2026-04-21T18:30:00Z", + "category": "tutorial", "notes": "Comprehensive setup guide for any parachain RPC node (archive or pruned) via Docker or systemd. Uses People Chain as example; substitution table provided for other system parachains. Covers snapshot download, binary install, service config, and sync verification. Node/validator setup shape.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -613,28 +610,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 - } - }, - { - "skill_id": "run-polkadot-hub-rpc-node", - "title": "Run an RPC Node for Polkadot Hub", + ] + }, + "skill_id": "run-parachain-rpc-node", "source_pages": [ - "node-infrastructure/run-a-node/polkadot-hub-rpc.md" + "node-infrastructure/run-a-node/parachain-rpc.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Run a Parachain RPC Node" + }, + { "built_at": "2026-04-21T19:00:00Z", + "category": "tutorial", "notes": "Setup guide for Polkadot Hub RPC node (archive or pruned) via Docker or systemd, plus optional Ethereum RPC adapter (eth-rpc) for EVM compatibility. Includes snapshot download, service config, and sync verification. CLI flags reference table (R2) is supplementary. Node/validator setup shape.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 4 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -647,27 +644,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 4, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 - } - }, - { - "skill_id": "set-up-relay-chain-bootnode", - "title": "Set Up a Relay Chain Bootnode", + ] + }, + "skill_id": "run-polkadot-hub-rpc-node", "source_pages": [ - "node-infrastructure/run-a-node/relay-chain/bootnode.md" + "node-infrastructure/run-a-node/polkadot-hub-rpc.md" ], + "status": "built", + "title": "Run an RPC Node for Polkadot Hub" + }, + { + "blocked_reason": "Impractical prereq: only relevant when bootstrapping a new network.", "category": "guide", - "priority": "high", - "status": "blocked", "notes": "Guide for running a Polkadot relay chain bootnode with P2P, WS, and WSS endpoints. P1 absent (no numbered-step lists; content is command blocks under section headings). Procedural score 14 → guide category. Includes nginx proxy config file. Prerequisites require an external polkadot binary and nginx install, but core steps are self-contained.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 14, + "reference": 4 + }, + "priority_score": 22, "signals": [ "P2", "P3", @@ -679,28 +677,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 14, - "reference": 4, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 + ] }, - "blocked_reason": "Impractical prereq: only relevant when bootstrapping a new network." - }, - { - "skill_id": "set-up-relay-chain-full-node", - "title": "Set Up a Polkadot Relay Chain Full Node", + "skill_id": "set-up-relay-chain-bootnode", "source_pages": [ - "node-infrastructure/run-a-node/relay-chain/full-node.md" + "node-infrastructure/run-a-node/relay-chain/bootnode.md" ], - "category": "guide", - "priority": "medium", "status": "blocked", + "title": "Set Up a Relay Chain Bootnode" + }, + { + "blocked_reason": "Impractical prereq: hundreds of GB to sync; most developers use public RPC endpoints.", + "category": "guide", "notes": "Guide covering binary installation (pre-built, compiled, snap, Docker) and startup commands for pruned, custom-pruned, and archive nodes. P1 absent (section headings, not numbered lists); no source files to create (P3 absent). Procedural score 9 → Rule 6 guide. K1=0 — prerequisites reference parachains/install-polkadot-sdk for system dependencies not covered on this page. Companion pages: secure-wss.md (WSS layer), polkadot-hub-rpc.md (hub-specific config).", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 9, + "reference": 4 + }, + "priority_score": 17, "signals": [ "P2", "P4", @@ -710,28 +708,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 9, - "reference": 4, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 17 + ] }, - "blocked_reason": "Impractical prereq: hundreds of GB to sync; most developers use public RPC endpoints." - }, - { - "skill_id": "set-up-secure-wss-relay-node", - "title": "Set Up Secure WebSocket (WSS) for a Relay Chain Node", + "skill_id": "set-up-relay-chain-full-node", "source_pages": [ - "node-infrastructure/run-a-node/relay-chain/secure-wss.md" + "node-infrastructure/run-a-node/relay-chain/full-node.md" ], - "category": "tutorial", - "priority": "medium", "status": "blocked", + "title": "Set Up a Polkadot Relay Chain Full Node" + }, + { + "blocked_reason": "Impractical prereq: mostly TLS/nginx config; minimal Polkadot value.", + "category": "tutorial", "notes": "Tutorial for enabling WSS on a running Polkadot node via nginx or Apache2 reverse proxy with SSL (Let's Encrypt or self-signed). P1+P2+P3+P4 all present — numbered steps, apt commands, nginx/apache2 config files, and connection verification. K1=0 (requires an existing running node; companion to set-up-relay-chain-full-node). K3=0 (no explicit prerequisites section). No cookbook badge.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 14, "signals": [ "P1", "P2", @@ -741,56 +739,56 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 14 + ] }, - "blocked_reason": "Impractical prereq: mostly TLS/nginx config; minimal Polkadot value." - }, - { - "skill_id": "manage-validator-session-keys", - "title": "Manage Validator Session Keys and Node Key", + "skill_id": "set-up-secure-wss-relay-node", "source_pages": [ - "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md" + "node-infrastructure/run-a-node/relay-chain/secure-wss.md" ], - "category": "tutorial", - "priority": "low", "status": "blocked", + "title": "Set Up Secure WebSocket (WSS) for a Relay Chain Node" + }, + { + "blocked_reason": "Tied to a running production validator; key rotation is an operational task, not a one-shot skill flow.", + "category": "tutorial", "notes": "Covers generating session keys via author_rotateKeysWithOwner RPC, submitting set_keys on-chain (Polkadot Hub path and legacy relay chain path), and generating a static node key. P1+P2+P4 present; P3 absent (no source files). K1=0 — intro explicitly states prerequisite of set-up-validator page. GUI-dependent key submission steps (S1=0). Key output is non-deterministic (S2=0). Part of composite skill onboard-polkadot-validator.", + "priority": "low", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 7, "signals": [ "P1", "P2", "P4", "K2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 7 + ] }, - "blocked_reason": "Tied to a running production validator; key rotation is an operational task, not a one-shot skill flow." - }, - { - "skill_id": "set-up-polkadot-validator-node", - "title": "Set Up a Polkadot Validator Node", + "skill_id": "manage-validator-session-keys", "source_pages": [ - "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" + "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md" ], + "status": "blocked", + "title": "Manage Validator Session Keys and Node Key" + }, + { + "built_at": "2026-04-21T19:30:00Z", "category": "tutorial", - "priority": "high", - "status": "built", "notes": "Complete binary installation guide covering NTP setup, Landlock verification, and installing polkadot/polkadot-prepare-worker/polkadot-execute-worker via four methods (curl+GPG, APT, Docker, build from source). P1+P2+P4 present; P3 absent (no source files). K1=5 — the installation workflow itself is self-contained; listed prerequisites are knowledge reading, not procedural dependencies. No cookbook badge (K4 absent). First step in the broader validator onboarding flow; see composite onboard-polkadot-validator.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -801,28 +799,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 + ] }, - "built_at": "2026-04-21T19:30:00Z" - }, - { - "skill_id": "start-validating-on-polkadot", - "title": "Start Validating on Polkadot", + "skill_id": "set-up-polkadot-validator-node", "source_pages": [ - "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md" + "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" ], + "status": "built", + "title": "Set Up a Polkadot Validator Node" + }, + { + "blocked_reason": "Redundant with `onboard-polkadot-validator` (same flow; onboard is the more comprehensive name).", "category": "tutorial", - "priority": "low", - "status": "blocked", "notes": "Covers syncing a validator node (full or warp sync), bonding DOT via Polkadot.js Apps staking UI, activating the validator, and running as a systemd service. P1+P2+P3+P4 all present; systemd unit file is a source file to create. K1=0 — intro states key-management must be completed first. GUI-heavy bonding and activation steps (S1=0). Bonding requires real DOT on mainnet (S2=0). C2 fired (multiple screenshots). Part of composite skill onboard-polkadot-validator.", + "priority": "low", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 7, "signals": [ "P1", "P2", @@ -831,31 +829,29 @@ "C2", "K2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 7 + ] }, - "blocked_reason": "Redundant with `onboard-polkadot-validator` (same flow; onboard is the more comprehensive name)." - }, - { - "skill_id": "onboard-polkadot-validator", - "title": "Onboard as a Polkadot Validator (Full Flow)", + "skill_id": "start-validating-on-polkadot", "source_pages": [ - "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", - "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md", "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md" ], + "status": "blocked", + "title": "Start Validating on Polkadot" + }, + { + "blocked_reason": "Complex production-only validator flow; meaningful execution requires MainNet bond, real DOT, and infra commitments outside what an automated skill should orchestrate.", "category": "tutorial", "composite": true, - "priority": "medium", - "status": "blocked", "notes": "Composite skill spanning the three validator onboarding pages. Together they cover the complete workflow: install binaries (set-up-validator), generate and register session keys and node key (key-management), sync chain, bond DOT, activate validator, and configure systemd (start-validating). K1 re-evaluated to 5 for the combined set. GUI-dependent bonding and on-chain key submission remain (S1=0, S2=0), limiting priority to medium. Priority score 15 → medium.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 15, "signals": [ "P1", "P2", @@ -865,28 +861,30 @@ "K2", "K3", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 15 + ] }, - "blocked_reason": "Complex production-only validator flow; meaningful execution requires MainNet bond, real DOT, and infra commitments outside what an automated skill should orchestrate." - }, - { - "skill_id": "set-up-validator-monitoring-stack", - "title": "Set Up Prometheus, Grafana, and Alertmanager for a Validator Node", + "skill_id": "onboard-polkadot-validator", "source_pages": [ - "node-infrastructure/run-a-validator/operational-tasks/general-management.md" + "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", + "node-infrastructure/run-a-validator/onboarding-and-offboarding/key-management.md", + "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md" ], - "category": "tutorial", - "priority": "high", "status": "blocked", + "title": "Onboard as a Polkadot Validator (Full Flow)" + }, + { + "blocked_reason": "Impractical prereq: generic Prometheus/Grafana setup, not Polkadot-specific.", + "category": "tutorial", "notes": "Broad operational guide covering CPU performance tuning (SMT/NUMA deactivation, Spectre mitigations), full Prometheus+Grafana+Alertmanager monitoring stack setup with systemd service files, and security best practices (key management, secure-validator mode, Linux hardening). P1+P2+P3+P4 all present. K1=5 for the monitoring subsections, which can be set up independently on any Linux server running a Polkadot node. K3 absent (no explicit prerequisites section). Code blocks use MkDocs snippet includes for config files (K2=4). Grafana UI steps required for dashboard configuration (C2 fired).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 8, + "not_applicable": 0, + "procedural": 20, + "reference": 4 + }, + "priority_score": 19, "signals": [ "P1", "P2", @@ -900,28 +898,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 4, - "conceptual": 8, - "not_applicable": 0 - }, - "priority_score": 19 + ] }, - "blocked_reason": "Impractical prereq: generic Prometheus/Grafana setup, not Polkadot-specific." - }, - { - "skill_id": "upgrade-validator-node-backup", - "title": "Upgrade a Validator Node Using a Backup Server", + "skill_id": "set-up-validator-monitoring-stack", "source_pages": [ - "node-infrastructure/run-a-validator/operational-tasks/upgrade-your-node.md" + "node-infrastructure/run-a-validator/operational-tasks/general-management.md" ], - "category": "guide", - "priority": "low", "status": "blocked", + "title": "Set Up Prometheus, Grafana, and Alertmanager for a Validator Node" + }, + { + "blocked_reason": "Impractical prereq: requires two pre-configured validator servers (primary + standby).", + "category": "guide", "notes": "Guide for safely rotating between Validator A and Validator B across session N and N+3 to allow maintenance without downtime or slashing. Numbered steps present (P1); outcome verification via session change logs (P4). No CLI commands on the page itself — delegates to key-management and set-up-validator guides for execution steps (K1=0). K3 present (prerequisites section). No source files. S1 absent (no runnable commands); S2 present (deterministic timing once session key propagation delay is understood). category_ambiguous: procedural=10 vs conceptual=7 (C1: session timing explanation, C3: no runnable code).", + "priority": "low", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 10, + "reference": 3 + }, + "contending_categories": [ + "guide", + "conceptual" + ], + "priority_score": 9, "signals": [ "P1", "P4", @@ -931,33 +934,28 @@ "K3", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 3, - "conceptual": 7, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "conceptual" - ], - "priority_score": 9 + ] }, - "blocked_reason": "Impractical prereq: requires two pre-configured validator servers (primary + standby)." - }, - { - "skill_id": "add-existing-pallet-to-runtime", - "title": "Add an Existing Pallet to a Parachain Runtime", + "skill_id": "upgrade-validator-node-backup", "source_pages": [ - "parachains/customize-runtime/add-existing-pallets.md" + "node-infrastructure/run-a-validator/operational-tasks/upgrade-your-node.md" ], + "status": "blocked", + "title": "Upgrade a Validator Node Using a Backup Server" + }, + { + "built_at": "2026-04-21T22:30:00Z", "category": "tutorial", - "priority": "medium", - "status": "built", "notes": "Complete guide for integrating a new pallet (pallet-utility as example) into a Polkadot SDK parachain runtime: add Cargo.toml dependency, implement Config trait in mod.rs, register in runtime construct in lib.rs, compile, generate chain spec, and run locally. P1+P2+P3+P4 all present. K1=0 — requires an existing working parachain template dev environment (set-up-the-parachain-template.md as prerequisite). K4 present (cookbook CI badge). Final verification step uses Polkadot.js Apps GUI (S1 absent). Same applies to adding any SDK pallet. Priority score 16 → medium.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 16, "signals": [ "P1", "P2", @@ -968,28 +966,28 @@ "K4", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 16 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "configure-multiple-pallet-instances", - "title": "Configure Multiple Instances of a Pallet in a Runtime", + "skill_id": "add-existing-pallet-to-runtime", "source_pages": [ - "parachains/customize-runtime/add-pallet-instances.md" + "parachains/customize-runtime/add-existing-pallets.md" ], - "category": "tutorial", - "priority": "medium", "status": "built", + "title": "Add an Existing Pallet to a Parachain Runtime" + }, + { + "built_at": "2026-04-21T22:30:00Z", + "category": "tutorial", "notes": "Demonstrates adding two instances of pallet-collective (TechnicalCommittee and Council) to a parachain runtime. Covers identifying instantiable pallets, defining instance parameters, implementing Config and Config traits, registering both in the runtime construct, compiling, generating a chain spec, and launching the node. P1+P2+P3+P4 all present. K1=0 — requires existing parachain template setup; also builds on add-existing-pallets.md concepts. K4 present (cookbook CI badge). Final instance-independence test uses Polkadot.js Apps GUI (S1 absent). Priority score 16 → medium.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 16, "signals": [ "P1", "P2", @@ -1000,28 +998,28 @@ "K4", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 16 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "create-frame-pallet", - "title": "Create a Custom FRAME Pallet", + "skill_id": "configure-multiple-pallet-instances", "source_pages": [ - "parachains/customize-runtime/pallet-development/create-a-pallet.md" + "parachains/customize-runtime/add-pallet-instances.md" ], - "category": "tutorial", - "priority": "medium", "status": "built", + "title": "Configure Multiple Instances of a Pallet in a Runtime" + }, + { + "built_at": "2026-04-21T22:30:00Z", + "category": "tutorial", "notes": "Step-by-step guide building a counter pallet from scratch inside the Polkadot SDK parachain template: create Cargo project, configure FRAME dependencies, define Config trait, add events/errors/storage/genesis/extrinsics, integrate into runtime, generate chain spec, and launch locally. Verification step uses Polkadot.js Apps GUI (S1=0). K1=0 — requires parachains/install-polkadot-sdk and parachains/launch-a-parachain/set-up-the-parachain-template as prerequisites. Primary code blocks use MkDocs snippet includes (--8<--) but complete inline reference is available in a collapsible (K2=4). Cookbook CI badge present. Next in series: set-up-pallet-mock-runtime, unit-test-frame-pallet, benchmark-frame-pallet.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 16, "signals": [ "P1", "P2", @@ -1033,28 +1031,28 @@ "K4", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 16 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "set-up-pallet-mock-runtime", - "title": "Set Up a Mock Runtime for Pallet Unit Testing", + "skill_id": "create-frame-pallet", "source_pages": [ - "parachains/customize-runtime/pallet-development/mock-runtime.md" + "parachains/customize-runtime/pallet-development/create-a-pallet.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Create a Custom FRAME Pallet" + }, + { + "built_at": "2026-04-21T20:30:00Z", + "category": "tutorial", "notes": "Tutorial for creating a minimal mock runtime (mock.rs) using construct_runtime! and #[derive_impl] for isolated pallet testing: create mock module, configure frame_system and pallet Config traits, set up genesis storage helpers. K1=0 — requires completed create-frame-pallet (counter pallet in pallets/pallet-custom). Code uses MkDocs snippet includes but complete reference available in collapsible (K2=4). All CLI steps are programmatic (S1=4). Cookbook CI badge present. Part of pallet development series alongside create-frame-pallet, unit-test-frame-pallet, and benchmark-frame-pallet.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 20, "signals": [ "P1", "P2", @@ -1067,28 +1065,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 20 + ] }, - "built_at": "2026-04-21T20:30:00Z" - }, - { - "skill_id": "unit-test-frame-pallet", - "title": "Write Unit Tests for a FRAME Pallet", + "skill_id": "set-up-pallet-mock-runtime", "source_pages": [ - "parachains/customize-runtime/pallet-development/pallet-testing.md" + "parachains/customize-runtime/pallet-development/mock-runtime.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Set Up a Mock Runtime for Pallet Unit Testing" + }, + { + "built_at": "2026-04-21T20:30:00Z", + "category": "tutorial", "notes": "Tutorial for writing comprehensive pallet unit tests using FRAME macros (assert_ok!, assert_noop!, System::assert_last_event!): create tests.rs, test basic operations, error conditions, access control, event emission, and genesis configuration. Inline Rust test code is fully shown (no snippet includes; K2=4). K1=0 — requires create-frame-pallet and set-up-pallet-mock-runtime. All steps are CLI/programmatic (S1=4). Cookbook CI badge present. Final step before benchmarking in the pallet development series.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 20, "signals": [ "P1", "P2", @@ -1101,28 +1099,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 20 + ] }, - "built_at": "2026-04-21T20:30:00Z" - }, - { - "skill_id": "benchmark-frame-pallet", - "title": "Benchmark a FRAME Pallet and Generate Weight Files", + "skill_id": "unit-test-frame-pallet", "source_pages": [ - "parachains/customize-runtime/pallet-development/benchmark-pallet.md" + "parachains/customize-runtime/pallet-development/pallet-testing.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Write Unit Tests for a FRAME Pallet" + }, + { + "built_at": "2026-04-21T20:30:00Z", + "category": "tutorial", "notes": "Tutorial covering the full benchmarking workflow: create benchmarking.rs module, define WeightInfo trait, annotate extrinsics, enable runtime-benchmarks Cargo feature, build WASM runtime with benchmarks, install frame-omni-bencher, download weight template, execute benchmarks, and integrate generated weights.rs into production runtime. K1=0 — requires a working parachain template and pallet (e.g., from create-frame-pallet series), but page explicitly supports substitution with any pallet. R2 fires for CLI flag descriptions (--steps, --repeat, --heap-pages, --wasm-execution). Cookbook CI badge present.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 4 + }, + "priority_score": 20, "signals": [ "P1", "P2", @@ -1135,28 +1133,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 4, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 20 + ] }, - "built_at": "2026-04-21T20:30:00Z" - }, - { - "skill_id": "open-hrmp-channel-between-parachains", - "title": "Open an HRMP Channel Between Parachains", + "skill_id": "benchmark-frame-pallet", "source_pages": [ - "parachains/interoperability/channels-between-parachains.md" + "parachains/customize-runtime/pallet-development/benchmark-pallet.md" ], + "status": "built", + "title": "Benchmark a FRAME Pallet and Generate Weight Files" + }, + { + "blocked_reason": "Needs two parachains running locally; skill cannot currently spin up that infra.", "category": "guide", - "priority": "medium", - "status": "blocked", "notes": "Two-step guide for establishing unidirectional (and then bidirectional) HRMP channels between two user parachains: fund sovereign accounts, encode hrmpInitOpenChannel and hrmpAcceptOpenChannel extrinsics, craft XCM messages, and submit via Polkadot.js Apps. No CLI commands; entirely GUI-driven. Cookbook CI badge present (K4). Ambiguous between guide and reference: numbered steps with P4 give procedural=10 (Rule 3 fires), reference=8 (extrinsic parameter tables R1 and R3). Tiebreaker: Rule 3 fires before Rule 4 because procedural=10 meets the guide threshold. GUI-only workflow (S1=0) and placeholder call data that must be recomputed per para ID (K2=0) limit priority to medium.", + "priority": "medium", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 10, + "reference": 8 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 17, "signals": [ "P1", "P4", @@ -1168,33 +1171,33 @@ "K4", "S2", "S3" - ], + ] + }, + "skill_id": "open-hrmp-channel-between-parachains", + "source_pages": [ + "parachains/interoperability/channels-between-parachains.md" + ], + "status": "blocked", + "title": "Open an HRMP Channel Between Parachains" + }, + { + "blocked_reason": "Redundant with `open-hrmp-channel-between-parachains` (system parachain is a special case of the general flow).", + "category": "guide", + "notes": "Simplified single-message procedure for bidirectional HRMP channel setup between a user parachain and a system parachain (e.g., Asset Hub): fund sovereign account, encode establish_channel_with_system extrinsic, submit XCM to relay chain via Polkadot.js Apps, verify at next session. More efficient than the two-step regular parachain flow. Entirely GUI-driven; no CLI commands. Cookbook CI badge present (K4). Same scoring pattern as channels-between-parachains: procedural=10 vs reference=8; Rule 3 fires first (guide). GUI-only (S1=0) and placeholder call data (K2=0) limit priority to medium.", + "priority": "medium", + "scoring": { + "category_ambiguous": true, "category_scores": { - "procedural": 10, - "reference": 8, "conceptual": 3, - "not_applicable": 0 + "not_applicable": 0, + "procedural": 10, + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "guide", "reference" ], - "priority_score": 17 - }, - "blocked_reason": "Needs two parachains running locally; skill cannot currently spin up that infra." - }, - { - "skill_id": "open-hrmp-channel-with-system-parachain", - "title": "Open an HRMP Channel with a System Parachain", - "source_pages": [ - "parachains/interoperability/channels-with-system-parachains.md" - ], - "category": "guide", - "priority": "medium", - "status": "blocked", - "notes": "Simplified single-message procedure for bidirectional HRMP channel setup between a user parachain and a system parachain (e.g., Asset Hub): fund sovereign account, encode establish_channel_with_system extrinsic, submit XCM to relay chain via Polkadot.js Apps, verify at next session. More efficient than the two-step regular parachain flow. Entirely GUI-driven; no CLI commands. Cookbook CI badge present (K4). Same scoring pattern as channels-between-parachains: procedural=10 vs reference=8; Rule 3 fires first (guide). GUI-only (S1=0) and placeholder call data (K2=0) limit priority to medium.", - "scoring": { + "priority_score": 17, "signals": [ "P1", "P4", @@ -1206,33 +1209,28 @@ "K4", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 8, - "conceptual": 3, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" - ], - "priority_score": 17 + ] }, - "blocked_reason": "Redundant with `open-hrmp-channel-between-parachains` (system parachain is a special case of the general flow)." - }, - { - "skill_id": "deploy-parachain-to-polkadot-testnet", - "title": "Deploy a Parachain to the Polkadot TestNet", + "skill_id": "open-hrmp-channel-with-system-parachain", "source_pages": [ - "parachains/launch-a-parachain/deploy-to-polkadot.md" + "parachains/interoperability/channels-with-system-parachains.md" ], + "status": "blocked", + "title": "Open an HRMP Channel with a System Parachain" + }, + { + "built_at": "2026-04-21T23:30:00Z", "category": "tutorial", - "priority": "low", - "status": "built", "notes": "End-to-end tutorial for deploying a Polkadot SDK parachain to Paseo TestNet: obtain PAS tokens (faucet via UI), reserve a para ID (Polkadot.js Apps), generate collator keys (subkey Docker), produce and edit plain/raw chain specs (chain-spec-builder), export genesis wasm and state (polkadot-omni-node), register a parathread (UI), generate a node key, start the collator, and insert session keys (curl RPC). K1=0 — intro explicitly references set-up-the-parachain-template as required prior step. K2=0 — INSERT_SECRET_PHRASE, INSERT_PUBLIC_KEY_HEX_FORMAT, and INSERT_PARA_ID placeholders present. GUI steps for para ID reservation and parathread registration (S1=0). No cookbook badge. Part of a three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Consider proposing a composite once set-up-the-parachain-template is classified.", + "priority": "low", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 20, + "reference": 4 + }, + "priority_score": 9, "signals": [ "P1", "P2", @@ -1243,28 +1241,33 @@ "K3", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 4, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 9 + ] }, - "built_at": "2026-04-21T23:30:00Z" - }, - { - "skill_id": "obtain-coretime-parachain", - "title": "Obtain Coretime for a Parachain", + "skill_id": "deploy-parachain-to-polkadot-testnet", "source_pages": [ - "parachains/launch-a-parachain/obtain-coretime.md" + "parachains/launch-a-parachain/deploy-to-polkadot.md" ], + "status": "built", + "title": "Deploy a Parachain to the Polkadot TestNet" + }, + { + "blocked_reason": "Requires acquiring on-chain coretime on a live network — financial and governance prerequisites make it impractical as a generated skill.", "category": "guide", - "priority": "low", - "status": "blocked", "notes": "Guide for obtaining validation time after deploying a parachain: place on-demand coretime orders (Polkadot.js Apps onDemand extrinsic) or purchase and assign bulk coretime (RegionX marketplace web UI). No CLI commands; entirely GUI-driven across two different web interfaces. K1=0 — explicitly requires deploy-to-polkadot tutorial as prerequisite. No cookbook badge. Part of the three-page launch series: set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime. Ambiguous between guide and reference: procedural=10 (R1 extrinsic descriptions, R3 parameter descriptions), reference=8; Rule 3 fires first (guide).", + "priority": "low", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 10, + "reference": 8 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 9, "signals": [ "P1", "P4", @@ -1274,33 +1277,33 @@ "K3", "S2", "S3" - ], + ] + }, + "skill_id": "obtain-coretime-parachain", + "source_pages": [ + "parachains/launch-a-parachain/obtain-coretime.md" + ], + "status": "blocked", + "title": "Obtain Coretime for a Parachain" + }, + { + "blocked_reason": "Redundant with `obtain-coretime-parachain` (renewing is a subset of obtaining).", + "category": "guide", + "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 — requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", + "priority": "low", + "scoring": { + "category_ambiguous": true, "category_scores": { + "conceptual": 7, + "not_applicable": 0, "procedural": 10, - "reference": 8, - "conceptual": 3, - "not_applicable": 0 + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "guide", "reference" ], - "priority_score": 9 - }, - "blocked_reason": "Requires acquiring on-chain coretime on a live network — financial and governance prerequisites make it impractical as a generated skill." - }, - { - "skill_id": "renew-parachain-coretime", - "title": "Renew Coretime for a Parachain", - "source_pages": [ - "parachains/runtime-maintenance/coretime-renewal.md" - ], - "category": "guide", - "priority": "low", - "status": "blocked", - "notes": "Guide covering manual coretime renewal (broker.renew extrinsic) and auto-renewal configuration (enable_auto_renew via XCM from the parachain). Entirely GUI-driven via Polkadot.js Apps (S1 absent). No CLI commands or code blocks (K2 absent). K1=0 — requires an existing parachain with active coretime and an HRMP channel to the Coretime chain (for auto-renewal). No explicit prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic parameter tables for enable_auto_renew/disable_auto_renew); Rule 3 fires first. C1+C3 fire for bulk sale phase explanations and query result descriptions.", - "scoring": { + "priority_score": 6, "signals": [ "P1", "P4", @@ -1310,33 +1313,28 @@ "C3", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 8, - "conceptual": 7, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" - ], - "priority_score": 6 + ] }, - "blocked_reason": "Redundant with `obtain-coretime-parachain` (renewing is a subset of obtaining)." - }, - { - "skill_id": "perform-runtime-upgrade", - "title": "Perform a Forkless Runtime Upgrade", + "skill_id": "renew-parachain-coretime", "source_pages": [ - "parachains/runtime-maintenance/runtime-upgrades.md" + "parachains/runtime-maintenance/coretime-renewal.md" ], - "category": "tutorial", - "priority": "medium", "status": "blocked", + "title": "Renew Coretime for a Parachain" + }, + { + "blocked_reason": "Prerequisite is a running parachain that the skill cannot itself bring up.", + "category": "tutorial", "notes": "Tutorial guiding through a full runtime upgrade cycle: add a new dispatchable function to a custom pallet, bump spec_version, rebuild the Wasm binary, and submit system.setCode via sudo through Polkadot.js Apps. P1+P2+P3+P4 all present (score 20). K1=0 — requires install-polkadot-sdk, set-up-the-parachain-template, and create-a-pallet as prerequisites. K2=4 — code uses --8<-- snippet includes rather than inline blocks, but complete code is referenced. Submission step is GUI-dependent (S1=0). Cookbook CI badge present. See storage-migrations.md as supplementary context for upgrade-related data transformations.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 3, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 16, "signals": [ "P1", "P2", @@ -1348,105 +1346,105 @@ "K4", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 3, - "not_applicable": 0 - }, - "priority_score": 16 + ] }, - "blocked_reason": "Prerequisite is a running parachain that the skill cannot itself bring up." - }, - { - "skill_id": "unlock-parachain-via-xcm", - "title": "Unlock a Parachain via XCM", + "skill_id": "perform-runtime-upgrade", "source_pages": [ - "parachains/runtime-maintenance/unlock-parachains.md" + "parachains/runtime-maintenance/runtime-upgrades.md" ], - "category": "guide", - "priority": "low", "status": "blocked", + "title": "Perform a Forkless Runtime Upgrade" + }, + { + "blocked_reason": "Too niche: rare one-off admin operation, unlikely most teams need it.", + "category": "guide", "notes": "Guide for unlocking a locked parachain by sending a registrar.removeLock XCM from the parachain to the relay chain using sudo. Covers checking lock status (Chain State query), encoding the removeLock extrinsic, funding the sovereign account, and crafting the XCM via Polkadot.js Apps UI. P1+P4=10 → Rule 3 guide. Entirely GUI-driven (S1=0). No code blocks (K2 absent). K1=0 — requires an existing running parachain. No prerequisites section; no cookbook badge. Ambiguous between guide and reference: procedural=10 vs reference=8 (extrinsic descriptions for registrar.removeLock and registrar.paras, sovereign account derivation table); Rule 3 fires first.", + "priority": "low", "scoring": { - "signals": [ - "P1", - "P4", - "R1", - "R3", - "C1", - "C3", - "S2", - "S3" - ], + "category_ambiguous": true, "category_scores": { - "procedural": 10, - "reference": 8, "conceptual": 7, - "not_applicable": 0 + "not_applicable": 0, + "procedural": 10, + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "guide", "reference" ], - "priority_score": 6 - }, - "blocked_reason": "Too niche: rare one-off admin operation, unlikely most teams need it." - }, - { - "skill_id": "fork-parachain-chopsticks", - "title": "Fork a Parachain Using Chopsticks", - "source_pages": [ - "parachains/testing/fork-a-parachain.md" - ], - "category": "tutorial", - "priority": "high", - "status": "blocked", - "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 — tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) → category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", - "scoring": { + "priority_score": 6, "signals": [ "P1", - "P2", "P4", "R1", - "R2", "R3", "C1", - "K1", - "K3", - "K4", - "S1", + "C3", "S2", "S3" - ], + ] + }, + "skill_id": "unlock-parachain-via-xcm", + "source_pages": [ + "parachains/runtime-maintenance/unlock-parachains.md" + ], + "status": "blocked", + "title": "Unlock a Parachain via XCM" + }, + { + "blocked_reason": "Redundant with `set-up-chopsticks-fork` (parachain-specific is just the generic case).", + "category": "tutorial", + "notes": "Tutorial covering Chopsticks installation (global and local npm) plus reference documentation for WebSocket manipulation methods (dev_newBlock, dev_setStorage, dev_timeTravel, etc.) and configuration parameters. P1+P2+P4=15 — tutorial threshold met via Rule 2. Reference score also maxes at 12 (R1 method signatures, R2 config flag list, R3 parameter descriptions) → category_ambiguous with 3-point gap. Skill should focus on the installation and basic fork workflow; WebSocket API reference section is supplementary context. K1=5 (Node.js/npm are standard dev prerequisites). K2=0 (YAML config and JS examples use --8<-- snippet includes). Cookbook CI badge present (K4).", + "priority": "high", + "scoring": { + "category_ambiguous": true, "category_scores": { - "procedural": 15, - "reference": 12, "conceptual": 5, - "not_applicable": 0 + "not_applicable": 0, + "procedural": 15, + "reference": 12 }, - "category_ambiguous": true, "contending_categories": [ "tutorial", "reference" ], - "priority_score": 21 + "priority_score": 21, + "signals": [ + "P1", + "P2", + "P4", + "R1", + "R2", + "R3", + "C1", + "K1", + "K3", + "K4", + "S1", + "S2", + "S3" + ] }, - "blocked_reason": "Redundant with `set-up-chopsticks-fork` (parachain-specific is just the generic case)." - }, - { - "skill_id": "spawn-local-parachain-network-zombienet", - "title": "Spawn a Local Parachain Test Network with Zombienet", + "skill_id": "fork-parachain-chopsticks", "source_pages": [ - "parachains/testing/run-a-parachain-network.md" + "parachains/testing/fork-a-parachain.md" ], - "category": "tutorial", - "priority": "medium", "status": "blocked", + "title": "Fork a Parachain Using Chopsticks" + }, + { + "blocked_reason": "Redundant with `spawn-test-network-zombienet` (broader applicability).", + "category": "tutorial", "notes": "End-to-end tutorial for spawning a local two-validator relay chain + collator test network using Zombienet's native provider: clone and build the parachain template (cargo build), download relay chain binaries (curl), generate a Paseo local chain spec (chain-spec-builder), write network.toml, spawn with zombienet, and verify via curl RPC. P1+P2+P3+P4 all present — score 20, no ambiguity. K1=0 — Zombienet installation itself is delegated to reference/tools/zombienet.md. K2=0 — INSERT_PASEO_RUNTIME_VERSION placeholder present in the Paseo runtime download curl command. K3=3 (Prerequisites section lists Rust/Cargo, wasm32 target, Zombienet, Git). Cookbook CI badge present (K4=3). Fully CLI-based (S1=4). Priority score 16 → medium.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 16, "signals": [ "P1", "P2", @@ -1457,28 +1455,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 16 + ] }, - "blocked_reason": "Redundant with `spawn-test-network-zombienet` (broader applicability)." - }, - { - "skill_id": "retrieve-runtime-metadata", - "title": "Retrieve Runtime Metadata", + "skill_id": "spawn-local-parachain-network-zombienet", "source_pages": [ - "reference/parachains/chain-data.md" + "parachains/testing/run-a-parachain-network.md" ], + "status": "blocked", + "title": "Spawn a Local Parachain Test Network with Zombienet" + }, + { + "built_at": "2026-04-21T22:30:00Z", "category": "guide", - "priority": "medium", - "status": "built", "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic — same endpoint, same output. K3/K4/S3 absent.", + "priority": "medium", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 11, + "reference": 12 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 16, "signals": [ "P1", "P2", @@ -1486,33 +1489,28 @@ "R2", "R3", "C1" - ], - "category_scores": { - "procedural": 11, - "reference": 12, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 16, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "set-up-chopsticks-fork", - "title": "Set Up and Use Chopsticks for Chain Forking", + "skill_id": "retrieve-runtime-metadata", "source_pages": [ - "reference/tools/chopsticks.md" + "reference/parachains/chain-data.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Retrieve Runtime Metadata" + }, + { + "built_at": "2026-04-21T19:30:00Z", + "category": "tutorial", "notes": "Full end-to-end workflow: install, configure, fork a live chain, interact via JS API, replay blocks, and test XCM. Verifiable outcome ('you will see output indicating the RPC is listening'). Also contains a rich WebSocket API reference section (R1+R2+R3=12) that co-exists with the procedural content, but procedural score dominates at 20.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 12 + }, + "priority_score": 22, "signals": [ "P1", "P2", @@ -1527,28 +1525,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 12, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 + ] }, - "built_at": "2026-04-21T19:30:00Z" - }, - { - "skill_id": "interact-with-chain-dedot", - "title": "Interact with Polkadot Chains Using Dedot", + "skill_id": "set-up-chopsticks-fork", "source_pages": [ - "reference/tools/dedot.md" + "reference/tools/chopsticks.md" ], - "category": "guide", - "priority": "high", "status": "built", + "title": "Set Up and Use Chopsticks for Chain Forking" + }, + { + "built_at": "2026-04-21T21:00:00Z", + "category": "guide", "notes": "Covers installation through reading on-chain data, querying storage, calling runtime APIs, and signing/sending transactions. No explicit prerequisites section (K3 absent) and no numbered ordered steps (P1 absent), so guide rather than tutorial. Category is ambiguous with reference (procedural=10, reference=8, diff=2); guide wins by Rule 3. High priority because the workflow is self-contained, code is complete, and the Dedot client is a first-class alternative to Polkadot.js in the ecosystem.", + "priority": "high", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 10, + "reference": 8 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 19, "signals": [ "P2", "P3", @@ -1559,33 +1562,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 8, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 19, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" ] }, - "built_at": "2026-04-21T21:00:00Z" - }, - { - "skill_id": "set-up-e2e-testing-moonwall", - "title": "Set Up End-to-End Testing with Moonwall", + "skill_id": "interact-with-chain-dedot", "source_pages": [ - "reference/tools/moonwall.md" + "reference/tools/dedot.md" ], - "category": "guide", - "priority": "high", "status": "built", + "title": "Interact with Polkadot Chains Using Dedot" + }, + { + "built_at": "2026-04-21T19:30:00Z", + "category": "guide", "notes": "Sequential workflow: install, initialize config via wizard, write a test suite, run tests. Verifiable outcome ('The test runner will output detailed results'). P1 absent because main flow lacks explicit numbered lists (wizard prompts are bullets, not ordered steps), putting procedural at 14 (below 15 threshold for tutorial). High priority due to self-contained workflow with prerequisites section, complete runnable code, and deterministic CLI-based execution.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 14, + "reference": 4 + }, + "priority_score": 22, "signals": [ "P2", "P3", @@ -1597,28 +1595,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 14, - "reference": 4, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 22 + ] }, - "built_at": "2026-04-21T19:30:00Z" - }, - { - "skill_id": "run-parachain-node-omni-node", - "title": "Run a Parachain Node with Polkadot Omni Node", + "skill_id": "set-up-e2e-testing-moonwall", "source_pages": [ - "reference/tools/omninode.md" + "reference/tools/moonwall.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Set Up End-to-End Testing with Moonwall" + }, + { + "built_at": "2026-04-21T21:00:00Z", + "category": "tutorial", "notes": "Complete tutorial: install binary (macOS/Ubuntu/cargo), obtain chain spec via numbered steps, launch a full node, interact with it. Verifiable outcome ('You should see the installed version number' and node sync logs). Includes runtime compatibility requirements with Rust code examples. K3 absent (no explicit prerequisites section), K4 absent (no reference repo for the tutorial itself).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 4 + }, + "priority_score": 19, "signals": [ "P1", "P2", @@ -1630,28 +1628,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 4, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 19 + ] }, - "built_at": "2026-04-21T21:00:00Z" - }, - { - "skill_id": "interact-with-chain-polkadart", - "title": "Interact with a Polkadot Chain Using Polkadart", + "skill_id": "run-parachain-node-omni-node", "source_pages": [ - "reference/tools/polkadart.md" + "reference/tools/omninode.md" ], + "status": "built", + "title": "Run a Parachain Node with Polkadot Omni Node" + }, + { + "blocked_reason": "Too niche: Dart/Flutter Polkadot developers are a very small audience.", "category": "guide", - "priority": "high", - "status": "blocked", "notes": "Covers the full Dart/Flutter SDK workflow: install packages, configure pubspec.yaml, generate types, create an API instance, read chain data, subscribe to new blocks, and send a signed transaction. Self-contained with complete code snippets. Ambiguous guide/reference (procedural=10, reference=9, diff=1) — Rule 3 wins because the page walks through a progressive workflow rather than cataloging an API surface.", + "priority": "high", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 10, + "reference": 9 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 19, "signals": [ "P2", "P3", @@ -1663,33 +1666,33 @@ "S1", "S2", "S3" - ], + ] + }, + "skill_id": "interact-with-chain-polkadart", + "source_pages": [ + "reference/tools/polkadart.md" + ], + "status": "blocked", + "title": "Interact with a Polkadot Chain Using Polkadart" + }, + { + "built_at": "2026-04-21T22:30:00Z", + "category": "guide", + "notes": "Covers installation, dynamic API generation, creating an API instance, reading chain constants/state, and sending transactions. Rubric yields priority=high (score=18) but overridden to medium: page carries a prominent maintenance-mode warning directing new projects to Dedot or PAPI. Building an agent skill around a deprecated library has limited value. Ambiguous guide/reference (procedural=11, reference=8, diff=3).", + "priority": "medium", + "scoring": { + "category_ambiguous": true, "category_scores": { - "procedural": 10, - "reference": 9, "conceptual": 5, - "not_applicable": 0 + "not_applicable": 0, + "procedural": 11, + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "guide", "reference" ], - "priority_score": 19 - }, - "blocked_reason": "Too niche: Dart/Flutter Polkadot developers are a very small audience." - }, - { - "skill_id": "use-polkadot-js-api", - "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", - "source_pages": [ - "reference/tools/polkadot-js-api.md" - ], - "category": "guide", - "priority": "medium", - "status": "built", - "notes": "Covers installation, dynamic API generation, creating an API instance, reading chain constants/state, and sending transactions. Rubric yields priority=high (score=18) but overridden to medium: page carries a prominent maintenance-mode warning directing new projects to Dedot or PAPI. Building an agent skill around a deprecated library has limited value. Ambiguous guide/reference (procedural=11, reference=8, diff=3).", - "scoring": { + "priority_score": 18, "signals": [ "P1", "P2", @@ -1701,33 +1704,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 11, - "reference": 8, - "conceptual": 5, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" - ], - "priority_score": 18 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "bootstrap-parachain-pop-cli", - "title": "Bootstrap Parachain Development with Pop CLI", + "skill_id": "use-polkadot-js-api", "source_pages": [ - "reference/tools/pop-cli.md" + "reference/tools/polkadot-js-api.md" ], + "status": "built", + "title": "Query and Transact on Polkadot Chains with the Polkadot.js API" + }, + { + "blocked_reason": "Not useful as a generated skill (per colleague review).", "category": "tutorial", - "priority": "medium", - "status": "blocked", "notes": "Sequential workflow from install (cargo install pop-cli) through environment setup (pop install), project scaffolding (pop new parachain), building (pop build --release), launching a local network (pop up network), and interacting via pop call chain. Verifiable install step (pop --help). Priority is medium because K2 and K3 don't fire: the network.toml config file is referenced but not shown, and prerequisites (Rust toolchain, Polkadot SDK dependencies) are not explicitly listed on the page.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 15, "signals": [ "P1", "P2", @@ -1737,28 +1735,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 15 + ] }, - "blocked_reason": "Not useful as a generated skill (per colleague review)." - }, - { - "skill_id": "interact-polkadot-node-py-substrate", - "title": "Interact with a Polkadot Node Using Python Substrate Interface", + "skill_id": "bootstrap-parachain-pop-cli", "source_pages": [ - "reference/tools/py-substrate-interface.md" + "reference/tools/pop-cli.md" ], + "status": "blocked", + "title": "Bootstrap Parachain Development with Pop CLI" + }, + { + "built_at": "2026-04-21T21:00:00Z", "category": "guide", - "priority": "high", - "status": "built", "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer — all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 10, + "reference": 3 + }, + "priority_score": 18, "signals": [ "P2", "P3", @@ -1768,28 +1766,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 3, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:00:00Z" - }, - { - "skill_id": "interact-polkadot-node-subxt", - "title": "Interact with a Polkadot Node Using Subxt (Rust)", + "skill_id": "interact-polkadot-node-py-substrate", "source_pages": [ - "reference/tools/subxt.md" + "reference/tools/py-substrate-interface.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Interact with a Polkadot Node Using Python Substrate Interface" + }, + { + "built_at": "2026-04-21T21:00:00Z", + "category": "tutorial", "notes": "Numbered installation steps (install CLI, add core deps), Rust/TOML files to create, and verifiable outcome language ('your Cargo.toml should look like this'). Covers the full workflow from prerequisites through reading chain constants/state and submitting a signed transaction. K2 not awarded due to INSERT_* placeholders (INSERT_NODE_URL, INSERT_ADDRESS, INSERT_SECRET_PHRASE) and unresolved {{dependencies.crates.*}} template variables.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 5 + }, + "priority_score": 18, "signals": [ "P1", "P2", @@ -1802,28 +1800,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 5, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:00:00Z" - }, - { - "skill_id": "spawn-test-network-zombienet", - "title": "Spawn and Test a Parachain Network with Zombienet", + "skill_id": "interact-polkadot-node-subxt", "source_pages": [ - "reference/tools/zombienet.md" + "reference/tools/subxt.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Interact with a Polkadot Node Using Subxt (Rust)" + }, + { + "built_at": "2026-04-21T21:30:00Z", + "category": "tutorial", "notes": "Covers the full Zombienet lifecycle: install (executable/Nix/Docker), write TOML or JSON network config files, spawn a relay chain with validators and parachain collators, author .zndsl test files with DSL assertions, and run the test suite. Explicit numbered outcome steps ('Zombienet will: 1. Download binaries ... 5. Display endpoints') and 'test runner will report pass/fail' confirm P4. Extensive Settings/RelayChain/Parachain/Collator config reference tables are present alongside the procedural flow. K2 not awarded due to INSERT_* placeholders and --8<-- snippet includes.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 12 + }, + "priority_score": 18, "signals": [ "P1", "P2", @@ -1838,28 +1836,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 12, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:30:00Z" - }, - { - "skill_id": "connect-polkadot-hub-testnet", - "title": "Connect to Polkadot Hub and Get Test Tokens", + "skill_id": "spawn-test-network-zombienet", "source_pages": [ - "smart-contracts/connect.md" + "reference/tools/zombienet.md" ], - "category": "guide", - "priority": "low", "status": "built", + "title": "Spawn and Test a Parachain Network with Zombienet" + }, + { + "built_at": "2026-04-21T23:59:00Z", + "category": "guide", "notes": "Procedural score (10) triggers rule 3 guide classification, but reference score (12) is numerically higher — flagged ambiguous. The bulk of the page is network configuration reference data (chain IDs, RPC URLs, WSS endpoints for TestNet/MainNet/Kusama). The only procedural workflow is a 3-step GUI-only faucet interaction (navigate → paste address → click button); no CLI commands or file creation. S1 = 0 (GUI-only), K1 = 0 (requires pre-existing wallet), K3 = 0 (no prerequisites section), resulting in priority_score of 3. Page's primary agent-facing value is as supplementary reference context for network connection details used by other skills.", + "priority": "low", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 2, + "not_applicable": 0, + "procedural": 10, + "reference": 12 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 3, "signals": [ "P1", "P4", @@ -1867,33 +1870,28 @@ "R2", "R3", "C3" - ], - "category_scores": { - "procedural": 10, - "reference": 12, - "conceptual": 2, - "not_applicable": 0 - }, - "priority_score": 3, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" ] }, - "built_at": "2026-04-21T23:59:00Z" - }, - { - "skill_id": "build-dapp-viem-nextjs", - "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js", + "skill_id": "connect-polkadot-hub-testnet", "source_pages": [ - "smart-contracts/cookbook/dapps/zero-to-hero.md" + "smart-contracts/connect.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Connect to Polkadot Hub and Get Test Tokens" + }, + { + "built_at": "2026-04-21T20:00:00Z", + "category": "tutorial", "notes": "End-to-end tutorial covering Hardhat contract creation and deployment plus a full Next.js/Viem dApp with wallet connect, read, and write components. All steps are on one page (K1 = 5). Reference repo revm-hardhat-examples is listed in context.md (K4 = 3). K2 = 0 due to INSERT_PRIVATE_KEY_HERE placeholder in .env. The CONTRACT_ADDRESS TODO comment in contract.ts is a developer note, not a blocking placeholder. Workflow is predominantly CLI-driven (S1 = 4); MetaMask interactions are the end-product being built, not tutorial build steps.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 21, "signals": [ "P1", "P2", @@ -1906,28 +1904,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 21 + ] }, - "built_at": "2026-04-21T20:00:00Z" - }, - { - "skill_id": "deploy-uniswap-v2-polkadot-hub", - "title": "Deploy Uniswap V2 on Polkadot Hub", + "skill_id": "build-dapp-viem-nextjs", "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v2.md" + "smart-contracts/cookbook/dapps/zero-to-hero.md" ], + "status": "built", + "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js" + }, + { + "blocked_reason": "Should be split into 4 sub-skills (env setup, deploy contracts, interact with router/pairs, add liquidity/swap). Sub-skills will be triaged separately.", "category": "tutorial", - "priority": "high", - "status": "blocked", "notes": "Tutorial for cloning, compiling, testing, and deploying Uniswap V2 (Factory + Pair contracts) to Polkadot Hub TestNet using Hardhat. Reference repo polkavm-hardhat-examples is listed in context.md (K4 = 3). K2 = 0 due to INSERT_LOCAL_PRIVATE_KEY and INSERT_AH_PRIVATE_KEY placeholders in .env. Local testing step references the Local Development Node guide, but the core deployment workflow to TestNet is fully self-contained (K1 = 5). Includes an architectural explanation of Factory/Pair contracts (C1) that serves as useful background context but does not detract from the tutorial classification.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 21, "signals": [ "P1", "P2", @@ -1940,29 +1938,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 21 + ] }, - "blocked_reason": "Should be split into 4 sub-skills (env setup, deploy contracts, interact with router/pairs, add liquidity/swap). Sub-skills will be triaged separately." - }, - { - "skill_id": "deploy-basic-contract-hardhat", - "title": "Deploy a Basic Smart Contract with Hardhat", + "skill_id": "deploy-uniswap-v2-polkadot-hub", "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" + "smart-contracts/cookbook/eth-dapps/uniswap-v2.md" ], - "category": "tutorial", - "priority": "high", - "status": "built", + "status": "blocked", + "title": "Deploy Uniswap V2 on Polkadot Hub" + }, + { "built_at": "2026-04-21T18:00:00Z", + "category": "tutorial", "notes": "Concise end-to-end tutorial: scaffold Hardhat project, configure polkadotTestnet network, create Storage.sol, compile, deploy via Ignition to TestNet. All code uses --8<-- snippet includes from polkadot-cookbook (K4 = 3; CI badge confirms tested). K2 = 4 (includes expand to complete code in the published site). All steps are CLI-driven with no GUI interactions (S1 = 4). Explicit prerequisites section covers Node.js version, test tokens, and wallet (K3 = 3). Verified outcome shown via terminal output snippet.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -1975,27 +1972,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 - } - }, - { - "skill_id": "deploy-basic-contract-remix", - "title": "Deploy a Basic Smart Contract with Remix IDE", + ] + }, + "skill_id": "deploy-basic-contract-hardhat", "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" + "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" ], - "category": "guide", - "priority": "medium", "status": "built", + "title": "Deploy a Basic Smart Contract with Hardhat" + }, + { + "built_at": "2026-04-21T22:30:00Z", + "category": "guide", "notes": "GUI-only guide for deploying the default Remix Storage.sol contract via Remix IDE browser interface. No CLI commands (P2 = 0) and no source files to create (P3 = 0) — uses Remix's built-in sample contract. All steps are click-based (S1 = 0). Procedural score (10) qualifies as guide via rule 3. Priority is medium (14) due to K1 = 5 (self-contained given MetaMask prerequisite), K3 = 3 (explicit prerequisites listed), S2 = 3, and S3 = 3 (deploy-contract-remix is a known skill shape in this vertical). K4 = 0 (no reference repo for Remix path); K2 = 0 (no code examples to assess).", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 10, + "reference": 0 + }, + "priority_score": 14, "signals": [ "P1", "P4", @@ -2003,29 +2001,28 @@ "K3", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 14 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "deploy-erc20-token-hardhat", - "title": "Deploy an ERC-20 Token Using Hardhat", + "skill_id": "deploy-basic-contract-remix", "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" + "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Deploy a Basic Smart Contract with Remix IDE" + }, + { "built_at": "2026-04-21T18:00:00Z", + "category": "tutorial", "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired — tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -2037,27 +2034,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 - } - }, - { - "skill_id": "deploy-erc20-token-remix", - "title": "Deploy an ERC-20 Token Using Remix IDE", + ] + }, + "skill_id": "deploy-erc20-token-hardhat", "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md" + "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Deploy an ERC-20 Token Using Hardhat" + }, + { + "built_at": "2026-04-21T20:00:00Z", + "category": "tutorial", "notes": "Complete browser-based tutorial: create contract in Remix, compile, deploy, and mint via MetaMask. No CLI commands (P2 not fired). S1 not fired — workflow is GUI-driven (Remix IDE + MetaMask); a skill agent would require browser automation. Contract code sourced from revm-hardhat-examples (K4 awarded).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 21, "signals": [ "P1", "P3", @@ -2068,28 +2066,28 @@ "K4", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 21 + ] }, - "built_at": "2026-04-21T20:00:00Z" - }, - { - "skill_id": "deploy-erc721-nft-hardhat", - "title": "Deploy an ERC-721 NFT Using Hardhat", + "skill_id": "deploy-erc20-token-remix", "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" + "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Deploy an ERC-20 Token Using Remix IDE" + }, + { + "built_at": "2026-04-21T21:30:00Z", + "category": "tutorial", "notes": "Complete CLI-based tutorial: initialise Hardhat project from scratch, create NFT contract and deployment module, compile, deploy. K2 not fired — deployment module contains INSERT_OWNER_ADDRESS placeholder that the reader must substitute. K4 not fired — code is served from local code/ snippets with no linked reference repo (unlike ERC-20 Hardhat which clones revm-hardhat-examples).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 18, "signals": [ "P1", "P2", @@ -2100,28 +2098,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:30:00Z" - }, - { - "skill_id": "deploy-erc721-nft-remix", - "title": "Deploy an ERC-721 NFT Using Remix IDE", + "skill_id": "deploy-erc721-nft-hardhat", "source_pages": [ - "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" + "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Deploy an ERC-721 NFT Using Hardhat" + }, + { + "built_at": "2026-04-21T21:30:00Z", + "category": "tutorial", "notes": "Complete browser-based tutorial: create ERC-721 contract in Remix, compile, deploy via MetaMask. No CLI commands (P2 not fired). S1 not fired — entirely GUI-driven (Remix IDE + MetaMask). K4 not fired — contract code served from local code/ snippets, no linked reference repo. Code appears complete with no placeholders (K2 fired).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 18, "signals": [ "P1", "P3", @@ -2131,28 +2129,28 @@ "K3", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:30:00Z" - }, - { - "skill_id": "set-up-foundry-polkadot-hub", - "title": "Use Foundry with Polkadot Hub", + "skill_id": "deploy-erc721-nft-remix", "source_pages": [ - "smart-contracts/dev-environments/foundry.md" + "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" ], - "category": "guide", - "priority": "high", "status": "built", + "title": "Deploy an ERC-721 NFT Using Remix IDE" + }, + { + "built_at": "2026-04-21T21:30:00Z", + "category": "guide", "notes": "Comprehensive dev-environment setup guide covering Foundry installation, project initialisation, compilation, configuration, deployment (forge create and forge script), verification (Blockscout and Routescan), contract interaction (cast), and testing. Classified as guide rather than tutorial: no numbered ordered lists (P1 not fired); primary structure is section headers with code blocks. R1 and R2 fire for the networks table and extensive CLI flag documentation. K2 not fired — multiple INSERT_* placeholders throughout (PRIVATE_KEY, CONTRACT_ADDRESS, DEPLOYER_ADDRESS, ACCOUNT_ADDRESS). K4 not fired — no linked reference repo. Ambiguity check: procedural=14, reference=9, delta=5 — not ambiguous.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 14, + "reference": 9 + }, + "priority_score": 18, "signals": [ "P2", "P3", @@ -2164,29 +2162,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 14, - "reference": 9, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:30:00Z" - }, - { - "skill_id": "set-up-hardhat-pvm", - "title": "Set Up Hardhat with the Polkadot Plugin (PVM)", + "skill_id": "set-up-foundry-polkadot-hub", "source_pages": [ - "smart-contracts/dev-environments/hardhat-polkadot.md" + "smart-contracts/dev-environments/foundry.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Use Foundry with Polkadot Hub" + }, + { "built_at": "2026-04-21T18:00:00Z", + "category": "tutorial", "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot — mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -2198,28 +2195,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 - } - }, - { - "skill_id": "set-up-hardhat-evm", - "title": "Set Up Hardhat for Polkadot Hub (EVM)", + ] + }, + "skill_id": "set-up-hardhat-pvm", "source_pages": [ - "smart-contracts/dev-environments/hardhat.md" + "smart-contracts/dev-environments/hardhat-polkadot.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Set Up Hardhat with the Polkadot Plugin (PVM)" + }, + { "built_at": "2026-04-21T18:00:00Z", + "category": "tutorial", "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown — P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -2232,27 +2229,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 25 - } - }, - { - "skill_id": "connect-remix-polkadot", - "title": "Connect Remix IDE to Polkadot Hub", + ] + }, + "skill_id": "set-up-hardhat-evm", "source_pages": [ - "smart-contracts/dev-environments/remix.md" + "smart-contracts/dev-environments/hardhat.md" ], - "category": "guide", - "priority": "medium", "status": "built", + "title": "Set Up Hardhat for Polkadot Hub (EVM)" + }, + { + "built_at": "2026-04-21T22:30:00Z", + "category": "guide", "notes": "Ambiguous: procedural=10 vs conceptual=7 (difference=3, within ambiguity threshold). Resolved as guide because numbered connection steps (1-4) and a verifiable outcome ('Remix will display your MetaMask account address') are present. However, all steps are GUI-only clicks/dropdowns (S1=0), reducing agent executability. K1=0: requires MetaMask already configured for Polkadot (references Wallet Integrations guide). No reference repo. Useful as a prerequisite context page before Remix-based deploy tutorials.", + "priority": "medium", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 10, + "reference": 0 + }, + "contending_categories": [ + "guide", + "conceptual" + ], + "priority_score": 10, "signals": [ "P1", "P4", @@ -2261,33 +2264,28 @@ "K2", "K3", "S2" - ], - "category_scores": { - "procedural": 10, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "conceptual" - ], - "priority_score": 10 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "get-testnet-tokens-faucet", - "title": "Get TestNet Tokens from the Polkadot Faucet", + "skill_id": "connect-remix-polkadot", "source_pages": [ - "smart-contracts/faucet.md" + "smart-contracts/dev-environments/remix.md" ], + "status": "built", + "title": "Connect Remix IDE to Polkadot Hub" + }, + { + "blocked_reason": "Redundant with `connect-polkadot-hub-testnet` which already covers getting testnet tokens.", "category": "guide", - "priority": "low", - "status": "blocked", "notes": "Three-step UI-only workflow (navigate, paste address, click button). Self-contained (K1=5) and deterministic (S2=3) but S1 does not fire because there are no CLI or SDK steps. Useful as a prerequisite step in deployment tutorials; limited standalone programmatic skill value.", + "priority": "low", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 10, + "reference": 0 + }, + "priority_score": 8, "signals": [ "P1", "P4", @@ -2295,28 +2293,28 @@ "C3", "K1", "S2" - ], - "category_scores": { - "procedural": 10, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 8 + ] }, - "blocked_reason": "Redundant with `connect-polkadot-hub-testnet` which already covers getting testnet tokens." - }, - { - "skill_id": "set-up-local-dev-node", - "title": "Set Up a Local Development Node", + "skill_id": "get-testnet-tokens-faucet", "source_pages": [ - "smart-contracts/dev-environments/local-dev-node.md" + "smart-contracts/faucet.md" ], + "status": "blocked", + "title": "Get TestNet Tokens from the Polkadot Faucet" + }, + { + "built_at": "2026-04-21T20:30:00Z", "category": "guide", - "priority": "high", - "status": "built", "notes": "Step-by-step guide to cloning, building, and running the Revive Dev node plus ETH-RPC adapter. Not self-contained (K1=0): explicit prerequisite is the Install Polkadot SDK Dependencies guide. All steps are CLI-based with verifiable outcome (node ready at http://localhost:8545). CI badge and test link confirm polkadot-cookbook coverage (K4).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 9, + "reference": 0 + }, + "priority_score": 20, "signals": [ "P2", "P4", @@ -2326,28 +2324,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 9, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 20 + ] }, - "built_at": "2026-04-21T20:30:00Z" - }, - { - "skill_id": "connect-wallet-polkadot-hub", - "title": "Connect a Wallet to Polkadot Hub", + "skill_id": "set-up-local-dev-node", "source_pages": [ - "smart-contracts/integrations/wallets.md" + "smart-contracts/dev-environments/local-dev-node.md" ], - "category": "guide", - "priority": "medium", "status": "built", + "title": "Set Up a Local Development Node" + }, + { + "built_at": "2026-04-21T22:30:00Z", + "category": "guide", "notes": "Numbered GUI steps for connecting MetaMask and Talisman to Polkadot Hub TestNet. K1=0: step 4 for MetaMask defers to smart-contracts/connect/ for RPC parameters. S1=0: all steps are browser-extension UI clicks with no CLI equivalent. Medium priority reflects GUI-only workflow limiting automation fitness.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 10, + "reference": 0 + }, + "priority_score": 10, "signals": [ "P1", "P4", @@ -2356,28 +2354,28 @@ "K2", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 10 + ] }, - "built_at": "2026-04-21T22:30:00Z" - }, - { - "skill_id": "deploy-contracts-ethers-js", - "title": "Deploy Contracts to Polkadot Hub with Ethers.js", + "skill_id": "connect-wallet-polkadot-hub", "source_pages": [ - "smart-contracts/libraries/ethers-js.md" + "smart-contracts/integrations/wallets.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Connect a Wallet to Polkadot Hub" + }, + { + "built_at": "2026-04-21T20:00:00Z", + "category": "tutorial", "notes": "Complete tutorial: project setup, Solidity compilation, deployment, and contract interaction via Ethers.js. Explicit numbered steps in the deploy section (P1). Self-contained with prerequisites listed. Code via --8<-- includes from polkadot-cookbook (K4). INSERT_* placeholders for RPC URL, chain ID, and mnemonic require substitution (K2=0).", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 21, "signals": [ "P1", "P2", @@ -2389,28 +2387,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 21 + ] }, - "built_at": "2026-04-21T20:00:00Z" - }, - { - "skill_id": "deploy-contracts-viem", - "title": "Deploy Contracts to Polkadot Hub with viem", + "skill_id": "deploy-contracts-ethers-js", "source_pages": [ - "smart-contracts/libraries/viem.md" + "smart-contracts/libraries/ethers-js.md" ], - "category": "guide", - "priority": "high", "status": "built", + "title": "Deploy Contracts to Polkadot Hub with Ethers.js" + }, + { + "built_at": "2026-04-21T20:00:00Z", + "category": "guide", "notes": "End-to-end TypeScript guide using viem: project init, chain config, wallet client, compilation, deployment, and contract interaction. No explicit numbered steps (P1=0) so scores as guide rather than tutorial despite full workflow coverage. Self-contained with prerequisites listed. INSERT_* placeholders for RPC URL, chain ID, and private key require substitution (K2=0). Companion to deploy-contracts-ethers-js.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 14, + "reference": 0 + }, + "priority_score": 21, "signals": [ "P2", "P3", @@ -2421,56 +2419,56 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 14, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 21 + ] }, - "built_at": "2026-04-21T20:00:00Z" - }, - { - "skill_id": "use-wagmi-polkadot-hub", - "title": "Use Wagmi to Interact with Smart Contracts on Polkadot Hub", + "skill_id": "deploy-contracts-viem", "source_pages": [ - "smart-contracts/libraries/wagmi.md" + "smart-contracts/libraries/viem.md" ], - "category": "guide", - "priority": "medium", "status": "built", + "title": "Deploy Contracts to Polkadot Hub with viem" + }, + { + "built_at": "2026-04-21T23:00:00Z", + "category": "guide", "notes": "Walks through creating a Next.js + Wagmi v3 dApp: project setup, provider config, wallet connection, querying chain data, and calling a pre-deployed Storage contract. No explicit prerequisites section and no verifiable CLI outcome (P4 absent) — hence guide rather than tutorial. Runtime wallet interaction is GUI-driven (S1=0). Code snippets use --8<-- includes with placeholder values (K2=0).", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 10, + "reference": 0 + }, + "priority_score": 11, "signals": [ "P2", "P3", "K1", "S2", "S3" - ], - "category_scores": { - "procedural": 10, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 11 + ] }, - "built_at": "2026-04-21T23:00:00Z" - }, - { - "skill_id": "deploy-interact-contracts-web3js", - "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.js", + "skill_id": "use-wagmi-polkadot-hub", "source_pages": [ - "smart-contracts/libraries/web3-js.md" + "smart-contracts/libraries/wagmi.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Use Wagmi to Interact with Smart Contracts on Polkadot Hub" + }, + { + "built_at": "2026-04-21T21:30:00Z", + "category": "tutorial", "notes": "Complete end-to-end tutorial: project init, provider setup, Solidity compilation, contract deployment, and interaction — all via CLI Node.js scripts. Explicit prerequisites section (K3) and fully CLI-driven workflow (S1). Priority score hits the high threshold at 18. Note: the page carries a sunset warning for Web3.js and recommends Ethers.js or Viem instead; a skill built from this page should include that deprecation notice.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 18, "signals": [ "P1", "P2", @@ -2481,28 +2479,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-04-21T21:30:00Z" - }, - { - "skill_id": "deploy-interact-contracts-web3py", - "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", + "skill_id": "deploy-interact-contracts-web3js", "source_pages": [ - "smart-contracts/libraries/web3-py.md" + "smart-contracts/libraries/web3-js.md" ], - "category": "tutorial", - "priority": "medium", "status": "built", + "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.js" + }, + { + "built_at": "2026-04-21T23:00:00Z", + "category": "tutorial", "notes": "Complete Python workflow: project setup with virtualenv, py-solc-x compilation of a Solidity contract, deployment via Web3.py, and read/write interaction — all CLI-driven. No explicit prerequisites section (K3=0) keeps priority below high. Code uses --8<-- includes with unfilled placeholders (K2=0). Parallel to the Web3.js tutorial but targets Python developers.", + "priority": "medium", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 3 + }, + "priority_score": 15, "signals": [ "P1", "P2", @@ -2512,28 +2510,33 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 3, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 15 + ] }, - "built_at": "2026-04-21T23:00:00Z" - }, - { - "skill_id": "interact-erc20-precompile-polkadot-hub", - "title": "Interact with the ERC20 Precompile on Polkadot Hub", + "skill_id": "deploy-interact-contracts-web3py", "source_pages": [ - "smart-contracts/precompiles/erc20.md" + "smart-contracts/libraries/web3-py.md" ], - "category": "guide", - "priority": "medium", "status": "built", + "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py" + }, + { + "built_at": "2026-04-21T23:00:00Z", + "category": "guide", "notes": "Predominantly a reference page (function signatures, parameter tables, address format specs, asset ID table) with a numbered procedural section for using the precompile via Remix IDE. Scores are close: procedural=10 vs reference=12 (within 3-point ambiguity threshold). Rule 3 fires first (procedural ≥ 10) assigning guide, but the reference character is strong. The interaction steps are GUI-only in Remix (S1=0), which limits skill-fit. Code examples are complete Solidity snippets with real precompile addresses (K2=4).", + "priority": "medium", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 10, + "reference": 12 + }, + "contending_categories": [ + "guide", + "reference" + ], + "priority_score": 15, "signals": [ "P1", "P4", @@ -2546,33 +2549,60 @@ "K2", "S2", "S3" - ], + ] + }, + "skill_id": "interact-erc20-precompile-polkadot-hub", + "source_pages": [ + "smart-contracts/precompiles/erc20.md" + ], + "status": "built", + "title": "Interact with the ERC20 Precompile on Polkadot Hub" + }, + { + "built_at": "2026-04-21T23:00:00Z", + "category": "tutorial", + "notes": "Complete walkthrough of connecting to and using the Storage precompile via Remix IDE. Reference API documentation (R1, R3) dominates the bulk of the page but a discrete 6-step interaction workflow with verifiable outcome qualifies it as a tutorial by Rule 2 (procedural=15, P4 present). GUI-only Remix steps prevent S1 from firing, capping priority at medium.", + "priority": "medium", + "scoring": { "category_scores": { - "procedural": 10, - "reference": 12, - "conceptual": 7, - "not_applicable": 0 + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 8 }, "priority_score": 15, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" + "signals": [ + "P1", + "P3", + "P4", + "R1", + "R3", + "K1", + "K2", + "S2", + "S3" ] }, - "built_at": "2026-04-21T23:00:00Z" - }, - { "skill_id": "interact-storage-precompile-remix", - "title": "Interact with the Storage Precompile", "source_pages": [ "smart-contracts/precompiles/storage.md" ], + "status": "built", + "title": "Interact with the Storage Precompile" + }, + { + "built_at": "2026-04-21T23:00:00Z", "category": "tutorial", + "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 → medium.", "priority": "medium", - "status": "built", - "notes": "Complete walkthrough of connecting to and using the Storage precompile via Remix IDE. Reference API documentation (R1, R3) dominates the bulk of the page but a discrete 6-step interaction workflow with verifiable outcome qualifies it as a tutorial by Rule 2 (procedural=15, P4 present). GUI-only Remix steps prevent S1 from firing, capping priority at medium.", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 15, + "reference": 8 + }, + "priority_score": 11, "signals": [ "P1", "P3", @@ -2580,137 +2610,139 @@ "R1", "R3", "K1", - "K2", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 8, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 15 + ] }, - "built_at": "2026-04-21T23:00:00Z" - }, - { "skill_id": "interact-system-precompile-remix", - "title": "Interact with the System Precompile", "source_pages": [ "smart-contracts/precompiles/system.md" ], + "status": "built", + "title": "Interact with the System Precompile" + }, + { + "built_at": "2026-04-21T23:30:00Z", "category": "tutorial", + "notes": "Demonstrates weighing, executing, and sending XCM messages from a Solidity smart contract via Remix IDE using the XCM precompile. Rule 2 fires (procedural=15, P4 present), selecting tutorial over reference despite strong reference signals (R1+R2+R3=12) and conceptual XCM architecture context (C1=5). Category is ambiguous with reference (diff=3 points). A complete SCALE-encoded example message is provided (K2 fires). GUI-only Remix steps prevent S1.", "priority": "medium", - "status": "built", - "notes": "Complete walkthrough of connecting to the System precompile (BLAKE2 hashing, sr25519 verification, account management, contract lifecycle) via Remix IDE. Rule 2 fires (procedural=15, P4 present). Several code examples contain unfilled placeholders (e.g. `sig = ...`, `pubKey = 0x...`), so K2 does not fire. GUI-only Remix steps prevent S1. Priority score=11 → medium.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 15, + "reference": 12 + }, + "contending_categories": [ + "tutorial", + "reference" + ], + "priority_score": 15, "signals": [ "P1", "P3", "P4", "R1", + "R2", "R3", + "C1", "K1", + "K2", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 8, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 11 + ] }, - "built_at": "2026-04-21T23:00:00Z" - }, - { "skill_id": "interact-xcm-precompile-remix", - "title": "Interact with the XCM Precompile", "source_pages": [ "smart-contracts/precompiles/xcm.md" ], - "category": "tutorial", - "priority": "medium", "status": "built", - "notes": "Demonstrates weighing, executing, and sending XCM messages from a Solidity smart contract via Remix IDE using the XCM precompile. Rule 2 fires (procedural=15, P4 present), selecting tutorial over reference despite strong reference signals (R1+R2+R3=12) and conceptual XCM architecture context (C1=5). Category is ambiguous with reference (diff=3 points). A complete SCALE-encoded example message is provided (K2 fires). GUI-only Remix steps prevent S1.", + "title": "Interact with the XCM Precompile" + }, + { + "built_at": "2026-05-15T15:30:00Z", + "category": "tutorial", + "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded — the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 0, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 18, "signals": [ "P1", + "P2", "P3", "P4", - "R1", - "R2", - "R3", - "C1", "K1", "K2", + "K3", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 12, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 15, - "category_ambiguous": true, - "contending_categories": [ - "tutorial", - "reference" ] }, - "built_at": "2026-04-21T23:30:00Z" - }, - { "skill_id": "store-retrieve-data-bulletin-chain", - "title": "Store and Retrieve Data on the Bulletin Chain", "source_pages": [ "chain-interactions/store-data/bulletin-chain.md" ], + "status": "built", + "title": "Store and Retrieve Data on the Bulletin Chain" + }, + { + "built_at": "2026-05-15T15:30:00Z", "category": "tutorial", + "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded — polkavm-hardhat-examples is listed in context.md.", "priority": "high", - "status": "built", - "notes": "Covers the full data lifecycle (authorize, store, verify, retrieve, renew) via Console UI and PAPI. S1 not awarded because the authorization step requires the Console UI faucet (no programmatic self-authorization on TestNet). K4 not awarded — the code lives in `code/chain-interactions/...` snippets rather than a named reference repo from context.md.", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", "P3", "P4", + "C1", "K1", "K2", "K3", + "K4", + "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 0, - "not_applicable": 0 - }, - "priority_score": 18 + ] }, - "built_at": "2026-05-15T15:30:00Z" - }, - { "skill_id": "deploy-uniswap-v2-core-pvm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", "source_pages": [ "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md" ], + "status": "built", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)" + }, + { + "built_at": "2026-05-15T15:30:00Z", "category": "tutorial", + "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded — key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded — revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", "priority": "high", - "status": "built", - "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded — polkavm-hardhat-examples is listed in context.md.", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", - "P3", "P4", "C1", "K1", @@ -2720,28 +2752,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-05-15T15:30:00Z" - }, - { "skill_id": "deploy-uniswap-v2-core-evm", - "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", "source_pages": [ "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md" ], + "status": "built", + "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)" + }, + { + "built_at": "2026-05-15T15:30:00Z", "category": "tutorial", + "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo — no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded — revm-hardhat-examples is listed in context.md.", "priority": "high", - "status": "built", - "notes": "EVM variant using revm-hardhat-examples repo and standard Hardhat + TypeScript; no PVM compiler plugin. P3 not awarded — key management is via `npx hardhat vars set` CLI, no file to write. K4 awarded — revm-hardhat-examples is listed in context.md. Testing step references Local Dev Node guide but TestNet deployment is self-contained.", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 15, + "reference": 0 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -2754,62 +2786,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-05-15T15:30:00Z" - }, - { "skill_id": "deploy-uniswap-v2-periphery-evm", - "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", "source_pages": [ "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md" ], - "category": "tutorial", - "priority": "high", "status": "built", - "notes": "Deploys WETH9, UniswapV2Factory, and UniswapV2Router02 via Hardhat Ignition. The V2 Core Solidity dependency is a local file reference resolved automatically by npm install from the same cloned repo — no separately deployed Core contracts are needed. K1 awarded as self-contained; the editorial prerequisite 'complete Core tutorial first' is educational context, not a technical blocker for deployment. K4 awarded — revm-hardhat-examples is listed in context.md.", - "scoring": { - "signals": [ - "P1", - "P2", - "P4", - "C1", - "K1", - "K2", - "K3", - "K4", - "S1", - "S2", - "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 25 - }, - "built_at": "2026-05-15T15:30:00Z" + "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)" }, { - "skill_id": "deploy-uniswap-v3-core-evm", - "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub", - "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" - ], + "built_at": "2026-05-15T15:30:00Z", "category": "tutorial", - "priority": "high", - "status": "built", "notes": "K1=0: test step references external Local Development Node guide; deployment workflow itself is self-contained. K4=3: revm-hardhat-examples repo with pinned commit (3ff28ae). No P3: reader clones the repo, not creates files.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 8, + "not_applicable": 0, + "procedural": 15, + "reference": 4 + }, + "priority_score": 20, "signals": [ "P1", "P2", @@ -2823,28 +2821,28 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 15, - "reference": 4, - "conceptual": 8, - "not_applicable": 0 - }, - "priority_score": 20 + ] }, - "built_at": "2026-05-15T15:30:00Z" - }, - { - "skill_id": "deploy-uniswap-v3-periphery-evm", - "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub", + "skill_id": "deploy-uniswap-v3-core-evm", "source_pages": [ - "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md" ], - "category": "tutorial", - "priority": "high", "status": "built", + "title": "Deploy Uniswap V3 Core with EVM on Polkadot Hub" + }, + { + "built_at": "2026-05-15T16:00:00Z", + "category": "tutorial", "notes": "Full deploy+test workflow for SwapRouter and NonfungiblePositionManager. The 'complete V3 Core tutorial' prereq is a code dependency resolved automatically via local npm file reference in the cloned monorepo; the Ignition module deploys Factory+WETH9+SwapRouter+NFPM in one shot, making this self-contained. Pinned to commit 96696ad15c3cf01b9168a71ad5114f27c34a8726 with docs.test.ts confirming CI-tested code.", + "priority": "high", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 20, + "reference": 9 + }, + "priority_score": 25, "signals": [ "P1", "P2", @@ -2860,388 +2858,338 @@ "S1", "S2", "S3" - ], - "category_scores": { - "procedural": 20, - "reference": 9, - "conceptual": 5, - "not_applicable": 0 - }, - "priority_score": 25 + ] }, - "built_at": "2026-05-15T16:00:00Z" + "skill_id": "deploy-uniswap-v3-periphery-evm", + "source_pages": [ + "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md" + ], + "status": "built", + "title": "Deploy Uniswap V3 Periphery with EVM on Polkadot Hub" } ], - "supplementary_only": [ + "generated": "2026-05-15T16:00:00Z", + "not_applicable": [ { - "page": "parachains/customize-runtime/add-smart-contract-functionality.md", - "category": "conceptual", - "relevant_to": [ - "add-existing-pallet-to-runtime", - "configure-multiple-pallet-instances" - ], - "notes": "Conceptual overview comparing three smart contract integration approaches for Polkadot SDK runtimes: pallet-revive (PVM/EVM dual backend), Frontier (full Ethereum compatibility layer), and pallet-contracts (legacy Wasm). No procedural steps, CLI commands, or source files. Includes a language/compiler comparison table (C2) and design rationale for each approach (C1). Useful background when deciding which smart contract pallet to add to a parachain runtime.", + "category": "not_applicable", + "page": "get-support.md", + "reason": "Community support hub listing channels (Telegram, Discord, Matrix, Stack Exchange, Reddit, YouTube, X/Twitter, Forum, Polkassembly). No procedural content, code, or tutorials.", "scoring": { - "signals": [ - "C1", - "C2", - "C3" - ], "category_scores": { + "conceptual": 0, + "not_applicable": 8, "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + "reference": 0 + }, + "signals": [ + "N1", + "N2" + ] } }, { - "page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md", - "category": "conceptual", - "relevant_to": [ - "onboard-polkadot-validator", - "start-validating-on-polkadot", - "manage-validator-session-keys" - ], - "notes": "Explains the three-step off-boarding process (chill, purge session keys, unbond) conceptually. No CLI commands or code blocks; steps are described in prose referencing UI actions on Polkadot.js Apps. Useful as background context for the full validator lifecycle.", + "category": "not_applicable", + "page": "parachains/get-started.md", + "reason": "Navigation landing page composed entirely of tables linking to other doc pages, grouped by topic (Quick Start, Launch, Customize, Testing, Runtime Upgrades, Interoperability, Integrations). No procedural steps, code, or substantive content of its own.", "scoring": { - "signals": [ - "C1", - "C3" - ], + "category_ambiguous": true, "category_scores": { + "conceptual": 2, + "not_applicable": 4, "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - } + "reference": 0 + }, + "contending_categories": [ + "not_applicable", + "conceptual" + ], + "signals": [ + "C3", + "N1" + ] } }, { - "page": "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md", - "category": "conceptual", - "relevant_to": [ - "onboard-polkadot-validator", - "start-validating-on-polkadot" - ], - "notes": "Short page explaining the chill/chillOther extrinsics and their timing behavior (era-based eligibility). No CLI, no code blocks; UI step references Polkadot.js Apps. Useful as conceptual context for any validator operational skill.", + "category": "not_applicable", + "page": "parachains/integrations/indexers.md", + "reason": "Tools directory listing Subsquid and SubQuery with external reference links. The brief conceptual intro ('What is a Blockchain Indexer?') scores conceptual higher (7) than not_applicable (4), but N1 fires because the page's primary purpose is a grid-card link directory to external tools. Rule 1 takes precedence over Rule 5.", "scoring": { - "signals": [ - "R3", - "C1", - "C3" - ], + "category_ambiguous": true, "category_scores": { - "procedural": 0, - "reference": 3, "conceptual": 7, - "not_applicable": 0 - } + "not_applicable": 4, + "procedural": 0, + "reference": 0 + }, + "contending_categories": [ + "not_applicable", + "conceptual" + ], + "signals": [ + "C1", + "C3", + "N1" + ] } }, { - "page": "node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy.md", - "category": "reference", - "relevant_to": [ - "set-up-polkadot-validator-node", - "manage-validator-session-keys", - "onboard-polkadot-validator", - "set-up-validator-monitoring-stack" - ], - "notes": "Documents the Staking Operator proxy type: permission model, extrinsic comparison table (Staking vs StakingOperator), 4-step setup procedure, and security properties. Ambiguous between reference and conceptual (scores 12 vs 10). Classified as reference because the extrinsic permission table (R1=5) and parameter specs for proxy.addProxy (R2=4) are the primary value; the mermaid diagram and security rationale push conceptual close but reference rule fires first.", + "category": "not_applicable", + "page": "parachains/integrations/oracles.md", + "reason": "Tools directory listing Acurast and DIA oracle providers with external reference links. Same pattern as indexers.md: conceptual intro scores higher (7) than not_applicable (4), but N1 fires (grid-card link directory) and Rule 1 takes precedence.", "scoring": { - "signals": [ - "P1", - "R1", - "R2", - "R3", - "C1", - "C2", - "C3" - ], + "category_ambiguous": true, "category_scores": { - "procedural": 6, - "reference": 12, - "conceptual": 10, - "not_applicable": 0 + "conceptual": 7, + "not_applicable": 4, + "procedural": 0, + "reference": 0 }, - "category_ambiguous": true, "contending_categories": [ - "reference", + "not_applicable", "conceptual" + ], + "signals": [ + "C1", + "C3", + "N1" ] } }, { - "page": "node-infrastructure/run-a-validator/requirements.md", - "category": "reference", - "relevant_to": [ - "set-up-polkadot-validator-node", - "onboard-polkadot-validator" - ], - "notes": "Covers hardware specs, VPS provider recommendations, minimum bond, minimum self-stake (10,000 DOT), and minimum commission (10%). No procedural steps or CLI commands. R2 fires for hardware spec listings and staking parameter tables; R3 for standalone parameter descriptions (min bond, self-stake, commission). C1 fires for design rationale explaining why self-stake and commission minimums are enforced. Reference and conceptual tie at 7; Rule 4 fires first and Rule 7 tiebreak favors reference. Essential pre-reading context for any validator setup skill.", + "category": "not_applicable", + "page": "parachains/integrations/wallets.md", + "reason": "Tools directory listing hot wallets (Nova, Talisman, SubWallet), cold wallets (Ledger, Polkadot Vault), and wallet tools (LunoKit) with external reference links only. Same pattern as indexers/oracles: conceptual intro scores higher (7) than not_applicable (4), but N1 fires (grid-card link directory) and Rule 1 takes precedence.", "scoring": { - "signals": [ - "R2", - "R3", - "C1", - "C3" - ], + "category_ambiguous": true, "category_scores": { - "procedural": 0, - "reference": 7, "conceptual": 7, - "not_applicable": 0 + "not_applicable": 4, + "procedural": 0, + "reference": 0 }, - "category_ambiguous": true, "contending_categories": [ - "reference", + "not_applicable", "conceptual" + ], + "signals": [ + "C1", + "C3", + "N1" ] } }, { - "page": "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md", - "category": "conceptual", - "relevant_to": [ - "onboard-polkadot-validator", - "start-validating-on-polkadot", - "set-up-polkadot-validator-node" - ], - "notes": "Pure conceptual page explaining the slashing system: offense types (invalid votes, equivocations), slash calculation formula, disabling mechanism, and reputation changes. Mermaid flowcharts illustrate formula outcomes for minor/moderate/major offenses. Historical incident table included. No CLI commands, no runnable code. C1+C2+C3 all fire; R3 for formula parameter descriptions. Useful background for any validator operating skill.", + "category": "not_applicable", + "page": "policies/ai-chatbot-policy.md", + "reason": "Legal policy page outlining AI chatbot usage terms and conditions. Content is legal boilerplate included via external snippet reference. N2 fires.", "scoring": { - "signals": [ - "R3", - "C1", - "C2", - "C3" - ], "category_scores": { + "conceptual": 0, + "not_applicable": 4, "procedural": 0, - "reference": 3, - "conceptual": 10, - "not_applicable": 0 - } + "reference": 0 + }, + "signals": [ + "N2" + ] } }, { - "page": "node-infrastructure/run-a-validator/staking-mechanics/rewards.md", - "category": "conceptual", - "relevant_to": [ - "onboard-polkadot-validator", - "start-validating-on-polkadot", - "set-up-polkadot-validator-node" - ], - "notes": "Explains era points, reward variance between para-validators and non-para-validators, payout scheme (equal base reward regardless of stake), running multiple validators, and commission-based nominator payment distribution. Multiple mermaid flowcharts illustrate reward split scenarios. No CLI commands, no runnable code. C1+C2+C3 all fire; R3 for commission rate and era point parameter descriptions. Useful background for any validator economics question.", + "category": "not_applicable", + "page": "policies/cookie-policy.md", + "reason": "Legal policy page explaining cookie usage on the site. Content is legal boilerplate included via external snippet reference. N2 fires.", "scoring": { - "signals": [ - "R3", - "C1", - "C2", - "C3" - ], "category_scores": { + "conceptual": 0, + "not_applicable": 4, "procedural": 0, - "reference": 3, - "conceptual": 10, - "not_applicable": 0 - } + "reference": 0 + }, + "signals": [ + "N2" + ] } }, { - "page": "parachains/interoperability/get-started.md", - "category": "conceptual", - "relevant_to": [ - "open-hrmp-channel-between-parachains", - "open-hrmp-channel-with-system-parachain", - "estimate-xcm-fees-teleport", - "replay-dry-run-xcm-chopsticks", - "transfer-assets-parachains-paraspell", - "bridge-eth-to-polkadot-snowbridge" - ], - "notes": "Conceptual introduction to XCM: the four principles (asynchronous, absolute, asymmetric, agnostic), the XCM tech stack diagram, core functionalities (programmability, multichain decomposition, bridging), and an annotated first-look example showing WithdrawAsset/BuyExecution/DepositAsset instructions. No procedural steps, no CLI commands, no source files to create. Code blocks are illustrative only (C3). Useful background for any XCM-related skill.", + "category": "not_applicable", + "page": "policies/privacy-policy.md", + "reason": "Legal policy page describing personal data collection and processing practices. Content is legal boilerplate included via external snippet reference. N2 fires.", "scoring": { - "signals": [ - "R3", - "C1", - "C2", - "C3" - ], "category_scores": { + "conceptual": 0, + "not_applicable": 4, "procedural": 0, - "reference": 3, - "conceptual": 10, - "not_applicable": 0 - } + "reference": 0 + }, + "signals": [ + "N2" + ] } }, { - "page": "parachains/runtime-maintenance/storage-migrations.md", - "category": "reference", - "relevant_to": [ - "perform-runtime-upgrade", - "create-frame-pallet", - "add-existing-pallet-to-runtime" - ], - "notes": "Reference guide for storage migrations in Polkadot SDK runtimes: explains when migrations are required (type changes, key changes, enum reordering), the OnRuntimeUpgrade trait (on_runtime_upgrade, pre_upgrade, post_upgrade), VersionedMigration pattern, single-block vs multi-block migration tradeoffs, and migration file organization. No numbered action steps or CLI commands. Code blocks are illustrative patterns pulled from the SDK (--8<-- includes), not files to create. R1 fires for OnRuntimeUpgrade trait method table; R3 for VersionedMigration type parameter descriptions. C1 fires for single-block vs multi-block rationale. Ambiguous between reference (8) and conceptual (7); Rule 4 fires first. Essential supplementary reading for any runtime upgrade or pallet modification skill.", + "category": "not_applicable", + "page": "policies/terms-of-use.md", + "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet — pure legal boilerplate with no procedural, reference, or conceptual developer content.", "scoring": { - "signals": [ - "R1", - "R3", - "C1", - "C3" - ], "category_scores": { + "conceptual": 0, + "not_applicable": 4, "procedural": 0, - "reference": 8, - "conceptual": 7, - "not_applicable": 0 + "reference": 0 }, - "category_ambiguous": true, - "contending_categories": [ - "reference", - "conceptual" + "signals": [ + "N2" ] } }, { - "page": "reference/glossary.md", - "category": "conceptual", - "relevant_to": [ - "deploy-erc20-token-hardhat", - "set-up-parachain-template", - "submit-governance-proposal", - "configure-runtime-pallet" - ], - "notes": "General-purpose ecosystem glossary covering consensus, pallets, extrinsics, accounts, and runtime concepts. Broadly useful as background context for any Polkadot SDK skill but provides no procedural content.", + "category": "not_applicable", + "page": "reference/tools/xcm-tools.md", + "reason": "Survey/index page presenting five XCM tools (Moonsong Labs XCM Tools, ParaSpell, Astar XCM Tools, Chopsticks, Moonbeam XCM SDK). Each section provides a short description and minimal bootstrap commands, then defers substantive usage to external documentation. No complete self-contained XCM workflow; primary function is discovery and navigation to the right tool. N1 fires (score 4), triggering Rule 1.", "scoring": { + "category_scores": { + "conceptual": 5, + "not_applicable": 4, + "procedural": 9, + "reference": 0 + }, "signals": [ - "R3", + "P2", + "P4", "C1", - "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 3, - "conceptual": 7, - "not_applicable": 0 - } + "N1" + ] } }, { - "page": "reference/governance/origins-tracks.md", - "category": "conceptual", - "relevant_to": [ - "submit-governance-proposal", - "vote-on-referendum", - "create-opengov-referendum" - ], - "notes": "Conceptual overview of OpenGov origins and tracks — explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", + "category": "not_applicable", + "page": "smart-contracts/get-started.md", + "reason": "Navigation hub / landing page. Content is primarily link tables across Quick Starts, Build and Test, Cookbooks, Libraries, Integrations, and Precompiles sections. The Ethereum Tool Differences block is brief conceptual prose but secondary to the navigation function. No procedures, CLI commands, or files to create.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 4, + "procedural": 0, + "reference": 0 + }, "signals": [ - "R3", "C1", - "C3" - ], + "C3", + "N1" + ] + } + }, + { + "category": "not_applicable", + "page": "smart-contracts/overview.md", + "reason": "Landing/overview page. Explains the dual-VM architecture and Ethereum compatibility at a high level, then ends with a card grid pointing to Get Started, Cookbook, Ethereum Developers, and Precompiles sections. No CLI commands, no files to create, no verifiable outcome.", + "scoring": { "category_scores": { - "procedural": 0, - "reference": 3, "conceptual": 7, - "not_applicable": 0 - } + "not_applicable": 4, + "procedural": 0, + "reference": 0 + }, + "signals": [ + "C1", + "C3", + "N1" + ] } - }, + } + ], + "project_id": "polkadot-docs", + "schema_version": "2", + "scoring_rubric_version": "1.0", + "supplementary_only": [ { - "page": "reference/parachains/accounts.md", - "category": "reference", + "category": "conceptual", + "notes": "Conceptual overview comparing three smart contract integration approaches for Polkadot SDK runtimes: pallet-revive (PVM/EVM dual backend), Frontier (full Ethereum compatibility layer), and pallet-contracts (legacy Wasm). No procedural steps, CLI commands, or source files. Includes a language/compiler comparison table (C2) and design rationale for each approach (C1). Useful background when deciding which smart contract pallet to add to a parachain runtime.", + "page": "parachains/customize-runtime/add-smart-contract-functionality.md", "relevant_to": [ - "manage-account-balances", - "validate-ss58-address", - "configure-runtime-pallet", - "set-up-parachain-template" + "add-existing-pallet-to-runtime", + "configure-multiple-pallet-instances" ], - "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points — but the page's primary purpose is deep reference exposition, not a task workflow.", "scoring": { - "signals": [ - "P2", - "P4", - "R1", - "R2", - "R3", - "C1", - "C2" - ], "category_scores": { - "procedural": 9, - "reference": 12, - "conceptual": 8, - "not_applicable": 0 + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 }, - "category_ambiguous": true, - "contending_categories": [ - "reference", - "guide" + "signals": [ + "C1", + "C2", + "C3" ] } }, { - "page": "reference/parachains/blocks-transactions-fees/blocks.md", - "category": "reference", + "category": "conceptual", + "notes": "Explains the three-step off-boarding process (chill, purge session keys, unbond) conceptually. No CLI commands or code blocks; steps are described in prose referencing UI actions on Polkadot.js Apps. Useful as background context for the full validator lifecycle.", + "page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/stop-validating.md", "relevant_to": [ - "set-up-parachain-template", - "implement-custom-consensus", - "configure-runtime-pallet" + "onboard-polkadot-validator", + "start-validating-on-polkadot", + "manage-validator-session-keys" ], - "notes": "Reference page explaining block structure (header components), the block production lifecycle (initialize, finalize), and the block import queue traits (ImportQueue, BasicQueue, Verifier, BlockImport). Numbered lists describe internal system operations, not developer actions. No CLI commands or file-creation steps.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ - "R1", - "R2", - "R3", "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 12, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/blocks-transactions-fees/fees.md", - "category": "reference", + "category": "conceptual", + "notes": "Short page explaining the chill/chillOther extrinsics and their timing behavior (era-based eligibility). No CLI, no code blocks; UI step references Polkadot.js Apps. Useful as conceptual context for any validator operational skill.", + "page": "node-infrastructure/run-a-validator/operational-tasks/pause-validating.md", "relevant_to": [ - "benchmark-pallet", - "customize-runtime-fees" + "onboard-polkadot-validator", + "start-validating-on-polkadot" ], - "notes": "Reference material covering the transaction weight system, fee calculation formula (base + weight + length fees), dispatch classes (Normal/Operational/Mandatory), weight annotations, and custom fee/weight implementations. Useful supplementary context for any skill involving pallet weight benchmarking or runtime fee customization.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 3 + }, "signals": [ - "R1", - "R2", "R3", "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 12, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/blocks-transactions-fees/transactions.md", "category": "reference", + "notes": "Documents the Staking Operator proxy type: permission model, extrinsic comparison table (Staking vs StakingOperator), 4-step setup procedure, and security properties. Ambiguous between reference and conceptual (scores 12 vs 10). Classified as reference because the extrinsic permission table (R1=5) and parameter specs for proxy.addProxy (R2=4) are the primary value; the mermaid diagram and security rationale push conceptual close but reference rule fires first.", + "page": "node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy.md", "relevant_to": [ - "submit-signed-transaction", - "construct-custom-extrinsic" + "set-up-polkadot-validator-node", + "manage-validator-session-keys", + "onboard-polkadot-validator", + "set-up-validator-monitoring-stack" ], - "notes": "Ambiguous between reference and conceptual (reference=12, conceptual=10, diff=2); rule 4 fires (reference >= 7, procedural < 10). Covers transaction types (signed/unsigned/inherent), data structure fields, signed extensions, a numbered 5-step construction walkthrough, encoding format, lifecycle, and mortality. Strong supplementary reference for any skill involving transaction submission or custom extrinsic development.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 6, + "reference": 12 + }, + "contending_categories": [ + "reference", + "conceptual" + ], "signals": [ "P1", "R1", @@ -3250,1016 +3198,1068 @@ "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 6, - "reference": 12, - "conceptual": 10, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "reference", - "conceptual" ] } }, { - "page": "reference/parachains/consensus/async-backing.md", "category": "reference", + "notes": "Covers hardware specs, VPS provider recommendations, minimum bond, minimum self-stake (10,000 DOT), and minimum commission (10%). No procedural steps or CLI commands. R2 fires for hardware spec listings and staking parameter tables; R3 for standalone parameter descriptions (min bond, self-stake, commission). C1 fires for design rationale explaining why self-stake and commission minimums are enforced. Reference and conceptual tie at 7; Rule 4 fires first and Rule 7 tiebreak favors reference. Essential pre-reading context for any validator setup skill.", + "page": "node-infrastructure/run-a-validator/requirements.md", "relevant_to": [ - "enable-async-backing", - "configure-parachain-consensus" + "set-up-polkadot-validator-node", + "onboard-polkadot-validator" ], - "notes": "Ambiguous between reference and conceptual (reference=12, conceptual=10, diff=2); rule 4 fires (reference >= 7, procedural < 10). Documents the two AsyncBackingParams configuration fields (max_candidate_depth, allowed_ancestry_len) with docs.rs links, alongside a thorough conceptual comparison of synchronous vs asynchronous backing using Gantt diagrams. Relevant supplementary context for any skill involving parachain consensus configuration.", "scoring": { - "signals": [ - "R1", - "R2", - "R3", - "C1", - "C2", - "C3" - ], + "category_ambiguous": true, "category_scores": { + "conceptual": 7, + "not_applicable": 0, "procedural": 0, - "reference": 12, - "conceptual": 10, - "not_applicable": 0 + "reference": 7 }, - "category_ambiguous": true, "contending_categories": [ "reference", "conceptual" + ], + "signals": [ + "R2", + "R3", + "C1", + "C3" ] } }, { - "page": "reference/parachains/consensus/elastic-scaling.md", "category": "conceptual", + "notes": "Pure conceptual page explaining the slashing system: offense types (invalid votes, equivocations), slash calculation formula, disabling mechanism, and reputation changes. Mermaid flowcharts illustrate formula outcomes for minor/moderate/major offenses. Historical incident table included. No CLI commands, no runnable code. C1+C2+C3 all fire; R3 for formula parameter descriptions. Useful background for any validator operating skill.", + "page": "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md", "relevant_to": [ - "acquire-coretime", - "enable-elastic-scaling" + "onboard-polkadot-validator", + "start-validating-on-polkadot", + "set-up-polkadot-validator-node" ], - "notes": "Pure conceptual page explaining how elastic scaling works, its performance characteristics, benefits, and use cases. No API references, no configuration option specs, no procedural steps for the reader. Sequence diagrams illustrate single-core vs multi-core validation timing. Useful background context for coretime acquisition or elastic scaling enablement skills.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 3 + }, "signals": [ + "R3", "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/consensus/inclusion-pipeline.md", "category": "conceptual", + "notes": "Explains era points, reward variance between para-validators and non-para-validators, payout scheme (equal base reward regardless of stake), running multiple validators, and commission-based nominator payment distribution. Multiple mermaid flowcharts illustrate reward split scenarios. No CLI commands, no runnable code. C1+C2+C3 all fire; R3 for commission rate and era point parameter descriptions. Useful background for any validator economics question.", + "page": "node-infrastructure/run-a-validator/staking-mechanics/rewards.md", "relevant_to": [ - "set-up-parachain-template", - "launch-parachain-testnet" + "onboard-polkadot-validator", + "start-validating-on-polkadot", + "set-up-polkadot-validator-node" ], - "notes": "Explains the multi-stage validation pipeline (Context, Generation, Backing, Inclusion) through which every parachain block is validated before relay-chain finalization. Includes a mermaid flowchart. Foundational background for any skill covering parachain development or deployment.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 3 + }, "signals": [ + "R3", "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/cryptography.md", "category": "conceptual", + "notes": "Conceptual introduction to XCM: the four principles (asynchronous, absolute, asymmetric, agnostic), the XCM tech stack diagram, core functionalities (programmability, multichain decomposition, bridging), and an annotated first-look example showing WithdrawAsset/BuyExecution/DepositAsset instructions. No procedural steps, no CLI commands, no source files to create. Code blocks are illustrative only (C3). Useful background for any XCM-related skill.", + "page": "parachains/interoperability/get-started.md", "relevant_to": [ - "generate-account-keys", - "sign-transactions-polkadot-js" + "open-hrmp-channel-between-parachains", + "open-hrmp-channel-with-system-parachain", + "estimate-xcm-fees-teleport", + "replay-dry-run-xcm-chopsticks", + "transfer-assets-parachains-paraspell", + "bridge-eth-to-polkadot-snowbridge" ], - "notes": "Conceptual overview of cryptographic primitives used in Polkadot: hash functions (Blake2), symmetric vs asymmetric encryption trade-offs, digital signatures, and elliptic curve schemes (ECDSA/secp256k1, Ed25519, SR25519). No diagrams or runnable code. Background reading for skills involving key management or transaction signing.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 3 + }, "signals": [ + "R3", "C1", + "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/data-encoding.md", "category": "reference", + "notes": "Reference guide for storage migrations in Polkadot SDK runtimes: explains when migrations are required (type changes, key changes, enum reordering), the OnRuntimeUpgrade trait (on_runtime_upgrade, pre_upgrade, post_upgrade), VersionedMigration pattern, single-block vs multi-block migration tradeoffs, and migration file organization. No numbered action steps or CLI commands. Code blocks are illustrative patterns pulled from the SDK (--8<-- includes), not files to create. R1 fires for OnRuntimeUpgrade trait method table; R3 for VersionedMigration type parameter descriptions. C1 fires for single-block vs multi-block rationale. Ambiguous between reference (8) and conceptual (7); Rule 4 fires first. Essential supplementary reading for any runtime upgrade or pallet modification skill.", + "page": "parachains/runtime-maintenance/storage-migrations.md", "relevant_to": [ - "build-parachain-runtime", - "set-up-parachain-template" + "perform-runtime-upgrade", + "create-frame-pallet", + "add-existing-pallet-to-runtime" ], - "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) — rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", "scoring": { - "signals": [ - "R1", - "R2", - "R3", - "C1", - "C2", - "C3" - ], + "category_ambiguous": true, "category_scores": { + "conceptual": 7, + "not_applicable": 0, "procedural": 0, - "reference": 12, - "conceptual": 10, - "not_applicable": 0 + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "reference", "conceptual" + ], + "signals": [ + "R1", + "R3", + "C1", + "C3" ] } }, { - "page": "reference/parachains/interoperability.md", "category": "conceptual", + "notes": "General-purpose ecosystem glossary covering consensus, pallets, extrinsics, accounts, and runtime concepts. Broadly useful as background context for any Polkadot SDK skill but provides no procedural content.", + "page": "reference/glossary.md", "relevant_to": [ - "send-xcm-message", - "transfer-assets-xcm", - "get-started-xcm" + "deploy-erc20-token-hardhat", + "set-up-parachain-template", + "submit-governance-proposal", + "configure-runtime-pallet" ], - "notes": "High-level conceptual overview of interoperability in Polkadot: why it matters, XCM as the cross-consensus messaging standard, and bridges to external networks (Ethereum, Bitcoin). No code or diagrams. Background reading for XCM-related skills.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 3 + }, "signals": [ + "R3", "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/networks.md", "category": "conceptual", + "notes": "Conceptual overview of OpenGov origins and tracks — explains privilege levels, proposal lifecycle stages (Preparation, Voting, Confirmation, Enactment), and parameter customization per track. No procedural steps or commands; purely explanatory background for governance-related skills.", + "page": "reference/governance/origins-tracks.md", "relevant_to": [ - "deploy-parachain-paseo", - "set-up-parachain-template", - "test-parachain-locally" + "submit-governance-proposal", + "vote-on-referendum", + "create-opengov-referendum" ], - "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local → Paseo → production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 3 + }, "signals": [ - "P1", + "R3", "C1", - "C2", "C3" - ], - "category_scores": { - "procedural": 6, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/parachains/node-and-runtime.md", - "category": "conceptual", + "category": "reference", + "notes": "Comprehensive reference on Polkadot SDK account data structures (StorageMap, AccountInfo), reference counters (providers, consumers, sufficients), balance types (free, locked, reserved, spendable), SS58 address format, and address validation via subkey and Polkadot.js API. Category is reference; flagged ambiguous against guide because subkey CLI commands (P2) and a verifiable outcome (P4) narrow the procedural gap to 3 points — but the page's primary purpose is deep reference exposition, not a task workflow.", + "page": "reference/parachains/accounts.md", "relevant_to": [ - "set-up-parachain-template", - "configure-parachain-node", - "build-custom-runtime" + "manage-account-balances", + "validate-ss58-address", + "configure-runtime-pallet", + "set-up-parachain-template" ], - "notes": "Explains the client/runtime architectural split (meta-protocol vs protocol), forkless upgrade model, SCALE encoding, runtime APIs, and host functions. Pure conceptual background with no commands or code blocks.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 8, + "not_applicable": 0, + "procedural": 9, + "reference": 12 + }, + "contending_categories": [ + "reference", + "guide" + ], "signals": [ + "P2", + "P4", + "R1", + "R2", + "R3", "C1", - "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - } + "C2" + ] } }, { - "page": "reference/parachains/randomness.md", - "category": "conceptual", + "category": "reference", + "notes": "Reference page explaining block structure (header components), the block production lifecycle (initialize, finalize), and the block import queue traits (ImportQueue, BasicQueue, Verifier, BlockImport). Numbered lists describe internal system operations, not developer actions. No CLI commands or file-creation steps.", + "page": "reference/parachains/blocks-transactions-fees/blocks.md", "relevant_to": [ - "set-up-validator-node", - "configure-babe-consensus", - "understand-pos-consensus" + "set-up-parachain-template", + "implement-custom-consensus", + "configure-runtime-pallet" ], - "notes": "Conceptual explanation of VRF-based randomness in Polkadot PoS, including how BABE slots work, the RANDAO alternative, and VDF security model. Includes a diagram. No procedural steps or runnable code.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 12 + }, "signals": [ + "R1", + "R2", + "R3", "C1", - "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/polkadot-hub/assets.md", "category": "reference", + "notes": "Reference material covering the transaction weight system, fee calculation formula (base + weight + length fees), dispatch classes (Normal/Operational/Mandatory), weight annotations, and custom fee/weight implementations. Useful supplementary context for any skill involving pallet weight benchmarking or runtime fee customization.", + "page": "reference/parachains/blocks-transactions-fees/fees.md", "relevant_to": [ - "register-foreign-asset", - "register-local-asset", - "convert-assets", - "transfer-assets-xcm" + "benchmark-pallet", + "customize-runtime-fees" ], - "notes": "Reference overview of the Assets pallet on Polkadot Hub: asset structure fields, common asset IDs table (USDt/USDC), roles and permissions, freeze/thaw and delegated transfer mechanics, and foreign asset handling. Ends with navigation cards pointing to procedural guides. No runnable steps on this page.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 12 + }, "signals": [ "R1", "R2", "R3", "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 12, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/polkadot-hub/bridging.md", "category": "reference", + "notes": "Ambiguous between reference and conceptual (reference=12, conceptual=10, diff=2); rule 4 fires (reference >= 7, procedural < 10). Covers transaction types (signed/unsigned/inherent), data structure fields, signed extensions, a numbered 5-step construction walkthrough, encoding format, lifecycle, and mortality. Strong supplementary reference for any skill involving transaction submission or custom extrinsic development.", + "page": "reference/parachains/blocks-transactions-fees/transactions.md", "relevant_to": [ - "bridge-polkadot-ethereum-snowbridge", - "send-xcm-cross-chain-message", - "set-up-bridge-hub" + "submit-signed-transaction", + "construct-custom-extrinsic" ], - "notes": "Reference overview of the Bridge Hub system parachain covering trustless bridging via on-chain light clients (BEEFY/GRANDPA), key pallets (Bridge GRANDPA, Bridge Parachains, Bridge Messages, XCM Bridge), and deployed bridges (Snowbridge, Hyperbridge, Polkadot<>Kusama). Ambiguous between reference and conceptual (8 vs 7): R1 fires on structured pallet listing; C1 fires on trustless bridging architecture prose. Rule 4 wins (reference_score >= 7, procedural < 10).", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 6, + "reference": 12 + }, + "contending_categories": [ + "reference", + "conceptual" + ], "signals": [ + "P1", "R1", + "R2", "R3", "C1", + "C2", "C3" - ], + ] + } + }, + { + "category": "reference", + "notes": "Ambiguous between reference and conceptual (reference=12, conceptual=10, diff=2); rule 4 fires (reference >= 7, procedural < 10). Documents the two AsyncBackingParams configuration fields (max_candidate_depth, allowed_ancestry_len) with docs.rs links, alongside a thorough conceptual comparison of synchronous vs asynchronous backing using Gantt diagrams. Relevant supplementary context for any skill involving parachain consensus configuration.", + "page": "reference/parachains/consensus/async-backing.md", + "relevant_to": [ + "enable-async-backing", + "configure-parachain-consensus" + ], + "scoring": { + "category_ambiguous": true, "category_scores": { + "conceptual": 10, + "not_applicable": 0, "procedural": 0, - "reference": 8, - "conceptual": 7, - "not_applicable": 0 + "reference": 12 }, - "category_ambiguous": true, "contending_categories": [ "reference", "conceptual" + ], + "signals": [ + "R1", + "R2", + "R3", + "C1", + "C2", + "C3" ] } }, { - "page": "reference/polkadot-hub/collectives-and-daos.md", "category": "conceptual", + "notes": "Pure conceptual page explaining how elastic scaling works, its performance characteristics, benefits, and use cases. No API references, no configuration option specs, no procedural steps for the reader. Sequence diagrams illustrate single-core vs multi-core validation timing. Useful background context for coretime acquisition or elastic scaling enablement skills.", + "page": "reference/parachains/consensus/elastic-scaling.md", "relevant_to": [ - "join-technical-fellowship", - "participate-polkadot-governance", - "create-on-chain-collective" + "acquire-coretime", + "enable-elastic-scaling" ], - "notes": "Short conceptual overview of the Collectives chain and its key governance bodies (Technical Fellowship and Polkadot Alliance). Explains purpose, establishment via Referendum 81, and cross-network governance via Bridge Hub. No reference tables, no procedural steps, no code.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", + "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/polkadot-hub/consensus-and-security/agile-coretime.md", "category": "conceptual", + "notes": "Explains the multi-stage validation pipeline (Context, Generation, Backing, Inclusion) through which every parachain block is validated before relay-chain finalization. Includes a mermaid flowchart. Foundational background for any skill covering parachain development or deployment.", + "page": "reference/parachains/consensus/inclusion-pipeline.md", "relevant_to": [ - "purchase-bulk-coretime", - "set-up-parachain-template" + "set-up-parachain-template", + "launch-parachain-testnet" ], - "notes": "Explains coretime scheduling concepts — bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/polkadot-hub/consensus-and-security/pos-consensus.md", "category": "conceptual", + "notes": "Conceptual overview of cryptographic primitives used in Polkadot: hash functions (Blake2), symmetric vs asymmetric encryption trade-offs, digital signatures, and elliptic curve schemes (ECDSA/secp256k1, Ed25519, SR25519). No diagrams or runnable code. Background reading for skills involving key management or transaction signing.", + "page": "reference/parachains/cryptography.md", "relevant_to": [ - "set-up-validator-node", - "stake-dot-tokens" + "generate-account-keys", + "sign-transactions-polkadot-js" ], - "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps — entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", - "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/polkadot-hub/consensus-and-security/relay-chain.md", - "category": "conceptual", + "category": "reference", + "notes": "SCALE codec reference: function signatures for Encode, Decode, CompactAs, HasCompact, and EncodeLike traits; a data-types encoding table mapping Rust types to their SCALE byte representations; and a multi-language library listing. Scores are close between reference (12) and conceptual (10) — rule 4 assigns reference; ambiguity flag set. Useful supplementary context for any skill involving Polkadot SDK runtime or parachain development.", + "page": "reference/parachains/data-encoding.md", "relevant_to": [ - "set-up-parachain-template", - "purchase-bulk-coretime" + "build-parachain-runtime", + "set-up-parachain-template" ], - "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram — no procedural content.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 12 + }, + "contending_categories": [ + "reference", + "conceptual" + ], "signals": [ + "R1", + "R2", + "R3", "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/polkadot-hub/people-and-identity.md", "category": "conceptual", + "notes": "High-level conceptual overview of interoperability in Polkadot: why it matters, XCM as the cross-consensus messaging standard, and bridges to external networks (Ethereum, Bitcoin). No code or diagrams. Background reading for XCM-related skills.", + "page": "reference/parachains/interoperability.md", "relevant_to": [ - "establish-on-chain-identity" + "send-xcm-message", + "transfer-assets-xcm", + "get-started-xcm" ], - "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) — not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ - "P1", - "R3", "C1", "C3" - ], + ] + } + }, + { + "category": "conceptual", + "notes": "Describes the Polkadot network landscape (MainNet, Paseo TestNet, Kusama, Westend, local) and the recommended three-stage development progression (local → Paseo → production). Includes a mermaid development-flow diagram and descriptions of Zombienet and Chopsticks. The numbered development path fires P1 (procedural=6) but conceptual=10 wins by rule 5. No ambiguity (difference of 4 points). Relevant supplementary context for any skill covering parachain or dApp deployment and testing.", + "page": "reference/parachains/networks.md", + "relevant_to": [ + "deploy-parachain-paseo", + "set-up-parachain-template", + "test-parachain-locally" + ], + "scoring": { "category_scores": { + "conceptual": 10, + "not_applicable": 0, "procedural": 6, - "reference": 3, - "conceptual": 7, - "not_applicable": 0 + "reference": 0 }, - "category_ambiguous": true, - "contending_categories": [ - "conceptual", - "guide" + "signals": [ + "P1", + "C1", + "C2", + "C3" ] } }, { - "page": "reference/polkadot-hub/smart-contracts.md", "category": "conceptual", + "notes": "Explains the client/runtime architectural split (meta-protocol vs protocol), forkless upgrade model, SCALE encoding, runtime APIs, and host functions. Pure conceptual background with no commands or code blocks.", + "page": "reference/parachains/node-and-runtime.md", "relevant_to": [ - "deploy-basic-contract-remix", - "deploy-erc20-token-remix" + "set-up-parachain-template", + "configure-parachain-node", + "build-custom-runtime" ], - "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps — serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "reference/tools/light-clients.md", "category": "conceptual", + "notes": "Conceptual explanation of VRF-based randomness in Polkadot PoS, including how BABE slots work, the RANDAO alternative, and VDF security model. Includes a diagram. No procedural steps or runnable code.", + "page": "reference/parachains/randomness.md", "relevant_to": [ - "interact-with-chain-dedot", - "set-up-chopsticks-fork" + "set-up-validator-node", + "configure-babe-consensus", + "understand-pos-consensus" ], - "notes": "Explains light client architecture, the trust-minimized verification model, comparison with full nodes/JSON-RPC, and PAPI/Smoldot integration. No procedural content. Useful background for skills that use Dedot with Smoldot or Chopsticks (which also uses Smoldot internally).", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "reference/tools/papi.md", "category": "reference", + "notes": "Reference overview of the Assets pallet on Polkadot Hub: asset structure fields, common asset IDs table (USDt/USDC), roles and permissions, freeze/thaw and delegated transfer mechanics, and foreign asset handling. Ends with navigation cards pointing to procedural guides. No runnable steps on this page.", + "page": "reference/polkadot-hub/assets.md", "relevant_to": [ - "interact-with-chain-polkadart", - "use-polkadot-js-api" + "register-foreign-asset", + "register-local-asset", + "convert-assets", + "transfer-assets-xcm" ], - "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) — reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 12 + }, "signals": [ - "P2", "R1", + "R2", "R3", - "C1" - ], + "C1", + "C3" + ] + } + }, + { + "category": "reference", + "notes": "Reference overview of the Bridge Hub system parachain covering trustless bridging via on-chain light clients (BEEFY/GRANDPA), key pallets (Bridge GRANDPA, Bridge Parachains, Bridge Messages, XCM Bridge), and deployed bridges (Snowbridge, Hyperbridge, Polkadot<>Kusama). Ambiguous between reference and conceptual (8 vs 7): R1 fires on structured pallet listing; C1 fires on trustless bridging architecture prose. Rule 4 wins (reference_score >= 7, procedural < 10).", + "page": "reference/polkadot-hub/bridging.md", + "relevant_to": [ + "bridge-polkadot-ethereum-snowbridge", + "send-xcm-cross-chain-message", + "set-up-bridge-hub" + ], + "scoring": { + "category_ambiguous": true, "category_scores": { - "procedural": 5, - "reference": 8, - "conceptual": 5, - "not_applicable": 0 + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "reference", "conceptual" + ], + "signals": [ + "R1", + "R3", + "C1", + "C3" ] } }, { - "page": "reference/tools/paraspell.md", "category": "conceptual", + "notes": "Short conceptual overview of the Collectives chain and its key governance bodies (Technical Fellowship and Polkadot Alliance). Explains purpose, establishment via Referendum 81, and cross-network governance via Bridge Hub. No reference tables, no procedural steps, no code.", + "page": "reference/polkadot-hub/collectives-and-daos.md", "relevant_to": [ - "transfer-assets-parachains" + "join-technical-fellowship", + "participate-polkadot-governance", + "create-on-chain-collective" ], - "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual — explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", "scoring": { - "signals": [ - "P2", - "C1" - ], "category_scores": { - "procedural": 5, - "reference": 0, - "conceptual": 5, - "not_applicable": 0 - } + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, + "signals": [ + "C1", + "C3" + ] } }, { - "page": "reference/tools/sidecar.md", - "category": "reference", + "category": "conceptual", + "notes": "Explains coretime scheduling concepts — bulk vs on-demand, interlacing, splitting. Pure conceptual overview with mermaid diagram but no executable steps or CLI commands.", + "page": "reference/polkadot-hub/consensus-and-security/agile-coretime.md", "relevant_to": [ - "interact-polkadot-node-py-substrate", - "interact-polkadot-node-subxt" + "purchase-bulk-coretime", + "set-up-parachain-template" ], - "notes": "Installation and basic usage of Substrate API Sidecar, a REST caching layer for Polkadot SDK nodes. Tied procedural (9) and reference (9) scores make this ambiguous: Rule 4 fires first (reference >= 7 AND procedural < 10) assigning 'reference', but the guide-like installation workflow is equally significant. Useful supplementary context for any skill that queries Polkadot chain state, offering a REST alternative to direct RPC or Rust/Python SDK approaches.", "scoring": { - "signals": [ - "P2", - "P4", - "R1", - "R2", - "C1" - ], "category_scores": { - "procedural": 9, - "reference": 9, - "conceptual": 5, - "not_applicable": 0 + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 }, - "category_ambiguous": true, - "contending_categories": [ - "guide", - "reference" + "signals": [ + "C1", + "C2", + "C3" ] } }, { - "page": "smart-contracts/explorers.md", "category": "conceptual", + "notes": "Deep architectural explanation of NPoS, BABE, GRANDPA, and BEEFY. No runnable steps — entirely conceptual with fork-choice diagram and external Web3 Foundation paper references.", + "page": "reference/polkadot-hub/consensus-and-security/pos-consensus.md", "relevant_to": [ - "set-up-hardhat-pvm", - "set-up-hardhat-evm", - "set-up-local-dev-node", - "connect-remix-polkadot" + "set-up-validator-node", + "stake-dot-tokens" ], - "notes": "Reference directory of available block explorers (BlockScout, Routescan, Subscan) for Polkadot Hub with descriptions, URLs, and screenshots. No procedural content. Useful supplementary context for any skill involving contract deployment or verification, as agents need explorer URLs for post-deployment steps. BlockScout and Routescan URLs are also referenced inline in the Hardhat config verification sections.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "smart-contracts/for-eth-devs/accounts.md", - "category": "reference", + "category": "conceptual", + "notes": "High-level overview of Polkadot relay chain architecture, DOT token, blockspace, and JAM. Background material with architectural diagram — no procedural content.", + "page": "reference/polkadot-hub/consensus-and-security/relay-chain.md", "relevant_to": [ - "set-up-local-dev-node", - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix" + "set-up-parachain-template", + "purchase-bulk-coretime" ], - "notes": "Documents the AccountId32Mapper address conversion system (map_account, to_address, to_fallback_account_id, create1/create2), account mapping requirements, and security design for the Polkadot/Ethereum dual-format model. Essential context for any tutorial requiring wallet or address setup on Polkadot Hub. Flagged ambiguous: reference=8, conceptual=7, diff=1.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ - "P1", - "R1", - "R3", "C1", + "C2", "C3" - ], - "category_scores": { - "procedural": 6, - "reference": 8, - "conceptual": 7, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "reference", - "conceptual" ] } }, { - "page": "smart-contracts/for-eth-devs/blocks-transactions-fees.md", - "category": "reference", + "category": "conceptual", + "notes": "Contains numbered steps for contacting registrars, but the workflow is an offline human process (research, contact, submit docs, pay fee) — not programmatically executable via CLI or SDK. R3 fires for judgment classification descriptions. Scores: conceptual=7, procedural=6 (gap=1, within 3 pts). Category_ambiguous resolved by Rule 5 (conceptual>=5, procedural<10, reference<7) firing before Rule 6.", + "page": "reference/polkadot-hub/people-and-identity.md", "relevant_to": [ - "set-up-local-dev-node", - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix" + "establish-on-chain-identity" ], - "notes": "Reference documentation for EVM transaction types (Legacy, EIP-1559, EIP-2930, EIP-4844 via pallet_revive) and the multi-dimensional fee model (ref_time, proof_size, storage_deposit). Useful background for understanding gas estimation behavior in deployment tutorials.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 6, + "reference": 3 + }, + "contending_categories": [ + "conceptual", + "guide" + ], "signals": [ - "R1", - "R2", + "P1", "R3", "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 12, - "conceptual": 7, - "not_applicable": 0 - } + ] } }, { - "page": "smart-contracts/for-eth-devs/contract-deployment.md", - "category": "reference", + "category": "conceptual", + "notes": "Overview of REVM and PVM smart contract engines on Polkadot Hub. No commands, no source files to create, no numbered steps — serves as conceptual background for smart contract deployment skills. Card-grid 'Where to Go Next' links out to actual tutorials.", + "page": "reference/polkadot-hub/smart-contracts.md", "relevant_to": [ - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix", - "set-up-local-dev-node" + "deploy-basic-contract-remix", + "deploy-erc20-token-remix" ], - "notes": "Conceptual signals score highest (C1+C2+C3=10) since the page primarily explains REVM vs PVM deployment architecture differences, factory pattern constraints, and gas estimation behavior. Rule 4 assigns reference because reference_score=7 >= 7 and procedural_score=6 < 10. Flagged ambiguous: conceptual=10, reference=7, diff=3.", "scoring": { + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ - "P1", - "R2", - "R3", "C1", - "C2", "C3" - ], - "category_scores": { - "procedural": 6, - "reference": 7, - "conceptual": 10, - "not_applicable": 0 - }, - "category_ambiguous": true, - "contending_categories": [ - "conceptual", - "reference" ] } }, { - "page": "smart-contracts/for-eth-devs/dual-vm-stack.md", "category": "conceptual", + "notes": "Explains light client architecture, the trust-minimized verification model, comparison with full nodes/JSON-RPC, and PAPI/Smoldot integration. No procedural content. Useful background for skills that use Dedot with Smoldot or Chopsticks (which also uses Smoldot internally).", + "page": "reference/tools/light-clients.md", "relevant_to": [ - "set-up-local-dev-node-revive", - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix" + "interact-with-chain-dedot", + "set-up-chopsticks-fork" ], - "notes": "Architecture overview of Polkadot's dual VM stack (REVM vs PVM). Provides background context on why REVM is used for Ethereum compatibility and when to choose PVM. Useful as supplementary context for any smart contract development skill on Polkadot Hub.", "scoring": { + "category_scores": { + "conceptual": 10, + "not_applicable": 0, + "procedural": 0, + "reference": 0 + }, "signals": [ "C1", "C2", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 10, - "not_applicable": 0 - } + ] } }, { - "page": "smart-contracts/for-eth-devs/evm-vs-pvm.md", "category": "reference", + "notes": "API reference and quickstart for the Polkadot-API (PAPI) library. Covers API instantiation (Smoldot, WSS), reading chain data via constants/storage/runtime APIs, and sending transactions with tx and txFromCallData. Useful as supplementary context for any skill involving PAPI-based chain interaction. Classified as reference per Rule 4 (reference=8, procedural=5). Ambiguous with conceptual (diff=3) — reference wins because the bulk of the page catalogs API method patterns rather than explaining architectural rationale.", + "page": "reference/tools/papi.md", "relevant_to": [ - "set-up-local-dev-node-revive", - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix" + "interact-with-chain-polkadart", + "use-polkadot-js-api" ], - "notes": "Detailed technical reference comparing EVM and PVM architectures, gas models, memory limits, account management, and YUL IR translation differences. Reference wins over conceptual (12 vs 10) due to structured comparison tables, memory limit tables, and YUL opcode listings. Ambiguous with conceptual given the volume of architectural prose. Essential supplementary context for any skill involving PVM contract development.", "scoring": { - "signals": [ - "R1", - "R2", - "R3", - "C1", - "C2", - "C3" - ], + "category_ambiguous": true, "category_scores": { - "procedural": 0, - "reference": 12, - "conceptual": 10, - "not_applicable": 0 + "conceptual": 5, + "not_applicable": 0, + "procedural": 5, + "reference": 8 }, - "category_ambiguous": true, "contending_categories": [ "reference", "conceptual" + ], + "signals": [ + "P2", + "R1", + "R3", + "C1" ] } }, { - "page": "smart-contracts/for-eth-devs/gas-model.md", "category": "conceptual", + "notes": "Overview of the ParaSpell tool suite (XCM SDK, XCM API, XCM Router, XCM Analyser, XCM Visualizator, XCM Playground) with a brief npm install snippet and a navigation card linking to the transfer-assets-parachains tutorial. Primarily conceptual — explains the purpose and design of each component without walking through a workflow. Useful as supplementary context for XCM cross-chain transfer skills.", + "page": "reference/tools/paraspell.md", "relevant_to": [ - "set-up-local-dev-node-revive", - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix" + "transfer-assets-parachains" ], - "notes": "Conceptual explanation of how Polkadot Hub maps Ethereum gas onto its three-dimensional resource system (ref_time, proof_size, storage_deposit). Contains a mermaid transaction flow diagram and prose on dynamic pricing. No CLI commands or procedures. Relevant to any skill where gas estimation or fee handling matters.", "scoring": { - "signals": [ - "R3", - "C1", - "C2", - "C3" - ], "category_scores": { - "procedural": 0, - "reference": 3, - "conceptual": 10, - "not_applicable": 0 - } + "conceptual": 5, + "not_applicable": 0, + "procedural": 5, + "reference": 0 + }, + "signals": [ + "P2", + "C1" + ] } }, { - "page": "smart-contracts/for-eth-devs/json-rpc-apis.md", "category": "reference", + "notes": "Installation and basic usage of Substrate API Sidecar, a REST caching layer for Polkadot SDK nodes. Tied procedural (9) and reference (9) scores make this ambiguous: Rule 4 fires first (reference >= 7 AND procedural < 10) assigning 'reference', but the guide-like installation workflow is equally significant. Useful supplementary context for any skill that queries Polkadot chain state, offering a REST alternative to direct RPC or Rust/Python SDK approaches.", + "page": "reference/tools/sidecar.md", "relevant_to": [ - "set-up-local-dev-node-revive", - "deploy-erc20-token-hardhat", - "deploy-erc20-token-remix" + "interact-polkadot-node-py-substrate", + "interact-polkadot-node-subxt" ], - "notes": "Comprehensive reference for supported Ethereum JSON-RPC methods on Polkadot Hub, with parameter specs and curl examples for each method. High reference score (R1+R2+R3=12) from structured method documentation. Procedural score is 5 (curl commands present) but there is no ordered workflow or verifiable outcome, so rule 4 assigns reference. Useful supplementary context for any skill involving on-chain queries or transaction submission.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 5, + "not_applicable": 0, + "procedural": 9, + "reference": 9 + }, + "contending_categories": [ + "guide", + "reference" + ], "signals": [ "P2", + "P4", "R1", "R2", - "R3" - ], - "category_scores": { - "procedural": 5, - "reference": 12, - "conceptual": 0, - "not_applicable": 0 - } + "C1" + ] } }, { - "page": "smart-contracts/precompiles/eth-native.md", "category": "conceptual", + "notes": "Reference directory of available block explorers (BlockScout, Routescan, Subscan) for Polkadot Hub with descriptions, URLs, and screenshots. No procedural content. Useful supplementary context for any skill involving contract deployment or verification, as agents need explorer URLs for post-deployment steps. BlockScout and Routescan URLs are also referenced inline in the Hardhat config verification sections.", + "page": "smart-contracts/explorers.md", "relevant_to": [ - "interact-storage-precompile-remix", - "interact-system-precompile-remix", - "interact-xcm-precompile-remix" + "set-up-hardhat-pvm", + "set-up-hardhat-evm", + "set-up-local-dev-node", + "connect-remix-polkadot" ], - "notes": "Overview of Ethereum-native precompiles in Polkadot Hub's Revive pallet: explains what precompiles are, how to call them, and lists all standard precompile addresses. No workflow, no commands, no file creation. Conceptual=7 (C1+C3) vs reference=5 (R1); ambiguous (diff=2). Rule 5 assigns conceptual because conceptual>=5, procedural<10, and reference<7. Useful background for any skill involving these precompiles.", "scoring": { - "signals": [ - "R1", - "C1", - "C3" - ], "category_scores": { + "conceptual": 10, + "not_applicable": 0, "procedural": 0, - "reference": 5, - "conceptual": 7, - "not_applicable": 0 + "reference": 0 }, - "category_ambiguous": true, - "contending_categories": [ - "conceptual", - "reference" + "signals": [ + "C1", + "C2", + "C3" ] } }, { - "page": "reference/polkadot-hub/data-storage.md", "category": "reference", + "notes": "Documents the AccountId32Mapper address conversion system (map_account, to_address, to_fallback_account_id, create1/create2), account mapping requirements, and security design for the Polkadot/Ethereum dual-format model. Essential context for any tutorial requiring wallet or address setup on Polkadot Hub. Flagged ambiguous: reference=8, conceptual=7, diff=1.", + "page": "smart-contracts/for-eth-devs/accounts.md", "relevant_to": [ - "store-retrieve-data-bulletin-chain" + "set-up-local-dev-node", + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix" ], - "notes": "Full technical reference for the Bulletin Chain: all extrinsics (with origins), storage items, events, size limits, retrieval methods, and network endpoints. Useful context for the storage tutorial skill.", "scoring": { + "category_ambiguous": true, + "category_scores": { + "conceptual": 7, + "not_applicable": 0, + "procedural": 6, + "reference": 8 + }, + "contending_categories": [ + "reference", + "conceptual" + ], "signals": [ + "P1", "R1", - "R2", "R3", "C1", "C3" - ], - "category_scores": { - "procedural": 0, - "reference": 12, - "conceptual": 7, - "not_applicable": 0 - } - } - } - ], - "not_applicable": [ - { - "page": "get-support.md", - "category": "not_applicable", - "reason": "Community support hub listing channels (Telegram, Discord, Matrix, Stack Exchange, Reddit, YouTube, X/Twitter, Forum, Polkassembly). No procedural content, code, or tutorials.", - "scoring": { - "signals": [ - "N1", - "N2" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 0, - "not_applicable": 8 - } + ] } }, { - "page": "parachains/get-started.md", - "category": "not_applicable", - "reason": "Navigation landing page composed entirely of tables linking to other doc pages, grouped by topic (Quick Start, Launch, Customize, Testing, Runtime Upgrades, Interoperability, Integrations). No procedural steps, code, or substantive content of its own.", + "category": "reference", + "notes": "Reference documentation for EVM transaction types (Legacy, EIP-1559, EIP-2930, EIP-4844 via pallet_revive) and the multi-dimensional fee model (ref_time, proof_size, storage_deposit). Useful background for understanding gas estimation behavior in deployment tutorials.", + "page": "smart-contracts/for-eth-devs/blocks-transactions-fees.md", + "relevant_to": [ + "set-up-local-dev-node", + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix" + ], "scoring": { - "signals": [ - "C3", - "N1" - ], "category_scores": { + "conceptual": 7, + "not_applicable": 0, "procedural": 0, - "reference": 0, - "conceptual": 2, - "not_applicable": 4 + "reference": 12 }, - "category_ambiguous": true, - "contending_categories": [ - "not_applicable", - "conceptual" + "signals": [ + "R1", + "R2", + "R3", + "C1", + "C3" ] } }, { - "page": "parachains/integrations/indexers.md", - "category": "not_applicable", - "reason": "Tools directory listing Subsquid and SubQuery with external reference links. The brief conceptual intro ('What is a Blockchain Indexer?') scores conceptual higher (7) than not_applicable (4), but N1 fires because the page's primary purpose is a grid-card link directory to external tools. Rule 1 takes precedence over Rule 5.", + "category": "reference", + "notes": "Conceptual signals score highest (C1+C2+C3=10) since the page primarily explains REVM vs PVM deployment architecture differences, factory pattern constraints, and gas estimation behavior. Rule 4 assigns reference because reference_score=7 >= 7 and procedural_score=6 < 10. Flagged ambiguous: conceptual=10, reference=7, diff=3.", + "page": "smart-contracts/for-eth-devs/contract-deployment.md", + "relevant_to": [ + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix", + "set-up-local-dev-node" + ], "scoring": { - "signals": [ - "C1", - "C3", - "N1" - ], + "category_ambiguous": true, "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 4 + "conceptual": 10, + "not_applicable": 0, + "procedural": 6, + "reference": 7 }, - "category_ambiguous": true, "contending_categories": [ - "not_applicable", - "conceptual" + "conceptual", + "reference" + ], + "signals": [ + "P1", + "R2", + "R3", + "C1", + "C2", + "C3" ] } }, { - "page": "parachains/integrations/oracles.md", - "category": "not_applicable", - "reason": "Tools directory listing Acurast and DIA oracle providers with external reference links. Same pattern as indexers.md: conceptual intro scores higher (7) than not_applicable (4), but N1 fires (grid-card link directory) and Rule 1 takes precedence.", + "category": "conceptual", + "notes": "Architecture overview of Polkadot's dual VM stack (REVM vs PVM). Provides background context on why REVM is used for Ethereum compatibility and when to choose PVM. Useful as supplementary context for any smart contract development skill on Polkadot Hub.", + "page": "smart-contracts/for-eth-devs/dual-vm-stack.md", + "relevant_to": [ + "set-up-local-dev-node-revive", + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix" + ], "scoring": { - "signals": [ - "C1", - "C3", - "N1" - ], "category_scores": { + "conceptual": 10, + "not_applicable": 0, "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 4 + "reference": 0 }, - "category_ambiguous": true, - "contending_categories": [ - "not_applicable", - "conceptual" + "signals": [ + "C1", + "C2", + "C3" ] } }, { - "page": "parachains/integrations/wallets.md", - "category": "not_applicable", - "reason": "Tools directory listing hot wallets (Nova, Talisman, SubWallet), cold wallets (Ledger, Polkadot Vault), and wallet tools (LunoKit) with external reference links only. Same pattern as indexers/oracles: conceptual intro scores higher (7) than not_applicable (4), but N1 fires (grid-card link directory) and Rule 1 takes precedence.", + "category": "reference", + "notes": "Detailed technical reference comparing EVM and PVM architectures, gas models, memory limits, account management, and YUL IR translation differences. Reference wins over conceptual (12 vs 10) due to structured comparison tables, memory limit tables, and YUL opcode listings. Ambiguous with conceptual given the volume of architectural prose. Essential supplementary context for any skill involving PVM contract development.", + "page": "smart-contracts/for-eth-devs/evm-vs-pvm.md", + "relevant_to": [ + "set-up-local-dev-node-revive", + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix" + ], "scoring": { - "signals": [ - "C1", - "C3", - "N1" - ], + "category_ambiguous": true, "category_scores": { + "conceptual": 10, + "not_applicable": 0, "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 4 + "reference": 12 }, - "category_ambiguous": true, "contending_categories": [ - "not_applicable", + "reference", "conceptual" + ], + "signals": [ + "R1", + "R2", + "R3", + "C1", + "C2", + "C3" ] } }, { - "page": "policies/ai-chatbot-policy.md", - "category": "not_applicable", - "reason": "Legal policy page outlining AI chatbot usage terms and conditions. Content is legal boilerplate included via external snippet reference. N2 fires.", + "category": "conceptual", + "notes": "Conceptual explanation of how Polkadot Hub maps Ethereum gas onto its three-dimensional resource system (ref_time, proof_size, storage_deposit). Contains a mermaid transaction flow diagram and prose on dynamic pricing. No CLI commands or procedures. Relevant to any skill where gas estimation or fee handling matters.", + "page": "smart-contracts/for-eth-devs/gas-model.md", + "relevant_to": [ + "set-up-local-dev-node-revive", + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix" + ], "scoring": { - "signals": [ - "N2" - ], "category_scores": { + "conceptual": 10, + "not_applicable": 0, "procedural": 0, - "reference": 0, - "conceptual": 0, - "not_applicable": 4 - } - } - }, - { - "page": "policies/cookie-policy.md", - "category": "not_applicable", - "reason": "Legal policy page explaining cookie usage on the site. Content is legal boilerplate included via external snippet reference. N2 fires.", - "scoring": { + "reference": 3 + }, "signals": [ - "N2" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 0, - "not_applicable": 4 - } + "R3", + "C1", + "C2", + "C3" + ] } }, { - "page": "policies/privacy-policy.md", - "category": "not_applicable", - "reason": "Legal policy page describing personal data collection and processing practices. Content is legal boilerplate included via external snippet reference. N2 fires.", + "category": "reference", + "notes": "Comprehensive reference for supported Ethereum JSON-RPC methods on Polkadot Hub, with parameter specs and curl examples for each method. High reference score (R1+R2+R3=12) from structured method documentation. Procedural score is 5 (curl commands present) but there is no ordered workflow or verifiable outcome, so rule 4 assigns reference. Useful supplementary context for any skill involving on-chain queries or transaction submission.", + "page": "smart-contracts/for-eth-devs/json-rpc-apis.md", + "relevant_to": [ + "set-up-local-dev-node-revive", + "deploy-erc20-token-hardhat", + "deploy-erc20-token-remix" + ], "scoring": { - "signals": [ - "N2" - ], "category_scores": { - "procedural": 0, - "reference": 0, "conceptual": 0, - "not_applicable": 4 - } + "not_applicable": 0, + "procedural": 5, + "reference": 12 + }, + "signals": [ + "P2", + "R1", + "R2", + "R3" + ] } }, { - "page": "policies/terms-of-use.md", - "category": "not_applicable", - "reason": "Legal/policy page. Content is a terms-of-use document included via remote snippet — pure legal boilerplate with no procedural, reference, or conceptual developer content.", + "category": "conceptual", + "notes": "Overview of Ethereum-native precompiles in Polkadot Hub's Revive pallet: explains what precompiles are, how to call them, and lists all standard precompile addresses. No workflow, no commands, no file creation. Conceptual=7 (C1+C3) vs reference=5 (R1); ambiguous (diff=2). Rule 5 assigns conceptual because conceptual>=5, procedural<10, and reference<7. Useful background for any skill involving these precompiles.", + "page": "smart-contracts/precompiles/eth-native.md", + "relevant_to": [ + "interact-storage-precompile-remix", + "interact-system-precompile-remix", + "interact-xcm-precompile-remix" + ], "scoring": { - "signals": [ - "N2" - ], + "category_ambiguous": true, "category_scores": { + "conceptual": 7, + "not_applicable": 0, "procedural": 0, - "reference": 0, - "conceptual": 0, - "not_applicable": 4 - } - } - }, - { - "page": "reference/tools/xcm-tools.md", - "category": "not_applicable", - "reason": "Survey/index page presenting five XCM tools (Moonsong Labs XCM Tools, ParaSpell, Astar XCM Tools, Chopsticks, Moonbeam XCM SDK). Each section provides a short description and minimal bootstrap commands, then defers substantive usage to external documentation. No complete self-contained XCM workflow; primary function is discovery and navigation to the right tool. N1 fires (score 4), triggering Rule 1.", - "scoring": { + "reference": 5 + }, + "contending_categories": [ + "conceptual", + "reference" + ], "signals": [ - "P2", - "P4", + "R1", "C1", - "N1" - ], - "category_scores": { - "procedural": 9, - "reference": 0, - "conceptual": 5, - "not_applicable": 4 - } + "C3" + ] } }, { - "page": "smart-contracts/get-started.md", - "category": "not_applicable", - "reason": "Navigation hub / landing page. Content is primarily link tables across Quick Starts, Build and Test, Cookbooks, Libraries, Integrations, and Precompiles sections. The Ethereum Tool Differences block is brief conceptual prose but secondary to the navigation function. No procedures, CLI commands, or files to create.", + "category": "reference", + "notes": "Full technical reference for the Bulletin Chain: all extrinsics (with origins), storage items, events, size limits, retrieval methods, and network endpoints. Useful context for the storage tutorial skill.", + "page": "reference/polkadot-hub/data-storage.md", + "relevant_to": [ + "store-retrieve-data-bulletin-chain" + ], "scoring": { - "signals": [ - "C1", - "C3", - "N1" - ], "category_scores": { - "procedural": 0, - "reference": 0, "conceptual": 7, - "not_applicable": 4 - } - } - }, - { - "page": "smart-contracts/overview.md", - "category": "not_applicable", - "reason": "Landing/overview page. Explains the dual-VM architecture and Ethereum compatibility at a high level, then ends with a card grid pointing to Get Started, Cookbook, Ethereum Developers, and Precompiles sections. No CLI commands, no files to create, no verifiable outcome.", - "scoring": { + "not_applicable": 0, + "procedural": 0, + "reference": 12 + }, "signals": [ + "R1", + "R2", + "R3", "C1", - "C3", - "N1" - ], - "category_scores": { - "procedural": 0, - "reference": 0, - "conceptual": 7, - "not_applicable": 4 - } + "C3" + ] } } ] diff --git a/skill_coverage.json b/skill_coverage.json index 41e2e5144..967736a35 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,15 +1,5 @@ { - "schema_version": "1", "generated": "2026-05-19T11:46:33Z", - "summary": { - "total_candidates": 142, - "up_to_date": 7, - "stale": 55, - "uncovered": 0, - "blocked": 23, - "not_applicable": 12, - "supplementary": 45 - }, "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -69,7 +59,7 @@ }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "estimate-xcm-fees-teleport" ], @@ -83,7 +73,7 @@ }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "transfer-assets-parachains-paraspell" ], @@ -91,7 +81,7 @@ }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "pay-fees-alternative-token" ], @@ -99,7 +89,7 @@ }, "chain-interactions/send-transactions/with-sdks.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "send-transactions-sdks" ], @@ -107,7 +97,7 @@ }, "chain-interactions/store-data/bulletin-chain.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:30:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "store-retrieve-data-bulletin-chain" ], @@ -145,7 +135,7 @@ }, "node-infrastructure/run-a-node/parachain-rpc.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "run-parachain-rpc-node" ], @@ -153,7 +143,7 @@ }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "run-polkadot-hub-rpc-node" ], @@ -987,5 +977,15 @@ ], "status": "stale" } + }, + "schema_version": "1", + "summary": { + "blocked": 23, + "not_applicable": 12, + "stale": 55, + "supplementary": 45, + "total_candidates": 142, + "uncovered": 0, + "up_to_date": 7 } } From f017ac1407bd50bd6caa5e1861b2091cbaf2c4ae Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:08:47 +0000 Subject: [PATCH 19/26] chore: auto-generate/update skills --- agent_skills_config.json | 58 ++++++++++++++++++++-------------------- skill_candidates.json | 14 +++++----- skill_coverage.json | 32 +++++++++++----------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 7592f4b2b..1d72fc520 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,5 +1,5 @@ { - "content_hash": "sha256:ec23f14bd159940274d4ec4472e015cdf778b24d8cf13b09564282a1aff7d504", + "content_hash": "sha256:6ef9119d0f883942811154fbd5097abb22a46239cf39519ba47113a277f87a2e", "generated": "2026-05-19T12:00:00Z", "outputs": { "public_root": "/ai/", @@ -718,7 +718,7 @@ }, { "action": "Fetch and adapt the send-transfer script", - "description": "Fetch the reference file and save it as 'send-transfer.ts'. The reference script uses literal placeholders INSERT_WS_ENDPOINT, INSERT_SENDER_MNEMONIC, and INSERT_DEST_ADDRESS. Replace them with process.env.WS_ENDPOINT!, process.env.SENDER_MNEMONIC!, and process.env.DEST_ADDRESS! respectively. Add 'import \"dotenv/config\";' as the first line and install dotenv separately: npm install dotenv.", + "description": "Fetch the reference file and save it as 'send-transfer.ts'. The reference script uses literal placeholders INSERT_WS_ENDPOINT, INSERT_MNEMONIC, and INSERT_DEST_ADDRESS. Replace them with process.env.WS_ENDPOINT!, process.env.SENDER_MNEMONIC!, and process.env.DEST_ADDRESS! respectively. Add 'import \"dotenv/config\";' as the first line and install dotenv separately: npm install dotenv.", "order": 5, "reference_file": "papi/send-transfer.ts", "working_directory": "papi-send-tx-example" @@ -764,7 +764,7 @@ ] }, "title": "Send Transactions with SDKs", - "version": "1.0.2", + "version": "1.0.3", "workflow_pattern": "sequential" }, { @@ -920,12 +920,12 @@ ] }, "title": "Install the Polkadot SDK", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Scaffolds a Hardhat v2 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", + "description": "Scaffolds a Hardhat 3 TypeScript project, configures it for Polkadot Hub TestNet (chain ID 420420417), creates a Storage.sol contract, compiles it, and deploys via Hardhat Ignition. Use when deploying a first EVM smart contract to Polkadot Hub using a professional Hardhat workflow. Uses dotenv for private key security. Trigger phrases: 'deploy smart contract with Hardhat', 'Hardhat deployment Polkadot', 'deploy Storage contract', 'Hardhat Ignition deploy'. Requires testnet PAS tokens. Do NOT use for PVM (PolkaVM) deployments; use set-up-hardhat-pvm for that.", "env_vars": [ { "description": "0x-prefixed EVM private key for the deployer account. Must correspond to a testnet account funded with PAS tokens. Never commit to version control.", @@ -1006,7 +1006,7 @@ "branch": "master", "files": [ { - "description": "Hardhat config with polkadotTestnet network (RPC, chain ID, accounts via vars). Requires dotenv conversion and gasPrice addition per skill steps.", + "description": "Hardhat 3 config with polkadotTestnet network (RPC, chain ID, accounts via vars). Uses plugins array syntax. Requires dotenv conversion and gasPrice addition per skill steps.", "path": "hardhat.config.ts" }, { @@ -1037,10 +1037,10 @@ { "action": "Install Hardhat and dependencies", "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox typescript ts-node @types/node", + "npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox-viem", "npm install dotenv" ], - "description": "Install Hardhat v2 with the Toolbox plugin, TypeScript support, and dotenv for secure private key loading. The dotenv package is required — the skill uses process.env.PRIVATE_KEY instead of Hardhat's interactive vars system, which does not work in agent shells.", + "description": "Install Hardhat 3 with the hardhat-toolbox-viem plugin (includes viem, chai, and testing utilities) and dotenv for secure private key loading. The dotenv package replaces Hardhat's interactive 'npx hardhat vars set' mechanism, which cannot be used in agent shells.", "order": 2, "working_directory": "hardhat-deployment" }, @@ -1065,7 +1065,7 @@ }, { "action": "Fetch and configure hardhat.config.ts", - "description": "Fetch the reference file and save it as 'hardhat.config.ts'. Then apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(4) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors).", + "description": "Fetch the reference file and save it as 'hardhat.config.ts'. The reference uses Hardhat 3 plugin syntax (plugins array with hardhatToolboxViemPlugin). Apply all of the following modifications before saving:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any 'vars.get(\"PRIVATE_KEY\")' or \"vars.get('PRIVATE_KEY')\" with 'process.env.PRIVATE_KEY as string'.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet base fee of 1000 gwei; prevents stuck transactions).\n(5) Add an 'ignition: { requiredConfirmations: 1 }' top-level key to the config object (zero confirmations only works on local nodes — on TestNet it causes IGN401 dropped-transaction errors).", "order": 5, "reference_file": "hardhat.config.ts", "working_directory": "hardhat-deployment" @@ -1124,16 +1124,16 @@ ] }, "title": "Deploy a Basic Smart Contract with Hardhat", - "version": "1.0.1", + "version": "1.1.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security, compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", + "description": "Clones the polkadot-developers/revm-hardhat-examples ERC-20 template, installs dependencies, configures dotenv for private key security (env var: TESTNET_PRIVATE_KEY), compiles the OpenZeppelin ERC-20 contract, runs the test suite against TestNet, and deploys via Hardhat Ignition. Use when deploying a standards-compliant ERC-20 fungible token on Polkadot Hub. Requires testnet PAS tokens. Trigger phrases: 'deploy ERC-20 token', 'deploy fungible token Polkadot', 'ERC-20 Hardhat Polkadot', 'deploy MyToken'. Uses OpenZeppelin Contracts v5.4.0+ — requires evmVersion cancun.", "env_vars": [ { - "description": "0x-prefixed EVM private key for the deployer/test account. Must be funded with testnet PAS. Never commit to version control.", - "name": "PRIVATE_KEY", + "description": "0x-prefixed EVM private key for the deployer/test account. The template uses the variable name TESTNET_PRIVATE_KEY (not PRIVATE_KEY). Must be funded with testnet PAS. Never commit to version control.", + "name": "TESTNET_PRIVATE_KEY", "required": true } ], @@ -1146,7 +1146,7 @@ { "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('TESTNET_PRIVATE_KEY') with process.env.TESTNET_PRIVATE_KEY as string." }, { "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", @@ -1164,7 +1164,7 @@ "actions": [ "Clone revm-hardhat-examples and cd erc20-hardhat", "Run npm i && npm install dotenv", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", + "Create .env with TESTNET_PRIVATE_KEY placeholder; ask user to fill it in", "Modify hardhat.config.ts: add dotenv import, replace vars.get, add gasPrice 5000 gwei, set evmVersion cancun, confirm requiredConfirmations: 1", "Run npx hardhat compile", "Run npx hardhat test --network polkadotTestnet to verify contract logic", @@ -1237,16 +1237,16 @@ { "action": "Create .env file and .gitignore for private key", "commands": [ - "printf 'PRIVATE_KEY=\\n' > .env", + "printf 'TESTNET_PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding.", + "description": "Create a .env file with an empty PRIVATE_KEY placeholder. Stop here and ask the user to edit .env directly — fill in TESTNET_PRIVATE_KEY with their 0x-prefixed EVM private key for an account funded with testnet PAS. Do NOT ask for the private key in chat. Wait for the user to confirm .env is filled before proceeding.", "order": 3, "working_directory": "revm-hardhat-examples/erc20-hardhat" }, { "action": "Convert hardhat.config.ts to use dotenv and add gas configuration", - "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') or vars.get(\"PRIVATE_KEY\") with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Confirm or add 'evmVersion: \"cancun\"' in the solidity compiler settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file.", + "description": "Open hardhat.config.ts and apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace 'vars.has(\"TESTNET_PRIVATE_KEY\") ? [vars.get(\"TESTNET_PRIVATE_KEY\")] : []' with '[process.env.TESTNET_PRIVATE_KEY as string]'.\n(3) Remove the 'import { HardhatUserConfig, vars } from \"hardhat/config\";' line and replace with 'import type { HardhatUserConfig } from \"hardhat/config\";'.\n(4) In the polkadotTestnet network block, add 'gasPrice: 5000000000000,' (5000 gwei — 5x the TestNet 1000 gwei base fee).\n(5) Add 'evmVersion: \"cancun\"' inside the solidity settings block — required because the template uses OpenZeppelin Contracts v5.4.0+ which uses the mcopy opcode from the Cancun EVM upgrade.\n(6) Confirm ignition.requiredConfirmations is 1 (not 0).\nSave the file.", "order": 4, "working_directory": "revm-hardhat-examples/erc20-hardhat" }, @@ -1300,7 +1300,7 @@ ] }, "title": "Deploy an ERC-20 Token Using Hardhat", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -1447,8 +1447,8 @@ "working_directory": "hardhat-pvm-example" }, { - "action": "Fetch and configure hardhat.config.ts for TestNet", - "description": "Fetch the reference hardhat.config.ts and replace the existing one in the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Confirm the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(4) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(5) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file.", + "action": "Configure hardhat.config.ts for TestNet", + "description": "The init wizard generated a hardhat.config.ts in the project root. Modify it to apply all of the following changes:\n(1) Add 'import \"dotenv/config\";' as the very first line of the file.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove any 'import { vars } from \"hardhat/config\";' line if present.\n(4) Ensure the polkadotTestnet network block contains: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, and accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the 1000 gwei TestNet base fee).\n(6) Set or confirm 'ignition: { requiredConfirmations: 1 }' in the top-level config object.\nSave the file.", "order": 6, "working_directory": "hardhat-pvm-example" }, @@ -1481,7 +1481,7 @@ ] }, "title": "Set Up Hardhat with the Polkadot Plugin (PVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -1608,8 +1608,8 @@ "working_directory": "hardhat-example" }, { - "action": "Fetch and configure hardhat.config.ts", - "description": "Fetch the reference hardhat.config.ts and save it to the project root. Then apply all of the following modifications:\n(1) Add 'import \"dotenv/config\";' as the very first line.\n(2) Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string.\n(3) Remove the 'import { vars } from \"hardhat/config\";' line if present.\n(4) In the polkadotTestnet network block, confirm or add: url: 'https://services.polkadothub-rpc.com/testnet', chainId: 420420417, accounts: [process.env.PRIVATE_KEY as string].\n(5) Add 'gasPrice: 5000000000000,' to the polkadotTestnet block (5000 gwei — 5x the TestNet 1000 gwei base fee; prevents stuck deployment transactions).\n(6) Add top-level 'ignition: { requiredConfirmations: 1 }' to the config object.\nSave the file.", + "action": "Create and configure hardhat.config.ts", + "description": "Create hardhat.config.ts in the project root with the following content:\n\n```typescript\nimport type { HardhatUserConfig } from \"hardhat/config\";\nimport \"@nomicfoundation/hardhat-toolbox\";\nimport \"dotenv/config\";\n\nconst config: HardhatUserConfig = {\n solidity: \"0.8.28\",\n networks: {\n polkadotTestnet: {\n url: \"https://services.polkadothub-rpc.com/testnet\",\n chainId: 420420417,\n accounts: [process.env.PRIVATE_KEY as string],\n gasPrice: 5000000000000,\n },\n },\n ignition: {\n requiredConfirmations: 1,\n },\n};\n\nexport default config;\n```\n\nDo NOT use vars.get() or vars.has() from Hardhat's configuration variables system — it requires interactive shell input which agents cannot provide. The config above loads PRIVATE_KEY from .env via dotenv and sets gasPrice to 5000 gwei (5x the 1000 gwei TestNet base fee) to prevent stuck deployment transactions.", "order": 5, "working_directory": "hardhat-example" } @@ -1632,7 +1632,7 @@ ] }, "title": "Set Up Hardhat for Polkadot Hub (EVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -9027,9 +9027,9 @@ "resolution": "Verify .env exists in uniswap-v2-polkadot/ and contains AH_PRIV_KEY=0x. The config uses require('dotenv').config() automatically." }, { - "cause": "Using wrong --network flag; documentation shows polkadotHubTestNet but the config defines passetHub.", - "pattern": "Error: network passetHub not found / unknown network", - "resolution": "Use --network passetHub instead of --network polkadotHubTestNet." + "cause": "Some documentation examples use --network polkadotHubTestNet but the hardhat.config.js defines the network as passetHub.", + "pattern": "HardhatError: Cannot find network 'polkadotHubTestNet' / unknown network", + "resolution": "Use --network passetHub (not polkadotHubTestNet). The correct network name defined in hardhat.config.js is passetHub." }, { "cause": "The resolc compiler is not installed or not accessible.", @@ -9160,7 +9160,7 @@ ] }, "title": "Deploy Uniswap V2 Core on Polkadot Hub (PVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { diff --git a/skill_candidates.json b/skill_candidates.json index ea8abc98f..53ac0d1b1 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -365,7 +365,7 @@ "title": "Pay Transaction Fees with an Alternative Token" }, { - "built_at": "2026-04-21T17:00:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "Multi-SDK tutorial for constructing, signing, and submitting balance transfers (PAPI, Polkadot.js, Dedot, Python Substrate Interface, Subxt). Cookbook-backed. Fundamental SDK skill and likely prerequisite for other chain-interaction skills.", "priority": "high", @@ -489,7 +489,7 @@ "title": "Register a Local Asset on Polkadot Hub" }, { - "built_at": "2026-04-21T18:00:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "Multi-OS installation guide (macOS, Linux, Windows WSL) for the Polkadot SDK. Numbered steps throughout, multiple CLI commands (brew, apt, rustup, cargo build), and verification steps with expected output. Cookbook CI badge present (K4). Self-contained from scratch — covers all dependency installation and SDK build. Optional kitchensink explore step uses Polkadot.js Apps GUI but does not affect the primary installation workflow (S1 still fires). Serves as prerequisite for most parachain development skills (create-frame-pallet, set-up-the-parachain-template, etc.).", "priority": "high", @@ -1948,7 +1948,7 @@ "title": "Deploy Uniswap V2 on Polkadot Hub" }, { - "built_at": "2026-04-21T18:00:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "Concise end-to-end tutorial: scaffold Hardhat project, configure polkadotTestnet network, create Storage.sol, compile, deploy via Ignition to TestNet. All code uses --8<-- snippet includes from polkadot-cookbook (K4 = 3; CI badge confirms tested). K2 = 4 (includes expand to complete code in the published site). All steps are CLI-driven with no GUI interactions (S1 = 4). Explicit prerequisites section covers Node.js version, test tokens, and wallet (K3 = 3). Verified outcome shown via terminal output snippet.", "priority": "high", @@ -2011,7 +2011,7 @@ "title": "Deploy a Basic Smart Contract with Remix IDE" }, { - "built_at": "2026-04-21T18:00:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "Complete end-to-end tutorial: clone template, compile, test, deploy to Polkadot Hub TestNet. Reference repo revm-hardhat-examples confirmed in context. P3 not fired — tutorial uses a cloned template repo rather than instructing the reader to create files from scratch. CI badge confirms recipe is tested in polkadot-cookbook.", "priority": "high", @@ -2172,7 +2172,7 @@ "title": "Use Foundry with Polkadot Hub" }, { - "built_at": "2026-04-21T18:00:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "Complete workflow: initialize project, compile Solidity to PVM bytecode via resolc, test locally with a Substrate node, and deploy to TestNet. Has an explicit prerequisites section, numbered steps throughout, and clear outcome language ('You should see JSON files containing the contract ABIs and bytecodes'). Backed by polkadot-cookbook CI. PVM variant using @parity/hardhat-polkadot — mirrors the EVM skill but targets PolkaVM runtime. P3 not fired: the init wizard creates project files; no explicit file-creation code block on the page.", "priority": "high", @@ -2205,7 +2205,7 @@ "title": "Set Up Hardhat with the Polkadot Plugin (PVM)" }, { - "built_at": "2026-04-21T18:00:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "Covers Hardhat project initialization, network configuration for Polkadot Hub TestNet (full hardhat.config.ts file shown — P3 fires), and contract verification with Blockscout or Routescan. Self-contained setup workflow with explicit prerequisites and outcome language. Backed by polkadot-cookbook CI. Standard EVM variant with no PVM/resolc dependency.", "priority": "high", @@ -2693,7 +2693,7 @@ "title": "Store and Retrieve Data on the Bulletin Chain" }, { - "built_at": "2026-05-15T15:30:00Z", + "built_at": "2026-05-19T12:00:00Z", "category": "tutorial", "notes": "PVM variant using polkavm-hardhat-examples repo. K1 awarded for the deploy-to-TestNet workflow, which is standalone; the local-node testing step references the Local Dev Node guide but the deployment outcome is achievable without it. K4 awarded — polkavm-hardhat-examples is listed in context.md.", "priority": "high", diff --git a/skill_coverage.json b/skill_coverage.json index 967736a35..cc908fdd2 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T11:46:33Z", + "generated": "2026-05-19T12:00:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -93,7 +93,7 @@ "skills": [ "send-transactions-sdks" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/store-data/bulletin-chain.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -299,11 +299,11 @@ }, "parachains/install-polkadot-sdk.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "install-polkadot-sdk" ], - "status": "stale" + "status": "up_to_date" }, "parachains/integrations/indexers.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -699,11 +699,11 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:30:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "deploy-uniswap-v2-core-pvm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -739,11 +739,11 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "deploy-basic-contract-hardhat" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -755,11 +755,11 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "deploy-erc20-token-hardhat" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -795,19 +795,19 @@ }, "smart-contracts/dev-environments/hardhat-polkadot.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "set-up-hardhat-pvm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T12:00:00Z", "skills": [ "set-up-hardhat-evm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/local-dev-node.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -982,10 +982,10 @@ "summary": { "blocked": 23, "not_applicable": 12, - "stale": 55, + "stale": 48, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 7 + "up_to_date": 14 } } From fcb8a6e2c645ee1d20f1abfeb2dbc421f4504efb Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:16:26 +0000 Subject: [PATCH 20/26] chore: auto-generate/update skills --- agent_skills_config.json | 21 ++++++++++++--------- skill_coverage.json | 34 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 1d72fc520..bc9c1d861 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "sha256:6ef9119d0f883942811154fbd5097abb22a46239cf39519ba47113a277f87a2e", - "generated": "2026-05-19T12:00:00Z", + "content_hash": "sha256:dbf02382dccebdb84371d6202cac7c69433df0182f9e3981ddabafc555c91a45", + "generated": "2026-05-19T14:00:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -2103,7 +2103,7 @@ ] }, "title": "Transfer Assets Between Parachains with ParaSpell XCM SDK", - "version": "1.1.0", + "version": "1.1.1", "workflow_pattern": "sequential" }, { @@ -2256,7 +2256,7 @@ ] }, "title": "Pay Transaction Fees with an Alternative Token", - "version": "1.1.0", + "version": "1.1.1", "workflow_pattern": "sequential" }, { @@ -2327,6 +2327,7 @@ ] }, "primary_page": "node-infrastructure/run-a-node/parachain-rpc.md", + "project_structure": "./\n├── chain-spec.json\n└── my-node-data/\n ├── chains/\n │ ├── people-polkadot/\n │ │ └── db/\n │ └── polkadot/\n │ └── db/", "reference_code": { "base_path": "", "files": [], @@ -2407,7 +2408,7 @@ ] }, "title": "Run a Parachain RPC Node", - "version": "1.1.0", + "version": "1.1.1", "workflow_pattern": "sequential" }, { @@ -2800,7 +2801,7 @@ ] }, "title": "Estimate XCM Fees for Asset Teleport", - "version": "1.1.0", + "version": "1.1.1", "workflow_pattern": "sequential" }, { @@ -3022,6 +3023,7 @@ ] }, "primary_page": "node-infrastructure/run-a-node/polkadot-hub-rpc.md", + "project_structure": "./\n├── asset-hub-polkadot.json\n└── my-node-data/\n ├── chains/\n │ ├── asset-hub-polkadot/\n │ │ └── db/\n │ └── polkadot/\n │ └── db/", "reference_code": { "base_path": "", "files": [], @@ -3108,7 +3110,7 @@ ] }, "title": "Run an RPC Node for Polkadot Hub", - "version": "1.1.0", + "version": "1.1.1", "workflow_pattern": "sequential" }, { @@ -3173,6 +3175,7 @@ ] }, "primary_page": "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md", + "project_structure": "/usr/local/bin/\n├── polkadot\n├── polkadot-prepare-worker\n└── polkadot-execute-worker", "reference_code": { "base_path": "", "files": [], @@ -3264,7 +3267,7 @@ ] }, "title": "Set Up a Polkadot Validator Node", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -8997,7 +9000,7 @@ ] }, "title": "Store and Retrieve Data on the Bulletin Chain", - "version": "1.0.1", + "version": "1.0.2", "workflow_pattern": "sequential" }, { diff --git a/skill_coverage.json b/skill_coverage.json index cc908fdd2..98f3afd4c 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T12:00:00Z", + "generated": "2026-05-19T14:00:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -59,11 +59,11 @@ }, "chain-interactions/send-transactions/interoperability/estimate-xcm-fees.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T12:00:00Z", + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "estimate-xcm-fees-teleport" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/interoperability/transfer-assets-into-polkadot.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -73,19 +73,19 @@ }, "chain-interactions/send-transactions/interoperability/transfer-assets-parachains.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T12:00:00Z", + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "transfer-assets-parachains-paraspell" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/pay-fees-with-different-tokens.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T12:00:00Z", + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "pay-fees-alternative-token" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/send-transactions/with-sdks.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -97,11 +97,11 @@ }, "chain-interactions/store-data/bulletin-chain.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T12:00:00Z", + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "store-retrieve-data-bulletin-chain" ], - "status": "stale" + "status": "up_to_date" }, "chain-interactions/token-operations/convert-assets.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -135,19 +135,19 @@ }, "node-infrastructure/run-a-node/parachain-rpc.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T12:00:00Z", + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "run-parachain-rpc-node" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-node/polkadot-hub-rpc.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T12:00:00Z", + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "run-polkadot-hub-rpc-node" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-node/relay-chain/bootnode.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -175,11 +175,11 @@ }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:00:00Z", "skills": [ "set-up-polkadot-validator-node" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-validator/onboarding-and-offboarding/start-validating.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -982,10 +982,10 @@ "summary": { "blocked": 23, "not_applicable": 12, - "stale": 48, + "stale": 41, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 14 + "up_to_date": 21 } } From c2990a971c7f0e0baaa66efab5615b4944971d27 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:25:25 +0000 Subject: [PATCH 21/26] chore: auto-generate/update skills --- agent_skills_config.json | 48 ++++++++++++++++++++++------------------ skill_coverage.json | 34 ++++++++++++++-------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index bc9c1d861..9c185e76f 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "sha256:dbf02382dccebdb84371d6202cac7c69433df0182f9e3981ddabafc555c91a45", - "generated": "2026-05-19T14:00:00Z", + "content_hash": "sha256:162429d7bfe728d7904fba39ed0632bcb6f5e5d36198353584383f123cf7f0aa", + "generated": "2026-05-19T14:30:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -4519,7 +4519,7 @@ ] }, "title": "Set Up a Mock Runtime for Pallet Unit Testing", - "version": "1.0.1", + "version": "1.0.2", "workflow_pattern": "sequential" }, { @@ -4586,11 +4586,12 @@ "wallet": [] }, "primary_page": "parachains/customize-runtime/pallet-development/pallet-testing.md", + "project_structure": "polkadot-sdk-parachain-template/\n└── pallets/\n └── pallet-custom/\n └── src/\n ├── lib.rs (modified)\n └── tests.rs", "reference_code": { - "base_path": "polkadot-docs/parachains/customize-runtime/pallet-development/pallet-testing", - "branch": "master", + "base_path": "", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-cookbook" + "repo": "none" }, "source_pages": [ "parachains/customize-runtime/pallet-development/pallet-testing.md" @@ -4676,7 +4677,7 @@ ] }, "title": "Write Unit Tests for a FRAME Pallet", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -4746,11 +4747,12 @@ "wallet": [] }, "primary_page": "parachains/customize-runtime/pallet-development/benchmark-pallet.md", + "project_structure": "polkadot-sdk-parachain-template/\n├── pallets/\n│ └── pallet-custom/\n│ ├── Cargo.toml (modified)\n│ ├── frame-weight-template.hbs\n│ └── src/\n│ ├── lib.rs (modified)\n│ ├── benchmarking.rs\n│ └── weights.rs\n├── runtime/\n│ ├── Cargo.toml (modified)\n│ └── src/\n│ ├── configs/\n│ │ └── mod.rs (modified)\n│ └── lib.rs (modified)\n└── Cargo.toml (modified)", "reference_code": { - "base_path": ".snippets/code/parachains/customize-runtime/pallet-development/benchmark-pallet", - "branch": "master", + "base_path": "", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-docs" + "repo": "none" }, "source_pages": [ "parachains/customize-runtime/pallet-development/benchmark-pallet.md" @@ -4881,7 +4883,7 @@ ] }, "title": "Benchmark a FRAME Pallet and Generate Weight Files", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -6456,11 +6458,12 @@ ] }, "primary_page": "parachains/customize-runtime/add-existing-pallets.md", + "project_structure": "polkadot-sdk-parachain-template/\n└── runtime/\n ├── Cargo.toml (modified)\n └── src/\n └── lib.rs (modified)", "reference_code": { "base_path": "", - "branch": "master", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-cookbook" + "repo": "none" }, "source_pages": [ "parachains/customize-runtime/add-existing-pallets.md" @@ -6561,7 +6564,7 @@ ] }, "title": "Add an Existing Pallet to a Parachain Runtime", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -6628,11 +6631,12 @@ ] }, "primary_page": "parachains/customize-runtime/add-pallet-instances.md", + "project_structure": "polkadot-sdk-parachain-template/\n└── runtime/\n ├── Cargo.toml (modified)\n └── src/\n └── lib.rs (modified)", "reference_code": { "base_path": "", - "branch": "master", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-cookbook" + "repo": "none" }, "source_pages": [ "parachains/customize-runtime/add-pallet-instances.md" @@ -6730,7 +6734,7 @@ ] }, "title": "Configure Multiple Instances of a Pallet in a Runtime", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -6802,11 +6806,12 @@ ] }, "primary_page": "parachains/customize-runtime/pallet-development/create-a-pallet.md", + "project_structure": "polkadot-sdk-parachain-template/\n├── pallets/\n│ └── counter/\n│ ├── Cargo.toml\n│ └── src/\n│ └── lib.rs\n├── runtime/\n│ ├── Cargo.toml (modified)\n│ └── src/\n│ └── lib.rs (modified)\n└── Cargo.toml (modified)", "reference_code": { "base_path": "", - "branch": "master", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-cookbook" + "repo": "none" }, "source_pages": [ "parachains/customize-runtime/pallet-development/create-a-pallet.md" @@ -6906,7 +6911,7 @@ ] }, "title": "Create a Custom FRAME Pallet", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -8598,6 +8603,7 @@ ] }, "primary_page": "parachains/launch-a-parachain/deploy-to-polkadot.md", + "project_structure": "parachain-template/\n├── chain_spec.json\n├── chain_spec_raw.json\n├── genesis_wasm\n├── genesis_state\n└── node-key.dat", "reference_code": { "base_path": "", "files": [], @@ -8723,7 +8729,7 @@ ] }, "title": "Deploy a Parachain to the Polkadot TestNet (Paseo)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { diff --git a/skill_coverage.json b/skill_coverage.json index 98f3afd4c..934da3a17 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T14:00:00Z", + "generated": "2026-05-19T14:30:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -239,19 +239,19 @@ }, "parachains/customize-runtime/add-existing-pallets.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "add-existing-pallet-to-runtime" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/add-pallet-instances.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "configure-multiple-pallet-instances" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/add-smart-contract-functionality.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -261,35 +261,35 @@ }, "parachains/customize-runtime/pallet-development/benchmark-pallet.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "benchmark-frame-pallet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/pallet-development/create-a-pallet.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "create-frame-pallet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/pallet-development/mock-runtime.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "set-up-pallet-mock-runtime" ], - "status": "stale" + "status": "up_to_date" }, "parachains/customize-runtime/pallet-development/pallet-testing.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "unit-test-frame-pallet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/get-started.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -343,11 +343,11 @@ }, "parachains/launch-a-parachain/deploy-to-polkadot.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T14:30:00Z", "skills": [ "deploy-parachain-to-polkadot-testnet" ], - "status": "stale" + "status": "up_to_date" }, "parachains/launch-a-parachain/obtain-coretime.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -982,10 +982,10 @@ "summary": { "blocked": 23, "not_applicable": 12, - "stale": 41, + "stale": 34, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 21 + "up_to_date": 28 } } From 86f26c92e7a6fdf45c4e2d6c627d8fac4433eb92 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:32:17 +0000 Subject: [PATCH 22/26] chore: auto-generate/update skills --- agent_skills_config.json | 40 +++++++++++++++++++++++++--------------- skill_coverage.json | 34 +++++++++++++++++----------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 9c185e76f..af02ff7cd 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "sha256:162429d7bfe728d7904fba39ed0632bcb6f5e5d36198353584383f123cf7f0aa", - "generated": "2026-05-19T14:30:00Z", + "content_hash": "sha256:d0535fc734923b45a55d343292ae46d0691cdb31262ca9febd1dfa12a83d2d1e", + "generated": "2026-05-19T15:00:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -3272,7 +3272,7 @@ }, { "chain_role": "isolated", - "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.2.7. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", + "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.3.0. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", "env_vars": [], "error_patterns": [ { @@ -3332,8 +3332,10 @@ ] }, "primary_page": "reference/tools/chopsticks.md", + "project_structure": "my-chopsticks-project/\n├── package.json\n├── node_modules/\n└── polkadot.yml", "reference_code": { "base_path": "", + "branch": "", "files": [], "repo": "none" }, @@ -3344,9 +3346,9 @@ { "action": "Install Chopsticks globally", "commands": [ - "npm i -g @acala-network/chopsticks@1.2.7" + "npm i -g @acala-network/chopsticks@1.3.0" ], - "description": "Install Chopsticks 1.2.7 globally. After installation, 'chopsticks' is available system-wide. For a project-local installation instead: mkdir my-chopsticks-project && cd my-chopsticks-project && npm init -y && npm i @acala-network/chopsticks@1.2.7. With local installation, run via 'npx @acala-network/chopsticks' instead of 'chopsticks' in subsequent steps.", + "description": "Install Chopsticks 1.3.0 globally. After installation, 'chopsticks' is available system-wide. For a project-local installation instead: mkdir my-chopsticks-project && cd my-chopsticks-project && npm init -y && npm i @acala-network/chopsticks@1.3.0. With local installation, run via 'npx @acala-network/chopsticks' instead of 'chopsticks' in subsequent steps.", "expected_output": "added N packages", "order": 1, "working_directory": "." @@ -3416,7 +3418,7 @@ ] }, "title": "Set Up and Use Chopsticks for Chain Forking", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -3482,8 +3484,10 @@ ] }, "primary_page": "reference/tools/moonwall.md", + "project_structure": "my-moonwall-project/\n├── node_modules/\n├── package.json\n├── moonwall.config.json\n└── tests/\n └── balance.test.ts", "reference_code": { "base_path": "", + "branch": "", "files": [], "repo": "none" }, @@ -3552,7 +3556,7 @@ ] }, "title": "Set Up End-to-End Testing with Moonwall", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -3839,7 +3843,12 @@ "reference_code": { "base_path": "erc20-hardhat", "branch": "master", - "files": [], + "files": [ + { + "description": "OpenZeppelin ERC-20 token contract (MyToken) with mint function and configurable name/symbol/owner", + "path": "contracts/MyToken.sol" + } + ], "repo": "polkadot-developers/revm-hardhat-examples" }, "source_pages": [ @@ -3848,8 +3857,9 @@ "steps": [ { "action": "Fetch the ERC-20 contract source", - "description": "Fetch the OpenZeppelin ERC-20 contract source from the reference repository. The contract file is at: https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol\n\nRun: curl -sL https://raw.githubusercontent.com/polkadot-developers/revm-hardhat-examples/master/erc20-hardhat/contracts/MyToken.sol and display the contract source to the user. The user will paste this into Remix in the next step.", + "description": "Fetch the reference file `contracts/MyToken.sol` and display its contents to the user. This is an OpenZeppelin ERC-20 contract (MyToken) with a configurable name, symbol, and mint function. The user will paste this into Remix IDE in the next step.", "order": 1, + "reference_file": "contracts/MyToken.sol", "working_directory": "." }, { @@ -3907,7 +3917,7 @@ ] }, "title": "Deploy an ERC-20 Token Using Remix IDE", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -4331,7 +4341,7 @@ }, { "action": "Fetch the deployment script", - "description": "Fetch the reference file `deploy.ts` and save it as `src/deploy.ts`. Apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line.\n(2) Replace `INSERT_PRIVATE_KEY` with `` process.env.PRIVATE_KEY as `0x${string}` ``.\n(3) Replace `INSERT_RPC_URL` with `https://services.polkadothub-rpc.com/testnet`.\n(4) Replace `INSERT_CHAIN_ID` with `420420417n` (BigInt).\n\nThe script reads from `../abis` and `../artifacts` relative to the script location, which resolves correctly when the script is in `src/`.", + "description": "Fetch the reference file `deploy.ts` and save it as `src/deploy.ts`. Apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the very first line.\n(2) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY as \\`0x${string}\\``.\n\nThe script reads chain config and RPC endpoint from `createClient.ts` and `createWallet.ts` (already substituted in steps 6-7). It reads artifacts from `../abis` and `../artifacts` relative to the script location, which resolves correctly when the script is in `src/`.", "order": 10, "reference_file": "deploy.ts", "working_directory": "viem-deploy" @@ -4377,7 +4387,7 @@ ] }, "title": "Deploy Contracts to Polkadot Hub with Viem", - "version": "1.0.1", + "version": "1.0.2", "workflow_pattern": "sequential" }, { @@ -9339,7 +9349,7 @@ ] }, "title": "Deploy Uniswap V2 Core on Polkadot Hub (EVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -9509,7 +9519,7 @@ ] }, "title": "Deploy Uniswap V2 Periphery (Router) on Polkadot Hub (EVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -9863,7 +9873,7 @@ ] }, "title": "Deploy Uniswap V3 Periphery on Polkadot Hub (EVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" } ] diff --git a/skill_coverage.json b/skill_coverage.json index 934da3a17..9f5729843 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T14:30:00Z", + "generated": "2026-05-19T15:00:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -577,11 +577,11 @@ }, "reference/tools/chopsticks.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "set-up-chopsticks-fork" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/dedot.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -599,11 +599,11 @@ }, "reference/tools/moonwall.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "set-up-e2e-testing-moonwall" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/omninode.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -707,19 +707,19 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:30:00Z", + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "deploy-uniswap-v2-core-evm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/periphery-v2.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:30:00Z", + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "deploy-uniswap-v2-periphery-evm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -731,11 +731,11 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T16:00:00Z", + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "deploy-uniswap-v3-periphery-evm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -763,11 +763,11 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "deploy-erc20-token-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -903,11 +903,11 @@ }, "smart-contracts/libraries/viem.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T15:00:00Z", "skills": [ "deploy-contracts-viem" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/wagmi.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -982,10 +982,10 @@ "summary": { "blocked": 23, "not_applicable": 12, - "stale": 34, + "stale": 27, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 28 + "up_to_date": 35 } } From 28f2e6c200232963c8616f9c286d4191b922adf2 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:40:12 +0000 Subject: [PATCH 23/26] chore: auto-generate/update skills --- agent_skills_config.json | 79 ++++++++++++++++++++++++++++------------ skill_candidates.json | 14 +++---- skill_coverage.json | 34 ++++++++--------- 3 files changed, 80 insertions(+), 47 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index af02ff7cd..21dbb7b0b 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "sha256:d0535fc734923b45a55d343292ae46d0691cdb31262ca9febd1dfa12a83d2d1e", - "generated": "2026-05-19T15:00:00Z", + "content_hash": "sha256:0dd644408aa84c6de37485b22d1a24ff6f8622bae7383c3c153762c5f4f49e98", + "generated": "2026-05-19T15:30:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -2866,11 +2866,12 @@ ] }, "primary_page": "parachains/launch-a-parachain/set-up-the-parachain-template.md", + "project_structure": "polkadot-sdk-parachain-template/\n├── node/\n├── pallets/\n│ └── template/\n├── runtime/\n├── Cargo.toml\n└── Cargo.lock", "reference_code": { - "base_path": "polkadot-docs/parachains/parachain-template", - "branch": "master", + "base_path": "", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-cookbook" + "repo": "none" }, "source_pages": [ "parachains/launch-a-parachain/set-up-the-parachain-template.md" @@ -2947,7 +2948,7 @@ ] }, "title": "Set Up the Polkadot SDK Parachain Template", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5192,7 +5193,7 @@ ] }, "title": "Interact with Polkadot Chains Using Dedot", - "version": "1.0.1", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5309,7 +5310,7 @@ ] }, "title": "Run a Parachain Node with Polkadot Omni Node", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5380,10 +5381,25 @@ ] }, "primary_page": "reference/tools/py-substrate-interface.md", + "project_structure": "py-substrate-example/\n├── connect.py\n├── read_state.py\n├── send_tx.py\n├── .env\n└── .gitignore", "reference_code": { - "base_path": "", - "files": [], - "repo": "none" + "base_path": ".snippets/code/reference/tools/py-substrate-interface", + "branch": "master", + "files": [ + { + "description": "Creates a SubstrateInterface connection to a Polkadot SDK node via WebSocket; verifies the connection by printing the chain name", + "path": "substrate_interface.py" + }, + { + "description": "Queries System.Account storage for the given SS58 address and prints free balance, reserved balance, and nonce (code fragment — prepend a SubstrateInterface connection)", + "path": "read_state.py" + }, + { + "description": "Composes and submits a Balances.transfer_keep_alive extrinsic using a keypair; waits for block inclusion and prints the extrinsic and block hash (code fragment — prepend connection and keypair setup)", + "path": "send_tx.py" + } + ], + "repo": "polkadot-developers/polkadot-docs" }, "source_pages": [ "reference/tools/py-substrate-interface.md" @@ -5402,20 +5418,23 @@ }, { "action": "Create the connection script", - "description": "Create 'connect.py' with the following content. Replace 'INSERT_WS_URL' with the WebSocket endpoint for your target node (e.g., 'wss://rpc.polkadot.io' for Polkadot mainnet or 'ws://127.0.0.1:9944' for a local dev node).\n\nFetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/substrate_interface.py -o connect.py\n\nThen replace INSERT_WS_URL in connect.py with the target endpoint. Run to verify the connection:\npython3 connect.py\n\nExpected output: 'Connected to chain: Polkadot' (or your target chain name). If the chain name is wrong or absent, the URL may be incorrect.", + "description": "Fetch the reference file `substrate_interface.py` and save it as `connect.py`. Make this substitution:\n- Replace `INSERT_WS_URL` with the WebSocket endpoint for your target node (e.g., `'wss://rpc.polkadot.io'` for Polkadot mainnet, or `'ws://127.0.0.1:9944'` for a local dev node).\n\nRun to verify the connection:\n```bash\npython3 connect.py\n```\nExpected output: `Connected to chain: Polkadot` (or your target chain name). If the chain name is wrong or absent, check the URL.", "order": 2, + "reference_file": "substrate_interface.py", "working_directory": "py-substrate-example" }, { "action": "Query account balance", - "description": "Create 'read_state.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/read_state.py -o read_state.py\n\nSubstitutions required:\n- At the top of the file, add the SubstrateInterface connection (same as connect.py, replacing INSERT_WS_URL with the endpoint).\n- Replace 'INSERT_ADDRESS' with the SS58 address to query.\n\nRun:\npython3 read_state.py\n\nExpected output:\n Account Details:\n - Free Balance: \n - Reserved: \n - Nonce: \n\nThe values are in planck (smallest unit). Divide by 10^10 for DOT values on Polkadot mainnet.", + "description": "Fetch the reference file `read_state.py` and save it as `read_state.py`. The file is a code fragment (starts with `# ...`). Prepend the following to the top of `read_state.py` so the script has a connection:\n\n```python\nfrom substrateinterface import SubstrateInterface\n\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\n```\n\nApply these substitutions:\n- Replace `INSERT_WS_URL` with the same WebSocket endpoint used in `connect.py`.\n- Replace `INSERT_ADDRESS` with the SS58 address to query.\n\nRun:\n```bash\npython3 read_state.py\n```\nExpected output:\n```\nAccount Details:\n- Free Balance: \n- Reserved: \n- Nonce: \n```", "order": 3, + "reference_file": "read_state.py", "working_directory": "py-substrate-example" }, { "action": "Submit a balance transfer", - "description": "Create '.env' file:\nMNEMONIC=\n\nAdd '.env' to .gitignore:\necho '.env' >> .gitignore\n\nStop and ask the user to fill in their mnemonic phrase in the .env file. Do NOT ask for the mnemonic phrase in chat. Wait for confirmation before continuing.\n\nInstall dotenv:\npip install python-dotenv\n\nCreate 'send_tx.py'. Fetch the template:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/py-substrate-interface/send_tx.py -o send_tx.py\n\nAt the top of send_tx.py, add:\n```python\nimport os\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom substrateinterface import SubstrateInterface, Keypair\n\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\nkeypair = Keypair.create_from_mnemonic(os.environ['MNEMONIC'])\n```\n\nThen add the template content below. Substitutions in send_tx.py:\n- Replace INSERT_WS_URL with the WebSocket endpoint.\n- Replace 'INSERT_ADDRESS' (in call_params 'dest') with the recipient SS58 address.\n- Replace 'INSERT_VALUE' with the transfer amount in planck (e.g., 1000000000000 for 0.1 DOT).\n\nRun:\npython3 send_tx.py\n\nExpected output:\n Transaction successful:\n - Extrinsic Hash: 0x...\n - Block Hash: 0x...", + "description": "Create `.env` with an empty placeholder and protect it from git:\n```bash\nprintf 'MNEMONIC=\\n' > .env\necho '.env' >> .gitignore\n```\n\nStop and ask the user to fill in their mnemonic phrase in the `.env` file. Do NOT ask for the mnemonic phrase in chat. Wait for confirmation before continuing.\n\nInstall python-dotenv:\n```bash\npip install python-dotenv\n```\n\nFetch the reference file `send_tx.py` and save it as `send_tx.py`. The file is a code fragment (starts with `# ...`). Prepend the following to the top of `send_tx.py`:\n\n```python\nimport os\nfrom dotenv import load_dotenv\nfrom substrateinterface import SubstrateInterface, Keypair\n\nload_dotenv()\nsubstrate = SubstrateInterface(url='INSERT_WS_URL')\nkeypair = Keypair.create_from_mnemonic(os.environ['MNEMONIC'])\n```\n\nApply these substitutions in `send_tx.py`:\n- Replace `INSERT_WS_URL` with the same WebSocket endpoint used in `connect.py`.\n- Replace `INSERT_ADDRESS` in `call_params['dest']` with the recipient SS58 address.\n- Replace `INSERT_VALUE` in `call_params['value']` with the transfer amount in planck (e.g., `1000000000000` for 0.1 DOT on Polkadot mainnet with 10 decimal places).\n\nRun:\n```bash\npython3 send_tx.py\n```\nExpected output:\n```\nTransaction successful:\n- Extrinsic Hash: 0x...\n- Block Hash: 0x...\n```", "order": 4, + "reference_file": "send_tx.py", "working_directory": "py-substrate-example" } ], @@ -5437,7 +5456,7 @@ ] }, "title": "Interact with a Polkadot Node Using Python Substrate Interface", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5506,10 +5525,21 @@ ] }, "primary_page": "reference/tools/subxt.md", + "project_structure": "subxt-example/\n├── polkadot_metadata.scale\n├── subxt.rs\n├── Cargo.toml\n├── .env\n└── .gitignore", "reference_code": { - "base_path": "", - "files": [], - "repo": "none" + "base_path": ".snippets/code/reference/tools/subxt", + "branch": "master", + "files": [ + { + "description": "Rust workspace manifest declaring subxt 0.50.0, subxt-signer 0.50.0, and tokio 1.44.2 dependencies; sets binary name to 'subxt' pointing to subxt.rs", + "path": "Cargo.toml" + }, + { + "description": "Async Rust main querying the existential deposit constant and account info, then submitting a signed balance transfer; contains INSERT_NODE_URL, INSERT_ADDRESS, INSERT_DEST_ADDRESS, INSERT_AMOUNT, and INSERT_SECRET_PHRASE placeholders", + "path": "subxt.rs" + } + ], + "repo": "polkadot-developers/polkadot-docs" }, "source_pages": [ "reference/tools/subxt.md" @@ -5532,8 +5562,9 @@ }, { "action": "Add Subxt dependencies to Cargo.toml", - "description": "Open Cargo.toml and replace the [dependencies] section with the following. Replace version numbers with the latest from crates.io if needed (reference: subxt 0.50.0, subxt-signer 0.50.0, tokio 1.44.2):\n\nFetch the reference Cargo.toml:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/Cargo.toml -o Cargo.toml\n\nThis Cargo.toml sets:\n- subxt = \"0.50.0\" — the main RPC and codec library\n- subxt-signer = \"0.50.0\" — provides Keypair for signing transactions\n- tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] } — async runtime\n\nThe file also sets the binary name to 'subxt' pointing to 'subxt.rs'. Keep the [[bin]] section as-is.", + "description": "Fetch the reference file `Cargo.toml` and replace the existing `Cargo.toml` in the project root entirely. No substitutions are needed — the file is ready to use as-is. It defines:\n- `subxt = \"0.50.0\"` — the main RPC and codec library\n- `subxt-signer = \"0.50.0\"` — provides `Keypair` for signing transactions\n- `tokio = { version = \"1.44.2\", features = [\"rt\", \"macros\"] }` — async runtime\n- A `[[bin]]` section with `name = \"subxt\"` pointing to `subxt.rs`", "order": 3, + "reference_file": "Cargo.toml", "working_directory": "subxt-example" }, { @@ -5544,8 +5575,9 @@ }, { "action": "Create the main Rust source file", - "description": "Fetch the reference subxt.rs file:\ncurl -sL https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.snippets/code/reference/tools/subxt/subxt.rs -o subxt.rs\n\nSubstitutions required in subxt.rs — each INSERT_* placeholder must be replaced before building:\n- INSERT_NODE_URL: WebSocket URL (same as used in step 4, e.g., 'wss://rpc.polkadot.io').\n- INSERT_ADDRESS: SS58 address to query for account info (e.g., a known validator address).\n- INSERT_DEST_ADDRESS: SS58 recipient address for the balance transfer.\n- INSERT_AMOUNT: Transfer amount as a u128 integer in planck (e.g., 1000000000000 for 0.1 DOT on Polkadot).\n- INSERT_SECRET_PHRASE: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the file — instead, store it in a .env file, add .env to .gitignore, and load it with std::env::var(\"SECRET_PHRASE\") at runtime.\n\nFor the SECRET_PHRASE substitution: create .env with SECRET_PHRASE= and stop to ask the user to fill it in before proceeding. Wait for confirmation.", + "description": "Fetch the reference file `subxt.rs` and save it as `subxt.rs` in the project root. Apply ALL of the following substitutions before building — each `INSERT_*` placeholder must be replaced:\n\n- `INSERT_NODE_URL`: WebSocket URL for the target node (e.g., `\"wss://rpc.polkadot.io\"` for Polkadot mainnet). Use the same URL you used for `subxt metadata` in step 4.\n- `INSERT_ADDRESS`: SS58 address to query for account info (e.g., a known validator address on the target network).\n- `INSERT_DEST_ADDRESS`: SS58 recipient address for the balance transfer.\n- `INSERT_AMOUNT`: Transfer amount as a `u128` integer in planck (e.g., `1000000000000u128` = 0.1 DOT on Polkadot mainnet with 10 decimal places). Do not include a string — this is a Rust numeric literal.\n- `INSERT_SECRET_PHRASE`: Sender's BIP39 mnemonic. Do NOT put a real mnemonic in the source file. Instead, store it in `.env` as `SECRET_PHRASE=your words here`, add `.env` to `.gitignore`, and replace the literal with `std::env::var(\"SECRET_PHRASE\").expect(\"SECRET_PHRASE not set\").as_str()`. Stop and ask the user to fill in `.env` before proceeding — do not ask for the phrase in chat.", "order": 5, + "reference_file": "subxt.rs", "working_directory": "subxt-example" }, { @@ -5577,7 +5609,7 @@ ] }, "title": "Interact with a Polkadot Node Using Subxt (Rust)", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -7040,7 +7072,7 @@ ] }, "title": "Retrieve Polkadot Runtime Metadata", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -7116,6 +7148,7 @@ ] }, "primary_page": "reference/tools/polkadot-js-api.md", + "project_structure": "polkadot-api-demo/\n├── query.ts\n├── transfer.ts\n├── .env\n├── .gitignore\n└── package.json", "reference_code": { "base_path": "", "files": [], @@ -7192,7 +7225,7 @@ ] }, "title": "Query and Transact on Polkadot Chains with the Polkadot.js API", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { diff --git a/skill_candidates.json b/skill_candidates.json index 53ac0d1b1..6f421b395 100644 --- a/skill_candidates.json +++ b/skill_candidates.json @@ -522,7 +522,7 @@ "title": "Install the Polkadot SDK" }, { - "built_at": "2026-04-21T19:00:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "tutorial", "notes": "Full workflow for cloning, building, running, and verifying the Polkadot SDK Parachain Template locally. P1+P2+P4 present (15 = tutorial threshold). P3 absent — directory structure is shown illustratively, not as files to create. GUI verification step (Polkadot.js Apps) does not block S1 since the primary workflow is CLI-only. K1=0 — requires install-polkadot-sdk as prerequisite. Cookbook CI badge present (K4). First step in the three-page parachain launch series (set-up-the-parachain-template → deploy-to-polkadot → obtain-coretime).", "priority": "high", @@ -1465,7 +1465,7 @@ "title": "Spawn a Local Parachain Test Network with Zombienet" }, { - "built_at": "2026-04-21T22:30:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "guide", "notes": "Ambiguous between guide and reference (procedural=11, reference=12, diff=1); rule 3 fires first (procedural >= 10). Page explains metadata concepts but also provides complete, runnable curl and subxt CLI commands for fetching chain metadata. K1: self-contained via CLI paths. K2: commands are complete. S1: programmatic options exist despite Polkadot.js GUI section. S2: deterministic — same endpoint, same output. K3/K4/S3 absent.", "priority": "medium", @@ -1535,7 +1535,7 @@ "title": "Set Up and Use Chopsticks for Chain Forking" }, { - "built_at": "2026-04-21T21:00:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "guide", "notes": "Covers installation through reading on-chain data, querying storage, calling runtime APIs, and signing/sending transactions. No explicit prerequisites section (K3 absent) and no numbered ordered steps (P1 absent), so guide rather than tutorial. Category is ambiguous with reference (procedural=10, reference=8, diff=2); guide wins by Rule 3. High priority because the workflow is self-contained, code is complete, and the Dedot client is a first-class alternative to Polkadot.js in the ecosystem.", "priority": "high", @@ -1605,7 +1605,7 @@ "title": "Set Up End-to-End Testing with Moonwall" }, { - "built_at": "2026-04-21T21:00:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "tutorial", "notes": "Complete tutorial: install binary (macOS/Ubuntu/cargo), obtain chain spec via numbered steps, launch a full node, interact with it. Verifiable outcome ('You should see the installed version number' and node sync logs). Includes runtime compatibility requirements with Rust code examples. K3 absent (no explicit prerequisites section), K4 absent (no reference repo for the tutorial itself).", "priority": "high", @@ -1676,7 +1676,7 @@ "title": "Interact with a Polkadot Chain Using Polkadart" }, { - "built_at": "2026-04-21T22:30:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "guide", "notes": "Covers installation, dynamic API generation, creating an API instance, reading chain constants/state, and sending transactions. Rubric yields priority=high (score=18) but overridden to medium: page carries a prominent maintenance-mode warning directing new projects to Dedot or PAPI. Building an agent skill around a deprecated library has limited value. Ambiguous guide/reference (procedural=11, reference=8, diff=3).", "priority": "medium", @@ -1745,7 +1745,7 @@ "title": "Bootstrap Parachain Development with Pop CLI" }, { - "built_at": "2026-04-21T21:00:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "guide", "notes": "Walks through installing the library, connecting to a node, reading chain state (account balance), and submitting a balance transfer — all CLI/SDK steps with no GUI dependencies. No explicit 'you should see' outcome language prevents P4 from firing. K2 not awarded because actual code lives in --8<-- snippet includes whose content is not visible in the page source.", "priority": "high", @@ -1776,7 +1776,7 @@ "title": "Interact with a Polkadot Node Using Python Substrate Interface" }, { - "built_at": "2026-04-21T21:00:00Z", + "built_at": "2026-05-19T15:30:00Z", "category": "tutorial", "notes": "Numbered installation steps (install CLI, add core deps), Rust/TOML files to create, and verifiable outcome language ('your Cargo.toml should look like this'). Covers the full workflow from prerequisites through reading chain constants/state and submitting a signed transaction. K2 not awarded due to INSERT_* placeholders (INSERT_NODE_URL, INSERT_ADDRESS, INSERT_SECRET_PHRASE) and unresolved {{dependencies.crates.*}} template variables.", "priority": "high", diff --git a/skill_coverage.json b/skill_coverage.json index 9f5729843..8170d28ba 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T15:00:00Z", + "generated": "2026-05-19T15:30:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -357,11 +357,11 @@ }, "parachains/launch-a-parachain/set-up-the-parachain-template.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "set-up-parachain-template" ], - "status": "stale" + "status": "up_to_date" }, "parachains/runtime-maintenance/coretime-renewal.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -461,11 +461,11 @@ }, "reference/parachains/chain-data.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "retrieve-runtime-metadata" ], - "status": "stale" + "status": "up_to_date" }, "reference/parachains/consensus/async-backing.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -585,11 +585,11 @@ }, "reference/tools/dedot.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "interact-with-chain-dedot" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/light-clients.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -607,11 +607,11 @@ }, "reference/tools/omninode.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "run-parachain-node-omni-node" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/papi.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -633,11 +633,11 @@ }, "reference/tools/polkadot-js-api.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "use-polkadot-js-api" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/pop-cli.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -647,11 +647,11 @@ }, "reference/tools/py-substrate-interface.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "interact-polkadot-node-py-substrate" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/sidecar.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -661,11 +661,11 @@ }, "reference/tools/subxt.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T15:30:00Z", "skills": [ "interact-polkadot-node-subxt" ], - "status": "stale" + "status": "up_to_date" }, "reference/tools/xcm-tools.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -982,10 +982,10 @@ "summary": { "blocked": 23, "not_applicable": 12, - "stale": 27, + "stale": 20, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 35 + "up_to_date": 42 } } From 2aa02e82e7036cea98476e386ec8cc1d3ede574a Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:58:25 +0000 Subject: [PATCH 24/26] chore: auto-generate/update skills --- agent_skills_config.json | 195 ++++++++++++++++++++------------------- skill_coverage.json | 34 +++---- 2 files changed, 119 insertions(+), 110 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index 21dbb7b0b..f55d647e0 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "sha256:0dd644408aa84c6de37485b22d1a24ff6f8622bae7383c3c153762c5f4f49e98", - "generated": "2026-05-19T15:30:00Z", + "content_hash": "de9bedab12699557c2904586fa456600f638523884ecacdebdf20f1536278153", + "generated": "2026-05-19T16:00:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -3601,11 +3601,11 @@ { "actions": [ "Clone revm-hardhat-examples and navigate to zero-to-hero-dapp/storage-contract", - "Install dependencies and create .env with PRIVATE_KEY (user fills in)", - "Update hardhat.config.ts: dotenv, gasPrice 5000 gwei, evmVersion cancun, requiredConfirmations 1", - "Compile Storage.sol and deploy to polkadotTestnet via Ignition", - "Scaffold Next.js 14 frontend with create-next-app@14", - "Install viem, wagmi, react-query; update contract.ts with deployed address", + "Install dependencies, create .env with PRIVATE_KEY (user fills in)", + "Update hardhat.config.ts: dotenv import, replace hardcoded key with process.env.PRIVATE_KEY, set testnet URL, chain ID 420420417, gasPrice 5000 gwei, evmVersion cancun, requiredConfirmations 1", + "Compile Storage.sol and deploy to polkadotTestNet via Ignition; save contract address", + "Install dApp deps in dapp/; update viem.ts with testnet chain ID and RPC URL", + "Update utils/contract.ts with deployed contract address", "Run npm run dev and test at localhost:3000" ], "result": "Storage contract deployed to TestNet; Next.js app at localhost:3000 with working wallet connect, read, and write contract interactions via MetaMask", @@ -3615,7 +3615,7 @@ { "actions": [ "Check ignition/deployments//deployed_addresses.json for a Storage address", - "If address is present: deployment succeeded — use that address in contract.ts and proceed to step 7", + "If address is present: deployment succeeded — use that address in utils/contract.ts and proceed to step 9", "If absent: delete ignition/deployments/ and redeploy with gasPrice: 5000000000000 confirmed in config" ], "result": "Contract address recovered from deployment state or clean redeployment completed", @@ -3644,11 +3644,12 @@ ] }, "primary_page": "smart-contracts/cookbook/dapps/zero-to-hero.md", + "project_structure": "revm-hardhat-examples/\n└── zero-to-hero-dapp/\n ├── storage-contract/\n │ ├── contracts/\n │ │ └── Storage.sol\n │ ├── ignition/\n │ │ └── modules/\n │ │ └── Storage.ts\n │ ├── hardhat.config.ts\n │ ├── .env\n │ └── package.json\n └── dapp/\n ├── abis/\n │ └── Storage.json\n ├── app/\n ├── utils/\n │ └── contract.ts\n ├── viem.ts\n └── package.json", "reference_code": { - "base_path": "zero-to-hero-dapp", - "branch": "master", + "base_path": "", + "branch": "", "files": [], - "repo": "polkadot-developers/revm-hardhat-examples" + "repo": "none" }, "source_pages": [ "smart-contracts/cookbook/dapps/zero-to-hero.md" @@ -3659,7 +3660,7 @@ "commands": [ "git clone https://github.com/polkadot-developers/revm-hardhat-examples.git" ], - "description": "Clone revm-hardhat-examples. This repo contains the complete zero-to-hero-dapp with both the Hardhat contract project and the Next.js frontend.", + "description": "Clone revm-hardhat-examples. This repo contains the complete zero-to-hero-dapp with a pre-built Hardhat contract project (storage-contract/) and a pre-built Next.js dApp (dapp/).", "order": 1, "working_directory": "." }, @@ -3669,7 +3670,7 @@ "npm install", "npm install dotenv" ], - "description": "Install Hardhat and contract dependencies, then add dotenv. dotenv replaces Hardhat's interactive vars system, which cannot be used in agent shells.", + "description": "Install Hardhat and contract dependencies, then add dotenv for private key management.", "order": 2, "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" }, @@ -3684,8 +3685,8 @@ "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" }, { - "action": "Update hardhat.config.ts for TestNet and security", - "description": "Open hardhat.config.ts and apply ALL of the following changes:\n1. Add 'import \"dotenv/config\";' as the very first line.\n2. Replace any vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string — Hardhat interactive vars do not work in agent shells.\n3. Remove 'import { vars } from \"hardhat/config\";' if present.\n4. In the polkadotTestnet network block, set url: 'https://services.polkadothub-rpc.com/testnet' and chainId: 420420417.\n5. Add gasPrice: 5000000000000 to the polkadotTestnet network block (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Add or confirm evmVersion: 'cancun' in the Solidity compiler settings — required for OpenZeppelin v5.4.0+ (mcopy opcode).\n7. Ensure requiredConfirmations is 1 in any ignition config block (never 0 — zero causes IGN401 errors on TestNet).\nSave the file.", + "action": "Update hardhat.config.ts for TestNet", + "description": "The reference `hardhat.config.ts` uses Hardhat v3 plugin syntax and currently targets localhost. Apply ALL of the following changes:\n1. Add `import 'dotenv/config';` as the very first line.\n2. In the `polkadotTestNet` network block, replace the hardcoded private key string with `process.env.PRIVATE_KEY as string`.\n3. Replace `url: 'http://127.0.0.1:8545'` with `url: 'https://services.polkadothub-rpc.com/testnet'`.\n4. Add `chainId: 420420417` to the `polkadotTestNet` network block.\n5. Add `gasPrice: 5000000000000` (5000 gwei = 5x the 1000 gwei TestNet base fee).\n6. Change `requiredConfirmations: 0` to `requiredConfirmations: 1` — zero causes IGN401 errors on TestNet.\n7. In the Solidity `profiles.default` block, add `settings: { evmVersion: 'cancun' }` — required for OpenZeppelin v5.4.0+ (mcopy opcode).\nSave the file.", "order": 4, "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" }, @@ -3694,7 +3695,7 @@ "commands": [ "npx hardhat compile" ], - "description": "Compile Storage.sol. If you see 'invalid opcode: MCOPY', verify evmVersion: 'cancun' is set in the Solidity compiler settings in hardhat.config.ts and recompile.", + "description": "Compile Storage.sol. If you see 'invalid opcode: MCOPY', verify `settings: { evmVersion: 'cancun' }` is set in the Solidity profiles block and recompile.", "expected_output": "Compiled 1 Solidity file successfully", "order": 5, "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" @@ -3702,46 +3703,43 @@ { "action": "Deploy the contract to Polkadot Hub TestNet", "commands": [ - "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestnet" + "npx hardhat ignition deploy ignition/modules/Storage.ts --network polkadotTestNet" ], - "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify gasPrice: 5000000000000 is in hardhat.config.ts, then redeploy.\n- IGN401 (transaction dropped): first check ignition/deployments//deployed_addresses.json — if the address is listed, deployment succeeded despite the error; use that address. If absent, delete ignition/deployments/ and redeploy.\n- 'Transaction Already Imported': do NOT retry at the same gas price. Delete ignition/deployments/ and add a higher gasPrice, then redeploy.", + "description": "Deploy via Hardhat Ignition. When prompted 'Do you want to deploy this?', answer 'yes'. Save the deployed contract address — needed in step 9.\n\nRecovery guidance:\n- 'priority is too low': verify `gasPrice: 5000000000000` is set, then redeploy.\n- IGN401 (transaction dropped): check `ignition/deployments//deployed_addresses.json` first — if the address is listed, deployment succeeded; use that address. If absent, delete `ignition/deployments/` and redeploy.\n- 'Transaction Already Imported': delete `ignition/deployments/` and redeploy with higher gasPrice.", "expected_output": "Storage deployed to: 0x...", "order": 6, "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/storage-contract" }, { - "action": "Scaffold the Next.js frontend", + "action": "Install dApp dependencies", "commands": [ - "echo 'n\\nn\\nn\\nn\\nn\\nn' | npx create-next-app@14 frontend --typescript --no-eslint --no-tailwind --no-src-dir --app" + "npm install" ], - "description": "Scaffold Next.js 14 TypeScript app named 'frontend'. Piped 'n' answers handle any unexpected interactive prompts from create-next-app. If this fails due to prompt changes, run: npx create-next-app@14 frontend and answer No to all questions except TypeScript (Yes) and App Router (Yes).", + "description": "Install the pre-built dApp dependencies. The repo's `dapp/` directory already contains the complete Next.js application with Viem and Wagmi configured — no scaffolding needed.", "order": 7, - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp" + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/dapp" }, { - "action": "Install frontend dependencies", - "commands": [ - "npm install viem wagmi @tanstack/react-query" - ], - "description": "Install viem for contract interaction, wagmi for React hooks, and React Query (required by wagmi v2).", + "action": "Update viem.ts with TestNet network parameters", + "description": "Open `viem.ts` at the dapp root. It currently targets localhost. Replace with TestNet values:\n1. Change `id: 420420420` to `id: 420420417`.\n2. Change `name: 'Polkadot Testnet'` (or similar) to `name: 'Polkadot Hub TestNet'`.\n3. Replace all `http://127.0.0.1:8545` occurrences with `https://services.polkadothub-rpc.com/testnet`.\nSave the file.", "order": 8, - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend" + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/dapp" }, { - "action": "Update contract address in contract config", - "description": "Open src/contracts/contract.ts (or the equivalent contract config file in the frontend). Replace the CONTRACT_ADDRESS placeholder or any hardcoded example address with the deployed contract address saved in step 6. Save the file.", + "action": "Update contract address in utils/contract.ts", + "description": "Open `utils/contract.ts`. Replace the existing `CONTRACT_ADDRESS` value with the deployed contract address saved in step 6. Example: `export const CONTRACT_ADDRESS = '0x';`. Save the file.", "order": 9, - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend" + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/dapp" }, { - "action": "Start the development server", + "action": "Start the development server and test", "commands": [ "npm run dev" ], "description": "Start the Next.js dev server. Open http://localhost:3000 in a browser. Click 'Connect Wallet' and approve the MetaMask popup. Read the current stored value, then write a new value and confirm the MetaMask transaction. Verify the displayed value updates after the transaction is mined.", "expected_output": "ready - started server on 0.0.0.0:3000", "order": 10, - "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/frontend" + "working_directory": "revm-hardhat-examples/zero-to-hero-dapp/dapp" } ], "supplementary_context": { @@ -3768,7 +3766,7 @@ ] }, "title": "Build a Zero-to-Hero Smart Contract DApp with Viem and Next.js", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -3926,8 +3924,8 @@ "description": "Sets up a Node.js project, compiles a Solidity contract with solc, deploys to Polkadot Hub TestNet using Ethers.js v6, and interacts with the deployed contract via read and write script calls. Use when integrating Ethers.js into a new or existing Node.js project targeting Polkadot Hub. Trigger phrases: 'deploy with ethers.js polkadot', 'ethers.js contract deployment', 'use ethers polkadot hub', 'ethersjs testnet'. Covers dotenv credential setup, custom Polkadot Hub provider configuration, and the compile-deploy-interact workflow. Produces a deployed contract with verified read/write interaction.", "env_vars": [ { - "description": "0x-prefixed EVM private key of the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", - "name": "PRIVATE_KEY", + "description": "12-word BIP-39 mnemonic phrase for the deployer wallet. Must be funded with testnet PAS. Edit .env directly — never paste in chat.", + "name": "MNEMONIC", "required": true }, { @@ -3945,29 +3943,32 @@ { "cause": "The RPC_URL in .env is incorrect or the endpoint is unreachable.", "pattern": "Error: could not detect network", - "resolution": "Verify RPC_URL=https://services.polkadothub-rpc.com/testnet in .env. Test connectivity: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'" + "resolution": "Verify RPC_URL=https://services.polkadothub-rpc.com/testnet in .env. Test connectivity: `curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'`" }, { - "cause": "Project is not configured as ESM.", - "pattern": "SyntaxError: Cannot use import statement in a module", - "resolution": "Run 'npm pkg set type=module' in the ethers-deploy directory to enable ES modules." + "cause": "Project is not configured as a CommonJS module, or solc is not installed.", + "pattern": "Error: Cannot find module 'solc' or SyntaxError", + "resolution": "Ensure solc is installed (`npm install solc`) and package.json does NOT have `\"type\": \"module\"` — the reference scripts use CommonJS require()." }, { "cause": "The gas price in the transaction is below the 1000 gwei TestNet base fee.", "pattern": "Error: transaction underpriced", - "resolution": "Add an explicit gasPrice to the transaction options: { gasPrice: ethers.parseUnits('5000', 'gwei') }. Polkadot Hub TestNet has a base fee of 1000 gwei." + "resolution": "Add an explicit gasPrice to the transaction options in deploy.js: `{ gasPrice: ethers.parseUnits('5000', 'gwei') }`. Polkadot Hub TestNet has a base fee of 1000 gwei." } ], "examples": [ { "actions": [ - "Create ethers-deploy ESM project and install ethers, dotenv, solc, tsx", - "Create .env with RPC_URL and empty PRIVATE_KEY (user fills in)", - "Fetch compile.ts, deploy.ts, interact.ts from polkadot-cookbook", - "Apply substitutions: dotenv import, process.env.RPC_URL, process.env.PRIVATE_KEY, chainId 420420417", - "Run npx tsx compile.ts to build ABI/bytecode", - "Run npx tsx deploy.ts and save deployed contract address", - "Run npx tsx interact.ts to verify read/write contract operations" + "Create ethers-deploy CJS project and install ethers, dotenv, solc", + "Create directory structure: contracts/, scripts/, abis/, artifacts/", + "Create .env with RPC_URL and empty MNEMONIC (user fills in BIP-39 mnemonic)", + "Fetch reference files: connectToProvider.js → scripts/, Storage.sol → contracts/, compile.js → scripts/", + "Substitute INSERT_RPC_URL, INSERT_CHAIN_ID, INSERT_CHAIN_NAME in connectToProvider.js", + "Run node scripts/compile.js to build ABI/bytecode in abis/ and artifacts/", + "Fetch deploy.js → scripts/; substitute INSERT_MNEMONIC with process.env.MNEMONIC; fix artifactsDir paths", + "Run node scripts/deploy.js and save deployed contract address", + "Fetch checkStorage.js → scripts/; substitute MNEMONIC and CONTRACT_ADDRESS; fix ABI path", + "Run node scripts/checkStorage.js to verify read/write operations" ], "result": "Solidity contract compiled, deployed to Polkadot Hub TestNet, and verified with read/write operations via Ethers.js scripts", "scenario": "Common scenario: deploy and interact with a Storage contract", @@ -3975,9 +3976,9 @@ }, { "actions": [ - "Open deploy.ts and add gasPrice override to the deployment transaction options", - "Set gasPrice: ethers.parseUnits('5000', 'gwei') — 5x the 1000 gwei TestNet base fee", - "Retry with npx tsx deploy.ts" + "Open scripts/deploy.js and add gasPrice override to the deployment transaction options", + "Set `gasPrice: ethers.parseUnits('5000', 'gwei')` — 5x the 1000 gwei TestNet base fee", + "Retry with node scripts/deploy.js" ], "result": "Deployment succeeds with sufficient gas price for Polkadot Hub TestNet", "scenario": "Edge case: transaction underpriced error during deployment", @@ -4003,7 +4004,7 @@ ] }, "primary_page": "smart-contracts/libraries/ethers-js.md", - "project_structure": "ethers-deploy/\n├── artifacts/\n├── contracts/\n│ └── Storage.sol\n├── checkStorage.js\n├── compile.js\n├── connectToProvider.js\n├── deploy.js\n├── .env\n├── .gitignore\n└── package.json", + "project_structure": "ethers-deploy/\n├── abis/\n│ └── Storage.json (created by compile.js)\n├── artifacts/\n│ └── Storage.bin (created by compile.js)\n├── contracts/\n│ └── Storage.sol\n├── scripts/\n│ ├── checkStorage.js\n│ ├── compile.js\n│ ├── connectToProvider.js\n│ └── deploy.js\n├── contract-address.json (created by deploy.js)\n├── .env\n├── .gitignore\n└── package.json", "reference_code": { "base_path": ".snippets/code/smart-contracts/libraries/ethers-js", "branch": "master", @@ -4039,50 +4040,49 @@ "action": "Create the project directory and initialize", "commands": [ "mkdir ethers-deploy && cd ethers-deploy", - "npm init -y", - "npm pkg set type=module" + "npm init -y" ], - "description": "Create and initialize an ESM Node.js project. ESM is required for Ethers.js v6 imports.", + "description": "Create and initialize a CommonJS Node.js project. Do NOT set `\"type\": \"module\"` — the reference scripts use CommonJS `require()`.", "order": 1, "working_directory": "." }, { - "action": "Install dependencies", + "action": "Install dependencies and create directory structure", "commands": [ - "npm install ethers dotenv", - "npm install --save-dev solc typescript tsx @types/node" + "npm install ethers dotenv solc", + "mkdir contracts scripts abis artifacts" ], - "description": "Install ethers (v6), dotenv for credential management, solc for Solidity compilation, and TypeScript tooling.", + "description": "Install ethers (v6), dotenv for credential management, and solc for Solidity compilation. Create the directory structure: `contracts/` for Solidity source, `scripts/` for JS scripts, `abis/` and `artifacts/` for compiled output.", "order": 2, "working_directory": "ethers-deploy" }, { "action": "Create .env file and .gitignore", "commands": [ - "printf 'PRIVATE_KEY=\\nRPC_URL=https://services.polkadothub-rpc.com/testnet\\n' > .env", + "printf 'MNEMONIC=\\nRPC_URL=https://services.polkadothub-rpc.com/testnet\\n' > .env", "echo '.env' > .gitignore" ], - "description": "Create .env with RPC_URL pre-filled and empty PRIVATE_KEY. Stop and ask the user to edit .env directly by adding their 0x-prefixed private key as PRIVATE_KEY=0x. Do NOT ask for the key in chat. Wait for confirmation before continuing.", + "description": "Create .env with RPC_URL pre-filled and empty MNEMONIC. Stop and ask the user to edit .env directly by adding their 12-word BIP-39 mnemonic as MNEMONIC=word1 word2 ... word12. Do NOT ask for the mnemonic in chat. Wait for confirmation before continuing.", "order": 3, "working_directory": "ethers-deploy" }, { "action": "Fetch the provider connection helper", - "description": "Fetch the reference file `connectToProvider.js` and save it to the project root. Verify it points at the Polkadot Hub TestNet RPC URL `https://services.polkadothub-rpc.com/testnet`; if it uses a placeholder, replace `INSERT_RPC_URL` with the testnet URL.", + "description": "Fetch the reference file `connectToProvider.js` and save it as `scripts/connectToProvider.js`. Then apply these substitutions:\n(1) Replace `INSERT_RPC_URL` with `process.env.RPC_URL`.\n(2) Replace `INSERT_CHAIN_ID` with `420420417`.\n(3) Replace `INSERT_CHAIN_NAME` with `'polkadot-hub-testnet'`.\nAdd `require('dotenv/config');` as the first line.\nSave the file.", "order": 4, "reference_file": "connectToProvider.js", "working_directory": "ethers-deploy" }, { "action": "Fetch the Storage.sol contract", - "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a simple Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed.", + "description": "Fetch the reference file `Storage.sol` and save it as `contracts/Storage.sol`. This is the deployment target — a Solidity contract with a `storedNumber` public state variable and a `setNumber(uint256 _newNumber)` setter. No substitutions needed.", "order": 5, "reference_file": "Storage.sol", "working_directory": "ethers-deploy" }, { "action": "Fetch the compilation script", - "description": "Fetch the reference file `compile.js` and save it to the project root. The script invokes `solc` against `Storage.sol` and writes the ABI and bytecode to `artifacts/`.", + "description": "Fetch the reference file `compile.js` and save it as `scripts/compile.js`. The script reads `../contracts/Storage.sol`, compiles it with solc, and writes the ABI to `../abis/Storage.json` and bytecode to `../artifacts/Storage.bin`. No substitutions needed — all paths use `__dirname` relative references.", "order": 6, "reference_file": "compile.js", "working_directory": "ethers-deploy" @@ -4090,15 +4090,15 @@ { "action": "Compile the Solidity contract", "commands": [ - "node compile.js" + "node scripts/compile.js" ], - "description": "Run the compile script to generate the contract ABI and bytecode. Confirm an `artifacts/` directory was created with `Storage.json` (or similar) before proceeding to deploy.", + "description": "Run the compile script to generate the contract ABI and bytecode. Confirm `abis/Storage.json` and `artifacts/Storage.bin` were created before deploying.", "order": 7, "working_directory": "ethers-deploy" }, { "action": "Fetch the deployment script", - "description": "Fetch the reference file `deploy.js` and save it to the project root. Then apply these substitutions:\n(1) Add `import \"dotenv/config\";` as the first line (or `require(\"dotenv\").config()` for CJS).\n(2) Replace `INSERT_RPC_URL` with `process.env.RPC_URL` (or hardcode the testnet URL).\n(3) Replace `INSERT_PRIVATE_KEY` with `process.env.PRIVATE_KEY`.\n(4) Replace any hardcoded chain ID with `420420417`.", + "description": "Fetch the reference file `deploy.js` and save it as `scripts/deploy.js`. Then apply these substitutions:\n(1) Add `require('dotenv/config');` as the first line.\n(2) Replace `const mnemonic = 'INSERT_MNEMONIC';` with `const mnemonic = process.env.MNEMONIC;`.\n(3) The deploy.js reads ABI from `join(__dirname, '../contracts')` but compile.js writes to `abis/` and `artifacts/`. Fix the paths:\n - Replace `const artifactsDir = join(__dirname, '../contracts');` with:\n `const abiDir = join(__dirname, '../abis');\n const bytecodeDir = join(__dirname, '../artifacts');`\n - In `getAbi`, change `join(artifactsDir, ...)` to `join(abiDir, ...)`.\n - In `getByteCode`, change `join(artifactsDir, ...)` to `join(bytecodeDir, ...)`.\nSave the file.", "order": 8, "reference_file": "deploy.js", "working_directory": "ethers-deploy" @@ -4106,15 +4106,15 @@ { "action": "Deploy the contract to Polkadot Hub TestNet", "commands": [ - "node deploy.js" + "node scripts/deploy.js" ], - "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is stuck or rejected: verify the RPC URL is correct and the account is funded, then retry.", + "description": "Run the deployment script. Save the deployed contract address from the output — needed for the interact step.\n\nIf deployment fails with `insufficient funds`: visit https://faucet.polkadot.io/ and fund the deployer account.\nIf the transaction is underpriced: add `gasPrice: ethers.parseUnits('5000', 'gwei')` to the factory.deploy() options in deploy.js and retry.", "order": 9, "working_directory": "ethers-deploy" }, { "action": "Fetch the interaction script and interact with the deployed contract", - "description": "Fetch the reference file `checkStorage.js` and save it as `checkStorage.js`. Then substitute `INSERT_CONTRACT_ADDRESS` with the deployed address from the previous step. Run it with `node checkStorage.js`. Verify the output shows successful read and write operations against the deployed contract.", + "description": "Fetch the reference file `checkStorage.js` and save it as `scripts/checkStorage.js`. Then apply these substitutions:\n(1) Add `require('dotenv/config');` as the first line.\n(2) Replace `const mnemonic = 'INSERT_MNEMONIC'` with `const mnemonic = process.env.MNEMONIC;`.\n(3) Replace `const contractAddress = 'INSERT_CONTRACT_ADDRESS'` with the deployed address from step 9.\n(4) Fix the ABI path: change `const artifactsDir = join(__dirname, '../contracts');` to `const artifactsDir = join(__dirname, '../abis');` so it reads from the compiled ABI location.\nRun `node scripts/checkStorage.js`. Verify the output shows successful read and write operations.", "order": 10, "reference_file": "checkStorage.js", "working_directory": "ethers-deploy" @@ -4144,7 +4144,7 @@ ] }, "title": "Deploy Contracts to Polkadot Hub with Ethers.js", - "version": "1.0.1", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -6160,6 +6160,7 @@ ] }, "primary_page": "reference/tools/zombienet.md", + "project_structure": "./\n├── network.toml (inline config from step 3)\n├── network-test.zndsl (inline test file from step 5)\n└── paseo-asset-hub.json (downloaded in step 3, if using parachain)", "reference_code": { "base_path": "", "files": [], @@ -6241,7 +6242,7 @@ ] }, "title": "Spawn and Test a Parachain Network with Zombienet", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -6433,7 +6434,7 @@ ] }, "title": "Deploy and Interact with Smart Contracts Using Web3.js", - "version": "1.0.1", + "version": "1.0.2", "workflow_pattern": "sequential" }, { @@ -7677,6 +7678,7 @@ ] }, "primary_page": "smart-contracts/libraries/wagmi.md", + "project_structure": "wagmi-dapp/\n├── src/\n│ ├── app/\n│ │ ├── layout.tsx\n│ │ ├── page.tsx\n│ │ └── providers.tsx\n│ └── components/\n│ ├── BlockNumber.tsx\n│ ├── ConnectWallet.tsx\n│ ├── StorageRead.tsx\n│ └── StorageWrite.tsx\n├── wagmi.ts\n├── tsconfig.json\n├── next.config.ts\n└── package.json", "reference_code": { "base_path": "", "files": [], @@ -7738,7 +7740,7 @@ }, { "action": "Add Storage contract write interaction using useWriteContract", - "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 6:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from .", + "description": "Create `src/components/StorageWrite.tsx` to send a `store(uint256)` transaction to the same contract used in step 7:\n\n```typescript\n'use client';\nimport { useWriteContract, useAccount } from 'wagmi';\nimport { polkadotHubTestnet } from '../wagmi';\nimport { useState } from 'react';\n\nconst STORAGE_ABI = [\n {\n inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],\n name: 'store',\n outputs: [],\n stateMutability: 'nonpayable',\n type: 'function',\n },\n] as const;\n\nconst CONTRACT_ADDRESS = '0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3' as `0x${string}`;\n\nexport function StorageWrite() {\n const { isConnected } = useAccount();\n const { writeContract, isPending, isSuccess } = useWriteContract();\n const [value, setValue] = useState('');\n\n const handleStore = () => {\n writeContract({\n address: CONTRACT_ADDRESS,\n abi: STORAGE_ABI,\n functionName: 'store',\n args: [BigInt(value)],\n chainId: polkadotHubTestnet.id,\n });\n };\n\n if (!isConnected) return

Connect your wallet to write.

;\n\n return (\n
\n setValue(e.target.value)} placeholder='Enter number' />\n \n {isSuccess &&

Transaction submitted!

}\n
\n );\n}\n```\n\nSame address as step 6. This step requires a connected wallet (from step 3's `ConnectWallet`) and PAS tokens for the transaction fee — get tokens from .", "order": 8, "working_directory": "wagmi-dapp" }, @@ -7785,7 +7787,7 @@ ] }, "title": "Build a Wagmi dApp Connected to Polkadot Hub", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { @@ -8777,13 +8779,13 @@ }, { "chain_role": "isolated", - "description": "Provides Polkadot Hub TestNet and MainNet network parameters and walks through adding the network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a new smart contract development environment or when a skill needs TestNet connection reference data (RPC URLs, WSS endpoints, chain IDs). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint'. Output: MetaMask configured with Polkadot Hub TestNet (chain ID 420420417) and account funded with PAS. This skill is primarily a network reference — consult it for endpoint URLs and chain IDs needed by other skills.", + "description": "Provides network parameters for Polkadot Hub TestNet, Polkadot Hub MainNet, and Kusama Hub, and walks through adding a network to MetaMask and requesting PAS test tokens from the faucet. Use when setting up a smart contract development environment or when a skill needs network reference data (RPC URLs, WSS endpoints, chain IDs, currency symbols). Trigger phrases: 'connect to Polkadot Hub', 'add TestNet to MetaMask', 'Polkadot Hub network config', 'chain ID 420420417', 'configure RPC endpoint', 'Kusama Hub network', 'Polkadot Hub MainNet RPC'. Output: MetaMask configured with the selected network; TestNet account funded with PAS.", "env_vars": [], "error_patterns": [ { "cause": "Incorrect chain ID or RPC URL entered in MetaMask network settings.", "pattern": "MetaMask: 'Invalid chain ID' or cannot connect to network", - "resolution": "Verify the chain ID is exactly 420420417 (decimal) and the RPC URL is https://services.polkadothub-rpc.com/testnet. Remove and re-add the network if the error persists." + "resolution": "For TestNet: chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet. For MainNet: chain ID 420420419, RPC https://eth-rpc.polkadot.io/. Remove and re-add the network in MetaMask if the error persists." }, { "cause": "The address already received tokens within the 24-hour rate limit window.", @@ -8793,27 +8795,28 @@ { "cause": "TestNet may be under maintenance, or the public RPC endpoint URL has changed.", "pattern": "RPC endpoint returns errors or is unreachable", - "resolution": "Check https://docs.polkadot.com/smart-contracts/connect/ for the latest endpoint URLs. The WSS alternative is wss://services.polkadothub-rpc.com/testnet." + "resolution": "Try the alternative TestNet endpoint: https://eth-rpc-testnet.polkadot.io/. Check https://docs.polkadot.com/smart-contracts/connect/ for the latest endpoint URLs." } ], "examples": [ { "actions": [ - "Add Polkadot Hub TestNet to MetaMask with chain ID 420420417 and RPC https://services.polkadothub-rpc.com/testnet", + "Select Polkadot Hub TestNet (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet)", + "Add TestNet to MetaMask with PAS as currency symbol", "Request PAS tokens from https://faucet.polkadot.io/", "Verify funded balance in MetaMask" ], "result": "MetaMask connected to Polkadot Hub TestNet with funded PAS balance, ready for smart contract deployment", - "scenario": "Common scenario: set up development environment from scratch", + "scenario": "Common scenario: set up TestNet development environment from scratch", "user_says": "I want to deploy a smart contract on Polkadot Hub — how do I connect to TestNet?" }, { "actions": [ - "Reference the MainNet network parameters from the source page", + "Select Polkadot Hub MainNet parameters: chain ID 420420419, RPC https://eth-rpc.polkadot.io/, currency DOT", "Warn: MainNet uses real DOT — use TestNet (chain ID 420420417) for development and testing" ], "result": "User receives Polkadot Hub MainNet network parameters with a caution about real token usage", - "scenario": "Edge case: looking up MainNet connection parameters", + "scenario": "Edge case: looking up MainNet or Kusama Hub connection parameters", "user_says": "What RPC endpoint and chain ID do I use for Polkadot Hub MainNet?" } ], @@ -8822,8 +8825,8 @@ "license": "CC-BY-4.0", "prerequisites": { "network": [ - "Internet access to Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet", - "Internet access to the faucet: https://faucet.polkadot.io/" + "Internet access to Polkadot Hub TestNet RPC: https://services.polkadothub-rpc.com/testnet (alt: https://eth-rpc-testnet.polkadot.io/)", + "Internet access to the PAS faucet: https://faucet.polkadot.io/" ], "wallet": [ "MetaMask browser extension installed (https://metamask.io/download/)" @@ -8840,22 +8843,28 @@ ], "steps": [ { - "action": "Add Polkadot Hub TestNet to MetaMask", - "description": "In MetaMask, open Settings > Networks > Add a network > Add a network manually. Enter these parameters: Network name: 'Polkadot Hub TestNet'; New RPC URL: 'https://services.polkadothub-rpc.com/testnet'; Chain ID: '420420417'; Currency symbol: 'PAS'. For the Block Explorer URL, check https://docs.polkadot.com/smart-contracts/connect/ for the current explorer URL, as it may change. Click Save and switch MetaMask to this network. This step must be delegated to the user.", + "action": "Select the target network and note its parameters", + "description": "Choose the network to configure. Reference parameters:\n\n**Polkadot Hub TestNet** (for development):\n- Chain ID: `420420417`\n- RPC: `https://services.polkadothub-rpc.com/testnet` (alt: `https://eth-rpc-testnet.polkadot.io/`)\n- Currency: PAS (18 decimals)\n- Block explorer: https://blockscout.passet-hub.parity-testnet.parity.io/\n\n**Polkadot Hub MainNet** (uses real DOT — use TestNet for development):\n- Chain ID: `420420419`\n- RPC: `https://eth-rpc.polkadot.io/`\n- Currency: DOT (10 decimals)\n\n**Kusama Hub** (canary network):\n- Chain ID: `420420418`\n- RPC: `https://eth-rpc-kusama.polkadot.io/`\n- Currency: KSM\n\nFor smart contract development, use Polkadot Hub TestNet (chain ID 420420417).", "order": 1, "working_directory": "." }, { - "action": "Request PAS test tokens from the faucet", - "description": "Navigate to https://faucet.polkadot.io/. Select 'Polkadot Hub TestNet', paste your 0x-prefixed EVM address, and click the request button. The faucet credits PAS tokens within seconds. Rate limit: approximately 500 PAS per 24 hours per address. This step must be delegated to the user.", + "action": "Add the network to MetaMask", + "description": "In MetaMask, open Settings > Networks > Add a network > Add a network manually. Enter the parameters for your chosen network from step 1. For TestNet: Network name 'Polkadot Hub TestNet', RPC URL 'https://services.polkadothub-rpc.com/testnet', Chain ID '420420417', Currency symbol 'PAS'. Click Save and switch MetaMask to this network. This step must be delegated to the user — agents cannot interact with browser extensions.", "order": 2, "working_directory": "." }, { - "action": "Verify MetaMask balance", - "description": "In MetaMask, confirm the PAS balance is non-zero on the Polkadot Hub TestNet network. To verify programmatically: curl -s -X POST https://services.polkadothub-rpc.com/testnet -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"YOUR_ADDRESS\",\"latest\"],\"id\":1}'. Replace YOUR_ADDRESS with the 0x-prefixed address. A non-zero hex result confirms the account is funded.", + "action": "Request PAS test tokens from the faucet (TestNet only)", + "description": "Navigate to https://faucet.polkadot.io/. Select 'Polkadot Hub TestNet', paste your 0x-prefixed EVM address, and click the request button. The faucet credits PAS tokens within seconds. Rate limit: approximately 500 PAS per 24 hours per address. This step must be delegated to the user. Skip this step if configuring MainNet or Kusama Hub — those require real tokens.", "order": 3, "working_directory": "." + }, + { + "action": "Verify balance", + "description": "In MetaMask, confirm a non-zero balance on the selected network. To verify programmatically (TestNet example):\n```bash\ncurl -s -X POST https://services.polkadothub-rpc.com/testnet \\\n -H 'Content-Type: application/json' \\\n -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"YOUR_ADDRESS\",\"latest\"],\"id\":1}'\n```\nReplace YOUR_ADDRESS with the 0x-prefixed address. A non-zero hex result confirms the account is funded.", + "order": 4, + "working_directory": "." } ], "supplementary_context": { @@ -8875,8 +8884,8 @@ } ] }, - "title": "Connect to Polkadot Hub and Get Test Tokens", - "version": "1.0.0", + "title": "Connect to Polkadot Hub Networks and Get Test Tokens", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -9722,7 +9731,7 @@ ] }, "title": "Deploy Uniswap V3 Core on Polkadot Hub (EVM)", - "version": "1.0.0", + "version": "1.0.1", "workflow_pattern": "sequential" }, { diff --git a/skill_coverage.json b/skill_coverage.json index 8170d28ba..1a8a7c647 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T15:30:00Z", + "generated": "2026-05-19T16:00:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -675,27 +675,27 @@ }, "reference/tools/zombienet.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "spawn-test-network-zombienet" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/connect.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "connect-polkadot-hub-testnet" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/dapps/zero-to-hero.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "build-dapp-viem-nextjs" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v2/core-v2-pvm.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -723,11 +723,11 @@ }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/core-v3.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:30:00Z", + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "deploy-uniswap-v3-core-evm" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/eth-dapps/uniswap-v3/periphery-v3.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -895,11 +895,11 @@ }, "smart-contracts/libraries/ethers-js.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "deploy-contracts-ethers-js" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/viem.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -911,19 +911,19 @@ }, "smart-contracts/libraries/wagmi.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "use-wagmi-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/web3-js.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T16:00:00Z", "skills": [ "deploy-interact-contracts-web3js" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/web3-py.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -982,10 +982,10 @@ "summary": { "blocked": 23, "not_applicable": 12, - "stale": 20, + "stale": 13, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 42 + "up_to_date": 49 } } From e5449b553026359cf9adfc4d5e375d3cf70a82ce Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 13:07:33 +0000 Subject: [PATCH 25/26] chore: auto-generate/update skills --- agent_skills_config.json | 27 +++++++++++++++------------ skill_coverage.json | 16 ++++++++-------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index f55d647e0..f75c596e7 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "de9bedab12699557c2904586fa456600f638523884ecacdebdf20f1536278153", - "generated": "2026-05-19T16:00:00Z", + "content_hash": "sha256:3076d122e54e7bc4847e4ddc93e404516599ae802a6cb740f8f48c63f9fb845f", + "generated": "2026-05-19T17:00:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -4960,10 +4960,10 @@ }, "primary_page": "smart-contracts/dev-environments/local-dev-node.md", "reference_code": { - "base_path": "polkadot-docs/smart-contracts/local-dev-node", - "branch": "master", + "base_path": "", + "branch": "", "files": [], - "repo": "polkadot-developers/polkadot-cookbook" + "repo": "none" }, "source_pages": [ "smart-contracts/dev-environments/local-dev-node.md" @@ -5038,7 +5038,7 @@ ] }, "title": "Set Up a Local Development Node", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5689,6 +5689,7 @@ ] }, "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", + "project_structure": "hardhat-nft-deployment/\n├── artifacts/\n├── contracts/\n│ └── MyNFT.sol\n├── ignition/\n│ ├── deployments/\n│ └── modules/\n│ └── MyNFT.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json", "reference_code": { "base_path": "", "branch": "", @@ -5797,7 +5798,7 @@ ] }, "title": "Deploy an ERC-721 NFT Using Hardhat", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5917,7 +5918,7 @@ ] }, "title": "Deploy an ERC-721 NFT Using Remix IDE", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -5998,8 +5999,10 @@ ] }, "primary_page": "smart-contracts/dev-environments/foundry.md", + "project_structure": "my-foundry-project/\n├── lib/\n├── out/\n├── script/\n│ └── Counter.s.sol\n├── src/\n│ └── Counter.sol\n├── test/\n│ └── Counter.t.sol\n├── .env\n├── .gitignore\n└── foundry.toml", "reference_code": { "base_path": "", + "branch": "", "files": [], "repo": "none" }, @@ -6095,7 +6098,7 @@ ] }, "title": "Use Foundry with Polkadot Hub", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -7366,7 +7369,7 @@ ] }, "title": "Deploy a Basic Smart Contract with Remix IDE", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -7479,7 +7482,7 @@ ] }, "title": "Connect Remix IDE to Polkadot Hub", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { @@ -7604,7 +7607,7 @@ ] }, "title": "Connect a Wallet to Polkadot Hub", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { diff --git a/skill_coverage.json b/skill_coverage.json index 1a8a7c647..0c5d79e56 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T16:00:00Z", + "generated": "2026-05-19T17:00:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -747,7 +747,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "deploy-basic-contract-remix" ], @@ -771,7 +771,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "deploy-erc721-nft-hardhat" ], @@ -779,7 +779,7 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "deploy-erc721-nft-remix" ], @@ -787,7 +787,7 @@ }, "smart-contracts/dev-environments/foundry.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "set-up-foundry-polkadot-hub" ], @@ -811,7 +811,7 @@ }, "smart-contracts/dev-environments/local-dev-node.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "set-up-local-dev-node" ], @@ -819,7 +819,7 @@ }, "smart-contracts/dev-environments/remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "connect-remix-polkadot" ], @@ -887,7 +887,7 @@ }, "smart-contracts/integrations/wallets.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T17:00:00Z", "skills": [ "connect-wallet-polkadot-hub" ], From e5a87b62ab42c66c0f9816fd0848da5c17e92747 Mon Sep 17 00:00:00 2001 From: "skill-bot[bot]" <3408791+skill-bot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 13:29:57 +0000 Subject: [PATCH 26/26] chore: auto-generate/update skills --- agent_skills_config.json | 791 +++++++++++++++++++++++---------------- skill_coverage.json | 58 +-- 2 files changed, 493 insertions(+), 356 deletions(-) diff --git a/agent_skills_config.json b/agent_skills_config.json index f75c596e7..66c432eae 100644 --- a/agent_skills_config.json +++ b/agent_skills_config.json @@ -1,6 +1,6 @@ { - "content_hash": "sha256:3076d122e54e7bc4847e4ddc93e404516599ae802a6cb740f8f48c63f9fb845f", - "generated": "2026-05-19T17:00:00Z", + "content_hash": "sha256:fe346a7e88c1f046e27b9003e71f8343e66d85d2ed727d4625501fb0a3b5b4cb", + "generated": "2026-05-19T20:00:00Z", "outputs": { "public_root": "/ai/", "skills_dir": "skills" @@ -3271,6 +3271,117 @@ "version": "1.0.1", "workflow_pattern": "sequential" }, + { + "chain_role": "isolated", + "description": "Assesses hardware, staking, and operational readiness to run a Polkadot validator node. Use when evaluating whether a machine or cloud instance meets Polkadot validator requirements, or when troubleshooting a validator that has been permissionlessly chilled. Key capabilities: minimum hardware checklist (CPU, NVMe SSD, RAM, network), tested VPS provider list, current minimum bond lookup, minimum self-stake (10,000 DOT since March 2026), minimum commission (10% since March 2026), slashing risk overview. Trigger phrases: 'validator requirements', 'become a polkadot validator', 'hardware for validator', 'minimum stake validator', 'validator self-stake', 'validator commission', 'chillOther validator'.", + "env_vars": [], + "error_patterns": [ + { + "cause": "Validator self-stake dropped below 10,000 DOT (e.g. after a slash) or commission rate is set below 10%. Both conditions allow any account to permissionlessly call staking.chillOther.", + "pattern": "Validator was chilled by staking.chillOther", + "resolution": "Check the stash account balance and rebond DOT to restore self-stake above 10,000 DOT. Verify the validator commission rate is at least 10% via the Staking Dashboard (https://staking.polkadot.cloud/#/overview) or Polkadot.js UI. After fixing both, re-declare intent to validate." + }, + { + "cause": "Total stake (self-stake plus nominators) is below the current era's minimum elected stake, which fluctuates and can exceed 1,000,000 DOT on Polkadot.", + "pattern": "Validator not elected despite meeting hardware requirements", + "resolution": "Check the current minimum elected stake on Subscan or the Staking Dashboard. Increase your bonded self-stake or attract more nominators. Consider starting on Kusama where the entry threshold is lower." + } + ], + "examples": [ + { + "actions": [ + "Present minimum CPU, storage, memory, and network requirements", + "List tested VPS provider families with notes on avoiding overprovisioned hosts", + "Advise to benchmark the chosen VPS before committing" + ], + "result": "User has a clear hardware checklist and a list of VPS families tested for Polkadot validators.", + "scenario": "Common scenario: pre-flight readiness check", + "user_says": "What hardware do I need to run a Polkadot validator?" + }, + { + "actions": [ + "Explain the permissionless chillOther mechanism introduced in the March 2026 upgrade", + "Direct user to check self-stake balance and commission rate", + "Instruct user to rebond DOT above 10,000 and set commission to at least 10%", + "Remind user to monitor self-stake balance to prevent recurrence" + ], + "result": "User understands why chillOther was triggered, restores compliant self-stake and commission, and re-declares intent to validate.", + "scenario": "Edge case: validator chilled by staking.chillOther", + "user_says": "Someone removed my validator from the active set using chillOther — how do I fix it?" + } + ], + "id": "onboard-polkadot-validator", + "invocation": "user", + "license": "CC-BY-4.0", + "prerequisites": { + "network": [ + "Polkadot network access to look up current minimum bond requirements" + ], + "runtime": [], + "tokens": [ + "DOT — minimum 10,000 DOT own self-stake required as of March 2026 runtime upgrade; elected-set minimum stake varies by era (can exceed 1,000,000 DOT on Polkadot)" + ], + "wallet": [ + "Stash account with sufficient DOT for bonding" + ] + }, + "primary_page": "node-infrastructure/run-a-validator/requirements.md", + "reference_code": { + "base_path": "", + "branch": "", + "files": [], + "repo": "none" + }, + "source_pages": [ + "node-infrastructure/run-a-validator/requirements.md" + ], + "steps": [ + { + "action": "Verify hardware meets minimum requirements", + "description": "Confirm the machine meets or exceeds all Polkadot validator hardware minimums:\n\n**CPU:** x86-64 compatible, 8 physical cores at 3.4 GHz or higher. Recommended processors: Intel Ice Lake or newer (Xeon or Core), AMD Zen3 or newer (EPYC or Ryzen). Disable simultaneous multithreading (Intel Hyper-Threading or AMD SMT). Single-threaded performance is prioritized over core count.\n\n**Storage:** NVMe SSD, minimum 2 TB. Storage requirements grow as the chain grows — check https://stakeworld.github.io/docs/dbsize for current estimates. Prioritize low latency over throughput. Avoid VPS providers that use NVMe-over-TCP (appears local but underperforms).\n\n**Memory:** 32 GB DDR4 ECC.\n\n**Network:** Symmetric 500 Mbit/s minimum.\n\nTested VPS families: GCP c2/c2d, AWS c6id, OVH (budget option if specs are met), Digital Ocean Premium Droplets, Vultr high-bandwidth plans, Akamai Cloud (Linode), Scaleway high-performance instances, OnFinality (blockchain-specialized). Always benchmark the VPS before committing — some providers overprovision shared hosts.", + "order": 1, + "working_directory": "." + }, + { + "action": "Check current minimum bond requirement", + "description": "The minimum total stake needed to be elected into the active validator set changes every era and can be in the millions of DOT. Check the current value using one of:\n\n- Chain State Values: https://wiki.polkadot.com/general/chain-state-values/\n- Subscan validator list: https://polkadot.subscan.io/validator_list?status=validator\n- Staking Dashboard: https://staking.polkadot.cloud/#/overview\n\nYour validator must be backed by at least this much stake (your own bonded DOT plus any nominator stake) to earn rewards. The minimum bond to start a validator instance is lower than the minimum elected stake — you can create a validator without being immediately elected.", + "order": 2, + "working_directory": "." + }, + { + "action": "Confirm self-stake and commission requirements", + "description": "As of the March 2026 runtime upgrade, two additional on-chain requirements apply:\n\n**Minimum self-stake (10,000 DOT):** Your validator stash account must bond at least 10,000 DOT as slashable self-stake. This must come from your own stash — nominator stake does not count. If self-stake drops below 10,000 DOT (e.g. due to a slash), any account can call `staking.chillOther` to remove your validator from the active set. Monitor your self-stake balance and rebond promptly if needed.\n\n**Minimum commission (10%):** Your validator must set a commission rate of at least 10%. Validators with sub-10% commission are also permissionlessly chill-able. For non-custodial operator scenarios, see https://docs.polkadot.com/node-infrastructure/run-a-validator/operational-tasks/staking-operator-proxy/ for managing commission without direct stash control.", + "order": 3, + "working_directory": "." + }, + { + "action": "Evaluate operational readiness and risk", + "description": "Before proceeding to node setup, confirm readiness on the following:\n\n**System administration skills:** Validators must troubleshoot and maintain node infrastructure. Unplanned downtime or misconfiguration can trigger slashing.\n\n**Security:** Review https://docs.polkadot.com/node-infrastructure/run-a-validator/operational-tasks/general-management/#secure-your-validator for required security hardening before exposing a validator node.\n\n**Start on Kusama:** If new to validation, start on Kusama (canary network, uses KSM) before Polkadot. Kusama has the same validator rules but lower financial stakes. Throughout the validator guides, look for 'Adjustments for Kusama' callouts.\n\n**Slashing risk:** Any DOT you bond is at risk if your validator fails or misbehaves. If you are uncertain about maintaining reliable uptime, consider nominating your DOT to a trusted existing validator instead.", + "order": 4, + "working_directory": "." + } + ], + "supplementary_context": { + "description": "Load for the operational setup steps that follow satisfying requirements.", + "pages": [ + { + "relevance": "Step-by-step guide to installing and configuring the Polkadot validator binary after requirements are confirmed.", + "slug": "node-infrastructure-run-a-validator-onboarding-and-offboarding-set-up-validator", + "title": "Set Up a Validator Node", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/onboarding-and-offboarding/set-up-validator.md" + }, + { + "relevance": "Explains how commission rates affect reward distribution between validators and nominators.", + "slug": "node-infrastructure-run-a-validator-staking-mechanics-rewards", + "title": "Rewards", + "url": "https://docs.polkadot.com/node-infrastructure/run-a-validator/staking-mechanics/rewards.md" + } + ] + }, + "title": "Validate Polkadot Validator Requirements", + "version": "1.0.0", + "workflow_pattern": "domain-intelligence" + }, { "chain_role": "isolated", "description": "Installs Chopsticks and creates a local fork of any Polkadot SDK-based chain for testing, debugging, and XCM simulation without touching live state. Covers global and local npm installation, YAML config file setup, fork creation via CLI flags or config, Polkadot.js interaction, block replay, and multi-chain XCM testing. Use when you need to test extrinsics, replay historical blocks, override chain storage, or simulate XCM messages locally. Trigger phrases: 'fork a chain with Chopsticks', 'local chain fork', 'replay blocks Chopsticks', 'test XCM locally', 'Chopsticks install'. Version: 1.3.0. Note: Chopsticks uses the Smoldot light client and does not support Ethereum JSON-RPC — MetaMask cannot connect to a Chopsticks fork.", @@ -4899,44 +5010,47 @@ }, { "chain_role": "isolated", - "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API.", + "description": "Builds and runs a local Polkadot SDK development node with smart contract support and an ETH-RPC adapter at http://localhost:8545. Use when you need a fee-free isolated environment for deploying and testing EVM smart contracts before using Polkadot Hub TestNet. Supports Ethereum tools (Hardhat, Remix, MetaMask, Ethers.js) via the ETH-RPC adapter. Trigger phrases: 'local dev node', 'run node locally', 'revive-dev-node', 'localhost:8545', 'local Polkadot blockchain'. Requires Rust and Polkadot SDK build dependencies installed. Do NOT use as a substitute for TestNet validation — the local node exposes a subset of the Ethereum JSON-RPC API; some Foundry/Hardhat time-manipulation and debug methods may be absent.", "env_vars": [], "error_patterns": [ { - "cause": "Missing system build dependencies such as clang, cmake, or protobuf-compiler.", - "pattern": "error: linker not found / error while loading shared libraries / could not compile", - "resolution": "Complete the Install Polkadot SDK Dependencies guide. On Ubuntu: sudo apt install -y cmake clang libclang-dev protobuf-compiler. On macOS: brew install cmake protobuf." + "cause": "The wasm32-unknown-unknown target is not installed.", + "pattern": "cargo build: error[E0463] can't find crate for 'std' / wasm32 target not found", + "resolution": "Run: rustup target add wasm32-unknown-unknown" + }, + { + "cause": "Release builds consume significant RAM. Low-memory machines may OOM.", + "pattern": "cargo build: out of memory / killed", + "resolution": "Limit Cargo parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" }, { - "cause": "The revive-dev-node is not running when eth-rpc starts, or it crashed before the adapter connected.", - "pattern": "Connection refused at localhost:8545 / eth-rpc exits immediately", - "resolution": "Ensure revive-dev-node --dev is running in a separate terminal and producing blocks before starting eth-rpc. The adapter connects via WebSocket to the node." + "cause": "The revive-dev-node is not running, or was started in a different directory.", + "pattern": "ETH-RPC adapter: connection refused / cannot connect to node", + "resolution": "Confirm the node is running in its terminal (should be producing blocks). Both processes must be in the polkadot-sdk directory." }, { - "cause": "An Ethereum tool is calling a JSON-RPC method not implemented by the ETH-RPC adapter (e.g., anvil_* or debug_*).", - "pattern": "Method not found / eth_method not supported", - "resolution": "The local node supports a subset of the Ethereum JSON-RPC API. Use Polkadot Hub TestNet for full API compatibility. Check the Polkadot EVM differences documentation for supported methods." + "cause": "The local ETH-RPC adapter exposes a subset of the Ethereum JSON-RPC API. Some debug/time methods are absent.", + "pattern": "Hardhat/Remix/MetaMask: method not supported / JSON-RPC method not found", + "resolution": "Check the Polkadot SDK documentation for supported RPC methods. Time-manipulation functions (evm_increaseTime, etc.) are not available. Use TestNet for behavior that requires full Ethereum RPC compatibility." } ], "examples": [ { "actions": [ - "Clone polkadot-sdk from GitHub", - "Build revive-dev-node with cargo build --release in polkadot-sdk/", - "Build eth-rpc adapter with cargo build --release", + "Clone polkadot-sdk repository", + "Build revive-dev-node with cargo build --release", + "Build eth-rpc with cargo build --release", "Start revive-dev-node --dev in terminal 1", "Start eth-rpc --dev in terminal 2", "Confirm ETH-RPC is listening at http://localhost:8545" ], - "result": "Local node produces blocks; ETH-RPC adapter accepts standard Ethereum JSON-RPC at localhost:8545.", - "scenario": "Common scenario: start a local dev environment for smart contract development", - "user_says": "Set up a local Polkadot node for testing my smart contracts" + "result": "Local Polkadot SDK node with ETH-RPC adapter running at http://localhost:8545, ready for smart contract deployment.", + "scenario": "Common scenario: start local dev environment", + "user_says": "Set up a local Polkadot node for smart contract testing" }, { "actions": [ - "Cancel the build with Ctrl+C", - "Check free disk space (df -h) — at least 20 GB required", - "Check free RAM — at least 8 GB recommended", + "Stop the build with Ctrl+C", "Restart build with limited parallelism: cargo build -p revive-dev-node --bin revive-dev-node --release -- -j2" ], "result": "Build completes more slowly with reduced CPU and memory usage.", @@ -5038,7 +5152,7 @@ ] }, "title": "Set Up a Local Development Node", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { @@ -5614,58 +5728,58 @@ }, { "chain_role": "isolated", - "description": "Scaffolds a Hardhat project manually, installs OpenZeppelin contracts and dotenv, creates a MyNFT.sol ERC-721 contract and Ignition deployment module, compiles with Cancun EVM version, and deploys to Polkadot Hub TestNet via Hardhat Ignition. Use when deploying an ERC-721 NFT to Polkadot Hub. Requires testnet PAS tokens and a funded EVM wallet. Trigger phrases: 'deploy NFT Polkadot', 'ERC-721 Hardhat', 'deploy non-fungible token', 'mint NFT Polkadot Hub'. Uses OpenZeppelin Contracts v5.4.0+ requiring evmVersion cancun. Do NOT use for ERC-20 tokens; use deploy-erc20-token-hardhat instead.", + "description": "Scaffolds a Hardhat project, creates an ERC-721 NFT contract using OpenZeppelin, and deploys it to Polkadot Hub TestNet via Hardhat Ignition. Use when deploying an NFT smart contract with a local CLI toolchain. Key capabilities: manual Hardhat project scaffold (no interactive init), OpenZeppelin ERC-721 contract, dotenv private key handling, evmVersion: cancun for OpenZeppelin v5+, explicit gasPrice to prevent 'priority too low' errors, requiredConfirmations: 1 to prevent Ignition false-drops on TestNet. Trigger phrases: 'deploy NFT Hardhat Polkadot', 'ERC-721 Hardhat', 'mint NFT Polkadot Hub Hardhat'. Output: NFT contract address on Polkadot Hub TestNet. Do NOT use for browser/Remix-based deployment — use deploy-erc721-nft-remix instead.", "env_vars": [ { - "description": "0x-prefixed EVM private key for the deployer account. Must be funded with testnet PAS. Never commit to version control.", + "description": "0x-prefixed EVM private key for the deployer account. Stored in .env — never commit to version control.", "name": "PRIVATE_KEY", "required": true } ], "error_patterns": [ { - "cause": "OpenZeppelin Contracts v5.4.0+ uses the mcopy opcode requiring Cancun EVM version.", - "pattern": "CompilationError: Invalid opcode: MCOPY", - "resolution": "In hardhat.config.ts set evmVersion: 'cancun' inside solidity.settings, then run 'npx hardhat compile' again." + "cause": "OpenZeppelin v5.4.0+ uses the mcopy opcode (Cancun EVM). Missing evmVersion: 'cancun' in hardhat.config.ts.", + "pattern": "Invalid opcode: MCOPY", + "resolution": "Add `evmVersion: 'cancun'` to the `settings` block in `hardhat.config.ts` and recompile." }, { - "cause": "Deployer account has insufficient PAS, or gasPrice is below the TestNet 1000 gwei base fee.", - "pattern": "Error: insufficient funds / Priority is too low", - "resolution": "Get testnet tokens at https://faucet.polkadot.io/. Verify hardhat.config.ts polkadotTestnet block includes gasPrice: 5000000000000." + "cause": "Gas price below the Polkadot Hub TestNet base fee of 1000 gwei.", + "pattern": "Error: Priority is too low / insufficient funds for gas", + "resolution": "Verify `gasPrice: 5000000000000` is set in the `polkadotTestnet` network config in `hardhat.config.ts`. If already set, check the PAS balance at https://faucet.polkadot.io/." }, { - "cause": "Ignition lost track of deployment due to missing gasPrice or requiredConfirmations: 0.", - "pattern": "IGN401: Transaction dropped / Transaction Already Imported", - "resolution": "Delete ignition/deployments/, ensure gasPrice: 5000000000000 and ignition.requiredConfirmations: 1 in config, then redeploy." + "cause": "Ignition lost track of a transaction (often because gasPrice was below TestNet base fee). Retrying sends a duplicate nonce transaction.", + "pattern": "IGN401 / Transaction Already Imported", + "resolution": "First check if the contract was actually deployed: look for `deployed_addresses.json` in `ignition/deployments/` and verify the address has contract code via `eth_getCode`. If genuinely not deployed: delete `ignition/deployments/` and redeploy after adding `gasPrice: 5000000000000` to the config." }, { - "cause": "hardhat.config.ts still uses Hardhat's vars.get() system rather than dotenv.", - "pattern": "Error: vars is not defined / Cannot read properties of undefined", - "resolution": "Add 'import \"dotenv/config\";' as the first line of hardhat.config.ts and replace vars.get('PRIVATE_KEY') with process.env.PRIVATE_KEY as string." + "cause": "Using @nomicfoundation/hardhat-toolbox instead of @nomicfoundation/hardhat-toolbox-viem.", + "pattern": "Compilation errors in hardhat.config.ts (Cannot find module)", + "resolution": "Run: npm install --save-dev @nomicfoundation/hardhat-toolbox-viem and update the import in hardhat.config.ts." } ], "examples": [ { "actions": [ - "Create project directory 'hardhat-nft-deployment' and run npm init -y", - "Install hardhat, OpenZeppelin contracts, and dotenv", - "Create hardhat.config.ts with dotenv, evmVersion: cancun, gasPrice 5000 gwei, requiredConfirmations: 1", - "Create .env with PRIVATE_KEY placeholder; ask user to fill it in", - "Create contracts/MyNFT.sol and ignition/modules/MyNFT.ts (replacing INSERT_OWNER_ADDRESS)", + "Scaffold hardhat-nft-deployment/ with npm init -y", + "Install Hardhat, hardhat-toolbox-viem, OpenZeppelin, dotenv", + "Fetch hardhat.config.ts and apply dotenv, evmVersion, gasPrice, requiredConfirmations substitutions", + "Create .env with PRIVATE_KEY and ask user to fill it in", + "Fetch MyNFT.sol to contracts/ and MyNFT.ts to ignition/modules/", "Run npx hardhat compile", - "Run npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet; delegate confirmation to user" + "Run npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet" ], - "result": "ERC-721 NFT contract deployed; contract address printed to console", - "scenario": "Common scenario: deploy a new ERC-721 NFT contract", - "user_says": "Deploy an ERC-721 NFT contract to Polkadot Hub TestNet" + "result": "ERC-721 NFT contract deployed to Polkadot Hub TestNet; contract address printed by Ignition.", + "scenario": "Common scenario: deploy ERC-721 NFT with Hardhat", + "user_says": "Deploy an ERC-721 NFT contract to Polkadot Hub TestNet using Hardhat" }, { "actions": [ - "Open hardhat.config.ts", - "Set solidity to: { version: '0.8.28', settings: { evmVersion: 'cancun' } }", + "Verify hardhat.config.ts has evmVersion: 'cancun' in the solidity settings block", + "If missing, add it: `settings: { evmVersion: 'cancun', optimizer: { enabled: true, runs: 200 } }`", "Run npx hardhat compile again" ], - "result": "Compilation succeeds with Cancun EVM version", + "result": "Compilation succeeds with Cancun EVM version enabled.", "scenario": "Edge case: compilation fails with MCOPY opcode error", "user_says": "Compilation fails with 'Invalid opcode: MCOPY'" } @@ -5691,10 +5805,23 @@ "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md", "project_structure": "hardhat-nft-deployment/\n├── artifacts/\n├── contracts/\n│ └── MyNFT.sol\n├── ignition/\n│ ├── deployments/\n│ └── modules/\n│ └── MyNFT.ts\n├── .env\n├── .gitignore\n├── hardhat.config.ts\n└── package.json", "reference_code": { - "base_path": "", - "branch": "", - "files": [], - "repo": "none" + "base_path": ".snippets/code/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat", + "branch": "master", + "files": [ + { + "description": "Hardhat v3 config with viem toolbox plugin; uses vars.get() — substitute with dotenv before use", + "path": "hardhat.config.ts" + }, + { + "description": "Minimal ERC-721 contract (ERC721 + Ownable) with safeMint(address to) and no URI storage", + "path": "MyNFT.sol" + }, + { + "description": "Hardhat Ignition deployment module for MyNFT; accepts initialOwner as a parameter", + "path": "MyNFT.ts" + } + ], + "repo": "polkadot-developers/polkadot-docs" }, "source_pages": [ "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md" @@ -5707,24 +5834,25 @@ "cd hardhat-nft-deployment", "npm init -y" ], - "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive and non-deterministic. Manual scaffolding is used instead.", + "description": "Create a new directory and initialize a Node.js project. Do not use 'npx hardhat init' — it is interactive. Manual scaffolding is used instead.", "order": 1, "working_directory": "." }, { "action": "Install Hardhat, OpenZeppelin, and dotenv", "commands": [ - "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox", + "npm install --save-dev hardhat@^2.27.0 @nomicfoundation/hardhat-toolbox-viem", "npm install @openzeppelin/contracts dotenv" ], - "description": "Install Hardhat v2 toolchain, OpenZeppelin contracts (v5.4.0+), and dotenv for secure private key handling.", + "description": "Install Hardhat with the viem toolbox plugin, OpenZeppelin contracts (v5+), and dotenv for secure private key handling.", "order": 2, "working_directory": "hardhat-nft-deployment" }, { "action": "Create the Hardhat configuration file", - "description": "Create a file named 'hardhat.config.ts' with the following content:\n\n```typescript\nimport { HardhatUserConfig } from 'hardhat/config';\nimport '@nomicfoundation/hardhat-toolbox';\nimport 'dotenv/config';\n\nconst config: HardhatUserConfig = {\n solidity: {\n version: '0.8.28',\n settings: { evmVersion: 'cancun' }\n },\n networks: {\n polkadotTestnet: {\n url: 'https://services.polkadothub-rpc.com/testnet',\n chainId: 420420417,\n accounts: [process.env.PRIVATE_KEY as string],\n gasPrice: 5000000000000\n }\n },\n ignition: { requiredConfirmations: 1 }\n};\n\nexport default config;\n```\n\nKey points: (1) 'dotenv/config' must be the first functional import. (2) evmVersion: 'cancun' is required for OpenZeppelin v5.4.0+ (mcopy opcode). (3) gasPrice: 5000000000000 (5000 gwei) prevents 'priority is too low' errors on TestNet. (4) requiredConfirmations: 1 prevents Ignition from misreading pending transactions as dropped.", + "description": "Fetch the reference file `hardhat.config.ts` and save it as `hardhat.config.ts`. Then apply ALL of the following substitutions:\n\n1. Remove the line `import { vars } from 'hardhat/config';` and replace it with `import 'dotenv/config';` (add as the first import, before `hardhatToolboxViemPlugin`).\n\n2. Replace `accounts: [vars.get('PRIVATE_KEY')]` with `accounts: [process.env.PRIVATE_KEY as string]`.\n\n3. Inside the `settings` block, add `evmVersion: 'cancun'` alongside the optimizer settings (required for OpenZeppelin v5.4.0+ which uses the mcopy opcode).\n\n4. Inside the `polkadotTestnet` network object, add `gasPrice: 5000000000000` (5000 gwei — prevents 'priority is too low' errors; TestNet base fee is 1000 gwei).\n\n5. After the `networks` block, add `ignition: { requiredConfirmations: 1 }` (prevents Ignition from misreading pending transactions as dropped on TestNet).\n\nThe final `hardhat.config.ts` should import: hardhatToolboxViemPlugin, dotenv/config. Apply the same substitutions here as to any other file in this project that uses `vars.get('PRIVATE_KEY')`.", "order": 3, + "reference_file": "hardhat.config.ts", "working_directory": "hardhat-nft-deployment" }, { @@ -5742,20 +5870,22 @@ "commands": [ "mkdir -p contracts ignition/modules" ], - "description": "Create the contracts and ignition/modules directories that Hardhat expects.", + "description": "Create the contracts and ignition/modules directories.", "order": 5, "working_directory": "hardhat-nft-deployment" }, { - "action": "Create the MyNFT.sol contract", - "description": "Create 'contracts/MyNFT.sol' with the following OpenZeppelin ERC-721 contract.Use the inline content below:\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```", + "action": "Fetch and save the MyNFT.sol contract", + "description": "Fetch the reference file `MyNFT.sol` and save it as `contracts/MyNFT.sol`. No substitutions needed — use the file as-is. The contract is a minimal ERC-721 (ERC721 + Ownable) with a `safeMint(address to)` function and no URI storage extension.", "order": 6, + "reference_file": "MyNFT.sol", "working_directory": "hardhat-nft-deployment" }, { - "action": "Create the Ignition deployment module", - "description": "Create 'ignition/modules/MyNFT.ts'. Replace INSERT_OWNER_ADDRESS with the Ethereum address that should own the NFT contract (typically the address derived from your PRIVATE_KEY):\n\n```typescript\nimport { buildModule } from '@nomicfoundation/hardhat-ignition/modules';\n\nconst MyNFTModule = buildModule('MyNFTModule', (m) => {\n const myNFT = m.contract('MyNFT', ['INSERT_OWNER_ADDRESS']);\n return { myNFT };\n});\n\nexport default MyNFTModule;\n```", + "action": "Fetch and save the Ignition deployment module", + "description": "Fetch the reference file `MyNFT.ts` and save it as `ignition/modules/MyNFT.ts`. Replace `INSERT_OWNER_ADDRESS` with the Ethereum address that should own the NFT contract (typically the address derived from your PRIVATE_KEY in .env).", "order": 7, + "reference_file": "MyNFT.ts", "working_directory": "hardhat-nft-deployment" }, { @@ -5763,7 +5893,7 @@ "commands": [ "npx hardhat compile" ], - "description": "Compile MyNFT.sol. If compilation fails with 'Invalid opcode: MCOPY', ensure evmVersion: 'cancun' is set in hardhat.config.ts and recompile.", + "description": "Compile MyNFT.sol. If compilation fails with 'Invalid opcode: MCOPY', confirm `evmVersion: 'cancun'` is present in the `settings` block of `hardhat.config.ts` and recompile.", "expected_output": "Compiled 1 Solidity file successfully", "order": 8, "working_directory": "hardhat-nft-deployment" @@ -5773,7 +5903,7 @@ "commands": [ "npx hardhat ignition deploy ignition/modules/MyNFT.ts --network polkadotTestnet" ], - "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success.", + "description": "Deploy the ERC-721 contract via Hardhat Ignition. Ignition will prompt to confirm the target network and chain ID — delegate this prompt to the user. Save the contract address printed on success (needed for NFT minting and frontend integration). If the transaction appears stuck, check `ignition/deployments/` for `deployed_addresses.json` to confirm whether the contract was actually deployed despite the error.", "expected_output": "MyNFTModule#MyNFT - 0x...", "interactive": true, "order": 9, @@ -5798,12 +5928,12 @@ ] }, "title": "Deploy an ERC-721 NFT Using Hardhat", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or rapid prototyping. Covers contract creation using OpenZeppelin ERC-721, compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", + "description": "Guides the user through deploying an ERC-721 NFT contract via Remix IDE (browser-based) connected to MetaMask on Polkadot Hub TestNet. Use when preferring no local toolchain or for rapid prototyping. Covers contract creation using OpenZeppelin ERC-721 (minimal: ERC721 + Ownable, no URI storage), compilation in Remix, and deployment via MetaMask injected provider. Trigger phrases: 'deploy NFT Remix', 'ERC-721 Remix Polkadot', 'mint NFT using browser', 'Remix NFT Polkadot Hub'. All steps are GUI-based — agent delegates browser actions to the user. Do NOT use for automated or CLI-based deployments; use deploy-erc721-nft-hardhat instead.", "env_vars": [], "error_patterns": [ { @@ -5819,7 +5949,7 @@ { "cause": "Contract code syntax error or Solidity version mismatch.", "pattern": "Compilation errors in Remix", - "resolution": "Verify the contract code was pasted correctly. In the Solidity Compiler tab, ensure the compiler version is 0.8.22 or later." + "resolution": "Verify the contract code was pasted correctly. In the Solidity Compiler tab, ensure the compiler version is 0.8.20 or later." } ], "examples": [ @@ -5831,7 +5961,7 @@ "Enter owner address as constructor argument and click Deploy", "Confirm transaction in MetaMask" ], - "result": "ERC-721 NFT contract deployed; contract address shown in Remix Deployed Contracts panel", + "result": "ERC-721 NFT contract deployed; contract address shown in Remix Deployed Contracts panel.", "scenario": "Common scenario: deploy ERC-721 NFT via Remix IDE", "user_says": "Deploy an ERC-721 NFT using Remix" }, @@ -5841,7 +5971,7 @@ "Paste the generated code into Remix as contracts/MyNFT.sol", "Proceed with the standard compile and deploy steps" ], - "result": "Custom ERC-721 contract with user-specified extensions compiled and deployed via Remix", + "result": "Custom ERC-721 contract with user-specified extensions compiled and deployed via Remix.", "scenario": "Edge case: user wants a custom NFT with additional features", "user_says": "I want to customize my NFT with a supply cap or royalties" } @@ -5863,9 +5993,15 @@ }, "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md", "reference_code": { - "base_path": "", - "files": [], - "repo": "none" + "base_path": ".snippets/code/smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix", + "branch": "master", + "files": [ + { + "description": "Minimal ERC-721 contract (ERC721 + Ownable) with safeMint(address to) — no URI storage extension", + "path": "MyNFT.sol" + } + ], + "repo": "polkadot-developers/polkadot-docs" }, "source_pages": [ "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md" @@ -5873,28 +6009,28 @@ "steps": [ { "action": "Open Remix IDE and create the contract file", - "description": "Delegate to the user: (1) Navigate to https://remix.ethereum.org in your web browser. (2) Under the 'contracts' folder, click 'Create new file' and name it 'MyNFT.sol'. (3) Paste the following ERC-721 contract code:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';\nimport {ERC721URIStorage} from '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\ncontract MyNFT is ERC721, ERC721URIStorage, Ownable {\n uint256 private _nextTokenId;\n constructor(address initialOwner) ERC721('MyNFT', 'MNFT') Ownable(initialOwner) {}\n function safeMint(address to, string memory uri) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n _setTokenURI(tokenId, uri);\n }\n function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); }\n function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); }\n}\n```\n\nWait for the user to confirm the file has been created and the code pasted.", + "description": "Delegate to the user:\n(1) Navigate to https://remix.ethereum.org in your web browser.\n(2) Under the 'contracts' folder, click 'Create new file' and name it 'MyNFT.sol'.\n(3) Paste the following ERC-721 contract code:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract MyNFT is ERC721, Ownable {\n uint256 private _nextTokenId;\n\n constructor(\n address initialOwner\n ) ERC721(\"MyToken\", \"MTK\") Ownable(initialOwner) {}\n\n function safeMint(address to) public onlyOwner {\n uint256 tokenId = _nextTokenId++;\n _safeMint(to, tokenId);\n }\n}\n```\n\nWait for the user to confirm the file has been created and the code pasted.\n\nTip: You can also generate a custom contract at https://wizard.openzeppelin.com/polkadot — paste the generated code instead if you want additional features.", "interactive": true, "order": 1, "working_directory": "." }, { "action": "Compile the contract in Remix", - "description": "Delegate to the user: (1) Click the 'Solidity Compiler' icon in the left panel. (2) Click the 'Compile MyNFT.sol' button. Success: the compiler icon shows a green checkmark. If errors appear, check that the contract code was pasted correctly and the Solidity version is 0.8.22+.", + "description": "Delegate to the user:\n(1) Click the 'Solidity Compiler' icon in the left panel.\n(2) Click the 'Compile MyNFT.sol' button.\nSuccess: the compiler icon shows a green checkmark. If errors appear, verify the contract code was pasted correctly and the Solidity version is 0.8.20+.", "interactive": true, "order": 2, "working_directory": "." }, { "action": "Connect MetaMask to Polkadot Hub TestNet", - "description": "Delegate to the user: (1) Click 'Deploy & Run Transactions' in the left panel. (2) In the 'Environment' dropdown, select 'Injected Provider - MetaMask'. (3) Approve the MetaMask connection popup. (4) Confirm MetaMask is on Polkadot Hub TestNet (chain ID 420420417). If not, add it via MetaMask Settings > Networks with RPC https://services.polkadothub-rpc.com/testnet. Confirm the funded account is visible.", + "description": "Delegate to the user:\n(1) Click 'Deploy & Run Transactions' in the left panel.\n(2) In the 'Environment' dropdown, hover over 'browser extension' and select 'Injected Provider - MetaMask'.\n(3) Approve the MetaMask connection popup.\n(4) Confirm MetaMask is on Polkadot Hub TestNet (chain ID 420420417). If not, add it via MetaMask Settings > Networks with RPC https://services.polkadothub-rpc.com/testnet. Confirm the funded account is visible.", "interactive": true, "order": 3, "working_directory": "." }, { "action": "Deploy the NFT contract", - "description": "Delegate to the user: (1) Ensure 'MyNFT' is selected in the Contract dropdown. (2) Expand the Deploy section and enter the constructor argument: the owner address (typically your MetaMask wallet address). (3) Click 'Deploy'. (4) Confirm the transaction in MetaMask. Once deployed, the contract appears in 'Deployed Contracts'. Record the contract address.", + "description": "Delegate to the user:\n(1) Ensure 'MyNFT' is selected in the Contract dropdown.\n(2) Expand the Deploy section and enter the constructor argument: the owner address (typically your MetaMask wallet address).\n(3) Click 'Deploy'.\n(4) Confirm the transaction in MetaMask.\nOnce deployed, the contract appears in 'Deployed Contracts'. Record the contract address shown in the Remix terminal.", "interactive": true, "order": 4, "working_directory": "." @@ -5918,12 +6054,12 @@ ] }, "title": "Deploy an ERC-721 NFT Using Remix IDE", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, and interacts with it using cast. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", + "description": "Installs the Foundry nightly build (required for native Polkadot chain support), initializes a Forge project, configures foundry.toml for Polkadot Hub TestNet, deploys a contract via forge create, verifies it on Blockscout or Routescan, interacts with it using cast, and runs Solidity tests with forge test. Use when building or deploying Solidity contracts on Polkadot Hub with the Foundry toolkit. Trigger phrases: 'Foundry Polkadot', 'forge deploy', 'forge create Polkadot', 'cast call Polkadot Hub', 'forge test Polkadot'. Requires Foundry nightly — stable release lacks Polkadot chain definitions.", "env_vars": [ { "description": "0x-prefixed EVM private key for the deployer account. Loaded via 'source .env' into the shell session. Must be funded with testnet PAS. Never commit to version control.", @@ -5961,10 +6097,10 @@ "Configure foundry.toml with Polkadot TestNet verifier settings", "Create .env with PRIVATE_KEY; ask user to fill in and run source .env", "Run forge build to compile", - "Run forge create src/Counter.sol:Counter --chain polkadot-testnet --rpc-url ... --private-key $PRIVATE_KEY --broadcast", + "Run forge create src/Counter.sol:Counter --chain polkadot-testnet --broadcast", "Verify: forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter --chain polkadot-testnet" ], - "result": "Contract compiled, deployed, and verified on Polkadot Hub TestNet", + "result": "Contract compiled, deployed, and verified on Polkadot Hub TestNet.", "scenario": "Common scenario: install Foundry and deploy a contract", "user_says": "Set up Foundry and deploy a contract to Polkadot Hub TestNet" }, @@ -5974,7 +6110,7 @@ "If not nightly, run 'foundryup --version nightly' to upgrade", "Retry the forge create command" ], - "result": "Foundry upgraded to nightly; polkadot-testnet chain recognized and deployment succeeds", + "result": "Foundry upgraded to nightly; polkadot-testnet chain recognized and deployment succeeds.", "scenario": "Edge case: stable Foundry installed instead of nightly", "user_says": "I get 'unknown chain polkadot-testnet' when running forge create" } @@ -6033,7 +6169,7 @@ }, { "action": "Configure foundry.toml for Polkadot Hub TestNet", - "description": "Replace the contents of 'foundry.toml' with the following. Choose Blockscout (no API key) or Routescan:\n\nBlockscout:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"\", url = \"https://blockscout-testnet.polkadot.io/api?\" }\n```\n\nRoutescan:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"verifyContract\", url = \"https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan\" }\n```\n\nWith this config, use --chain polkadot-testnet in commands without specifying the RPC URL explicitly.", + "description": "Replace the contents of 'foundry.toml' with the following. Choose Blockscout (no API key) or Routescan:\n\nBlockscout:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"\", url = \"https://blockscout-testnet.polkadot.io/api?\" }\n```\n\nRoutescan:\n```toml\n[profile.default]\nsrc = \"src\"\nout = \"out\"\nlibs = [\"lib\"]\nsolc_version = \"0.8.28\"\n\n[etherscan]\npolkadot-testnet = { key = \"verifyContract\", url = \"https://api.routescan.io/v2/network/testnet/evm/420420417/etherscan\" }\n```\n\nWith this config, use `--chain polkadot-testnet` in commands without specifying the RPC URL explicitly.", "order": 3, "working_directory": "my-foundry-project" }, @@ -6043,7 +6179,7 @@ "printf 'PRIVATE_KEY=\\n' > .env", "echo '.env' >> .gitignore" ], - "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env with their 0x-prefixed private key for a testnet-funded account. Do NOT ask for the private key in chat. After confirmation, instruct the user to run 'source .env' in their terminal to load the variable into their shell session.", + "description": "Create a .env file with an empty PRIVATE_KEY placeholder and add .env to .gitignore. Stop here and ask the user to edit .env with their 0x-prefixed private key for a testnet-funded account. Do NOT ask for the private key in chat. After confirmation, instruct the user to run 'source .env' in their terminal to load the variable.", "order": 4, "working_directory": "my-foundry-project" }, @@ -6078,6 +6214,12 @@ "description": "Replace INSERT_CONTRACT_ADDRESS and INSERT_ACCOUNT_ADDRESS with actual values.\n\nRead from contract:\n```bash\ncast call INSERT_CONTRACT_ADDRESS 'number()(uint256)' --chain polkadot-testnet\n```\n\nWrite to contract:\n```bash\ncast send INSERT_CONTRACT_ADDRESS 'setNumber(uint256)' 42 --chain polkadot-testnet --private-key $PRIVATE_KEY\n```\n\nCheck balance:\n```bash\ncast balance INSERT_ACCOUNT_ADDRESS --chain polkadot-testnet\n```", "order": 8, "working_directory": "my-foundry-project" + }, + { + "action": "Run Solidity tests with forge test", + "description": "Foundry uses Solidity for writing tests. Run the test suite:\n\n```bash\nforge test\n```\n\nFor verbose output showing gas usage and detailed logs:\n```bash\nforge test -vvv\n```\n\nRun specific tests by name:\n```bash\nforge test --match-test testIncrement\n```\n\nRun tests with gas reporting:\n```bash\nforge test --gas-report\n```\n\n**Important:** `forge test` runs against standard Anvil (Ethereum node), not a Polkadot node. Anvil does not enforce Existential Deposit, and gas, timestamps, and runtime behavior follow Ethereum semantics — tests that pass locally may behave differently on Polkadot Hub TestNet. Use forge test for unit testing contract logic; test Polkadot-specific behavior against a local dev node or TestNet.", + "order": 9, + "working_directory": "my-foundry-project" } ], "supplementary_context": { @@ -6098,7 +6240,7 @@ ] }, "title": "Use Foundry with Polkadot Hub", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { @@ -7234,50 +7376,46 @@ }, { "chain_role": "isolated", - "description": "Guides the user through deploying Remix IDE's default Storage.sol contract to Polkadot Hub TestNet via the browser-based Remix IDE and MetaMask wallet. All steps are browser GUI interactions — no local toolchain required. Use when testing the Polkadot Hub EVM environment, learning contract deployment basics, or demonstrating how Remix works with Polkadot Hub. Prerequisites: MetaMask installed and configured for Polkadot Hub TestNet (chain ID 420420417), testnet PAS tokens from https://faucet.polkadot.io/. Trigger phrases: 'deploy contract remix polkadot', 'remix ide polkadot hub', 'deploy storage contract remix'. Outcome: Storage contract deployed on Polkadot Hub TestNet.", + "description": "Guides the user through deploying Remix IDE's built-in Storage.sol contract to Polkadot Hub TestNet using MetaMask as the injected provider, then interacting with it. Use when introducing smart contract deployment on Polkadot Hub without a local toolchain. All steps are browser GUI interactions delegated to the user. Trigger phrases: 'deploy basic contract Remix', 'Remix Polkadot deploy', 'deploy Storage.sol Polkadot', 'first smart contract Polkadot Remix'. Requires MetaMask connected to Polkadot Hub TestNet (chain ID 420420417) with testnet PAS tokens. Do NOT use for CLI-based deployment — use deploy-basic-contract-hardhat instead.", "env_vars": [], "error_patterns": [ { - "cause": "MetaMask connection was rejected or MetaMask is not installed.", - "pattern": "MetaMask: User denied account access / MetaMask not connected", - "resolution": "Click the MetaMask extension icon and ensure it is unlocked. Re-select 'Injected Provider - MetaMask' in the Remix Environment dropdown and approve the connection prompt." - }, - { - "cause": "The connected MetaMask account has no testnet PAS tokens.", - "pattern": "Transaction failed: insufficient funds for gas", - "resolution": "Visit https://faucet.polkadot.io/ to obtain testnet PAS tokens. Ensure the faucet is sending to the correct address shown in MetaMask." + "cause": "MetaMask is connected to a different network than Polkadot Hub TestNet.", + "pattern": "MetaMask: Wrong network / chain ID mismatch", + "resolution": "Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet)." }, { - "cause": "MetaMask is connected to a different network instead of Polkadot Hub TestNet.", - "pattern": "Wrong network in MetaMask / chain ID mismatch", - "resolution": "Open MetaMask and switch to 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add it." + "cause": "The MetaMask account does not have enough PAS tokens to pay for deployment gas.", + "pattern": "insufficient funds for gas", + "resolution": "Get testnet PAS tokens from https://faucet.polkadot.io/ for the connected account, then retry." }, { - "cause": "The Remix compiler version does not match the pragma in Storage.sol.", - "pattern": "Solidity compile error: pragma mismatch", - "resolution": "In the Solidity Compiler tab, select a compiler version compatible with the pragma at the top of the contract file." + "cause": "Gas price below the Polkadot Hub TestNet base fee of 1000 gwei.", + "pattern": "Transaction stuck / Priority is too low", + "resolution": "Retry the deployment; Remix and MetaMask typically handle gas estimation automatically. If repeated failures occur, check the TestNet status page or Discord for outages." } ], "examples": [ { "actions": [ - "Verify MetaMask is on Polkadot Hub TestNet and account has PAS tokens", - "Open remix.ethereum.org and locate contracts/1_Storage.sol", - "Compile with Solidity Compiler tab", - "Select Injected Provider - MetaMask in Deploy tab and click Deploy", - "Confirm MetaMask transaction and wait for mining" + "Confirm MetaMask is on Polkadot Hub TestNet with PAS balance", + "Open https://remix.ethereum.org and locate contracts/Storage.sol", + "Compile via the Solidity Compiler plugin", + "Select Injected Provider - MetaMask in the Deploy panel", + "Click Deploy and confirm transaction in MetaMask" ], - "result": "Storage contract deployed; appears in Remix Deployed Contracts panel with callable store/retrieve functions", - "scenario": "Common scenario: first contract deployment", - "user_says": "Deploy a smart contract on Polkadot Hub using Remix IDE" + "result": "Storage.sol deployed to Polkadot Hub TestNet; contract address shown in Remix Deployed Contracts panel.", + "scenario": "Common scenario: first contract deployment via Remix", + "user_says": "Deploy a basic smart contract to Polkadot Hub using Remix" }, { "actions": [ - "Direct user to https://faucet.polkadot.io/ to request testnet PAS tokens", - "Ask user to confirm the faucet sent tokens to the correct address", - "Once confirmed, retry the Deploy step in Remix" + "Direct user to https://faucet.polkadot.io/", + "User pastes MetaMask address and selects Polkadot Hub TestNet in the faucet", + "Wait for tokens to arrive (typically 30 seconds)", + "Retry the Deploy step in Remix" ], - "result": "User obtains testnet PAS tokens and deployment proceeds successfully", + "result": "User obtains testnet PAS tokens and deployment proceeds successfully.", "scenario": "Edge case: no testnet tokens", "user_says": "I try to deploy but MetaMask says insufficient funds" } @@ -7294,7 +7432,7 @@ "Modern web browser (Chrome or Firefox recommended)" ], "tokens": [ - "Testnet PAS tokens from https://faucet.polkadot.io/ (rate limit: check faucet page)" + "Testnet PAS tokens from https://faucet.polkadot.io/ (base fee: 1000 gwei)" ], "wallet": [ "MetaMask browser extension installed and configured for Polkadot Hub TestNet", @@ -7304,6 +7442,7 @@ "primary_page": "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md", "reference_code": { "base_path": "", + "branch": "", "files": [], "repo": "none" }, @@ -7313,33 +7452,33 @@ "steps": [ { "action": "Verify MetaMask is connected to Polkadot Hub TestNet", - "description": "Instruct the user: Open MetaMask and confirm the selected network is 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add the network. Also confirm the account balance shows testnet PAS tokens. If the balance is zero, visit https://faucet.polkadot.io/ to request tokens. Wait for the user to confirm both conditions before proceeding.", + "description": "Instruct the user: Open MetaMask and confirm the selected network is 'Polkadot Hub TestNet' (chain ID 420420417). If not configured, use the connect-wallet-polkadot-hub skill to add the network. Confirm the account balance shows testnet PAS tokens. If the balance is zero, visit https://faucet.polkadot.io/ to request tokens. Wait for the user to confirm both conditions before proceeding.", "order": 1, "working_directory": "." }, { "action": "Open Remix IDE and locate Storage.sol", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on '1_Storage.sol' to open it in the editor. This is the default Remix sample contract — no changes needed.", + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. In the left file explorer panel, expand the 'contracts/' folder. Click on 'Storage.sol' to open it in the editor. This is the default Remix sample contract — no code changes are needed.", "order": 2, "working_directory": "." }, { "action": "Compile Storage.sol in Remix", - "description": "Instruct the user: Click the Solidity Compiler icon (second icon in the left sidebar). Confirm the compiler version is 0.8.x or higher. Click the blue 'Compile 1_Storage.sol' button. Wait for the green checkmark indicating successful compilation. If there are errors, verify the compiler version matches the pragma in the file.", + "description": "Instruct the user: Click the Solidity Compiler icon in the left sidebar. Click the blue 'Compile Storage.sol' button. Wait for the green checkmark indicating successful compilation. If there are errors, verify Storage.sol is selected and the compiler version is 0.8.x or higher.", "expected_output": "Green checkmark on the Solidity Compiler sidebar icon; no errors shown.", "order": 3, "working_directory": "." }, { "action": "Deploy to Polkadot Hub TestNet via MetaMask", - "description": "Instruct the user: Click the Deploy and Run Transactions icon (fourth icon in the left sidebar). In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the Network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", + "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will prompt for permission — click Connect. Confirm the Account field shows your MetaMask address and the network shows Polkadot Hub TestNet (chain ID 420420417). Click the orange 'Deploy' button. MetaMask will open a transaction confirmation — click Confirm. Wait for the transaction to be mined (typically 15-30 seconds on TestNet).", "expected_output": "Deployed contract appears in the 'Deployed Contracts' section at the bottom of the Deploy panel.", "order": 4, "working_directory": "." }, { "action": "Interact with the deployed Storage contract", - "description": "Instruct the user: In the 'Deployed Contracts' panel, expand the deployed Storage contract. To write a value: enter a number in the field next to the 'store' button and click 'store'. Confirm the MetaMask transaction. To read the stored value: click the 'retrieve' button (no transaction needed). The return value shows in the Remix console below.", + "description": "Instruct the user: In the 'Deployed Contracts' panel, expand the deployed Storage contract. To write a value: enter a number in the field next to the 'store' button and click 'store'. Confirm the MetaMask transaction. To read the stored value: click the 'retrieve' button (read-only, no transaction needed). The return value appears in the Remix terminal below.", "expected_output": "retrieve() returns the number stored in the previous store() call.", "order": 5, "working_directory": "." @@ -7355,13 +7494,7 @@ "url": "https://docs.polkadot.com/smart-contracts/integrations/wallets.md" }, { - "relevance": "How to obtain testnet PAS tokens for deployment transactions.", - "slug": "smart-contracts-faucet", - "title": "Get Tokens from the Official Faucet", - "url": "https://docs.polkadot.com/smart-contracts/faucet.md" - }, - { - "relevance": "CLI-based alternative: deploy the same Storage contract using Hardhat and a local toolchain.", + "relevance": "CLI-based alternative to deploy the same type of contract using Hardhat and a local toolchain.", "slug": "smart-contracts-cookbook-smart-contracts-deploy-basic-basic-hardhat", "title": "Deploy a Basic Contract with Hardhat", "url": "https://docs.polkadot.com/smart-contracts/cookbook/smart-contracts/deploy-basic/basic-hardhat.md" @@ -7369,12 +7502,12 @@ ] }, "title": "Deploy a Basic Smart Contract with Remix IDE", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Walks the user through selecting the MetaMask injected provider in Remix IDE's Deploy and Run tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and already configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", + "description": "Walks the user through opening Remix IDE, understanding its interface, and selecting the MetaMask injected provider in the Deploy and Run Transactions tab to connect the browser-based IDE to Polkadot Hub TestNet. All steps are browser GUI interactions. Use before deploying or testing smart contracts via Remix on Polkadot Hub. Prerequisite: MetaMask must be installed and configured with Polkadot Hub TestNet (chain ID 420420417) — use the connect-wallet-polkadot-hub skill first if needed. Trigger phrases: 'connect remix to polkadot', 'use remix ide polkadot hub', 'remix metamask polkadot', 'injected provider remix'. Outcome: Remix displays the MetaMask account address and is ready for contract deployment.", "env_vars": [], "error_patterns": [ { @@ -7398,11 +7531,11 @@ "actions": [ "Open https://remix.ethereum.org", "Click Deploy and Run Transactions tab", - "Select Injected Provider - MetaMask in the Environment dropdown", + "Click Environment dropdown, hover over 'browser extension', select 'Injected Provider - MetaMask'", "Approve the MetaMask connection popup", "Verify account address and chain ID 420420417 appear in the Deploy panel" ], - "result": "Remix IDE is connected to Polkadot Hub TestNet via MetaMask and ready for contract deployment", + "result": "Remix IDE is connected to Polkadot Hub TestNet via MetaMask and ready for contract deployment.", "scenario": "Common scenario: connect Remix before deployment", "user_says": "Connect Remix IDE to Polkadot Hub so I can deploy a contract" }, @@ -7412,7 +7545,7 @@ "If network not present, direct to connect-wallet-polkadot-hub skill", "Confirm Remix updates to show chain ID 420420417 automatically" ], - "result": "Remix shows correct network after MetaMask network switch", + "result": "Remix shows correct network after MetaMask network switch.", "scenario": "Edge case: MetaMask on wrong network", "user_says": "Remix shows the wrong chain ID after connecting" } @@ -7431,6 +7564,7 @@ "primary_page": "smart-contracts/dev-environments/remix.md", "reference_code": { "base_path": "", + "branch": "", "files": [], "repo": "none" }, @@ -7440,25 +7574,25 @@ "steps": [ { "action": "Open Remix IDE", - "description": "Instruct the user: Open https://remix.ethereum.org in the browser. Wait for the IDE to fully load (the file explorer appears on the left). Ensure MetaMask is unlocked and set to Polkadot Hub TestNet before proceeding.", + "description": "Instruct the user: Open https://remix.ethereum.org in the browser. Wait for the IDE to fully load (the file explorer appears on the left). Ensure MetaMask is unlocked and set to Polkadot Hub TestNet before proceeding.\n\nThe Remix interface has three main areas:\n- **Plugin panel** (left sidebar): icons for file explorer, search, Solidity compiler, Deploy & run transactions, debugger, and Git.\n- **Editor panel** (center): where you write and modify Solidity files.\n- **Terminal** (bottom): shows compiler logs, deployment events, transactions, and console.log output.", "order": 1, "working_directory": "." }, { "action": "Navigate to the Deploy and Run Transactions tab", - "description": "Instruct the user: Click the Deploy and Run Transactions icon in the left sidebar (it looks like a plug/Ethereum diamond — the fourth icon from top). The Deploy and Run Transactions panel opens on the left.", + "description": "Instruct the user: In the left plugin panel, click the Deploy and Run Transactions icon (the plug/Ethereum diamond icon). The Deploy and Run Transactions panel opens on the left.", "order": 2, "working_directory": "." }, { "action": "Select Injected Provider - MetaMask", - "description": "Instruct the user: In the Environment dropdown at the top of the Deploy panel, select 'Injected Provider - MetaMask'. MetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step.", + "description": "Instruct the user:\n(1) Click on the **Environment** dropdown at the top of the Deploy panel.\n(2) Hover over **browser extension**.\n(3) Select **Injected Provider - MetaMask**.\nMetaMask will open a connection request popup — click 'Next' then 'Connect' to approve. If MetaMask does not open automatically, click the MetaMask extension icon, unlock it, then repeat this step.", "order": 3, "working_directory": "." }, { "action": "Verify the connection", - "description": "Instruct the user: Confirm the following in the Deploy panel: 1) The Account field shows your MetaMask wallet address. 2) The network indicator shows chain ID 420420417 (Polkadot Hub TestNet). If the chain ID shows a different value, switch networks in MetaMask to Polkadot Hub TestNet and the Remix panel will update automatically.", + "description": "Instruct the user: Confirm the following in the Deploy panel:\n1) The **Account** field shows your MetaMask wallet address.\n2) The network indicator shows chain ID 420420417 (Polkadot Hub TestNet).\nIf the chain ID shows a different value, switch networks in MetaMask to Polkadot Hub TestNet and the Remix panel will update automatically.", "expected_output": "Remix Deploy panel shows MetaMask account address and Polkadot Hub TestNet (chain ID 420420417).", "order": 4, "working_directory": "." @@ -7482,51 +7616,48 @@ ] }, "title": "Connect Remix IDE to Polkadot Hub", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Guides the user through adding Polkadot Hub TestNet (RPC, chain ID 420420417, PAS token) to MetaMask or Talisman and switching the active network. All steps are browser extension GUI interactions. Use before deploying smart contracts, using Remix IDE with Polkadot Hub, or obtaining testnet tokens. Trigger phrases: 'connect metamask polkadot hub', 'add polkadot network metamask', 'set up wallet polkadot testnet', 'configure talisman polkadot hub'. Outcome: wallet shows Polkadot Hub TestNet as active network and is ready for on-chain interactions.", + "description": "Adds Polkadot Hub TestNet to MetaMask or Talisman and funds the account with PAS tokens from the faucet. Use when a user needs to set up their browser wallet before deploying contracts or interacting with Polkadot Hub. Key capabilities: MetaMask 'Custom' tab flow to add a custom network, Talisman 'More → Manage Networks' flow, faucet token request. Trigger phrases: 'add polkadot hub metamask', 'configure wallet polkadot hub', 'connect metamask polkadot', 'add custom network metamask', 'talisman polkadot hub network'.", "env_vars": [], "error_patterns": [ { - "cause": "The RPC URL was entered incorrectly or the TestNet endpoint is temporarily unavailable.", - "pattern": "Could not fetch chain ID / Invalid RPC URL", - "resolution": "Verify the RPC URL is exactly: https://services.polkadothub-rpc.com/testnet (no trailing slash). Check the Polkadot Hub TestNet status page or Discord for outages." - }, - { - "cause": "A different endpoint was used that returns a different chain ID.", - "pattern": "Chain ID mismatch: expected 420420417", - "resolution": "Delete the incorrectly configured network from MetaMask and re-add it using the exact RPC URL and chain ID 420420417." + "cause": "Polkadot Hub TestNet is already added to MetaMask under a different network name.", + "pattern": "MetaMask: 'Chain ID already exists'", + "resolution": "In MetaMask, go to Settings > Networks and look for a network with chain ID 420420417. Switch to that network instead of adding a duplicate." }, { - "cause": "Account not funded with testnet tokens.", - "pattern": "Network added but balance shows 0 PAS", - "resolution": "Visit https://faucet.polkadot.io/, paste your address, select Polkadot Hub TestNet, and request tokens." + "cause": "The Polkadot Hub TestNet RPC endpoint is temporarily unavailable.", + "pattern": "MetaMask: RPC URL request failed / network unreachable", + "resolution": "Check the Polkadot Hub TestNet status. The standard RPC is https://services.polkadothub-rpc.com/testnet. Retry after a few minutes." } ], "examples": [ { "actions": [ - "Open MetaMask, click network selector, click Add network, then Add a network manually", - "Enter network name, RPC URL (https://services.polkadothub-rpc.com/testnet), chain ID 420420417, symbol PAS", + "Open MetaMask and click the network dropdown", + "Click the Custom tab and then 'Add a custom network'", + "Fill in the network name, RPC URL (https://services.polkadothub-rpc.com/testnet), chain ID 420420417, and symbol PAS", "Save and switch to Polkadot Hub TestNet", - "Visit https://faucet.polkadot.io/ and request testnet PAS tokens" + "Obtain PAS tokens from https://faucet.polkadot.io/ if balance is 0" ], - "result": "MetaMask shows Polkadot Hub TestNet as active network with a non-zero PAS balance", + "result": "MetaMask connected to Polkadot Hub TestNet with a non-zero PAS balance", "scenario": "Common scenario: add Polkadot Hub TestNet to MetaMask", - "user_says": "Add Polkadot Hub TestNet to MetaMask and get some test tokens" + "user_says": "How do I connect MetaMask to Polkadot Hub TestNet?" }, { "actions": [ - "Verify the RPC URL is exactly https://services.polkadothub-rpc.com/testnet with no trailing slash", - "Check whether the Polkadot Hub TestNet is experiencing downtime", - "Retry adding the network once the endpoint is confirmed reachable" + "Open Talisman extension and click 'More'", + "Click 'Manage Networks' then '+ Add network'", + "Enter RPC URL, chain ID 420420417, and symbol PAS, then Save", + "Switch to Polkadot Hub TestNet in the Talisman network list" ], - "result": "Network added successfully once the correct RPC URL is verified and the endpoint is reachable", - "scenario": "Edge case: RPC URL rejected by MetaMask", - "user_says": "MetaMask says it could not fetch the chain ID when I enter the RPC URL" + "result": "Talisman configured for Polkadot Hub TestNet; ready to sign transactions", + "scenario": "Edge case: user prefers Talisman over MetaMask", + "user_says": "I use Talisman wallet — how do I add Polkadot Hub TestNet to it?" } ], "id": "connect-wallet-polkadot-hub", @@ -7552,20 +7683,20 @@ "steps": [ { "action": "Open MetaMask network settings", - "description": "Instruct the user: Click the MetaMask extension icon in the browser toolbar to open it. Click the network selector at the top of the MetaMask popup (shows 'Ethereum Mainnet' by default). Click 'Add network' at the bottom of the network list. On the Add Network page, click 'Add a network manually'.", + "description": "Instruct the user: Click the MetaMask extension icon in the browser toolbar. Click the network dropdown at the top of the MetaMask popup (shows the currently active network). A network list appears — at the bottom, click the 'Custom' tab to switch to the custom networks view.", "order": 1, "working_directory": "." }, { "action": "Enter Polkadot Hub TestNet network details", - "description": "Instruct the user: Fill in the following fields exactly as shown:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added.", + "description": "Instruct the user: In the Custom networks view, click the 'Add a custom network' button. Fill in the following fields exactly:\n- Network name: Polkadot Hub TestNet\n- New RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\n- Block explorer URL: (leave blank — no standard explorer URL is defined)\nClick 'Save'. MetaMask will confirm the network was added.", "order": 2, "working_directory": "." }, { "action": "Switch to Polkadot Hub TestNet", - "description": "Instruct the user: In MetaMask, click the network selector again. Select 'Polkadot Hub TestNet' from the list. The header now shows 'Polkadot Hub TestNet' and the balance shows PAS.", - "expected_output": "MetaMask header displays 'Polkadot Hub TestNet'; account balance shows PAS balance.", + "description": "Instruct the user: MetaMask will show the network in the list. Click 'Polkadot Hub TestNet' to switch to it. The network selector at the top of MetaMask now shows 'Polkadot Hub TestNet' and the balance shows PAS.", + "expected_output": "MetaMask network selector displays 'Polkadot Hub TestNet'; account balance shows PAS balance.", "order": 3, "working_directory": "." }, @@ -7578,7 +7709,7 @@ }, { "action": "Alternative: Add Polkadot Hub TestNet to Talisman", - "description": "If the user prefers Talisman instead of MetaMask, instruct them: Open the Talisman extension and click the settings icon. Go to Networks and Tokens, then Add Network. Enter the same network details as step 2: RPC URL: https://services.polkadothub-rpc.com/testnet, Chain ID: 420420417, Symbol: PAS. Save and switch to the Polkadot Hub TestNet network in Talisman.", + "description": "If the user prefers Talisman instead of MetaMask, instruct them: Open the Talisman extension and click on the Talisman icon in the browser toolbar. Click the 'More' button. Click 'Manage Networks'. Click the '+ Add network' button. Fill in the following details:\n- RPC URL: https://services.polkadothub-rpc.com/testnet\n- Chain ID: 420420417\n- Currency symbol: PAS\nClick Save. Polkadot Hub TestNet will now appear in the Talisman network list.", "order": 5, "working_directory": "." } @@ -7607,7 +7738,7 @@ ] }, "title": "Connect a Wallet to Polkadot Hub", - "version": "1.1.0", + "version": "1.2.0", "workflow_pattern": "sequential" }, { @@ -7795,7 +7926,7 @@ }, { "chain_role": "isolated", - "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, interact.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", + "description": "Python workflow for compiling Solidity contracts with py-solc-x, deploying them to Polkadot Hub TestNet (chain ID 420420417) using Web3.py, and reading/writing contract state. Uses a Python virtualenv, .env file for the private key, and three scripts: compile.py, deploy.py, update_storage.py. Use when your project is Python-based and you need EVM contract interaction without Node.js. Key capabilities: virtualenv setup, py-solc-x Solidity compilation, Web3.py contract deployment, transaction signing with a local account, contract function calls. NOTE: Web3.js (the JavaScript library) has been sunset — for new JavaScript/TypeScript projects prefer Ethers.js or viem. This skill covers Web3.py (Python), which is actively maintained. Trigger phrases: 'web3.py polkadot', 'python smart contract deployment', 'deploy contract python', 'web3 python tutorial'.", "env_vars": [ { "description": "EVM account private key (with or without 0x prefix) for signing deployment and write transactions. Do NOT ask the user to type or paste their private key in the chat — they should edit the .env file directly.", @@ -7817,36 +7948,36 @@ { "cause": "The .env file is missing or PRIVATE_KEY is not defined in it.", "pattern": "KeyError: 'PRIVATE_KEY'", - "resolution": "Create a .env file in the web3py-contracts directory with the line: PRIVATE_KEY=0xYOUR_KEY. Ensure python-dotenv is installed and load_dotenv() is called before accessing os.environ['PRIVATE_KEY']." + "resolution": "Create a .env file in the web3py-project directory with the line: PRIVATE_KEY=0xYOUR_KEY. Ensure python-dotenv is installed and load_dotenv() is called before accessing os.environ['PRIVATE_KEY']." }, { "cause": "Solidity pragma version mismatch.", "pattern": "SolcError: Source file requires different compiler version", - "resolution": "Ensure install_solc('0.8.20') is called in compile.py before compile_standard. The py-solc-x library downloads the compiler if not already cached." + "resolution": "Ensure solcx.install_solc('0.8.9') is called in compile.py before compile_source. The py-solc-x library downloads the compiler if not already cached." } ], "examples": [ { "actions": [ - "Create web3py-contracts/ directory and Python virtualenv", + "Create web3py-project/ directory and Python virtualenv", "Install web3, py-solc-x, python-dotenv via pip", "Instruct user to add PRIVATE_KEY to .env file directly (not in chat)", - "Create Storage.sol, compile.py, deploy.py, interact.py from inline code", + "Create Storage.sol, compile.py, deploy.py, update_storage.py from inline code", "Run python compile.py then python deploy.py", - "Run python interact.py to verify store/retrieve" + "Run python update_storage.py to verify setNumber/storedNumber" ], - "result": "Contract deployed and verified on Polkadot Hub TestNet; stored value updated to 42", + "result": "Contract deployed and verified on Polkadot Hub TestNet; storedNumber updated to 42", "scenario": "Common scenario: deploy a Storage contract to Polkadot Hub TestNet using Python", "user_says": "Deploy a smart contract to Polkadot Hub using Python and Web3.py" }, { "actions": [ - "Replace Storage.sol with the user's .sol file in the web3py-contracts/ directory", - "Update compile.py: change 'Storage.sol' key and 'Storage' contract name to match the user's file and contract name", - "Update deploy.py: change compiled['contracts']['YourFile.sol']['YourContract'] to match", - "Update interact.py: change the ABI extraction path and function calls to match the user's contract ABI" + "Replace Storage.sol with the user's .sol file in web3py-project/", + "Update compile.py: change 'Storage.sol' and 'Storage' to match the user's file and contract name", + "Update deploy.py: change the ABI/bytecode file paths to match", + "Update update_storage.py: change function calls to match the user's contract ABI" ], - "result": "Custom contract compiled and deployed; interact.py calls the contract's specific functions", + "result": "Custom contract compiled and deployed; update_storage.py calls the contract's specific functions", "scenario": "Edge case: user wants to deploy a custom contract instead of Storage", "user_says": "I have my own Solidity file — how do I deploy it with Web3.py?" } @@ -7870,6 +8001,7 @@ ] }, "primary_page": "smart-contracts/libraries/web3-py.md", + "project_structure": "web3py-project/\n├── Storage.sol\n├── compile.py\n├── deploy.py\n├── update_storage.py\n├── .env\n├── .gitignore\n├── abis/\n│ └── Storage.json\n└── artifacts/\n └── Storage.bin", "reference_code": { "base_path": "", "files": [], @@ -7882,11 +8014,11 @@ { "action": "Create project directory and Python virtual environment", "commands": [ - "mkdir web3py-contracts && cd web3py-contracts", + "mkdir web3py-project && cd web3py-project", "python3 -m venv venv", "source venv/bin/activate" ], - "description": "Create a new directory named 'web3py-contracts', create a Python virtual environment inside it, and activate it. On Windows use 'venv\\Scripts\\activate' instead.", + "description": "Create a new directory named 'web3py-project', create a Python virtual environment inside it, and activate it. On Windows use `venv\\Scripts\\activate` instead.", "order": 1, "working_directory": "." }, @@ -7895,51 +8027,51 @@ "commands": [ "pip install web3 py-solc-x python-dotenv" ], - "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). This may take a minute as py-solc-x downloads a Solidity compiler binary.", + "description": "Install web3 (Web3.py — Ethereum/EVM Python client), py-solc-x (Python wrapper for the Solidity compiler), and python-dotenv (loads .env variables). py-solc-x downloads a Solidity compiler binary on first install.", "order": 2, - "working_directory": "web3py-contracts" + "working_directory": "web3py-project" }, { "action": "Create the .env file with PRIVATE_KEY", - "description": "Create a file named '.env' in the web3py-contracts directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n\nReplace 0xYOUR_PRIVATE_KEY_HERE with the actual private key for the funded TestNet account. Also create a .gitignore file and add '.env' to it to prevent accidental commits:\n\necho '.env' > .gitignore", + "description": "Create a file named `.env` in the web3py-project directory. Do NOT ask the user to type or paste their private key in the chat — instruct them to open the .env file in a text editor and add:\n\n```\nPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE\n```\n\nReplace `0xYOUR_PRIVATE_KEY_HERE` with the actual private key for the funded TestNet account. Also create a `.gitignore` file with `.env` to prevent accidental commits:\n\n```bash\necho '.env' > .gitignore\n```", "order": 3, - "working_directory": "web3py-contracts" + "working_directory": "web3py-project" }, { "action": "Create the Storage.sol Solidity contract", - "description": "Create a file named 'Storage.sol' with the following content:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ncontract Storage {\n uint256 private storedNumber;\n\n function store(uint256 num) public {\n storedNumber = num;\n }\n\n function retrieve() public view returns (uint256) {\n return storedNumber;\n }\n}\n```\n\nThis is a simple Storage contract with a store(uint256) write function and a retrieve() read function.", + "description": "Create a file named `Storage.sol` with the following content:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\ncontract Storage {\n uint256 public storedNumber;\n\n function setNumber(uint256 _newNumber) public {\n storedNumber = _newNumber;\n }\n}\n```\n\nThis contract stores a number in `storedNumber` (publicly readable via auto-generated getter) and exposes `setNumber(uint256)` as the write function.", "order": 4, - "working_directory": "web3py-contracts" + "working_directory": "web3py-project" }, { "action": "Create compile.py to compile the Solidity contract", "commands": [ "python compile.py" ], - "description": "Create a file named 'compile.py' with the following content:\n\n```python\nfrom solcx import compile_standard, install_solc\nimport json\n\ninstall_solc('0.8.20')\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = compile_standard(\n {\n 'language': 'Solidity',\n 'sources': {'Storage.sol': {'content': source}},\n 'settings': {\n 'outputSelection': {\n '*': {'*': ['abi', 'evm.bytecode']}\n }\n },\n },\n solc_version='0.8.20',\n)\n\nwith open('compiled.json', 'w') as f:\n json.dump(compiled, f)\n\nprint('Compilation successful — compiled.json written.')\n```\n\nRun it to verify compilation works before proceeding.", - "expected_output": "Compilation successful — compiled.json written.", + "description": "Create a file named `compile.py` with the following content:\n\n```python\nimport json\nimport solcx\nfrom pathlib import Path\n\nSOLC_VERSION = '0.8.9'\ntry:\n solcx.install_solc(SOLC_VERSION)\nexcept Exception as e:\n print(f'Solc {SOLC_VERSION} already installed or error: {e}')\n\nsolcx.set_solc_version(SOLC_VERSION)\n\nwith open('Storage.sol', 'r') as f:\n source = f.read()\n\ncompiled = solcx.compile_source(source, output_values=['abi', 'bin'], solc_version=SOLC_VERSION)\ncontract_id, iface = compiled.popitem()\n\nPath('abis').mkdir(exist_ok=True)\nPath('artifacts').mkdir(exist_ok=True)\n\nwith open('abis/Storage.json', 'w') as f:\n json.dump(iface['abi'], f, indent=2)\n\nwith open('artifacts/Storage.bin', 'w') as f:\n f.write(iface['bin'])\n\nprint('Contract compiled. ABI: abis/Storage.json, Bytecode: artifacts/Storage.bin')\n```\n\nRun it to confirm compilation succeeds before proceeding.", + "expected_output": "Contract compiled. ABI: abis/Storage.json, Bytecode: artifacts/Storage.bin", "order": 5, - "working_directory": "web3py-contracts" + "working_directory": "web3py-project" }, { "action": "Create deploy.py to deploy the contract", "commands": [ "python deploy.py" ], - "description": "Create a file named 'deploy.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\naccount = w3.eth.account.from_key(PRIVATE_KEY)\nprint(f'Deploying from account: {account.address}')\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\n\ncontract_data = compiled['contracts']['Storage.sol']['Storage']\nabi = contract_data['abi']\nbytecode = contract_data['evm']['bytecode']['object']\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nThe RPC URL is set to https://services.polkadothub-rpc.com/testnet and chain ID to 420420417 — no substitutions needed.", - "expected_output": "Deploying from account: 0x...\nContract deployed at: 0x...\nAddress saved to contract_address.txt", + "description": "Create a file named `deploy.py` with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL, request_kwargs={'timeout': 120}))\nassert w3.is_connected(), 'Failed to connect to RPC endpoint'\n\nformatted_key = PRIVATE_KEY if PRIVATE_KEY.startswith('0x') else f'0x{PRIVATE_KEY}'\naccount = w3.eth.account.from_key(formatted_key)\nprint(f'Deploying from: {account.address}')\n\nwith open('abis/Storage.json') as f:\n abi = json.load(f)\nwith open('artifacts/Storage.bin') as f:\n bytecode = f.read().strip()\n if not bytecode.startswith('0x'):\n bytecode = f'0x{bytecode}'\n\nContract = w3.eth.contract(abi=abi, bytecode=bytecode)\nnonce = w3.eth.get_transaction_count(account.address)\ngas_price = w3.to_wei('1000', 'gwei')\n\ntx = Contract.constructor().build_transaction({\n 'chainId': CHAIN_ID, 'from': account.address,\n 'nonce': nonce, 'gasPrice': gas_price\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\n\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=300)\n\nprint(f'Contract deployed at: {receipt.contractAddress}')\nwith open('contract_address.txt', 'w') as f:\n f.write(receipt.contractAddress)\nprint('Address saved to contract_address.txt')\n```\n\nRPC URL and chain ID are hardcoded to Polkadot Hub TestNet — no substitutions needed.", + "expected_output": "Deploying from: 0x...\nContract deployed at: 0x...\nAddress saved to contract_address.txt", "order": 6, - "working_directory": "web3py-contracts" + "working_directory": "web3py-project" }, { - "action": "Create interact.py to call store and retrieve", + "action": "Create update_storage.py to call setNumber and read storedNumber", "commands": [ - "python interact.py" + "python update_storage.py" ], - "description": "Create a file named 'interact.py' with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\naccount = w3.eth.account.from_key(PRIVATE_KEY)\n\nwith open('contract_address.txt') as f:\n contract_address = f.read().strip()\n\nwith open('compiled.json') as f:\n compiled = json.load(f)\nabi = compiled['contracts']['Storage.sol']['Storage']['abi']\n\ncontract = w3.eth.contract(address=contract_address, abi=abi)\n\n# Read current value\nvalue = contract.functions.retrieve().call()\nprint(f'Current stored value: {value}')\n\n# Write new value\nnonce = w3.eth.get_transaction_count(account.address)\ntx = contract.functions.store(42).build_transaction({\n 'chainId': CHAIN_ID,\n 'from': account.address,\n 'nonce': nonce,\n 'gasPrice': w3.to_wei('1000', 'gwei'),\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\nprint(f'store(42) tx: {receipt.transactionHash.hex()}')\n\n# Verify\nvalue = contract.functions.retrieve().call()\nprint(f'Updated stored value: {value}')\n```", - "expected_output": "Current stored value: 0\nstore(42) tx: 0x...\nUpdated stored value: 42", + "description": "Create a file named `update_storage.py` with the following content:\n\n```python\nimport json\nimport os\nfrom web3 import Web3\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nRPC_URL = 'https://services.polkadothub-rpc.com/testnet'\nCHAIN_ID = 420420417\nPRIVATE_KEY = os.environ['PRIVATE_KEY']\n\nw3 = Web3(Web3.HTTPProvider(RPC_URL))\nformatted_key = PRIVATE_KEY if PRIVATE_KEY.startswith('0x') else f'0x{PRIVATE_KEY}'\naccount = w3.eth.account.from_key(formatted_key)\n\nwith open('contract_address.txt') as f:\n contract_address = f.read().strip()\n\nwith open('abis/Storage.json') as f:\n abi = json.load(f)\n\ncontract = w3.eth.contract(address=contract_address, abi=abi)\n\n# Read current value\nvalue = contract.functions.storedNumber().call()\nprint(f'Current storedNumber: {value}')\n\n# Write new value\nnonce = w3.eth.get_transaction_count(account.address)\ntx = contract.functions.setNumber(42).build_transaction({\n 'chainId': CHAIN_ID, 'from': account.address,\n 'nonce': nonce, 'gasPrice': w3.to_wei('1000', 'gwei')\n})\ntx['gas'] = w3.eth.estimate_gas(tx)\nsigned = account.sign_transaction(tx)\ntx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)\nreceipt = w3.eth.wait_for_transaction_receipt(tx_hash)\nprint(f'setNumber(42) tx: {receipt.transactionHash.hex()}')\n\n# Verify\nvalue = contract.functions.storedNumber().call()\nprint(f'Updated storedNumber: {value}')\n```", + "expected_output": "Current storedNumber: 0\nsetNumber(42) tx: 0x...\nUpdated storedNumber: 42", "order": 7, - "working_directory": "web3py-contracts" + "working_directory": "web3py-project" } ], "supplementary_context": { @@ -7966,53 +8098,57 @@ ] }, "title": "Deploy and Interact with Smart Contracts on Polkadot Hub Using Web3.py", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which wraps the native PAS token as an ERC-20 compatible interface at a fixed precompile address. Workflow: connect MetaMask to Polkadot Hub TestNet, load the precompile ABI in Remix IDE, and call read functions (balanceOf, allowance) and write functions (transfer, approve, transferFrom). Use when you need to interact with native PAS as an ERC-20 token from a Solidity contract or Remix. Key capabilities: MetaMask wallet connection, Remix At Address deployment against precompile, ERC-20 standard function calls on the native token. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find it. Trigger phrases: 'erc20 precompile polkadot', 'native token as erc20', 'pas erc20 interface', 'precompile erc20 remix'.", + "description": "Guides through using the ERC-20 precompile on Polkadot Hub, which provides a standard ERC-20 interface for assets managed by the Assets pallet (e.g., USDt at 0x000007C000000000000000000000000001200000, USDC at 0x0000053900000000000000000000000001200000). Supports three asset types: Trust-Backed Assets (suffix 01200000), Foreign Assets (suffix 02200000), and Pool Assets (suffix 03200000). NOTE: Metadata functions (name, symbol, decimals) are NOT available via this precompile. Workflow: connect MetaMask to Polkadot Hub TestNet, create IERC20-precompile.sol in Remix, load via At Address, and call read/write functions. Use when you need to interact with native Polkadot assets using standard ERC-20 calls. Trigger phrases: 'erc20 precompile polkadot', 'usdt usdc polkadot hub erc20', 'assets pallet erc20', 'precompile erc20 remix'.", "env_vars": [], "error_patterns": [ { - "cause": "The IERC20.sol file was not compiled successfully before clicking At Address.", + "cause": "The IERC20-precompile.sol file was not compiled successfully before clicking At Address.", "pattern": "Remix: 'At Address' button disabled or no output", - "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20, and click 'Compile IERC20.sol'. Then return to Deploy and Run and try At Address again." + "resolution": "Go to the Solidity Compiler tab, select compiler version 0.8.20 or higher, and click 'Compile IERC20-precompile.sol'. Then return to Deploy and Run and try At Address again." }, { "cause": "MetaMask is still on Ethereum Mainnet or another network.", "pattern": "MetaMask: Wrong network — expected Custom (420420417)", - "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the add-polkadot-hub-testnet-metamask skill if the network is not listed." + "resolution": "Open MetaMask, click the network selector, and switch to Polkadot Hub TestNet (chain ID 420420417). Use the connect-wallet-polkadot-hub skill if the network is not listed." }, { - "cause": "Attempting to transfer more PAS than the account holds.", + "cause": "Attempting to transfer more tokens than the account holds.", "pattern": "Transaction reverted: insufficient balance", - "resolution": "Call balanceOf first to confirm the available balance, then use an amount in wei that does not exceed it." + "resolution": "Call balanceOf first to confirm the available balance, then use an amount that does not exceed it." + }, + { + "cause": "Metadata functions are not implemented in the ERC-20 precompile.", + "pattern": "Remix: call reverts on name() / symbol() / decimals()", + "resolution": "Remove name(), symbol(), and decimals() from the IERC20 interface. Query decimals from the Assets pallet storage directly if needed." } ], "examples": [ { "actions": [ - "Load source page to find the ERC-20 precompile address", + "Note the USDt precompile address: 0x000007C000000000000000000000000001200000", "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create IERC20.sol in Remix with the standard ERC-20 interface and compile it", + "Create IERC20-precompile.sol in Remix with the 6 standard ERC-20 functions and compile it", "Set Environment to Injected Provider - MetaMask in Deploy and Run tab", - "Click At Address with the precompile address", - "Expand balanceOf, enter the MetaMask address, and click call" + "Click At Address with 0x000007C000000000000000000000000001200000", + "Expand balanceOf, enter an account address, and click call" ], - "result": "uint256 PAS balance in wei displayed in Remix console", - "scenario": "Common scenario: check PAS balance via ERC-20 precompile in Remix", - "user_says": "How do I call balanceOf on the ERC-20 precompile in Remix?" + "result": "uint256 USDt balance in smallest units (6 decimals) displayed in Remix console", + "scenario": "Common scenario: check USDt balance via ERC-20 precompile in Remix", + "user_says": "How do I call balanceOf on the USDt ERC-20 precompile in Remix?" }, { "actions": [ - "Load source page to get the ERC-20 precompile address", - "In the Solidity contract, import or define the IERC20 interface", - "Store the precompile address as a constant: IERC20 constant PAS_ERC20 = IERC20(PRECOMPILE_ADDRESS)", - "Call PAS_ERC20.balanceOf(msg.sender) or PAS_ERC20.transfer(to, amount) as needed", + "Import or define the IERC20 interface (6 functions, no metadata functions)", + "Store the precompile address as a constant: IERC20 constant USDT = IERC20(0x000007C000000000000000000000000001200000)", + "Call USDT.balanceOf(msg.sender) or USDT.transfer(to, amount) as needed", "Deploy the wrapper contract via Remix with MetaMask connected to Polkadot Hub TestNet" ], - "result": "Solidity contract calls the ERC-20 precompile to read balances or transfer native PAS tokens", + "result": "Solidity contract calls the ERC-20 precompile to read balances or transfer Assets pallet tokens", "scenario": "Edge case: user wants to call the precompile from a Solidity contract", "user_says": "How do I use the ERC-20 precompile from inside my Solidity contract?" } @@ -8045,55 +8181,54 @@ ], "steps": [ { - "action": "Find the ERC-20 precompile address from the source page", - "description": "Delegate to the user: Load the source page https://docs.polkadot.com/smart-contracts/precompiles/erc20.md to find the exact ERC-20 precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix. The address is a fixed precompile address that wraps the native PAS token.", - "interactive": true, + "action": "Identify the ERC-20 precompile address for your asset", + "description": "The ERC-20 precompile addresses are deterministic based on asset ID. Well-known Trust-Backed Assets on Polkadot Hub MainNet:\n- USDt (asset ID 1984): `0x000007C000000000000000000000000001200000`\n- USDC (asset ID 1337): `0x0000053900000000000000000000000001200000`\n\nAddress formula for Trust-Backed Assets: `0x` + assetId (8 hex digits, zero-padded) + 24 zero digits + `01200000`.\n\nFor Foreign Assets, use suffix `02200000` with the foreign asset index (not the XCM Location). For Pool Assets, use suffix `03200000`. Load https://docs.polkadot.com/smart-contracts/precompiles/erc20.md for the address converter and complete reference. Note the address you intend to use — it is required in step 4.", "order": 1, "working_directory": "." }, { "action": "Connect MetaMask to Polkadot Hub TestNet", - "description": "Delegate to the user: Open MetaMask and switch to the Polkadot Hub TestNet network (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet). If the network is not added yet, use the add-polkadot-hub-testnet-metamask skill to add it. Verify your PAS balance is non-zero before proceeding.", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417, RPC https://services.polkadothub-rpc.com/testnet). If the network is not added yet, use the connect-wallet-polkadot-hub skill to add it. Verify your PAS balance is non-zero before proceeding.", "interactive": true, "order": 2, "working_directory": "." }, { "action": "Open Remix IDE and create the ERC-20 interface file", - "description": "Delegate to the user: Open https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), create a new file named 'IERC20.sol'. Paste the standard ERC-20 interface into the file:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function balanceOf(address account) external view returns (uint256);\n function transfer(address to, uint256 amount) external returns (bool);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n```\n\nClick the Solidity Compiler tab (shield icon), set the compiler version to 0.8.20, and click 'Compile IERC20.sol'.", + "description": "Delegate to the user: Open https://remix.ethereum.org in the browser. In the File Explorer panel (left sidebar), create a new file named `IERC20-precompile.sol`. Paste the following interface into the file:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function transfer(address to, uint256 amount) external returns (bool);\n function balanceOf(address account) external view returns (uint256);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n```\n\nIMPORTANT: The optional ERC-20 metadata functions (`name()`, `symbol()`, `decimals()`) are NOT implemented in this precompile — do not add them to the interface or they will revert.\n\nClick the Solidity Compiler tab (shield icon), set the compiler version to 0.8.20 (or higher), and click 'Compile IERC20-precompile.sol'.", "interactive": true, "order": 3, "working_directory": "." }, { "action": "Connect Remix to MetaMask and load the precompile using At Address", - "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address obtained in step 1. Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below.", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab (Ethereum icon) in Remix. In the Environment dropdown, select 'Injected Provider - MetaMask'. MetaMask will pop up — confirm the connection to Remix. Verify the network shows 'Custom (420420417) network'. In the Contract dropdown, select 'IERC20'. In the 'At Address' input field, paste the ERC-20 precompile address from step 1 (e.g., `0x000007C000000000000000000000000001200000` for USDt). Click the 'At Address' button. A new IERC20 instance should appear in the Deployed Contracts section below.", "interactive": true, "order": 4, "working_directory": "." }, { - "action": "Call balanceOf to check PAS token balance", - "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste your MetaMask account address. Click the 'call' button (blue). The output shows your PAS balance as a uint256 value in wei (18 decimals). To convert: divide the result by 10^18 to get the PAS amount.", - "expected_output": "uint256 balance in wei — should match MetaMask PAS balance * 10^18", + "action": "Call balanceOf to check token balance", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IERC20 instance. Click 'balanceOf' to expand it. In the 'account' field, paste an account address. Click the 'call' button (blue). The output shows the token balance as a uint256 value. Divide by 10^decimals to get the human-readable amount (USDt and USDC use 6 decimals). To check total supply, call 'totalSupply' with no arguments.", + "expected_output": "uint256 balance for the account; 0 if the account holds none of this asset", "interactive": true, "order": 5, "working_directory": "." }, { "action": "Call approve and transfer functions", - "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in wei in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm. To transfer tokens, expand 'transfer', enter the recipient address and amount in wei, and click 'transact'. Confirm in MetaMask.", - "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console", + "description": "Delegate to the user: To approve a spender, expand the 'approve' function. Enter a spender address in the 'spender' field and an amount in the smallest unit (e.g., 1000000 for 1 USDt with 6 decimals) in the 'amount' field. Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. To transfer tokens, expand 'transfer', enter the recipient address and amount, and click 'transact'. Note: transfers will fail if the caller doesn't hold sufficient balance of the asset.", + "expected_output": "Transaction confirmed in MetaMask; tx hash visible in Remix console; bool true returned", "interactive": true, "order": 6, "working_directory": "." } ], "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to add MetaMask, or wants to call the precompile from a contract.", + "description": "Load these pages when the user needs the precompile address converter, wants to add MetaMask, or wants to call the precompile from a contract.", "pages": [ { - "relevance": "Full ERC-20 precompile source page with the exact precompile address, complete ABI, and additional usage examples.", + "relevance": "Full ERC-20 precompile source page with the address converter, complete ABI, and all three asset type formats.", "slug": "smart-contracts-precompiles-erc20", "title": "Interact with the ERC20 Precompile", "url": "https://docs.polkadot.com/smart-contracts/precompiles/erc20.md" @@ -8113,12 +8248,12 @@ ] }, "title": "Interact with the ERC-20 Precompile on Polkadot Hub via Remix", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile provides a persistent key-value store accessible from smart contracts, mapping bytes32 keys to bytes values. Workflow: connect MetaMask, find the precompile address from the source page, create the precompile interface in Remix, load it via At Address, and call setBytes/getBytes. Use when you need on-chain persistent storage accessible directly from precompile calls without deploying a storage contract. Key capabilities: bytes32 key encoding, bytes value storage, Storage precompile ABI definition in Remix, MetaMask transaction signing. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find it. Trigger phrases: 'storage precompile polkadot', 'setBytes getBytes precompile', 'polkadot hub key value storage', 'precompile storage remix'.", + "description": "Six-step workflow for using the Storage precompile on Polkadot Hub via Remix IDE. The Storage precompile (address 0x0000000000000000000000000000000000000901) provides low-level access to contract storage: read full/partial values (get, get_range, get_prefix), write/delete (set, remove), and inspect (has_key, length). Workflow: connect MetaMask, create IStorage.sol in Remix, load via At Address, and call storage functions. Use when you need fine-grained control over storage layout or want to read/write raw storage keys from Remix. Key capabilities: bytes32 key operations, partial reads with offset/length, key existence checks, storage management. Trigger phrases: 'storage precompile polkadot', 'IStorage precompile', 'polkadot hub key value storage', 'precompile storage remix'.", "env_vars": [], "error_patterns": [ { @@ -8127,36 +8262,40 @@ "resolution": "Compile IStorage.sol first via the Solidity Compiler tab, then return to Deploy and Run. Ensure 'IStorage' is selected in the Contract dropdown before clicking At Address." }, { - "cause": "The key used in getBytes does not match the key used in setBytes, or the setBytes transaction did not confirm.", - "pattern": "getBytes returns 0x (empty bytes)", - "resolution": "Ensure the bytes32 key in getBytes is identical to the one used in setBytes. Check Remix console for the setBytes transaction hash to confirm it was mined." + "cause": "The key used in get does not exist (has never been set), or the set transaction did not confirm.", + "pattern": "get reverts: key does not exist in storage", + "resolution": "Use has_key first to check existence. If it returns false, the key was not written. Check Remix console for the set transaction hash to confirm it was mined." + }, + { + "cause": "The sum of offset and length is greater than the actual stored value length.", + "pattern": "get_range reverts: offset + length exceeds value length", + "resolution": "Call length(key) first to check the stored value size, then ensure offset + length does not exceed it." }, { - "cause": "Gas estimate too low for setBytes with a large value.", + "cause": "Gas estimate too low for set with a large value.", "pattern": "MetaMask: Transaction failed with out of gas", - "resolution": "In MetaMask, increase the gas limit manually before confirming the setBytes transaction." + "resolution": "In MetaMask, increase the gas limit manually before confirming the set transaction. Large storage writes are more expensive." } ], "examples": [ { "actions": [ - "Load source page to get the Storage precompile address", "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create IStorage.sol in Remix with setBytes/getBytes interface and compile it", - "Set Environment to Injected Provider - MetaMask, select IStorage, click At Address with the precompile address", - "Call setBytes with a bytes32 key (hex-padded) and hex-encoded bytes value", - "Call getBytes with the same key to verify the stored value" + "Create IStorage.sol in Remix with the full IStorage interface (get, set, remove, has_key, etc.) and compile it", + "Set Environment to Injected Provider - MetaMask, select IStorage, click At Address with 0x0000000000000000000000000000000000000901", + "Call set with a bytes32 key and hex-encoded bytes value", + "Call get with the same key to verify the stored value", + "Call has_key to confirm the key exists" ], - "result": "Value stored on-chain via setBytes; getBytes returns the same hex-encoded bytes", + "result": "Value stored on-chain via set; get returns the same hex-encoded bytes; has_key returns true", "scenario": "Common scenario: store and retrieve bytes using the Storage precompile", "user_says": "How do I use the Storage precompile in Remix to store a value?" }, { "actions": [ - "Load source page to get the precompile address", - "In the user's contract, import or define the IStorage interface", - "Store the precompile address as a constant: IStorage constant STORE = IStorage(PRECOMPILE_ADDRESS)", - "Call STORE.setBytes(key, value) and STORE.getBytes(key) in contract functions", + "Import or define the IStorage interface in the user's contract", + "Store the precompile address as a constant: IStorage constant STORE = IStorage(0x0000000000000000000000000000000000000901)", + "Call STORE.set(key, value) and STORE.get(key) in contract functions", "Deploy the wrapper contract in Remix targeting Polkadot Hub TestNet" ], "result": "User's contract reads and writes key-value data through the Storage precompile", @@ -8175,7 +8314,7 @@ "Modern web browser (Chrome or Firefox recommended)" ], "tokens": [ - "PAS balance for setBytes write transactions — obtain from https://faucet.polkadot.io/" + "PAS balance for set/remove write transactions — obtain from https://faucet.polkadot.io/" ], "wallet": [ "MetaMask installed with Polkadot Hub TestNet configured (chain ID 420420417)" @@ -8192,55 +8331,55 @@ ], "steps": [ { - "action": "Find the Storage precompile address from the source page", - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/storage.md to find the exact Storage precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix.", + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance. The Storage precompile is at the fixed address `0x0000000000000000000000000000000000000901` — note this for step 4.", "interactive": true, "order": 1, "working_directory": "." }, { - "action": "Connect MetaMask to Polkadot Hub TestNet", - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance.", + "action": "Open Remix IDE", + "description": "Delegate to the user: Open https://remix.ethereum.org in the browser.", "interactive": true, "order": 2, "working_directory": "." }, { - "action": "Create the Storage precompile interface in Remix", - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'IStorage.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IStorage {\n function setBytes(bytes32 key, bytes memory value) external;\n function getBytes(bytes32 key) external view returns (bytes memory);\n}\n```\n\nGo to the Solidity Compiler tab, set the version to 0.8.20, and click 'Compile IStorage.sol'. Confirm there are no errors.", + "action": "Create the IStorage interface in Remix and compile it", + "description": "Delegate to the user: Create a new file named `IStorage.sol` in Remix and paste the following interface (from the Polkadot SDK IStorage.sol at https://github.com/paritytech/polkadot-sdk/blob/62fa27df30d985600963fd5bcec1080e4c63fd4b/substrate/frame/revive/uapi/sol/IStorage.sol):\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IStorage {\n // Read full value at key\n function get(bytes32 key) external view returns (bytes memory);\n // Read a range: offset bytes in, length bytes out\n function get_range(bytes32 key, uint32 offset, uint32 length) external view returns (bytes memory);\n // Read up to max_length bytes from the start\n function get_prefix(bytes32 key, uint32 max_length) external view returns (bytes memory);\n // Write value at key\n function set(bytes32 key, bytes memory value) external;\n // Delete value at key\n function remove(bytes32 key) external;\n // True if key exists (even if value is empty)\n function has_key(bytes32 key) external view returns (bool);\n // Length in bytes of the stored value\n function length(bytes32 key) external view returns (uint32);\n}\n```\n\nGo to the Solidity Compiler tab, set the version to 0.8.20 or higher, and click 'Compile IStorage.sol'. Confirm there are no errors.", "interactive": true, "order": 3, "working_directory": "." }, { "action": "Connect Remix to MetaMask and load the precompile using At Address", - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection popup. Verify the network displays 'Custom (420420417) network'. Select 'IStorage' in the Contract dropdown. Paste the Storage precompile address (from step 1) into the 'At Address' input. Click 'At Address'. An IStorage instance should appear in the Deployed Contracts section.", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection popup. Verify the network displays 'Custom (420420417) network'. Select 'IStorage' in the Contract dropdown. Enter the Storage precompile address `0x0000000000000000000000000000000000000901` into the 'At Address' input. Click 'At Address'. An IStorage instance should appear in the Deployed Contracts section.", "interactive": true, "order": 4, "working_directory": "." }, { - "action": "Call setBytes to store a value", - "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'setBytes' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: 0x6d79546573744b657900000000000000000000000000000000000000000000000 (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter the hex-encoded bytes value you want to store — for example: 0x68656c6c6f (which is 'hello' in UTF-8). Click 'transact' (orange button). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", + "action": "Call set to store a value", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IStorage instance. Expand the 'set' function. In the 'key' field, enter a bytes32 value — for example, a 32-byte hex string: `0x6d79546573744b6579000000000000000000000000000000000000000000000000` (this is 'myTestKey' zero-padded to 32 bytes). In the 'value' field, enter hex-encoded bytes to store — for example: `0x68656c6c6f` (which is 'hello' in UTF-8). Click 'transact' (orange). MetaMask will prompt for confirmation — approve it. Wait for the transaction to confirm.", "expected_output": "Transaction confirmed; tx hash shown in Remix console", "interactive": true, "order": 5, "working_directory": "." }, { - "action": "Call getBytes to retrieve the stored value", - "description": "Delegate to the user: In the IStorage instance, expand 'getBytes'. Enter the same bytes32 key used in setBytes. Click 'call' (blue button). The output displays the stored bytes value in hex. To decode: the hex 0x68656c6c6f decodes to 'hello' in UTF-8.", - "expected_output": "bytes output matching the hex value stored in setBytes", + "action": "Call get to retrieve the stored value", + "description": "Delegate to the user: In the IStorage instance, expand 'get'. Enter the same bytes32 key used in step 5. Click 'call' (blue). The output displays the stored bytes value in hex — `0x68656c6c6f` decodes to 'hello' in UTF-8. To check if the key exists, expand 'has_key' and call it with the same key — it should return true. To get the byte length without reading the data, call 'length' with the key.", + "expected_output": "bytes output matching the hex value stored in set; has_key returns true", "interactive": true, "order": 6, "working_directory": "." } ], "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to call the precompile from Solidity, or needs Remix connection help.", + "description": "Load these pages when the user needs the authoritative interface, wants to call the precompile from Solidity, or needs Remix connection help.", "pages": [ { - "relevance": "Full Storage precompile source page with the exact precompile address, full ABI, and key encoding examples.", + "relevance": "Full Storage precompile source page with the authoritative IStorage interface, all function signatures, and usage examples.", "slug": "smart-contracts-precompiles-storage", "title": "Interact with the Storage Precompile", "url": "https://docs.polkadot.com/smart-contracts/precompiles/storage.md" @@ -8260,50 +8399,49 @@ ] }, "title": "Interact with the Storage Precompile on Polkadot Hub via Remix", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Guides through using the System precompile on Polkadot Hub via Remix IDE. The System precompile exposes Substrate-native operations to EVM contracts: BLAKE2b/BLAKE2s hashing, sr25519 signature verification, account existence checks, and contract lifecycle operations (self-destruct, code retrieval). Workflow: connect MetaMask, find the precompile address from the source page, create the interface in Remix, load it via At Address, and call individual functions. NOTE: Several example inputs (sig, pubKey) are placeholder values — the step descriptions mark these as INSERT_SIGNATURE and INSERT_PUBLIC_KEY with instructions on how to obtain them. NOTE: The exact precompile address is documented in the source page — load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find it. Use when you need Substrate cryptographic primitives from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", + "description": "Guides through using the System precompile on Polkadot Hub (address 0x0000000000000000000000000000000000000900) via Remix IDE. The System precompile exposes Substrate-native operations: BLAKE2 hashing (hashBlake256, hashBlake128), sr25519 signature verification, ECDSA address conversion, account management (toAccountId), runtime queries (callerIsOrigin, callerIsRoot, minimumBalance, ownCodeHash, weightLeft), and contract lifecycle (terminate). Workflow: connect MetaMask, create ISystem.sol with the authoritative interface, load via At Address, and call functions. Use when you need Polkadot-native cryptographic primitives or runtime state queries from an EVM contract. Trigger phrases: 'system precompile polkadot', 'blake2 hash solidity', 'sr25519 verification evm', 'polkadot hub system precompile remix'.", "env_vars": [], "error_patterns": [ { - "cause": "The sig or pubKey bytes are not the correct length or format for sr25519.", - "pattern": "Remix: call reverts on verifySignature with invalid inputs", - "resolution": "sr25519 signatures are 64 bytes (128 hex chars with 0x prefix = 130 chars total). Public keys are 32 bytes (64 hex chars). Use the subkey tool to generate a valid signature: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'. Ensure all inputs are hex-encoded with 0x prefix." + "cause": "The interface definition uses old function names (blake2b, blake2s, verifySignature, accountExists) that do not exist in the actual precompile.", + "pattern": "ISystem.sol compilation error: function not found / wrong parameter types", + "resolution": "Replace with the correct interface: hashBlake256, hashBlake128, sr25519Verify (with uint8[64] calldata signature, bytes calldata message, bytes32 publicKey), toAccountId, etc. The authoritative source is at https://github.com/paritytech/polkadot-sdk/blob/62fa27df30d985600963fd5bcec1080e4c63fd4b/substrate/frame/revive/uapi/sol/ISystem.sol." }, { - "cause": "Incorrect precompile address entered, or MetaMask connected to the wrong network.", - "pattern": "At Address: no contract at address", - "resolution": "Re-load the source page to get the correct System precompile address. Verify MetaMask shows chain ID 420420417." + "cause": "The signature bytes (uint8[64]) or public key (bytes32) are not in the correct format.", + "pattern": "sr25519Verify returns false for known-valid signature", + "resolution": "sr25519 signatures are exactly 64 bytes (128 hex chars). Public keys are 32 bytes (64 hex chars). Ensure the uint8[64] array is passed as 64 separate decimal or hex values, not as a concatenated bytes string." }, { - "cause": "The interface in ISystem.sol may not match the exact ABI exposed by the precompile.", - "pattern": "ISystem.sol compilation error: function not found in interface", - "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/system.md for the authoritative ABI and update the interface accordingly." + "cause": "Incorrect precompile address entered, or MetaMask connected to the wrong network.", + "pattern": "At Address: no contract at address", + "resolution": "Re-verify the address is exactly 0x0000000000000000000000000000000000000900. Verify MetaMask shows chain ID 420420417." } ], "examples": [ { "actions": [ - "Load source page to get the System precompile address", "Connect MetaMask to Polkadot Hub TestNet (chain ID 420420417)", - "Create ISystem.sol in Remix with the BLAKE2 and other function signatures, then compile", - "Set Environment to Injected Provider - MetaMask, select ISystem, click At Address with the precompile address", - "Expand blake2b, enter hex-encoded input data (e.g. 0x68656c6c6f for 'hello'), click call", - "Record the BLAKE2b hash output" + "Create ISystem.sol in Remix with the correct interface (hashBlake256, hashBlake128, sr25519Verify, etc.) and compile", + "Set Environment to Injected Provider - MetaMask, select ISystem, click At Address with 0x0000000000000000000000000000000000000900", + "Expand hashBlake256, enter hex-encoded input data (e.g. 0x68656c6c6f for 'hello'), click call", + "Record the BLAKE2-256 hash output as bytes32" ], - "result": "BLAKE2b hash of the input bytes returned as hex in the Remix console", - "scenario": "Common scenario: compute a BLAKE2b hash using the System precompile in Remix", - "user_says": "How do I use the System precompile to hash data with BLAKE2b in Remix?" + "result": "BLAKE2-256 hash of the input bytes returned as bytes32 in the Remix console", + "scenario": "Common scenario: compute a BLAKE2-256 hash using the System precompile in Remix", + "user_says": "How do I use the System precompile to hash data with BLAKE2 in Remix?" }, { "actions": [ - "Generate a test signature using subkey: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'", - "Note the 64-byte signature hex and the 32-byte Alice public key hex", - "In Remix, expand verifySignature on the ISystem instance", - "Enter INSERT_SIGNATURE (replace with the 64-byte sig), INSERT_PUBLIC_KEY (replace with Alice pubkey), and message (0x74657374 for 'test')", + "Generate a test signature: subkey sign --scheme sr25519 --suri '//Alice' --message 'test'", + "Note the 64-byte signature and get Alice's public key: subkey inspect --scheme sr25519 '//Alice'", + "In Remix, expand sr25519Verify on the ISystem instance", + "Enter the signature as uint8[64], the message as bytes, and publicKey as bytes32", "Click call and confirm the result is true" ], "result": "bool true returned — sr25519 signature verified on-chain via the System precompile", @@ -8339,63 +8477,63 @@ ], "steps": [ { - "action": "Find the System precompile address from the source page", - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/system.md to find the exact System precompile address for Polkadot Hub TestNet. Note this address — it will be used in step 4 as the 'At Address' target in Remix.", + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance. The System precompile is at the fixed address `0x0000000000000000000000000000000000000900` — note this for step 4.", "interactive": true, "order": 1, "working_directory": "." }, { - "action": "Connect MetaMask to Polkadot Hub TestNet", - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the add-polkadot-hub-testnet-metamask skill. Confirm a non-zero PAS balance.", + "action": "Open Remix IDE", + "description": "Delegate to the user: Open https://remix.ethereum.org in the browser.", "interactive": true, "order": 2, "working_directory": "." }, { - "action": "Create the System precompile interface in Remix", - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new file named 'ISystem.sol' and paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ISystem {\n // BLAKE2 hashing\n function blake2b(bytes memory data) external view returns (bytes memory);\n function blake2s(bytes memory data) external view returns (bytes memory);\n // sr25519 signature verification\n function verifySignature(\n bytes memory sig,\n bytes memory pubKey,\n bytes memory message\n ) external view returns (bool);\n // Account management\n function accountExists(address account) external view returns (bool);\n // Contract lifecycle\n function killContract(address target) external;\n function getCode(address target) external view returns (bytes memory);\n}\n```\n\nNote: Refer to the source page (https://docs.polkadot.com/smart-contracts/precompiles/system.md) for the authoritative function signatures — the interface above captures the key functions but the source page is the definitive reference. Go to the Solidity Compiler tab, set version to 0.8.20, and click 'Compile ISystem.sol'.", + "action": "Create the ISystem interface in Remix and compile it", + "description": "Delegate to the user: Create a new file named `ISystem.sol` in Remix. The authoritative source is at https://github.com/paritytech/polkadot-sdk/blob/62fa27df30d985600963fd5bcec1080e4c63fd4b/substrate/frame/revive/uapi/sol/ISystem.sol. Paste the following interface:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISystem {\n // BLAKE2 hashing\n function hashBlake256(bytes memory input) external pure returns (bytes32 digest);\n function hashBlake128(bytes memory input) external pure returns (bytes32 digest);\n // Signature verification\n function sr25519Verify(uint8[64] calldata signature, bytes calldata message, bytes32 publicKey) external view returns (bool);\n // ECDSA address conversion\n function ecdsaToEthAddress(uint8[33] calldata publicKey) external view returns (bytes20);\n // Account management\n function toAccountId(address input) external view returns (bytes memory account_id);\n // Runtime queries\n function callerIsOrigin() external view returns (bool);\n function callerIsRoot() external view returns (bool);\n function minimumBalance() external view returns (uint);\n function ownCodeHash() external view returns (bytes32);\n function weightLeft() external view returns (uint64 refTime, uint64 proofSize);\n // Contract lifecycle\n function terminate(address beneficiary) external;\n}\n```\n\nGo to the Solidity Compiler tab, set version to 0.8.20 or higher, and click 'Compile ISystem.sol'.", "interactive": true, "order": 3, "working_directory": "." }, { "action": "Connect Remix to MetaMask and load the precompile using At Address", - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection. Verify the network shows 'Custom (420420417) network'. Select 'ISystem' in the Contract dropdown. Paste the System precompile address (from step 1) into 'At Address'. Click 'At Address'. An ISystem instance should appear in the Deployed Contracts section.", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask' and confirm the MetaMask connection. Verify the network shows 'Custom (420420417) network'. Select 'ISystem' in the Contract dropdown. Enter the System precompile address `0x0000000000000000000000000000000000000900` into 'At Address'. Click 'At Address'. An ISystem instance should appear in the Deployed Contracts section.", "interactive": true, "order": 4, "working_directory": "." }, { - "action": "Call blake2b to hash data", - "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'blake2b' function. In the 'data' field, enter hex-encoded bytes to hash — for example: 0x68656c6c6f (UTF-8 for 'hello'). Click 'call' (blue). The output is the BLAKE2b hash of the input as hex-encoded bytes. You can also call 'blake2s' with the same input to get the BLAKE2s variant.", - "expected_output": "bytes: hex-encoded BLAKE2b hash of the input data", + "action": "Call hashBlake256 to hash data using BLAKE2-256", + "description": "Delegate to the user: In the Deployed Contracts section, expand the ISystem instance. Expand the 'hashBlake256' function. In the 'input' field, enter hex-encoded bytes to hash — for example: `0x68656c6c6f` (UTF-8 for 'hello'). Click 'call' (blue button). The output is the BLAKE2-256 hash as a bytes32 value. You can also call 'hashBlake128' with the same input to get the BLAKE2-128 variant (returned as bytes32 with trailing zeros).", + "expected_output": "bytes32: the BLAKE2-256 hash of the input data", "interactive": true, "order": 5, "working_directory": "." }, { - "action": "Call verifySignature for sr25519 verification", - "description": "Delegate to the user: Expand the 'verifySignature' function. To use this function you need three values:\n\n- INSERT_SIGNATURE: Replace with a real sr25519 signature as hex bytes (64 bytes, 128 hex chars). Generate a signature using a Polkadot.js signer or subkey tool: subkey sign --scheme sr25519 --suri '//Alice' --message 'your_message'\n\n- INSERT_PUBLIC_KEY: Replace with the sr25519 public key (32 bytes, 64 hex chars) corresponding to the signer.\n\n- message: The message bytes that were signed (hex-encoded).\n\nEnter the three values in the 'sig', 'pubKey', and 'message' fields respectively, then click 'call'. The function returns true if the signature is valid, false otherwise.", - "expected_output": "bool: true if the sr25519 signature is valid for the given pubKey and message", + "action": "Call sr25519Verify for Polkadot signature verification", + "description": "Delegate to the user: Expand the 'sr25519Verify' function. You need three values:\n\n- **signature** (uint8[64]): A real sr25519 signature as a 64-element byte array. Generate using `subkey sign --scheme sr25519 --suri '//Alice' --message 'your_message'`, then split the 64-byte hex output into 64 decimal values.\n- **message** (bytes calldata): The message bytes that were signed (hex-encoded).\n- **publicKey** (bytes32): The sr25519 public key of the signer (32 bytes, from `subkey inspect --scheme sr25519 '//Alice'`).\n\nEnter all three values and click 'call'. The function returns `true` if the signature is valid.", + "expected_output": "bool: true if the sr25519 signature is valid for the given publicKey and message", "interactive": true, "order": 6, "working_directory": "." }, { - "action": "Call accountExists to check account presence", - "description": "Delegate to the user: Expand the 'accountExists' function. Enter an Ethereum-format address (0x...) in the 'account' field. Click 'call'. The function returns true if the account exists on-chain (i.e. has been funded or deployed), false if it has never received any funds or activity.", - "expected_output": "bool: true if the account exists on Polkadot Hub TestNet, false otherwise", + "action": "Call toAccountId to convert an Ethereum address to a Substrate account ID", + "description": "Delegate to the user: Expand the 'toAccountId' function. Enter an Ethereum-format address (0x..., 20 bytes) in the 'input' field. Click 'call'. The function returns the native Substrate account ID bytes corresponding to that address. This is useful when contracts need to interact with runtime functionality that expects Substrate account IDs rather than H160 addresses.", + "expected_output": "bytes: the Substrate account ID corresponding to the input Ethereum address", "interactive": true, "order": 7, "working_directory": "." } ], "supplementary_context": { - "description": "Load these pages when the user needs the precompile address, wants to use sr25519 signing tools, or needs Remix setup help.", + "description": "Load these pages when the user needs the authoritative ISystem interface, wants to use sr25519 signing tools, or needs Remix setup help.", "pages": [ { - "relevance": "Full System precompile source page with the exact precompile address, authoritative ABI, and all function signatures.", + "relevance": "Full System precompile source page with the exact precompile address, authoritative ISystem interface, and all function signatures.", "slug": "smart-contracts-precompiles-system", "title": "Interact with the System Precompile", "url": "https://docs.polkadot.com/smart-contracts/precompiles/system.md" @@ -8415,31 +8553,31 @@ ] }, "title": "Interact with the System Precompile on Polkadot Hub via Remix", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { "chain_role": "isolated", - "description": "Guides you through creating an IXcm Solidity interface in Remix, loading the XCM precompile, and calling weighMessage, execute, and send functions. Use when you need to test XCM message dispatch from a smart contract, estimate XCM execution weight, or send cross-chain messages from Polkadot Hub via Solidity. All interaction is browser-based using Remix IDE with MetaMask. Trigger phrases: 'XCM precompile', 'send XCM from contract', 'weighMessage', 'xcm execute', 'cross-chain from Solidity', 'IXcm interface'. Output: XCM weight estimates and on-chain transaction receipts.", + "description": "Guides you through creating an IXcm Solidity interface in Remix, loading the XCM precompile at 0x00000000000000000000000000000000000a0000, and calling weighMessage, execute, and send functions. Use when you need to test XCM message dispatch from a smart contract, estimate XCM execution weight, or send cross-chain messages from Polkadot Hub via Solidity. All interaction is browser-based using Remix IDE with MetaMask. The source page includes an example SCALE-encoded XCM message for testing weighMessage. Trigger phrases: 'XCM precompile', 'send XCM from contract', 'weighMessage', 'xcm execute', 'cross-chain from Solidity', 'IXcm interface'. Output: XCM weight estimates and on-chain transaction receipts.", "env_vars": [], "error_patterns": [ { - "cause": "SCALE-encoded XCM bytes are malformed or the weight limit is too low.", + "cause": "SCALE-encoded XCM bytes are malformed, the weight limit is too low, or insufficient PAS balance.", "pattern": "Remix: transaction reverted on execute or send", - "resolution": "Verify the encoding against the example on the source page. Use weighMessage first to obtain the correct maxWeight. Ensure the correct precompile address is used for TestNet." + "resolution": "Use weighMessage first to get the correct weight. Verify the SCALE encoding against the example on the source page. Ensure the account has sufficient PAS (base fee is 1000 gwei on Polkadot Hub TestNet)." }, { "cause": "Incorrect precompile address entered, or MetaMask is connected to the wrong network.", "pattern": "At Address: no contract at address", - "resolution": "Reload the source page to get the authoritative XCM precompile address. Verify MetaMask shows chain ID 420420417." + "resolution": "The XCM precompile address is exactly 0x00000000000000000000000000000000000a0000. Verify MetaMask shows chain ID 420420417." }, { - "cause": "The interface definition does not match the ABI the precompile exposes.", - "pattern": "IXcm.sol compilation error: function not found", - "resolution": "Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md for the authoritative ABI and update IXcm.sol accordingly." + "cause": "The Weight struct is missing from the IXcm interface definition.", + "pattern": "IXcm.sol compilation error: Weight struct not found", + "resolution": "Load the authoritative interface from https://github.com/paritytech/polkadot-sdk/blob/cb629d46ebf00aa65624013a61f9c69ebf02b0b4/polkadot/xcm/pallet-xcm/src/precompiles/IXcm.sol and include the Weight struct definition." }, { - "cause": "PAS balance too low to cover gas on Polkadot Hub TestNet (base fee: 1000 gwei).", + "cause": "PAS balance too low to cover gas (base fee: 1000 gwei on Polkadot Hub TestNet).", "pattern": "MetaMask: insufficient funds for gas", "resolution": "Obtain PAS tokens from https://faucet.polkadot.io/. Ensure the account has at least 0.01 PAS before submitting state-changing calls." } @@ -8447,11 +8585,10 @@ "examples": [ { "actions": [ - "Load XCM precompile address and IXcm ABI from source page", - "Create and compile IXcm.sol in Remix", - "Load precompile via At Address", - "Call weighMessage with encoded destination and message bytes", - "Read the returned weight estimate" + "Create and compile IXcm.sol in Remix with the IXcm interface and Weight struct from the Polkadot SDK", + "Load precompile via At Address: 0x00000000000000000000000000000000000a0000", + "Call weighMessage with the example SCALE-encoded message from the source page", + "Read the returned refTime and proofSize weight estimate" ], "result": "Weight estimate (refTime and proofSize) indicating the execution cost on the destination chain", "scenario": "Common scenario: estimate XCM execution fee before sending", @@ -8459,10 +8596,10 @@ }, { "actions": [ - "Compare SCALE-encoded message bytes against the source page example", - "Call weighMessage first to confirm the message decodes correctly", - "Check that maxWeight is at least the refTime returned by weighMessage", - "If revert persists, reload IXcm.sol ABI from source page to confirm interface matches precompile" + "Compare SCALE-encoded message bytes against the example in the source page", + "Call weighMessage first to confirm the message decodes correctly — if weighMessage also fails, the encoding is malformed", + "Check that maxWeight refTime is at least the value returned by weighMessage", + "If revert persists, reload IXcm.sol from the authoritative GitHub source to confirm Weight struct and function signatures match" ], "result": "Root cause identified (malformed encoding or wrong interface); corrected message executes successfully", "scenario": "Edge case: execute call reverts due to malformed SCALE encoding", @@ -8497,36 +8634,36 @@ ], "steps": [ { - "action": "Find the XCM precompile address and IXcm ABI from the source page", - "description": "Delegate to the user: Load https://docs.polkadot.com/smart-contracts/precompiles/xcm.md to find the exact XCM precompile address for Polkadot Hub TestNet and the IXcm interface definition. Note the address — it will be used in step 4 as the 'At Address' target in Remix. The source page is the authoritative reference for the ABI.", + "action": "Connect MetaMask to Polkadot Hub TestNet", + "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance for gas fees. The XCM precompile is at the fixed address `0x00000000000000000000000000000000000a0000`.", "interactive": true, "order": 1, "working_directory": "." }, { - "action": "Connect MetaMask to Polkadot Hub TestNet", - "description": "Delegate to the user: Open MetaMask and switch to Polkadot Hub TestNet (chain ID 420420417). If the network is not added, use the connect-wallet-polkadot-hub skill. Confirm a non-zero PAS balance for gas fees.", + "action": "Open Remix IDE", + "description": "Delegate to the user: Open https://remix.ethereum.org in the browser.", "interactive": true, "order": 2, "working_directory": "." }, { "action": "Create the IXcm interface in Remix and compile it", - "description": "Delegate to the user: Open https://remix.ethereum.org. Create a new Solidity file named 'IXcm.sol'. Copy the IXcm interface definition from the source page (step 1) — the interface includes at minimum: weighMessage(bytes dest, bytes message), execute(bytes message, uint64 maxWeight), and send(bytes dest, bytes message). The source page is authoritative; use its exact ABI. Go to the Solidity Compiler tab, select version 0.8.20, and click 'Compile IXcm.sol'. Confirm there are no compilation errors.", + "description": "Delegate to the user: Create a new Solidity file named `IXcm.sol`. The authoritative interface is at https://github.com/paritytech/polkadot-sdk/blob/cb629d46ebf00aa65624013a61f9c69ebf02b0b4/polkadot/xcm/pallet-xcm/src/precompiles/IXcm.sol — copy the interface from there. The `IXcm` interface defines a `Weight` struct with `refTime` (uint64) and `proofSize` (uint64), and three functions:\n\n- `weighMessage(bytes calldata message) external view returns (Weight memory)` — estimates cost\n- `execute(bytes calldata message, Weight calldata maxWeight) external` — runs XCM locally\n- `send(bytes calldata dest, bytes calldata message) external` — dispatches XCM cross-chain\n\nAll XCM messages must be SCALE-encoded. Go to the Solidity Compiler tab, select version 0.8.20 or higher, and click 'Compile IXcm.sol'. Confirm there are no compilation errors.", "interactive": true, "order": 3, "working_directory": "." }, { "action": "Load the XCM precompile using At Address in Remix", - "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Paste the XCM precompile address (from step 1) into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section.", + "description": "Delegate to the user: Click the Deploy and Run Transactions tab in Remix. Set Environment to 'Injected Provider - MetaMask'. Confirm the MetaMask connection — verify the network shows 'Custom (420420417) network'. Select 'IXcm' in the Contract dropdown. Enter `0x00000000000000000000000000000000000a0000` into the 'At Address' field. Click 'At Address'. An IXcm instance should appear in the Deployed Contracts section.", "interactive": true, "order": 4, "working_directory": "." }, { "action": "Call weighMessage to estimate XCM execution cost", - "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For 'dest': provide the SCALE-encoded multilocation bytes of the destination chain (see source page for encoding examples). For 'message': provide the SCALE-encoded XCM message bytes (the source page includes a complete encoded example). Click 'call' (blue button — read-only). The output is the estimated weight (refTime and proofSize) needed to execute the message.", + "description": "Delegate to the user: In the Deployed Contracts section, expand the IXcm instance. Expand the 'weighMessage' function. For the `message` parameter, you can use this example SCALE-encoded XCM message from the source page (withdraws assets, buys execution, and deposits to a beneficiary):\n\n```\n0x050c000401000003008c86471301000003008c8647000d010101000000010100368e8759910dab756d344995f1d3c79374ca8f70066d3a709e48029f6bf0ee7e\n```\n\nClick 'call' (blue button — read-only). The output is the estimated weight struct containing `refTime` and `proofSize` values.", "expected_output": "Weight object with refTime and proofSize fields representing the estimated execution cost", "interactive": true, "order": 5, @@ -8534,7 +8671,7 @@ }, { "action": "Call execute to run an XCM message locally", - "description": "Delegate to the user: Expand the 'execute' function. For 'message': provide SCALE-encoded XCM bytes (use the example from the source page). For 'maxWeight': enter the refTime value returned by weighMessage in step 5, or a conservative upper bound. Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", + "description": "Delegate to the user: Expand the 'execute' function. For `message`: provide SCALE-encoded XCM bytes (use the example from step 5). For `maxWeight`: enter the `Weight` struct using the `refTime` and `proofSize` values returned by weighMessage in step 5 (or a conservative upper bound). Click 'transact' (orange button — state-changing). MetaMask will prompt for gas approval — confirm. The call executes the XCM message in the context of the calling address on the local chain.", "expected_output": "Transaction receipt with status 1 (success); XCM execution event visible in Remix logs", "interactive": true, "order": 6, @@ -8542,7 +8679,7 @@ }, { "action": "Call send to dispatch an XCM to another chain", - "description": "Delegate to the user: Expand the 'send' function. For 'dest': provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding). For 'message': provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Delivery and execution on the remote chain are asynchronous — use the xcm-tools skill to monitor delivery if needed.", + "description": "Delegate to the user: Expand the 'send' function. For `dest`: provide SCALE-encoded multilocation bytes for the target parachain (refer to source page for encoding examples). For `message`: provide SCALE-encoded XCM message bytes. Click 'transact' (orange). MetaMask prompts for gas — confirm. The XCM is dispatched toward the destination chain. Unlike execute, send does not require a weight parameter — the destination chain handles execution costs according to its fee structure. Delivery and execution on the remote chain are asynchronous.", "expected_output": "Transaction receipt with status 1; XcmMessageSent event in Remix logs", "interactive": true, "order": 7, @@ -8553,7 +8690,7 @@ "description": "Load these pages for XCM encoding details, precompile addresses, and cross-chain messaging concepts.", "pages": [ { - "relevance": "Authoritative XCM precompile address, IXcm interface definition, and SCALE-encoded message examples.", + "relevance": "Authoritative XCM precompile address, IXcm interface definition, SCALE-encoded message examples, and the execute/weighMessage/send workflow.", "slug": "smart-contracts-precompiles-xcm", "title": "Interact with the XCM Precompile", "url": "https://docs.polkadot.com/smart-contracts/precompiles/xcm.md" @@ -8573,7 +8710,7 @@ ] }, "title": "Interact with the XCM Precompile via Remix IDE", - "version": "1.0.0", + "version": "1.1.0", "workflow_pattern": "sequential" }, { diff --git a/skill_coverage.json b/skill_coverage.json index 0c5d79e56..99dc6717e 100644 --- a/skill_coverage.json +++ b/skill_coverage.json @@ -1,5 +1,5 @@ { - "generated": "2026-05-19T17:00:00Z", + "generated": "2026-05-19T18:00:00Z", "pages": { "chain-interactions/accounts/create-account.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -219,11 +219,11 @@ }, "node-infrastructure/run-a-validator/requirements.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-15T15:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "onboard-polkadot-validator" ], - "status": "stale" + "status": "up_to_date" }, "node-infrastructure/run-a-validator/staking-mechanics/offenses-and-slashes.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -747,11 +747,11 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-basic/basic-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "deploy-basic-contract-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-erc20/erc20-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -771,27 +771,27 @@ }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-hardhat.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "deploy-erc721-nft-hardhat" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/cookbook/smart-contracts/deploy-nft/nft-remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "deploy-erc721-nft-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/foundry.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "set-up-foundry-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/hardhat-polkadot.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -811,19 +811,19 @@ }, "smart-contracts/dev-environments/local-dev-node.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "set-up-local-dev-node" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/dev-environments/remix.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T18:00:00Z", "skills": [ "connect-remix-polkadot" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/explorers.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -887,11 +887,11 @@ }, "smart-contracts/integrations/wallets.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": "2026-05-19T17:00:00Z", + "last_scanned": "2026-05-19T20:00:00Z", "skills": [ "connect-wallet-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/libraries/ethers-js.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -927,11 +927,11 @@ }, "smart-contracts/libraries/web3-py.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T20:00:00Z", "skills": [ "deploy-interact-contracts-web3py" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/overview.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -941,11 +941,11 @@ }, "smart-contracts/precompiles/erc20.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T20:00:00Z", "skills": [ "interact-erc20-precompile-polkadot-hub" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/precompiles/eth-native.md": { "last_edited": "2026-05-19T08:40:20+00:00", @@ -955,37 +955,37 @@ }, "smart-contracts/precompiles/storage.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T20:00:00Z", "skills": [ "interact-storage-precompile-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/precompiles/system.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T20:00:00Z", "skills": [ "interact-system-precompile-remix" ], - "status": "stale" + "status": "up_to_date" }, "smart-contracts/precompiles/xcm.md": { "last_edited": "2026-05-19T08:40:20+00:00", - "last_scanned": null, + "last_scanned": "2026-05-19T20:00:00Z", "skills": [ "interact-xcm-precompile-remix" ], - "status": "stale" + "status": "up_to_date" } }, "schema_version": "1", "summary": { "blocked": 23, "not_applicable": 12, - "stale": 13, + "stale": 0, "supplementary": 45, "total_candidates": 142, "uncovered": 0, - "up_to_date": 49 + "up_to_date": 62 } }