Как понять tx.origin и msg.sender в Solidity
9 января 2024 г.Разработчикам необходимо понимать разницу между tx.origin и msg.sender в Solidity. Эти две глобальные переменные часто нуждаются в разъяснении друг друга, несмотря на их фундаментальные различия. Хотя на первый взгляд они могут показаться похожими, tx.origin и msg.sender представляют разные адреса в контексте транзакции.
В этой записи блога мы углубимся в значение каждой из этих переменных.
Что такое tx.origin?
В Solidity tx.origin идентифицирует исходного отправителя транзакции. Он указывает на внешнюю учетную запись, инициирующую транзакцию, и остается постоянным на протяжении последующих взаимодействий смарт-контракта (полная цепочка вызовов).
Когда транзакция инициируется через кошелек MetaMask, адрес кошелька MetaMask пользователя сохраняется в tx.origin. Этот адрес остается неизменным, даже если транзакция проходит через несколько контрактов. Согласованность этого адреса необходима для отслеживания первоначального отправителя транзакции.
Что такое msg.sender?
При разработке смарт-контрактов msg.sender идентифицирует отправителя текущего вызова. Эта переменная является динамической и может меняться в ходе процесса транзакции.
Когда транзакция проходит через несколько смарт-контрактов, значение msg.sender изменяется, указывая адрес самого последнего контракта в цепочке вызовов. Например, если Контракт А вызывает Контракт Б, то значение msg.sender в Контракте Б будет распознано как Контракт А.
Написание кода
Чтобы продемонстрировать, как tx.origin и msg.sender изменяются между вызовами смарт-контракта, мы создадим смарт-контракт EntryContract, который ссылается на контракт UnderlyingContract.
Давайте добавим функцию printTxOriginAndMsgSender, которая печатает каждый адрес.
Здесь у нас есть смарт-контракт Entry:
contract EntryContract {
IUnderlyingContract private underlyingContract;
constructor(IUnderlyingContract _underlyingContract) {
underlyingContract = _underlyingContract;
}
function printTxOriginAndMsgSender() public view {
console.log("tx.origin", tx.origin);
console.log("msg.sender", msg.sender);
}
function callUnderlyingContract() external {
underlyingContract.printTxOriginAndMsgSender();
}
}
Теперь давайте определим UnderlyingContract и его интерфейс:
interface IUnderlyingContract {
function printTxOriginAndMsgSender() external ;
}
contract UnderlyingContract is IUnderlyingContract {
function printTxOriginAndMsgSender() external view {
console.log("tx.origin", tx.origin);
console.log("msg.sender", msg.sender);
}
}
Чтобы выполнить тест, мы должны сначала развернуть UnderlyingContract и использовать его адрес при развертывании EntryContract.
Когда мы вызываем функцию printTxOriginAndMsgSender контракта EntryContract, мы видим, что оба адреса одинаковы.
Давайте вызовем функцию callUnderlyingContract для контракта EntryContract. Мы видим, что tx.origin и msg.sender теперь отличаются. tx.origin — это исходный адрес вызывающего абонента, а msg.sender — это адрес смарт-контракта EntryContract.
TL;DR
В Solidity tx.origin и msg.sender — это две переменные, которые служат разным, но важным целям. tx.origin всегда относится к адресу, который первоначально инициировал транзакцию, и остается постоянным на протяжении всей цепочки транзакций.
С другой стороны, msg.sender представляет отправителя текущего сообщения или взаимодействия с контрактом и изменяется при каждом вызове.
Крайне важно соблюдать осторожность при использовании msg.sender, поскольку он не всегда может представлять первоначальную вызывающую транзакцию.
Ссылки
- Демо-код
- Документация Solidity: специальные переменные и функции
- Документация по Solidity: свойства блоков и транзакций ли>
- Разгадка тайн Solidity: демистификация tx.origin и msg.sender
Оригинал