git.delta.rocks / unique-network / refs/commits / 137320b42c82

difftreelog

Merge pull request #1050 from UniqueNetwork/feat/hrmp-channels-proposals-script

Yaroslav Bolyukin2024-02-13parents: #a44df49 #e2c10c5.patch.diff
in: master

4 files changed

modifiedjs-packages/scripts/councilproposal.tsdiffbeforeafterboth
before · js-packages/scripts/councilproposal.ts
1import {ApiPromise, WsProvider} from '@polkadot/api';2import {blake2AsHex} from '@polkadot/util-crypto';34async function main() {5  const networkUrl = process.argv[2];6  const democracyProposalContent = process.argv[3];78  const wsProvider = new WsProvider(networkUrl);9  const api = await ApiPromise.create({provider: wsProvider});1011  const councilMembers = (await api.query.council.members()).toJSON() as any[];12  const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1;1314  const democracyProposalHash = blake2AsHex(democracyProposalContent, 256);15  const democracyProposalPreimage = api.tx.preimage.notePreimage(democracyProposalContent).method.toHex();1617  const democracyProposal = api.tx.democracy.externalProposeDefault({18    Legacy: democracyProposalHash,19  });2021  const councilProposal = api.tx.council.propose(22    councilProposalThreshold,23    democracyProposal,24    democracyProposal.method.encodedLength,25  ).method.toHex();2627  const proposeUpgradeBatch = api.tx.utility.batchAll([28    democracyProposalPreimage,29    councilProposal,30  ]);3132  const encodedCall = proposeUpgradeBatch.method.toHex();3334  console.log('-----------------');35  console.log('Council Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`);36  console.log('-----------------');3738  await api.disconnect();39}4041await main();
addedjs-packages/scripts/hrmpchannel.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/hrmpchannel.ts
@@ -0,0 +1,214 @@
+import {ApiPromise, WsProvider} from '@polkadot/api';
+import {blake2AsHex, encodeAddress} from '@polkadot/util-crypto';
+
+export const paraChildSovereignAccount = (relayApi: ApiPromise, paraid: number) => {
+  // We are getting a *child* parachain sovereign account,
+  // so we need a child prefix: encoded(b"para") == 0x70617261
+  const childPrefix = '70617261';
+
+  const encodedParaId = relayApi
+    .createType('u32', paraid)
+    .toHex(true)
+    .substring(2 /* skip 0x */);
+
+  const addrBytesLen = 32;
+  const byteLenInHex = 2;
+  return '0x' + (childPrefix + encodedParaId).padEnd(addrBytesLen * byteLenInHex, '0');
+};
+
+const proposeOpenChannel = async (relayApi: ApiPromise, relayFee: bigint, senderParaId: number, receiverParaId: number, skipBalanceCheck: boolean) => {
+  const conf: any = await relayApi.query.configuration.activeConfig()
+    .then(data => data.toJSON());
+  const maxCapacity = conf.hrmpChannelMaxCapacity;
+  const maxSize = conf.hrmpChannelMaxMessageSize;
+
+  const senderDeposit = BigInt(conf.hrmpSenderDeposit);
+
+  const requiredBalance = relayFee + senderDeposit;
+  const sovereignAccount = paraChildSovereignAccount(relayApi, senderParaId);
+  const balance = await relayApi.query.system.account(sovereignAccount)
+    .then(accountInfo => (accountInfo.toJSON() as any).data.free as bigint);
+
+  if(!skipBalanceCheck && balance < requiredBalance) {
+    throw Error(`Not enough balance on the sender's sovereign account: balance(${balance}) < requiredBalance(${requiredBalance})`);
+  }
+
+  return relayApi.tx.hrmp.hrmpInitOpenChannel(
+    receiverParaId,
+    maxCapacity,
+    maxSize,
+  ).method.toHex();
+};
+
+const proposeAcceptChannel = async (relayApi: ApiPromise, relayFee: bigint, senderParaId: number, recipientParaId: number, skipBalanceCheck: boolean) => {
+  const conf: any = await relayApi.query.configuration.activeConfig()
+    .then(data => data.toJSON());
+  const recipientDeposit = BigInt(conf.hrmpRecipientDeposit);
+
+  const requiredBalance = relayFee + recipientDeposit;
+
+  const sovereignAccount = paraChildSovereignAccount(relayApi, recipientParaId);
+  const balance = await relayApi.query.system.account(sovereignAccount)
+    .then(accountInfo => (accountInfo.toJSON() as any).data.free as bigint);
+
+  if(!skipBalanceCheck && balance < requiredBalance) {
+    throw Error(`Not enough balance on the recipient's sovereign account: balance(${balance}) < requiredBalance(${requiredBalance})`);
+  }
+
+  return relayApi.tx.hrmp.hrmpAcceptOpenChannel(senderParaId).method.toHex();
+};
+
+async function main() {
+  let skipBalanceCheck;
+  if(process.argv.length == 6) {
+    skipBalanceCheck = false;
+  }
+  else if(process.argv.length == 7 && process.argv[6] == 'skip-balance-check') {
+    skipBalanceCheck = true;
+  } else {
+    console.log('Usage: yarn hrmpChannel <RELAY_URL> <OUR_CHAIN_URL> <OTHER_CHAIN_URL> <open | accept> [skip-balance-check]');
+    process.exit(1);
+  }
+
+  const relayUrl = process.argv[2];
+  const uniqueParachainUrl = process.argv[3];
+  const otherParachainUrl = process.argv[4];
+  const op = process.argv[5];
+
+  const relayWsProvider = new WsProvider(relayUrl);
+  const relayApi = await ApiPromise.create({provider: relayWsProvider});
+
+  const uniqueWsProvider = new WsProvider(uniqueParachainUrl);
+  const uniqueApi = await ApiPromise.create({provider: uniqueWsProvider});
+
+  const otherWsProvider = new WsProvider(otherParachainUrl);
+  const otherApi = await ApiPromise.create({provider: otherWsProvider});
+
+  const uniqueParaId = await uniqueApi.query.parachainInfo.parachainId()
+    .then(data => data.toJSON() as number);
+
+  const otherParaId = await otherApi.query.parachainInfo.parachainId()
+    .then(data => data.toJSON() as number);
+
+  const relayDecimals = await relayApi.rpc.system.properties()
+    .then(data => data.tokenDecimals.unwrap()[0].toNumber());
+
+  const relayFee = 2n * BigInt(10 ** relayDecimals);
+
+  let encodedRelayCall: string;
+
+  if(op == 'open') {
+    encodedRelayCall = await proposeOpenChannel(relayApi, relayFee, uniqueParaId, otherParaId, skipBalanceCheck);
+  } else if(op == 'accept') {
+    encodedRelayCall = await proposeAcceptChannel(relayApi, relayFee, otherParaId, uniqueParaId, skipBalanceCheck);
+  } else {
+    throw Error(`Unknown hrmp channel operation: ${op}`);
+  }
+
+  const proposal = uniqueApi.tx.polkadotXcm.send(
+    {
+      V3: {
+        parents: 1,
+        interior: 'Here',
+      },
+    },
+    {
+      V3: [
+        {
+          WithdrawAsset: [
+            {
+              id: {
+                Concrete: {
+                  parents: 0,
+                  interior: 'Here',
+                },
+              },
+              fun: {
+                Fungible: relayFee,
+              },
+            },
+          ],
+        },
+        {
+          BuyExecution: {
+            fees: {
+              id: {
+                Concrete: {
+                  parents: 0,
+                  interior: 'Here',
+                },
+              },
+              fun: {
+                Fungible: relayFee,
+              },
+            },
+            weightLimit: 'Unlimited',
+          },
+        },
+        {
+          Transact: {
+            originKind: 'Native',
+            requireWeightAtMost: {
+              refTime: 1000000000,
+              proofSize: 65536,
+            },
+            call: {
+              encoded: encodedRelayCall,
+            },
+          },
+        },
+        'RefundSurplus',
+        {
+          DepositAsset: {
+            assets: {
+              Wild: {
+                AllCounted: 1,
+              },
+            },
+            beneficiary: {
+              parents: 0,
+              interior: {
+                X1: {
+                  Parachain: uniqueParaId,
+                },
+              },
+            },
+          },
+        },
+      ],
+    },
+  ).method.toHex();
+
+  const councilMembers = (await uniqueApi.query.council.members()).toJSON() as any[];
+  const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1;
+
+  const democracyProposalHash = blake2AsHex(proposal, 256);
+  const democracyProposalPreimage = uniqueApi.tx.preimage.notePreimage(proposal).method.toHex();
+
+  const democracyProposal = uniqueApi.tx.democracy.externalProposeDefault({
+    Legacy: democracyProposalHash,
+  });
+
+  const councilProposal = uniqueApi.tx.council.propose(
+    councilProposalThreshold,
+    democracyProposal,
+    democracyProposal.method.encodedLength,
+  ).method.toHex();
+
+  const proposeBatch = uniqueApi.tx.utility.batchAll([
+    democracyProposalPreimage,
+    councilProposal,
+  ]);
+
+  const encodedCall = proposeBatch.method.toHex();
+
+  console.log('-----------------');
+  console.log('Council Proposal: ', `https://polkadot.js.org/apps/?rpc=${uniqueParachainUrl}#/extrinsics/decode/${encodedCall}`);
+  console.log('-----------------');
+
+  await relayApi.disconnect();
+  await uniqueApi.disconnect();
+  await otherApi.disconnect();
+}
+
+await main();
modifiedjs-packages/scripts/package.jsondiffbeforeafterboth
--- a/js-packages/scripts/package.json
+++ b/js-packages/scripts/package.json
@@ -13,8 +13,9 @@
     "benchMintingFee": "yarn ts-node benchmarks/mintFee/benchmark.ts",
     "loadTransfer": "yarn ts-node transfer.nload.ts",
     "generateEnv": "yarn ts-node --esm ./generateEnv.ts",
-    "propose-upgrade": "yarn ts-node --esm ./proposeupgrade.ts",
-    "propose-fast-track": "yarn ts-node --esm ./proposefasttrack.ts",
-    "council-democracy-proposal": "yarn ts-node --esm ./councilproposal.ts"
+    "proposeUpgrade": "yarn ts-node --esm ./proposeupgrade.ts",
+    "proposeFastTrack": "yarn ts-node --esm ./proposefasttrack.ts",
+    "councilDemocracyProposal": "yarn ts-node --esm ./councilproposal.ts",
+    "hrmpChannel": "yarn ts-node --esm ./hrmpchannel.ts"
   }
 }
modifiedjs-packages/scripts/proposeupgrade.tsdiffbeforeafterboth
--- a/js-packages/scripts/proposeupgrade.ts
+++ b/js-packages/scripts/proposeupgrade.ts
@@ -3,6 +3,11 @@
 import {readFileSync} from 'fs';
 
 async function main() {
+  if(process.argv.length != 4) {
+    console.log('Usage: proposeUpgrade <CHAIN_URL> <WASM_FILE_PATH>');
+    process.exit(1);
+  }
+
   const networkUrl = process.argv[2];
   const wasmFile = process.argv[3];