No method named "xyz" for type ContractCaller<MyContract>

To make a cross-contract call in Sway, one must use the following:

...
let handle = abi(MyContract, contract_addr.into());
handle.call_some_method();
...

Here, the type of handle is ContractCaller<MyContract>.

Easy enough, but let’s say you have multiple functions within this contract and you’ll be calling a lot of methods from the MyContract contract, instead of doing this:

#[storage(read)]
fn fn1() {
    let handle = abi(MyContract, storage.contr_add.into());
    handle.call_some_method_1();
}

#[storage(read)]
fn fn2() {
    let handle = abi(MyContract, storage.contr_add.into());
    handle.call_some_method_2();
}

#[storage(read)]
fn fn3() {
    let handle = abi(MyContract, storage.contr_add.into());
    handle.call_some_method_3();
}

we can try to do:

#[storage(read)]
fn fn1() {
    let handle = _get_handle();
    handle.call_some_method_1();
}

#[storage(read)]
fn fn2() {
    let handle = _get_handle();
    handle.call_some_method_2();
}

#[storage(read)]
fn fn3() {
    let handle = _get_handle();
    handle.call_some_method_3();
}

#[storage(read)]
fn _get_handle() -> ContractCaller<MyContract> {
    let handle = abi(MyContract, storage.contr_add.into());
    handle
}

But this doesn’t work. The compiler throws the errors:

No method named "call_some_method_1" found for type "ContractCaller<MyContract>".
...
No method named "call_some_method_2" found for type "ContractCaller<MyContract>".
...
No method named "call_some_method_3" found for type "ContractCaller<MyContract>".

even though all 3 of these methods are in the MyContract abi. The moment I add the internal helper function, the compiler throws the errrors above.

any thoughts? @calldelegation @nedsalk @IGI-111

ContractCaller is magical and can’t be shared across scopes. This is a bug, but it’s more of a bug in the design of the syntax than anything else. As the name of the methods has to be statically known.

If _get_handle were to be a function you could write, it would have to be a const fn because it doesn’t reflect any actual computation inside of the contract, merely some static artifice to pass around values from the compiler to itself.

We’ve been considering changing the syntax for abi calls to address this problem (such as having an extern mapping item you could import around). But in the meantime, please avoid sharing ContractCallers accross scopes.

1 Like

noted, thank you. The const fn syntax seems interesting. Tried to use it outside the impl context (so, essentially, an internal function), and got the following error: ^^ Identifiers cannot be a reserved keyword.

I used:

#[storage(read)]
const fn _get_handle() -> ContractCaller<MyContract> {
    let handle = abi(MyContract, storage.contr_add.into());
    handle
}

Well we don’t support const fn yet, so no surprises there. It’s on the roadmap, but I was really just thinking about it conceptually: functions are supposed to broadly represent runtime computation in the contract, whilst this is a compile time computation.

1 Like