difftreelog
feat check required balance before sending the msg
in: master
1 file changed
js-packages/scripts/hrmpchannel.tsdiffbeforeafterboth1import {ApiPromise, WsProvider} from '@polkadot/api';2import {blake2AsHex} from '@polkadot/util-crypto';34const proposeOpenChannel = async (relayApi: ApiPromise, receiverParaId: number) => {5 const conf: any = await relayApi.query.configuration.activeConfig()6 .then(data => data.toJSON());7 const maxCapacity = conf.hrmpChannelMaxCapacity;8 const maxSize = conf.hrmpChannelMaxMessageSize;910 return relayApi.tx.hrmp.hrmpInitOpenChannel(11 receiverParaId,12 maxCapacity,13 maxSize,14 ).method.toHex();15};1617const proposeAcceptChannel = (relayApi: ApiPromise, senderParaId: number) => {18 return relayApi.tx.hrmp.hrmpAcceptOpenChannel(senderParaId).method.toHex();19};2021async function main() {22 const relayUrl = process.argv[2]23 const uniqueParachainUrl = process.argv[3];24 const otherParachainUrl = process.argv[4];25 const op = process.argv[5];2627 const relayWsProvider = new WsProvider(relayUrl);28 const relayApi = await ApiPromise.create({provider: relayWsProvider});2930 const uniqueWsProvider = new WsProvider(uniqueParachainUrl);31 const uniqueApi = await ApiPromise.create({provider: uniqueWsProvider});3233 const otherWsProvider = new WsProvider(otherParachainUrl);34 const otherApi = await ApiPromise.create({provider: otherWsProvider});3536 const uniqueParaId = await uniqueApi.query.parachainInfo.parachainId()37 .then(data => data.toJSON() as number);3839 const otherParaId = await otherApi.query.parachainInfo.parachainId()40 .then(data => data.toJSON() as number);4142 let encodedRelayCall: string;43 if(op == 'open') {44 encodedRelayCall = await proposeOpenChannel(relayApi, otherParaId);45 } else if(op == 'accept') {46 encodedRelayCall = proposeAcceptChannel(relayApi, otherParaId);47 } else {48 throw Error(`Unknown hrmp channel operation: ${op}`);49 }5051 const relayDecimals = await relayApi.rpc.system.properties()52 .then(data => data.tokenDecimals.unwrap()[0].toNumber());5354 const relayFee = 1 * (10 ** relayDecimals);5556 const proposal = uniqueApi.tx.polkadotXcm.send(57 {58 V3: {59 parents: 1,60 interior: 'Here',61 }62 },63 {64 V3: [65 {66 WithdrawAsset: [67 {68 id: {69 Concrete: {70 parents: 0,71 interior: 'Here',72 },73 },74 fun: {75 Fungible: relayFee,76 },77 },78 ],79 },80 {81 BuyExecution: {82 fees: {83 id: {84 Concrete: {85 parents: 0,86 interior: 'Here',87 },88 },89 fun: {90 Fungible: relayFee,91 },92 },93 weightLimit: 'Unlimited',94 },95 },96 {97 Transact: {98 originKind: 'Native',99 requireWeightAtMost: {100 refTime: 1000000000,101 proofSize: 65536,102 },103 call: {104 encoded: encodedRelayCall,105 },106 },107 },108 'RefundSurplus',109 {110 DepositAsset: {111 assets: {112 Wild: {113 AllCounted: 1,114 },115 },116 beneficiary: {117 parents: 0,118 interior: {119 X1: {120 Parachain: uniqueParaId,121 },122 },123 },124 },125 },126 ],127 },128 ).method.toHex();129130 const councilMembers = (await uniqueApi.query.council.members()).toJSON() as any[];131 const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1;132133 const democracyProposalHash = blake2AsHex(proposal, 256);134 const democracyProposalPreimage = uniqueApi.tx.preimage.notePreimage(proposal).method.toHex();135136 const democracyProposal = uniqueApi.tx.democracy.externalProposeDefault({137 Legacy: democracyProposalHash,138 });139140 const councilProposal = uniqueApi.tx.council.propose(141 councilProposalThreshold,142 democracyProposal,143 democracyProposal.method.encodedLength,144 ).method.toHex();145146 const proposeBatch = uniqueApi.tx.utility.batchAll([147 democracyProposalPreimage,148 councilProposal,149 ]);150151 const encodedCall = proposeBatch.method.toHex();152153 console.log('-----------------');154 console.log('Council Proposal: ', `https://polkadot.js.org/apps/?rpc=${uniqueParachainUrl}#/extrinsics/decode/${encodedCall}`);155 console.log('-----------------');156157 await relayApi.disconnect();158 await uniqueApi.disconnect();159 await otherApi.disconnect();160}161162await main();1import {ApiPromise, WsProvider} from '@polkadot/api';2import {blake2AsHex, encodeAddress} from '@polkadot/util-crypto';34export const paraChildSovereignAccount = (relayApi: ApiPromise, paraid: number) => {5 // We are getting a *child* parachain sovereign account,6 // so we need a child prefix: encoded(b"para") == 0x706172617 const childPrefix = '0x70617261';89 const encodedParaId = relayApi.createType('u32', paraid).toHex(true).substring(2);10 const suffix = '000000000000000000000000000000000000000000000000';1112 return childPrefix + encodedParaId + suffix;13};1415const proposeOpenChannel = async (relayApi: ApiPromise, relayFee: bigint, senderParaId: number, receiverParaId: number) => {16 const conf: any = await relayApi.query.configuration.activeConfig()17 .then(data => data.toJSON());18 const maxCapacity = conf.hrmpChannelMaxCapacity;19 const maxSize = conf.hrmpChannelMaxMessageSize;2021 const senderDeposit = BigInt(conf.hrmpSenderDeposit);2223 const requiredBalance = relayFee + senderDeposit;24 const sovereignAccount = await paraChildSovereignAccount(relayApi, senderParaId);25 const balance = await relayApi.query.system.account(sovereignAccount)26 .then(accountInfo => (accountInfo.toJSON() as any).data.free as bigint);2728 if(balance < requiredBalance) {29 throw Error(`Not enough balance on the sender's sovereign account: balance(${balance}) < requiredBalance(${requiredBalance})`);30 }3132 return relayApi.tx.hrmp.hrmpInitOpenChannel(33 receiverParaId,34 maxCapacity,35 maxSize,36 ).method.toHex();37};3839const proposeAcceptChannel = async (relayApi: ApiPromise, relayFee: bigint, senderParaId: number, recipientParaId: number) => {40 const conf: any = await relayApi.query.configuration.activeConfig()41 .then(data => data.toJSON());42 const recipientDeposit = BigInt(conf.hrmpRecipientDeposit);4344 const requiredBalance = relayFee + recipientDeposit;4546 const sovereignAccount = await paraChildSovereignAccount(relayApi, recipientParaId);47 const balance = await relayApi.query.system.account(sovereignAccount)48 .then(accountInfo => (accountInfo.toJSON() as any).data.free as bigint);4950 if(balance < requiredBalance) {51 throw Error(`Not enough balance on the recipient's sovereign account: balance(${balance}) < requiredBalance(${requiredBalance})`);52 }5354 return relayApi.tx.hrmp.hrmpAcceptOpenChannel(senderParaId).method.toHex();55};5657async function main() {58 const relayUrl = process.argv[2]59 const uniqueParachainUrl = process.argv[3];60 const otherParachainUrl = process.argv[4];61 const op = process.argv[5];6263 const relayWsProvider = new WsProvider(relayUrl);64 const relayApi = await ApiPromise.create({provider: relayWsProvider});6566 const uniqueWsProvider = new WsProvider(uniqueParachainUrl);67 const uniqueApi = await ApiPromise.create({provider: uniqueWsProvider});6869 const otherWsProvider = new WsProvider(otherParachainUrl);70 const otherApi = await ApiPromise.create({provider: otherWsProvider});7172 const uniqueParaId = await uniqueApi.query.parachainInfo.parachainId()73 .then(data => data.toJSON() as number);7475 const otherParaId = await otherApi.query.parachainInfo.parachainId()76 .then(data => data.toJSON() as number);7778 const relayDecimals = await relayApi.rpc.system.properties()79 .then(data => data.tokenDecimals.unwrap()[0].toNumber());8081 const relayFee = 2n * BigInt(10 ** relayDecimals);8283 let encodedRelayCall: string;84 if(op == 'open') {85 encodedRelayCall = await proposeOpenChannel(relayApi, relayFee, uniqueParaId, otherParaId);86 } else if(op == 'accept') {87 encodedRelayCall = await proposeAcceptChannel(relayApi, relayFee, otherParaId, uniqueParaId);88 } else {89 throw Error(`Unknown hrmp channel operation: ${op}`);90 }9192 const proposal = uniqueApi.tx.polkadotXcm.send(93 {94 V3: {95 parents: 1,96 interior: 'Here',97 }98 },99 {100 V3: [101 {102 WithdrawAsset: [103 {104 id: {105 Concrete: {106 parents: 0,107 interior: 'Here',108 },109 },110 fun: {111 Fungible: relayFee,112 },113 },114 ],115 },116 {117 BuyExecution: {118 fees: {119 id: {120 Concrete: {121 parents: 0,122 interior: 'Here',123 },124 },125 fun: {126 Fungible: relayFee,127 },128 },129 weightLimit: 'Unlimited',130 },131 },132 {133 Transact: {134 originKind: 'Native',135 requireWeightAtMost: {136 refTime: 1000000000,137 proofSize: 65536,138 },139 call: {140 encoded: encodedRelayCall,141 },142 },143 },144 'RefundSurplus',145 {146 DepositAsset: {147 assets: {148 Wild: {149 AllCounted: 1,150 },151 },152 beneficiary: {153 parents: 0,154 interior: {155 X1: {156 Parachain: uniqueParaId,157 },158 },159 },160 },161 },162 ],163 },164 ).method.toHex();165166 const councilMembers = (await uniqueApi.query.council.members()).toJSON() as any[];167 const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1;168169 const democracyProposalHash = blake2AsHex(proposal, 256);170 const democracyProposalPreimage = uniqueApi.tx.preimage.notePreimage(proposal).method.toHex();171172 const democracyProposal = uniqueApi.tx.democracy.externalProposeDefault({173 Legacy: democracyProposalHash,174 });175176 const councilProposal = uniqueApi.tx.council.propose(177 councilProposalThreshold,178 democracyProposal,179 democracyProposal.method.encodedLength,180 ).method.toHex();181182 const proposeBatch = uniqueApi.tx.utility.batchAll([183 democracyProposalPreimage,184 councilProposal,185 ]);186187 const encodedCall = proposeBatch.method.toHex();188189 console.log('-----------------');190 console.log('Council Proposal: ', `https://polkadot.js.org/apps/?rpc=${uniqueParachainUrl}#/extrinsics/decode/${encodedCall}`);191 console.log('-----------------');192193 await relayApi.disconnect();194 await uniqueApi.disconnect();195 await otherApi.disconnect();196}197198await main();