Is there a way to mint ERC20 asset balance through Root in runtime?
Dear Moonbeam team,
We are trying to create dryrun balance bypass by minting ourselves assets through root so we can try out the XCM messages, however, the Moonbeam chain uses ERC20 assets so assets pallet is no longer working. Is there a way to modify ourselves a balance through root so we can mint ourselves erc20 assets to be able to send XCM?
Scenario:
Suppose we want to send DOT from Moonbeam to Hydration
We mind ourselves GLMR through balances pallet
But we also need to mint ourselves xcDOT erc20, in order for the XCM to be successful. We have not found a way to do this. Is there a way?
Thanks in advance!
Moonbeam foreign assets are native ERC-20 contracts, https://github.com/moonbeam-foundation/moonbeam/blob/master/pallets/moonbeam-foreign-assets/resources/foreign_erc20.sol
Storage layout:
{
"astId": 332,
"contract": "contracts/erc20.sol:MyToken",
"label": "_balances",
"offset": 0,
"slot": "0",
"type": "t_mapping(t_address,t_uint256)"
},
{
"astId": 8,
"contract": "contracts/erc20.sol:MyToken",
"label": "_owner",
"offset": 1,
"slot": "5",
"type": "t_address"
}
Each entry in the balances mapping is stored at: keccak256(paddress_address + padded_slot)
Where:
paddress_address is the 20-byte address, left-padded to 32 bytes.
padded_slot is the slot number of the mapping (0 for balances), left-padded to 32 bytes.
+ denotes concatenation.
You can calculate the slot with:
pragma solidity ^0.8.0;
contract StorageSlotCalculator {
function calculateMappingSlot(address key, uint256 mappingSlot) external pure returns (bytes32) {
return keccak256(abi.encodePacked(
bytes32(uint256(uint160(key))), // pad the address to 32 bytes
bytes32(mappingSlot) // pad the slot to 32 bytes
));
}
}
Example: calculateMappingSlot(0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac, 0)
Result: 0x01fdf5799e0754c521507d0817d9efa5aac0acf23d4df8a7c71e8fc760ec7d14
You can then copy the encoded storage key:
0xffffffff0000000000000000000000000000000a is an example erc20 contract address
And then set 100000000000000000000 balance for your account:
If you find the above too complex, you can also modify the asset owner, which is at slot 5 and then call function mintInto(address to, uint256 amount) in the erc-20 contract.
0x6d6f646c666f7267617373740000000000000000 is the EvmForeignAssets account. You just need to replace it with your own address, mint some balance and then change back the owner to 0x6d6f646c666f7267617373740000000000000000.
@RomarQ thank you for your thorough explanation!
We are going to try it and will let you know if we succeed.
With kind regards, Team ParaSpell✨
@RomarQ
Do you know which extrinsic should we use and what to fill where?
Thanks in advance!
With kind regards, Team ParaSpell✨
You can use https://remix.ethereum.org to interact with erc-20.
If it is easier I can provide the storage key and value you need to set with system.setStorage.
For that I just need to know in which account you need the balance.
EDIT:
NVM, we understand it now. Thanks a lot!
I will close this issue if we manage to perform these actions successfully.
With kind regards, Team ParaSpell✨
Storage key: 0x1da53b775b270400e7e61ed5cbc5a146ab1160471b1418779239ba8e2b847e421f720ca3a567a7892f51ba4eabe649ccffffffff1fcacbd218edc0eba20fc2308c778080adf054024eb585d942fdcc11210daf107a1bc6e523bf240517ff9a4d6c4555b1a9ec0fd6a35164bd2ea9c9462b97370d
Storage value: 0x0000000000000000000000000000000000000000000000056bc75e2d63100000
This will set the balance to 100000000000000000000 on account 0x24D18dbFBcEd732EAdF98EE520853e13909fE258 for asset xcDOT.
Thanks!
With kind regards, Team ParaSpell✨
Yep, just verified it, it works. I tried sending 100 DOT, which I did not have on that account to Hydration and it worked.
Thanks a lot again!
Closing this issue as matter is now resolved.
With kind regards, Team ParaSpell✨
Hey @RomarQ reopening this, as Wormhole assets do not seem to work this way. Can you please let us know how to mint wormhole assets through root? When applying same process we get local execution incomplete meaning assets did not mint.
Thanks!
For example WH USDC - 0x931715fee2d06333043d11f658c8ce934ac61d0c
cc @RomarQ is there any way? Thanks in advance!
You could use the same approach I shared above, by overriding the storage to change the owner and then minting some tokens.
@RomarQ unfortunately this does not seem to be the case, as WH assets do not have regular ids, rather they already have hexes, and when trying those directly or compiling them same way as other assets this fails. Can you confirm, that this is correct approach? Their IDs seem to be addresses of the contracts already.
With kind regards, Team ParaSpell✨️
This is an example of asset id pulled from onchain query directly from Moonbeam (compared to what is also on Hydration they are correct (when comparing locations not ids)
Compared to regular asset such as ZTG which works to mint perfectly fine:
You can see ids are hexes for WH assets and bigints for regular assets, thus we are unable to apply the same process.
Or is it possible we do not havw correct IDs for WH assets? If so, do you know a query that can give us correct asset ids for WH assets? Thanks!
For mocking the asset balances it should be identical, both assets use an erc20 native evm contract. Later today I could provide you with an example similar to: https://github.com/moonbeam-foundation/moonbeam/issues/3404#issuecomment-3215220955
@RomarQ we have tried it, but it probably isn't identical because it does not work for us. The normal assets with standard ids work and we are able to use them in dryrun bypass just fine. WH asset minting do not work however.
The example would be very welcome. Thanks!
With kind regards, Team ParaSpell✨
cc @RomarQ any news regarding this?
Thanks!
cc @RomarQ
Any news? Haven't heard from you for a while.
We have meanwhile stumbled uppon another issue. How can we do local transfers? Do we have to do it in similar way with system pallet or is there any other way around this? For example I want to transfer WETH from Wormhole from one account to another within Moonbeam. Or any other foreign asset.
Thanks in advance!
With kind regards, Team ParaSpell✨️
I did not had the time to look into this. From what I understand you are mocking asset balances with chopsticks to test integration with other parachains, right? (mocking the balances of any ERC-20 contracts should be similar to what I described above)
@albertov19, do you have any easier suggestion for what ParaSpell is trying to do?
@dudo50, what I did for Wormhole assets was that I performed a super small transfer and traced the call with the storage component enabled to see what the storage consisted of, which consisted of the balance of a particular account.
For example, for WBTC, this would be the override that you needed to set in Chopsticks Moonbeaml yml configuration file:
EVM:
AccountStorages:
- [
[
'0xE57eBd2d67B462E9926e04a8e33f01cD0D64346D',
'0xf8e48ec07a2c6e821f6d50dfc3b27c87665a699d3788ee5db873275abd50ca64',
],
'0x00000000000000000000000000000000000000000000000000000002540BE400',
]
I believe this is for Alith. I tried to calculate the storage slot, but I could not find the right way to do so.
Thanks for quick reply @albertov19 !
The reply however is not very helpful for us unfortunately. We are looking for universal solution that can perform local erc20 transfers and also allow minting wormhole assets the way we do other erc20 assets so that we can use them in dryrun bypass to give users correct xcm transfer fees.
With kind regards, Team ParaSpell✨️
Hi @dudo50,
To be able to help you, please describe exactly what you are trying to do, and possibly provide a reproducible example.
From the following so that we can use them in dryrun bypass to give users correct xcm transfer fees, I assume you are using https://github.com/AcalaNetwork/chopsticks#dry-run. Would supporting mock-signature-host: true be enough for what you are trying to achieve?
Hey @RomarQ , our bad, sorry that we weren't more specific. We do not use chopsticks in this process at all.
1.) Regarding dry-run bypass and what it is, essentially, we mint ourselves a balance through root to see if the message passes, so that we can calculate fees in any scenario, even if the user does not have any balance - this is a very helpful feature for the front end. It looks like this:
batch{
mint native currency to the user - as root
mint asset that is transported to the user - as root
xcm call defined by user - as root, but descended to the concrete user
}
First part we have working now, thanks to you - minting foreign assets that are not from Wormhole. But minting balance for Wormhole assets does not work by the process you described here. We asked you to try it yourself by the process you described, so that you can see it does not work. It only worked for assets with IDs that did not start with 0xHEX, such as WBTC(0xe57ebd2d67b462e9926e04a8e33f01cd0d64346d)❌ - eg 66249247788835743307843320271452141931 (xcvBNC)✅.
We tried skipping the precompile to 0x hex and directly entered WBTC 0xe57ebd2d67b462e9926e04a8e33f01cd0d64346d address as contract address, it did not work either.
2.) Now, for the second problem, our SDK also allows for local transfers. So, for example, sending 10WBTC from one account on Moonbeam to another one on Moonbeam. However, with this new ERC20 process, we are unsure how to perform such a transfer using pallets. Do you have any clue? Previously, we used pallet assets.transfer or assets.transferAll. This no longer works. Eg: https://dev.papi.how/extrinsics#networkId=moonbeam&endpoint=wss%3A%2F%2Fmoonbeam.ibp.network&data=0x6808338080778c30c20fa2ebc0ed18d2cbca1f9e3e46ed10825bf0bb9c21dfb2094ca89309c7330700c817a804
Hope this clarifies what we are trying to achieve. Feel free to add any follow-up questions. Thanks!
With kind regards, Team ParaSpell✨
Now I understand what you mean by local transfers. Since all foreign assets now are native ERC-20 tokens, you need to send an ethereum transaction. Pallet Assets is no longer used and will be removed in runtime 4000.
Example:
https://github.com/moonbeam-foundation/moonbeam/blob/4f247b61d7ebbb86b3d0a3bc34cd750b1a36db97/README.md?plain=1#L64
import { createWalletClient, http, parseUnits } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
const ERC20_ABI = [
{
name: 'transfer',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' }
],
outputs: [{ type: 'bool' }]
}
];
async function transferERC20(
rpc,
tokenAddress,
recipient,
amount,
private_key
) {
const account = privateKeyToAccount(private_key);
const client = createWalletClient({
account,
transport: http(rpc)
});
// Transfer tokens
const hash = await client.writeContract({
address: tokenAddress,
abi: ERC20_ABI,
functionName: 'transfer',
args: [recipient, amount]
});
console.log(`Transaction hash: ${hash}`);
return hash;
}
// Configuration
const PRIVATE_KEY = '0x...'; // Your private key
const TOKEN_ADDRESS = '0x...'; // ERC20 token contract address
const RECIPIENT = '0x...'; // Recipient address
const AMOUNT = parseUnits('10', 18); // e.g. 10 tokens (18 decimals)
const RPC_URL = 'https://wss.api.moondev.network'; // RPC endpoint
transferERC20(
RPC_URL,
TOKEN_ADDRESS,
RECIPIENT,
AMOUNT,
PRIVATE_KEY
).catch(console.error);
Hey @RomarQ ,
Ahh, this is what we feared, so you need to use VIEM in order to do a local transfer instead of having the possibility to do it through pallet. Thanks!
Do you have any snippets or information on how to achieve point 1? Minting of Wormhole assets.
Thanks!
Hey @RomarQ ,
Ahh, this is what we feared, so you need to use VIEM in order to do a local transfer instead of having the possibility to do it through pallet. Thanks!
Do you have any snippets or information on how to achieve point 1? Minting of Wormhole assets.
Thanks!
For point 1, the following example should work for almost erc20 assets:
import { keccak256, hexToBytes, concat } from 'viem';
function mappingStorageSlot(address, slotIndex = 0) {
// strip 0x and pad to 32 bytes (64 hex chars)
const a = address.replace(/^0x/i, '').toLowerCase().padStart(64, '0');
const slotHex = BigInt(slotIndex).toString(16).padStart(64, '0');
// build bytes: padded address (32) || padded slot (32)
const data = concat([hexToBytes('0x' + a), hexToBytes('0x' + slotHex)]);
return keccak256(data);
}
function uint256ToStorageValue(n) {
const h = BigInt(n).toString(16).padStart(64, '0');
return '0x' + h;
}
// Example
const account_address_to_fund = '0x24D18dbFBcEd732EAdF98EE520853e13909fE258'; //
const slot = 0; // Balance storage slot index (may need to be changed, depending on the ERC-20 implementation)
const amount_to_fund = 1000n;
console.log('storage key:', mappingStorageSlot(account_address_to_fund, slot));
console.log('storage value:', uint256ToStorageValue(amount_to_fund));
Output:
storage key: 0x7a1bc6e523bf240517ff9a4d6c4555b1a9ec0fd6a35164bd2ea9c9462b97370d
storage value: 0x00000000000000000000000000000000000000000000000000000000000003e8
Then, as Alberto suggested, you can override the storage using chopsticks:
endpoint: wss://wss.api.moonbeam.network
import-storage:
EVM:
AccountStorages:
- [
[
'0xffffffff1fcacbd218edc0eba20fc2308c778080', # <- erc20 token
'0x7a1bc6e523bf240517ff9a4d6c4555b1a9ec0fd6a35164bd2ea9c9462b97370d',
],
'0x00000000000000000000000000000000000000000000000000000000000003e8',
]
@RomarQ thanks for quick reply,
Can you please try it for WBTC for example? It has an ID of 0xe57ebd2d67b462e9926e04a8e33f01cd0d64346d.
Just so we know it works, because we got the regular ERCs working just like you have guided us in the beginning of this issue we only have problem with Wormhole assets because their IDs are different from regular assets.
Thanks in advance!
I tried your approach, @RomarQ, but I was unsuccessful with Wormhole assets. They have a specific asset structure that follows the ERC-20 Interface but not the actual storage structure (probably the order is different or something?)
That is why I had to do a small transfer to Alith (in Mainnet!!) and trace it to get the storage key
@albertov19 thanks for confirming this approach does not work for wormhole assets.
Doing real transfers manually and finding out storage keys is a lengthy process. Is there any other way to find the storage keys of wormhole assets? We need to implement it generically, should new WH assets come in the future, also, and this does not seem doable at the moment.
Thanks a lot in advance!