Как понять 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
Оригинал