diff --git a/package-lock.json b/package-lock.json index df6057d..8bcd5bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.2", "license": "GPL-3.0", "dependencies": { - "@moonbeam-network/api-augment": "^0.3200.3", + "@moonbeam-network/api-augment": "^0.3400.0", "@polkadot/api": "^12.4.2", "@polkadot/apps-config": "^0.138.1", "@polkadot/util": "^13.1.1", @@ -2806,22 +2806,22 @@ } }, "node_modules/@moonbeam-network/api-augment": { - "version": "0.3200.3", - "resolved": "https://registry.npmjs.org/@moonbeam-network/api-augment/-/api-augment-0.3200.3.tgz", - "integrity": "sha512-YXOPW30HTuLf2BUbfgDzlj6rEqXH789FPp4FxiB0EAEHBAK2ijfQpAe3qtdTWXXxdnt9rVdiwlUf2wJQWC4O/Q==", + "version": "0.3400.0", + "resolved": "https://registry.npmjs.org/@moonbeam-network/api-augment/-/api-augment-0.3400.0.tgz", + "integrity": "sha512-Sj0r5pJIjV8IyVljNert+4qCxkRpurmRHR2WMGZfxvKf0RLorxCIvwaKVZgiv8wome3l81yMof7BIOX4oBDlYw==", "license": "GPL-3.0-only", "dependencies": { - "@polkadot/api": "14.0.1", - "@polkadot/api-base": "14.0.1", - "@polkadot/rpc-core": "14.0.1", - "@polkadot/typegen": "14.0.1", - "@polkadot/types": "14.0.1", - "@polkadot/types-codec": "14.0.1", - "@types/node": "^22.5.5", + "@polkadot/api": "14.3.1", + "@polkadot/api-base": "14.3.1", + "@polkadot/rpc-core": "14.3.1", + "@polkadot/typegen": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@types/node": "^22.9.1", "prettier": "2.8.8", "prettier-plugin-jsdoc": "^0.3.38", - "tsx": "^4.19.1", - "typescript": "^5.6.2" + "tsx": "^4.19.2", + "typescript": "^5.6.3" }, "engines": { "node": ">=20.0.0" @@ -3564,25 +3564,25 @@ } }, "node_modules/@polkadot/typegen": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/typegen/-/typegen-14.0.1.tgz", - "integrity": "sha512-BYwpo7a9gHYw/PoR+XzTE0gZU0ionGVwEu7HXoejNT6cxsmT709S8OMaKcPzA8IvKwcKeBKie9QvXvFXVoQyKQ==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/api": "14.0.1", - "@polkadot/api-augment": "14.0.1", - "@polkadot/rpc-augment": "14.0.1", - "@polkadot/rpc-provider": "14.0.1", - "@polkadot/types": "14.0.1", - "@polkadot/types-augment": "14.0.1", - "@polkadot/types-codec": "14.0.1", - "@polkadot/types-create": "14.0.1", - "@polkadot/types-support": "14.0.1", - "@polkadot/util": "^13.1.1", - "@polkadot/util-crypto": "^13.1.1", - "@polkadot/x-ws": "^13.1.1", + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/typegen/-/typegen-14.3.1.tgz", + "integrity": "sha512-S6G7BwmsC57zvUKoqYMF2+3SUIB2f8r6VDgUhdO2hdgexXTDeVHFtJx3FE2opPlkVR05EKgV+rGH/9Rn91HjOQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "14.3.1", + "@polkadot/api-augment": "14.3.1", + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/rpc-provider": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/types-support": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "@polkadot/x-ws": "^13.2.3", "handlebars": "^4.7.8", - "tslib": "^2.7.0", + "tslib": "^2.8.0", "yargs": "^17.7.2" }, "bin": { @@ -3596,13 +3596,137 @@ "node": ">=18" } }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/networks": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.2.3.tgz", + "integrity": "sha512-mG+zkXg/33AyPrkv2xBbAo3LBUwOwBn6qznBU/4jxiZPnVvCwMaxE7xHM22B5riItbNJ169FXv3wy0v6ZmkFbw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.2.3", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/util": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.2.3.tgz", + "integrity": "sha512-pioNnsig3qHXrfOKMe4Yqos8B8N3/EZUpS+WfTpWnn1VjYban/0GrTXeavPlAwggnY27b8fS6rBzQBhnVYDw8g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "13.2.3", + "@polkadot/x-global": "13.2.3", + "@polkadot/x-textdecoder": "13.2.3", + "@polkadot/x-textencoder": "13.2.3", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/util-crypto": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.2.3.tgz", + "integrity": "sha512-5sbggmLbn5eiuVMyPROPlT5roHRqdKHOfSpioNbGvGIZ1qIWVoC1RfsK0NWJOVGDzy6DpQe0KYT/kgcU5Xsrzw==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.2.3", + "@polkadot/util": "13.2.3", + "@polkadot/wasm-crypto": "^7.4.1", + "@polkadot/wasm-util": "^7.4.1", + "@polkadot/x-bigint": "13.2.3", + "@polkadot/x-randomvalues": "13.2.3", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.3" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/x-bigint": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.2.3.tgz", + "integrity": "sha512-VKgEAh0LsxTd/Hg517Tt5ZU4CySjBwMpaojbkjgv3fOdg1cN7t4eFEUxpyj7mlO0cp22SzDh7nmy4TO98qhLQA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/x-global": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.2.3.tgz", + "integrity": "sha512-7MYQIAEwBkRcNrgqac5PbB0kNPlI6ISJEy6/Nb+crj8BFjQ8rf11PF49fq0QsvDeuYM1aNLigrvYZNptQs4lbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/x-randomvalues": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.2.3.tgz", + "integrity": "sha512-Zf0GTfLmVk+VzPUmcQSpXjjmFzMTjPhXoLuIoE7xIu73T+vQ+TX9j7DvorN6bIRsnZ9l1SyTZsSf/NTjNZKIZg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.3", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/x-textdecoder": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.2.3.tgz", + "integrity": "sha512-i8hRXPtGknmdm3FYv6/94I52VXHJZa5sgYNw1+Hqb4Jqmq4awUjea35CKXd/+aw70Qn8Ngg31l2GoiH494fa+Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/typegen/node_modules/@polkadot/x-textencoder": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.2.3.tgz", + "integrity": "sha512-wJI3Bb/dC4zyBXJFm5+ZhyBXWoI5wvP8k8qX0/ZC0PQsgSAqs7LVhiofk4Wd94n0P41W5re58LrGXLyziSAshw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polkadot/typegen/node_modules/@polkadot/x-ws": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.2.2.tgz", - "integrity": "sha512-WEygcHPB55cKLiNoejJ0Lq3Z1fb4hUO3FmYTXdpHgk0xIOfYDrr7rTlI2cZ4Nb32MofeehN/ZStmEW5Edib6TQ==", + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.2.3.tgz", + "integrity": "sha512-Y6MTAWgcnrnx/LkBx65X3ZyoJH5EFj3tXtflRoKg1+PLHSLuNBV7Wi5mLcE70z4e5c+4hgBbLq+8SqCqzFtSPw==", "license": "Apache-2.0", "dependencies": { - "@polkadot/x-global": "13.2.2", + "@polkadot/x-global": "13.2.3", "tslib": "^2.8.0", "ws": "^8.18.0" }, @@ -4962,12 +5086,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/stack-utils": { @@ -13587,9 +13711,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, "node_modules/unique-filename": { diff --git a/package.json b/package.json index 0f8609f..e747551 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "browser": "dist/index.esm.js", "types": "dist/index.d.ts", "dependencies": { - "@moonbeam-network/api-augment": "^0.3200.3", + "@moonbeam-network/api-augment": "^0.3400.0", "@polkadot/api": "^12.4.2", "@polkadot/apps-config": "^0.138.1", "@polkadot/util": "^13.1.1", diff --git a/src/lazy-migrations/005-foreign-assets-migration.ts b/src/lazy-migrations/005-foreign-assets-migration.ts new file mode 100644 index 0000000..7e6f0de --- /dev/null +++ b/src/lazy-migrations/005-foreign-assets-migration.ts @@ -0,0 +1,167 @@ +// 005-foreign-assets-migration.ts +/** + * Script to migrate foreign assets from the old system to the new one. + * + * Usage: + * bun src/lazy-migrations/005-foreign-assets-migration.ts \ + * --url wss://wss.api.moondev.network \ + * --asset-id 1234 \ + * --alith \ + * --limit 50 + * + * Options: + * --url Websocket url + * --asset-id Asset ID to migrate + * --account-priv-key Private key of the account to use + * --limit Maximum number of balances/approvals to migrate per batch (default: 100) + */ + +import yargs from "yargs"; +import "@polkadot/api-augment"; +import "@moonbeam-network/api-augment"; +import { Keyring } from "@polkadot/api"; +import { KeyringPair } from "@polkadot/keyring/types"; +import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts"; +import { monitorSubmittedExtrinsic, waitForAllMonitoredExtrinsics } from "../utils/monitoring.ts"; +import { ALITH_PRIVATE_KEY } from "../utils/constants.ts"; +import { PalletMoonbeamLazyMigrationsForeignAssetForeignAssetMigrationStatus } from "@polkadot/types/lookup"; + +const argv = yargs(process.argv.slice(2)) + .usage("Usage: $0") + .version("1.0.0") + .options({ + ...NETWORK_YARGS_OPTIONS, + "account-priv-key": { + type: "string", + demandOption: false, + alias: "account", + }, + "asset-id": { + type: "string", + demandOption: true, + describe: "Asset ID to migrate", + }, + limit: { + type: "number", + default: 100, + describe: "Maximum number of balances/approvals to migrate per batch", + }, + alith: { + type: "boolean", + demandOption: false, + conflicts: ["account-priv-key"], + }, + }) + .check((argv) => { + if (!(argv["account-priv-key"] || argv["alith"])) { + throw new Error("Missing --account-priv-key or --alith"); + } + return true; + }).argv; + +async function main() { + const api = await getApiFor(argv); + const keyring = new Keyring({ type: "ethereum" }); + const assetId = argv["asset-id"]; + + try { + let account: KeyringPair; + let nonce: bigint; + let remainingBalances: number; + let remainingApprovals: number; + + // Setup account + const privKey = argv["alith"] ? ALITH_PRIVATE_KEY : argv["account-priv-key"]; + account = keyring.addFromUri(privKey, null, "ethereum"); + const { nonce: rawNonce } = await api.query.system.account(account.address); + nonce = BigInt(rawNonce.toString()); + + // Step 1: Start migration + const migrationInfo: PalletMoonbeamLazyMigrationsForeignAssetForeignAssetMigrationStatus = + await api.query.moonbeamLazyMigrations.foreignAssetMigrationStatusValue(); + + if (migrationInfo.isIdle) { + const txStart = api.tx.moonbeamLazyMigrations.startForeignAssetsMigration(assetId); + await txStart.signAndSend( + account, + { nonce: nonce++ }, + monitorSubmittedExtrinsic(api, { id: `start-migration-${assetId}` }), + ); + await waitForAllMonitoredExtrinsics(); + + const status: PalletMoonbeamLazyMigrationsForeignAssetForeignAssetMigrationStatus = + await api.query.moonbeamLazyMigrations.foreignAssetMigrationStatusValue(); + + if (!status.isMigrating) { + console.error("Migration did not start correctly"); + return; + } + + remainingBalances = status.asMigrating?.remainingBalances.toNumber(); + remainingApprovals = status.asMigrating?.remainingApprovals.toNumber(); + console.log("Started migration for asset", assetId); + } else { + console.log("Migration already in progress for asset", assetId); + remainingBalances = migrationInfo.asMigrating?.remainingBalances.toNumber(); + remainingApprovals = migrationInfo.asMigrating?.remainingApprovals.toNumber(); + } + + // Step 2: Migrate balances + while (remainingBalances > 0) { + console.log(`Migrating batch of balances (${remainingBalances} remaining)...`); + const txBalances = api.tx.moonbeamLazyMigrations.migrateForeignAssetBalances(argv.limit); + await txBalances.signAndSend( + account, + { nonce: nonce++ }, + monitorSubmittedExtrinsic(api, { id: `migrate-balances-${assetId}` }), + ); + remainingBalances -= argv.limit; + } + console.log("Completed balances migration for asset", assetId); + + // Step 3: Migrate approvals + while (remainingApprovals > 0) { + console.log(`Migrating batch of approvals (${remainingApprovals} remaining)...`); + const txApprovals = api.tx.moonbeamLazyMigrations.migrateForeignAssetApprovals(argv.limit); + await txApprovals.signAndSend( + account, + { nonce: nonce++ }, + monitorSubmittedExtrinsic(api, { id: `migrate-approvals-${assetId}` }), + ); + remainingApprovals -= argv.limit; + } + console.log("Completed approvals migration for asset", assetId); + + await waitForAllMonitoredExtrinsics(); + const status: PalletMoonbeamLazyMigrationsForeignAssetForeignAssetMigrationStatus = + await api.query.moonbeamLazyMigrations.foreignAssetMigrationStatusValue(); + if ( + (status.asMigrating?.remainingBalances.toNumber() || 0) > 0 || + (status.asMigrating?.remainingApprovals.toNumber() || 0) > 0 + ) { + // If there are still balances or approvals to migrate, we should not finish the migration + console.log("Migration is still in progress, not finishing yet"); + return; + } + + // Step 4: Finish migration + const txFinish = api.tx.moonbeamLazyMigrations.finishForeignAssetsMigration(); + await txFinish.signAndSend( + account, + { nonce: nonce++ }, + monitorSubmittedExtrinsic(api, { id: `finish-migration-${assetId}` }), + ); + console.log("Finished migration for asset", assetId); + } catch (error) { + console.error("Migration error:", error); + throw error; + } finally { + await waitForAllMonitoredExtrinsics(); + await api.disconnect(); + } +} + +main().catch((err) => { + console.error("ERR!", err); + process.exit(1); +});