Как понять tx.origin и msg.sender в Solidity

Как понять 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, мы видим, что оба адреса одинаковы.

tx.origin and msg.sender are the same

Давайте вызовем функцию callUnderlyingContract для контракта EntryContract. Мы видим, что tx.origin и msg.sender теперь отличаются. tx.origin — это исходный адрес вызывающего абонента, а msg.sender — это адрес смарт-контракта EntryContract.

tx.origin and msg.sender are not the same

TL;DR

В Solidity tx.origin и msg.sender — это две переменные, которые служат разным, но важным целям. tx.origin всегда относится к адресу, который первоначально инициировал транзакцию, и остается постоянным на протяжении всей цепочки транзакций.

С другой стороны, msg.sender представляет отправителя текущего сообщения или взаимодействия с контрактом и изменяется при каждом вызове.

Крайне важно соблюдать осторожность при использовании msg.sender, поскольку он не всегда может представлять первоначальную вызывающую транзакцию.

Ссылки


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE