diff --git a/.snippets/code/builders/interoperability/xcm/core-concepts/weights-fees/units-per-second.js b/.snippets/code/builders/interoperability/xcm/core-concepts/weights-fees/units-per-second.js deleted file mode 100644 index 0c4c16f43..000000000 --- a/.snippets/code/builders/interoperability/xcm/core-concepts/weights-fees/units-per-second.js +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiPromise, WsProvider } from '@polkadot/api'; // Version 10.9.1 - -const providerWsURL = 'wss://wss.api.moonbeam.network'; - -const getUnitsPerSecond = async () => { - const substrateProvider = new WsProvider(providerWsURL); - const api = await ApiPromise.create({ provider: substrateProvider }); - - const xcDOTAssetId = 42259045809535163221576417993425387648n; - const assetType = ( - await api.query.assetManager.assetIdType(xcDOTAssetId) - ).toJSON(); - - const unitsPerSecond = await api.query.assetManager.assetTypeUnitsPerSecond( - assetType - ); - console.log(`The UnitsPerSecond for xcDOT is ${unitsPerSecond.toHuman()}`); -}; - -getUnitsPerSecond(); diff --git a/.snippets/code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/calculate-relative-price.ts b/.snippets/code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/calculate-relative-price.ts new file mode 100644 index 000000000..c14cd8c67 --- /dev/null +++ b/.snippets/code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/calculate-relative-price.ts @@ -0,0 +1,125 @@ +import axios from 'axios'; +import chalk from 'chalk'; + +// CoinGecko IDs for the networks +const NETWORK_IDS = { + GLMR: 'moonbeam', + MOVR: 'moonriver', +}; + +async function calculateRelativePrice( + assetPrice: number, + network: 'GLMR' | 'MOVR' +): Promise { + try { + // Fetch the native token price from CoinGecko + const response = await axios.get( + `https://api.coingecko.com/api/v3/simple/price?ids=${NETWORK_IDS[network]}&vs_currencies=usd` + ); + + const nativeTokenPrice = response.data[NETWORK_IDS[network]].usd; + + // Calculate relative price with 18 decimal places + // Formula: (nativeTokenPrice / assetPrice) * 10^18 + // This gives us how many units of the asset we need to equal 1 unit of native token + const relativePrice = (nativeTokenPrice / assetPrice) * Math.pow(10, 18); + + // Return as string to preserve precision + return relativePrice.toFixed(0); + } catch (error) { + if (error instanceof Error) { + throw new Error(`Failed to calculate relative price: ${error.message}`); + } + throw error; + } +} + +function validateInput( + price: string, + network: string +): { assetPrice: number; network: 'GLMR' | 'MOVR' } { + // Validate price + const assetPrice = parseFloat(price); + if (isNaN(assetPrice) || assetPrice <= 0) { + throw new Error('Price must be a positive number'); + } + + // Validate network + const upperNetwork = network.toUpperCase() as 'GLMR' | 'MOVR'; + if (!['GLMR', 'MOVR'].includes(upperNetwork)) { + throw new Error('Network must be either GLMR or MOVR'); + } + + return { assetPrice, network: upperNetwork }; +} + +function printUsage() { + console.log('\nUsage:'); + console.log('npx ts-node relative-price-calculator.ts '); + console.log('\nExample:'); + console.log('npx ts-node relative-price-calculator.ts 0.25 GLMR'); + console.log('\nParameters:'); + console.log('price - The price of your asset in USD'); + console.log('network - Either GLMR or MOVR'); +} + +async function main() { + try { + // Get command line arguments + const [, , price, network] = process.argv; + + // Check if help flag is passed + if (price === '--help' || price === '-h') { + printUsage(); + return; + } + + // Check if required arguments are provided + if (!price || !network) { + console.error('Error: Missing required arguments'); + printUsage(); + process.exit(1); + } + + // Validate inputs + const { assetPrice, network: validNetwork } = validateInput(price, network); + + console.log( + `\nCalculating relative price for asset worth $${assetPrice} against ${validNetwork}...` + ); + const relativePrice = await calculateRelativePrice( + assetPrice, + validNetwork + ); + const nativeTokenPrice = ( + await axios.get( + `https://api.coingecko.com/api/v3/simple/price?ids=${NETWORK_IDS[validNetwork]}&vs_currencies=usd` + ) + ).data[NETWORK_IDS[validNetwork]].usd; + + const decimalRatio = nativeTokenPrice / assetPrice; + + console.log(`\nResults:`); + console.log(`Asset Price: $${assetPrice}`); + console.log(`Network: ${validNetwork}`); + console.log(`Native Token Price (from CoinGecko): $${nativeTokenPrice}`); + console.log(`\nRelative Price Analysis:`); + console.log( + `1 ${validNetwork} is equal to approximately ${decimalRatio.toFixed( + 3 + )} of your specified token.` + ); + console.log( + `With 18 decimals, 1 ${validNetwork} or in WEI, 1000000000000000000 is equal to a relative price of ${relativePrice} units of your token` + ); + console.log(chalk.bold(`\nRelative Price: ${relativePrice}`)); + console.log( + `\nThe relative price you should specify in asset registration steps is ${relativePrice}\n` + ); + } catch (error) { + console.error('\nError:', error instanceof Error ? error.message : error); + process.exit(1); + } +} + +main(); diff --git a/.snippets/code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/destination-asset-fee-per-second.js b/.snippets/code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/destination-asset-fee-per-second.js deleted file mode 100644 index 77e218198..000000000 --- a/.snippets/code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/destination-asset-fee-per-second.js +++ /dev/null @@ -1,26 +0,0 @@ -import { ApiPromise, WsProvider } from '@polkadot/api'; // Version 10.13.1 - -const providerWsURL = 'wss://wss.api.moonbase.moonbeam.network'; - -const location = { - parents: 1, - interior: { X2: [{ Parachain: 888 }, { PalletInstance: 3 }] }, -}; - -const main = async () => { - const substrateProvider = new WsProvider(providerWsURL); - const api = await ApiPromise.create({ provider: substrateProvider }); - - const destinationAssetFeePerSecond = - await api.query.xcmTransactor.destinationAssetFeePerSecond(location); - - if (destinationAssetFeePerSecond.isSome) { - const data = destinationAssetFeePerSecond.unwrap(); - const unitsPerSecond = data.toString(); - console.log(unitsPerSecond); - } - - api.disconnect(); -}; - -main(); \ No newline at end of file diff --git a/builders/interoperability/xcm/core-concepts/weights-fees.md b/builders/interoperability/xcm/core-concepts/weights-fees.md index 27c71c47a..9889c3291 100644 --- a/builders/interoperability/xcm/core-concepts/weights-fees.md +++ b/builders/interoperability/xcm/core-concepts/weights-fees.md @@ -213,7 +213,7 @@ Now that you know the weight costs for database reads and writes for Moonbase Al For example, the `WithdrawAsset` instruction is part of the fungible XCM instructions. Therefore, it is not benchmarked, and the total weight cost of the [`WithdrawAsset` instruction](https://github.com/moonbeam-foundation/moonbeam/blob/{{ networks.moonbase.spec_version }}/pallets/moonbeam-xcm-benchmarks/src/weights/fungible.rs#L36){target=\_blank} is `{{ xcm.fungible_weights.display }}`, except for when transferring local XC-20s. The total weight cost for the `WithdrawAsset` instruction for local XC-20s is based on a conversion of Ethereum gas to Substrate weight. -The [`BuyExecution` instruction](https://github.com/moonbeam-foundation/moonbeam/blob/{{ networks.moonbase.spec_version }}/pallets/moonbeam-xcm-benchmarks/src/weights/generic.rs#L128-L129){target=\_blank} has a base weight of `{{ xcm.generic_weights.ref_time.buy_exec.base_weight.display }}`, and performs four database reads (`assetManager` pallet to get the `unitsPerSecond`). Therefore, the total weight cost of the `BuyExecution` instruction is calculated as follows: +The [`BuyExecution` instruction](https://github.com/moonbeam-foundation/moonbeam/blob/{{ networks.moonbase.spec_version }}/pallets/moonbeam-xcm-benchmarks/src/weights/generic.rs#L128-L129){target=\_blank} has a base weight of `{{ xcm.generic_weights.ref_time.buy_exec.base_weight.display }}`, and performs four database reads. Therefore, the total weight cost of the `BuyExecution` instruction is calculated as follows: ```text {{ xcm.generic_weights.ref_time.buy_exec.base_weight.numbers_only }} + 4 * {{ xcm.db_weights.rocksdb_read.numbers_only }} = {{ xcm.generic_weights.ref_time.buy_exec.total_weight.numbers_only }} diff --git a/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet.md b/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet.md index a2b5b6d1e..025f05531 100644 --- a/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet.md +++ b/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet.md @@ -409,16 +409,19 @@ To estimate the amount of tokens Alice's Computed Origin account will need to ex --8<-- 'code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/transact-info-with-weight-limit.js' ``` -From the response, you can see that the `transactExtraWeightSigned` is `{{ networks.moonbase_beta.xcm_message.transact.weight.display }}`. This is the weight needed to execute the four XCM instructions for this remote call in that specific destination chain. Next, you need to find out how much the destination chain charges per weight of XCM execution. Normally, you would look into the units per second for that particular chain. But in this scenario, no XC-20 tokens are burned. Therefore, units per second can be used for reference, but it does not ensure that the estimated number of tokens is correct. To get the units per second as a reference value, you can use the following script: +The response shows that the `transactExtraWeightSigned` is `{{ networks.moonbase_beta.xcm_message.transact.weight.display }}`. This weight is needed to execute the four XCM instructions for this remote call in that specific destination chain. Next, you need to find out how much the destination chain charges per weight of XCM execution in reference to the asset's price. Previously, this was done by sourcing a units per second value. However, this method has been replaced by calculating a relative price. Relative price refers to how many units of the foreign asset correspond to one unit of the native token (GLMR or MOVR) from a value (i.e., price) perspective. For example, if the foreign asset is worth $5 and GLMR is worth $0.25, the relative price would be 0.05. However, we must scale the result to 18 decimals to correspond to the Wei units used. In this case, the relative price would be `50000000000000000`. -```js ---8<-- 'code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/destination-asset-fee-per-second.js' -``` +You can calculate the relative price with a script in the [XCM Tools repo](https://github.com/Moonsong-Labs/xcm-tools?tab=readme-ov-file#calculate-relative-price){target=\_blank}. The script is also reproduced below: + +??? code "Calculate Relative Price" + ```typescript + --8<-- 'code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/calculate-relative-price.ts' + ``` -Note that the units per second value is related to the cost estimated in the [Relay Chain XCM Fee Calculation](/builders/interoperability/xcm/core-concepts/weights-fees/#polkadot){target=\_blank} section or to the one shown in the [Units per weight](/builders/interoperability/xcm/core-concepts/weights-fees/#moonbeam-reserve-assets){target=\_blank} section if the target is another parachain. You'll need to find the correct value to ensure that the amount of tokens the Computed Origin account holds is correct. Calculating the associated XCM execution fee is as simple as multiplying the `transactExtraWeightSigned` times the `unitsPerSecond` (for an estimation): +Note that the relative price value is related to the cost estimated in the [relay chain XCM fee calculation](/builders/interoperability/xcm/core-concepts/weights-fees/#polkadot){target=\_blank} section or to the one shown in the [units per weight](/builders/interoperability/xcm/core-concepts/weights-fees/#moonbeam-reserve-assets){target=\_blank} section if the target is another parachain. You'll need to find the correct value to ensure that the amount of tokens the Computed Origin account holds is correct. Calculating the associated XCM execution fee is as simple as multiplying the `transactExtraWeightSigned` times the `relativePrice` (for an estimation): ```text -XCM-Wei-Token-Cost = transactExtraWeightSigned * unitsPerSecond +XCM-Wei-Token-Cost = transactExtraWeightSigned * relativePrice XCM-Token-Cost = XCM-Wei-Token-Cost / DecimalConversion ``` diff --git a/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-precompile.md b/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-precompile.md index 6234bb117..4b72219e3 100644 --- a/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-precompile.md +++ b/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-precompile.md @@ -349,16 +349,19 @@ To estimate the amount of token Alice's Computed Origin account will need to hav --8<-- 'code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-precompile/transact-info-with-signed.js' ``` -From the response, you can see that the `transactExtraWeightSigned` is `{{ networks.moonbase_beta.xcm_message.transact.weight.display }}`. This is the weight needed to execute the four XCM instructions for this remote call in that specific destination chain. Next, you need to find out how much the destination chain charges per weight of XCM execution. Normally, you would look into the units per second for that particular chain. But in this scenario, no XC-20 tokens are burned. Therefore, units per second can be used for reference, but it does not ensure that the estimated number of tokens is correct. To get the units per second as a reference value, you can use the following script: +The response shows that the `transactExtraWeightSigned` is `{{ networks.moonbase_beta.xcm_message.transact.weight.display }}`. This weight is needed to execute the four XCM instructions for this remote call in that specific destination chain. Next, you need to find out how much the destination chain charges per weight of XCM execution in reference to the asset's price. Previously, this was done by sourcing a units per second value. However, this method has been replaced by calculating a relative price. Relative price refers to how many units of the foreign asset correspond to one unit of the native token (GLMR or MOVR) from a value (i.e., price) perspective. For example, if the foreign asset is worth $5 and GLMR is worth $0.25, the relative price would be 0.05. However, we must scale the result to 18 decimals to correspond to the Wei units used. In this case, the relative price would be `50000000000000000`. -```js ---8<-- 'code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-precompile/fee-per-second.js' -``` +You can calculate the relative price with a script in the [XCM Tools repo](https://github.com/Moonsong-Labs/xcm-tools?tab=readme-ov-file#calculate-relative-price){target=\_blank}. The script is also reproduced below: + +??? code "Calculate Relative Price" + ```typescript + --8<-- 'code/builders/interoperability/xcm/remote-execution/substrate-calls/xcm-transactor-pallet/calculate-relative-price.ts' + ``` -Note that the units per second is related to the cost estimated in the [Relay Chain XCM Fee Calculation](/builders/interoperability/xcm/core-concepts/weights-fees/#polkadot){target=\_blank} section or to the one shown in the [Units per weight](/builders/interoperability/xcm/core-concepts/weights-fees/#moonbeam-reserve-assets){target=\_blank} section if the target is another parachain. You'll need to find the correct value to ensure that the amount of tokens the Computed Origin account holds is correct. Calculating the associated XCM execution fee is as simple as multiplying the `transactExtraWeightSigned` times the `unitsPerSecond` (for an estimation): +Note that the relative price value is related to the cost estimated in the [relay chain XCM fee calculation](/builders/interoperability/xcm/core-concepts/weights-fees/#polkadot){target=\_blank} section or to the one shown in the [units per weight](/builders/interoperability/xcm/core-concepts/weights-fees/#moonbeam-reserve-assets){target=\_blank} section if the target is another parachain. You'll need to find the correct value to ensure that the amount of tokens the Computed Origin account holds is correct. Calculating the associated XCM execution fee is as simple as multiplying the `transactExtraWeightSigned` times the `relativePrice` (for an estimation): ```text -XCM-Wei-Token-Cost = transactExtraWeightSigned * unitsPerSecond +XCM-Wei-Token-Cost = transactExtraWeightSigned * relativePrice XCM-Token-Cost = XCM-Wei-Token-Cost / DecimalConversion ``` diff --git a/builders/interoperability/xcm/xcm-utils.md b/builders/interoperability/xcm/xcm-utils.md index 713a4a63a..f209d789a 100644 --- a/builders/interoperability/xcm/xcm-utils.md +++ b/builders/interoperability/xcm/xcm-utils.md @@ -43,7 +43,11 @@ The interface includes the following functions: - **multilocationToAddress**(*Multilocation memory* multilocation) — read-only function that returns the Computed Origin account from a given multilocation - **weightMessage**(*bytes memory* message) — read-only function that returns the weight that an XCM message will consume on the chain. The message parameter must be a SCALE encoded XCM versioned XCM message - - **getUnitsPerSecond**(*Multilocation memory* multilocation) — read-only function that gets the units per second for a given asset in the form of a `Multilocation`. The multilocation must describe an asset that can be supported as a fee payment, such as an [external XC-20](/builders/interoperability/xcm/xc20/overview/#external-xc20s){target=\_blank}, or else this function will revert + - **getUnitsPerSecond**(*Multilocation memory* multilocation) — read-only function that gets the units per second for a given asset in the form of a `Multilocation`. The multilocation must describe an asset that can be supported as a fee payment, such as an [external XC-20](/builders/interoperability/xcm/xc20/overview/#external-xc20s){target=\_blank}, or else this function will revert. + + !!! note + Note that this function still returns units per second data but units per second has been deprecated and replaced by the calculation of relative price. See [XC asset registration](/builders/interoperability/xcm/xc-registration/assets#generate-encoded-calldata-for-asset-registration){target=\_blank} for more details. + - **xcmExecute**(*bytes memory* message, *uint64* maxWeight) - **available on Moonbase Alpha only** - executes a custom XCM message given the SCALE encoded versioned message to be executed and the maximum weight to be consumed. This function *cannot* be called from a smart contract due to the nature of the `Transact` instruction - **xcmSend**(*Multilocation memory* dest, *bytes memory* message) - **available on Moonbase Alpha only** - sends a custom XCM message given the multilocation of the destination chain to send the message to and the SCALE encoded versioned message to be sent