Message Calls
- Send value and
calldatato contracts - The first message call is the beginning of the transaction (EOA -> contract)
- Each subsequent message call is part of the same transaction (contract -> contract)
- The transaction and any state changes only complete when the initial function call finishes execution
💡 Message calls are actions taken to communicate with a smart contract.
💡 EOA can communicate with a smart contract or a smart contract can communicate with another smart contract.

Message Call Breakdown
- As we saw message calls can contain
gas,valueandcalldata - These message values become available as globals in solidity:
msg.sender– who made the last message callmsg.value– amount in wei sentmsg.data– calldatamsg.sig– the function identifier
How Do EOAs and Contracts Call Other Contracts?
- They use the
addressof the contract they want to communicate with - This is a data type in solidity that is 20 bytes long
- The
msg.senderfrom the previous slide is an address
What does that look like?
contract X {
address deployer;
address otherContract;
constructor(address _otherContract) {
deployer = msg.sender;
otherContract = _otherContract;
}
}Can any method call receive ether?
Only payable methods can:
contract X {
receive() external payable {
// no calldata necessary here
// just send a value on the message call
}
function pay() external payable {
// in this case, we target pay with a value
}
}What does sending value look like?
The .call syntax is .call{ gas, value }(calldata)
contract X {
address otherContract;
constructor(address _otherContract) payable {
otherContract = _otherContract;
_otherContract.call{ value: msg.value }("");
}
}Handle the success
The solidity compiler will warn you if you don’t handle success
contract X {
address otherContract;
constructor(address _otherContract) payable {
otherContract = _otherContract;
(bool success, ) = _otherContract.call{ value: msg.value }("");
require(success);
}
}Hands-on
learn-solidity-presentations\2-sending-ether\examples\0-send-ether\src\Example.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "forge-std/console.sol";
contract A {
address b;
constructor(address _b) payable {
b = _b;
console.log(msg.value);
console.log(address(this).balance);
}
function payHalf() external {
uint256 balance = address(this).balance;
(bool success,) = b.call{value: balance / 2}("");
require(success);
}
}
contract B {
receive() external payable {}
}
learn-solidity-presentations\2-sending-ether\examples\0-send-ether\test\Example.t.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Example.sol";
contract ExampleTest is Test {
A public a;
B public b;
function setUp() public {
b = new B();
a = new A{ value: 1 ether }(address(b));
}
function testExample() public {
assertEq(address(a).balance, 1 ether);
assertEq(address(b).balance, 0 ether);
a.payHalf();
assertEq(address(a).balance, 0.5 ether);
assertEq(address(b).balance, 0.5 ether);
a.payHalf();
assertEq(address(a).balance, 0.25 ether);
assertEq(address(b).balance, 0.75 ether);
a.payHalf();
assertEq(address(a).balance, 0.125 ether);
assertEq(address(b).balance, 0.875 ether);
}
}edo@DESKTOP-4POSOVK:/mnt/e/Programming/Solidity/learn-solidity-presentations/2-sending-ether/examples/0-send-ether$ forge test -vv
[⠰] Compiling...
No files changed, compilation skipped
Ran 1 test for test/Example.t.sol:ExampleTest
[PASS] testExample() (gas: 36663)
Logs:
1000000000000000000
1000000000000000000
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 558.50µs (87.50µs CPU time)
Ran 1 test suite in 48.58ms (558.50µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
Learn Solidity. Alchemy University.
Accessed November 25, 2024
https://university.alchemy.com/overview/solidity