Hi @filipaze,
The function is first making sure that _toContract
is actually a contract (an account with code in it). Then in general lines it calls _toContract
method _method
with parameters _args
, _fromContractAddr
and _fromChainId
. In particular the _method
parameters is a string (encoded in bytes
) with the name of the function to be called.
If _method
has, for example, the value “doSomething”, the code above would be executing:
_toContract.doSomething(_args, _fromContractAddr, _fromChainId)
The way it does this call is by first calculating the function selector for the _method
parameters. This is done by hashing the signature of the method “doSomething(bytes,bytes,uint64)” and getting the first 4 bytes. This is what the first part of the last line is doing. It first concatenates the _method
name to the rest of the signature:
abi.encodePacked(_method, "(bytes,bytes,uint64)")
Then it hashes it:
keccak256(abi.encodePacked(_method, "(bytes,bytes,uint64)"))
And finally gets the first 4 bytes:
bytes4(keccak256(abi.encodePacked(_method, "(bytes,bytes,uint64)")))
That will yield a 4 bytes value with the function selector for the function name in _method
. Let’s call this value _selector
.
Finally it appends to this selector the required parameters in order to call that function in the other contract:
abi.encodePacked(_selector, abi.encode(_args, _fromContractAddr, _fromChainId))
This yields a sequence of bytes that encode a call to _selector
with the rest of the parameters as arguments. Let’s call this sequence of bytes _calldata
. Notice that _args
, _fromContractAddr
and _fromChaind
parameters follow the signature used to calculate the selector (bytes,bytes,uint64)
.
Then it goes into calling that function in _toContract
:
_toContract.call(_calldata)
Hope this helps!