Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Units Per Second #1074

Merged
merged 12 commits into from
Jan 22, 2025

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import axios from 'axios';
themacexpert marked this conversation as resolved.
Show resolved Hide resolved
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<string> {
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 <price> <network>');
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();

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
themacexpert marked this conversation as resolved.
Show resolved Hide resolved

??? 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
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
themacexpert marked this conversation as resolved.
Show resolved Hide resolved

??? 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
```

Expand Down
6 changes: 5 additions & 1 deletion builders/interoperability/xcm/xcm-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down