几句话说清楚函数调用和转账的问题
函数调用与转账是所有教程(可能夸大),也包括官方文档,搞得极其混乱的地方。
函数调用和转账关系密切,概念上转账是函数调用的附属物,通过{value,gas}附着在函数调用上,无论是自定义函数,合约部署,还是call方法。当然是这样,因为value和calldata都是transaction(message)的数据字段。
下面这个图是被广泛使用的:

但它既不准确,也不清晰清晰,理解困难。下面我们给出另一种方式
我们将问题分解成两部:
第一步是解析逻辑
为调用解析出一个处理这个调用的合约函数。这个过程与转账无关,即与value无关,如下图:

这是一个过滤器模式。自定义函数使用selecor匹配,receive匹配calldata为空的情况,而fallback是最后兜底。receive和自定义的函数可以任意交换,但fallback必须在最后。当然receive和fallback都可能缺席。当calldata穿透这个链条没有被拦截,调用失败。
这就是解析逻辑。
第二步是检查逻辑
如果被某一函数拦截了,检查 是否有value>0而函数没有被payble修饰的情况。如果有,调用被拒绝,否则,执行解析出的函数的逻辑。
以上逻辑清晰易懂,关键是一定将解析逻辑和检查逻辑分离。只要混在一起讲,一定是一团乱麻。遗憾的是,这似乎是所有文章和课程的做法。起码我看到的是这样。