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

[EVM] Cannot call subnet precompile methods from another smart contract #1111

Open
gztensor opened this issue Dec 20, 2024 · 5 comments
Open
Assignees

Comments

@gztensor
Copy link
Contributor

The example smart contract gets error "Error(InvalidCode(Opcode(94)))", when calling setHyperParameter:

pragma solidity ^0.8.3;

address constant ISUBTENSOR_SUBNET_ADDRESS = 0x0000000000000000000000000000000000000803;

interface ISubnet {
    /// Registers a new network without specifying details.
    function registerNetwork() external payable;

    function getServingRateLimit(uint16 netuid) external view returns (uint64);

    function setServingRateLimit(
        uint16 netuid,
        uint64 servingRateLimit
    ) external payable;
}

contract Subnet {
    function setHyperParameter(uint16 netuid, uint64 value) external {
        ISubnet subnetPrecompile = ISubnet(ISUBTENSOR_SUBNET_ADDRESS);
        (bool success, ) = ISUBTENSOR_SUBNET_ADDRESS.call(
            abi.encodeWithSelector(
                subnetPrecompile.setServingRateLimit.selector,
                netuid,
                value
            )
        );
        require(success, "Subnet call failed");
    }
}
@ales-otf
Copy link
Contributor

I tried it on subnet-hyperparameter branch and couldn't reproduce it with this MPR: https://github.com/ales-otf/contract-in-contract-mpr

I modified the contract to call a getter for simplicity, as for setters there are additional checks.

To run the MPR, you need Alith account (5Fghzk1AJt88PeFEzuRfXzbPchiBbsVGTTXcdx599VdZzkTA) to be prefunded. Then:

npx hardhat run scripts/deploy.js --network subtensorEvm

@open-junius
Copy link
Contributor

thanks. let me try your test code.

@ales-otf
Copy link
Contributor

ales-otf commented Dec 24, 2024

For the context, we discussed it more with Junius personally. And he suggested trying reproducing it with a setter instead. I've updated the code (same repo, latest commit) in MPR to dispatch setServingRateLimit. And I still see the same behavior. The contract is called, and the method is triggered correctly. It obviously won't work in the MPR, because it's a sudo call. I don't know what is ETH private key for Alice to test it, but anyway it failed on origin check and the node returned an error right after my print:

Screenshot 2024-12-24 at 18 23 54

or with text:

2024-12-24 18:17:36 I'm ok!    
2024-12-24 18:17:36 Dispatch failed. Error: DispatchErrorWithPostInfo { post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, error: BadOrigin }    

So if we talk about calling contract from within a contract, the issue is most likely in the test setup. As the method is dispatched.

But notice that if you really want to test/trigger something like setServingRateLimit, you should also call it from sudo account as it's a sudo call. Also, I'm not sure whether it should be wrapped into sudo->call extrinsic, or just calling it from sudo would work.

@ales-otf
Copy link
Contributor

For ref, here is where I put my "I'm ok" log:

fn dispatch(
    handle: &mut impl PrecompileHandle,
    call: RuntimeCall,
    smart_contract_address: &str,
) -> PrecompileResult {
    let account_id =
        <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
            handle.context().caller,
        );
	log::error!("I'm ok!");

    // Transfer the amount back to the caller before executing the staking operation
    // let caller = handle.context().caller;
    let amount = handle.context().apparent_value;

    if !amount.is_zero() {
        transfer_back_to_caller(smart_contract_address, &account_id, amount)?;
    }

    let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into());
    match &result {
        Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info),
        Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error),
    }
    match result {
        Ok(_) => Ok(PrecompileOutput {
            exit_status: ExitSucceed::Returned,
            output: vec![],
        }),
        Err(_) => Err(PrecompileFailure::Error {
            exit_status: ExitError::Other("Subtensor call failed".into()),
        }),
    }
}

@ales-otf
Copy link
Contributor

For the context, we discussed it more with Junius personally. And he suggested trying reproducing it with a setter instead. I've updated the code (same repo, latest commit) in MPR to dispatch setServingRateLimit. And I still see the same behavior. The contract is called, and the method is triggered correctly. It obviously won't work in the MPR, because it's a sudo call. I don't know what is ETH private key for Alice to test it, but anyway it failed on origin check and the node returned an error right after my print:

Screenshot 2024-12-24 at 18 23 54 or with text:
2024-12-24 18:17:36 I'm ok!    
2024-12-24 18:17:36 Dispatch failed. Error: DispatchErrorWithPostInfo { post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, error: BadOrigin }    

So if we talk about calling contract from within a contract, the issue is most likely in the test setup. As the method is dispatched.

But notice that if you really want to test/trigger something like setServingRateLimit, you should also call it from sudo account as it's a sudo call. Also, I'm not sure whether it should be wrapped into sudo->call extrinsic, or just calling it from sudo would work.

Just realized, that I forgot to push the changes. I've also deleted the code locally, as Junius figured out this issue. Let me know if you need it, and I'll rewrite it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants