Перейти к основному содержанию
Переопределения состояния позволяют настраивать состояние сети для целей вашей симуляции. Эта возможность полезна, когда нужно тестировать сценарии в условиях, которых сейчас нет в живой сети. Чтобы применить те же переопределения через панель управления, а не через API, см. переопределения состояния в UI Simulator. Чтобы проиллюстрировать работу переопределений состояния, воспользуемся контрактом стейблкоина DAI. В системе DAI определённые действия, например минтинг новых токенов DAI, могут выполняться только адресами, обозначенными как wards. Если ваш адрес не является ward, вы не сможете протестировать операции минтинга.

Настройка адреса в роли ward через переопределения состояния

Чтобы симулировать транзакцию, в которой мы минтим DAI, нам необходимо переопределить состояние так, чтобы наш адрес выглядел как ward. Нам нужно скорректировать map wards в контракте DAI, чтобы включить в него наш адрес с необходимыми разрешениями. Чтобы иметь возможность выполнить mint, отправитель транзакции должен быть ward. Другими словами, должно выполняться следующее условие: wards['0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2'] == 1. В запросе к API назначьте карту переопределений объекту state_objects. Эта карта указывает на конкретные позиции хранилища в смарт-контракте и назначает новые значения для симуляции.
  • Адрес контракта DAI: 0x6b175474e89094c44da98b954eedeac495271d0f
  • Позиция слота хранилища: 0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03
  • Значение переопределения: 0x0000000000000000000000000000000000000000000000000000000000000001
example.json
{
  "state_objects": {
    "0x6b175474e89094c44da98b954eedeac495271d0f": {
      "storage": {
        "0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03": "0x0000000000000000000000000000000000000000000000000000000000000001"
      }
    }
  }
}
Приведённый ниже скрипт показывает, как задать переопределения и отправить запрос в Simulation API. Убедитесь, что TENDERLY_ACCOUNT_SLUG и TENDERLY_ACCESS_KEY заданы как переменные окружения. Узнайте, как получить slug и ключ доступа.
example.ts

dotenv.config();

// assuming environment variables TENDERLY_ACCOUNT_SLUG, TENDERLY_PROJECT_SLUG and TENDERLY_ACCESS_KEY are set
// https://docs.tenderly.co/platform/account/projects/slug
// https://docs.tenderly.co/platform/account/projects/api-tokens
const { TENDERLY_ACCOUNT_SLUG, TENDERLY_PROJECT_SLUG, TENDERLY_ACCESS_KEY } = process.env;

const mintDai = async () => {
  console.time('Simulation');
  const fakeWardAddress = '0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2';
  // calculate the storage location of `wards[fakeWardAddress]`
  // yields 0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03
  const overrideStorageLocation = ethers.utils.keccak256(
    ethers.utils.concat([
      ethers.utils.hexZeroPad(fakeWardAddress, 32), // the ward address (address 0x000..0) - mapping key
      ethers.utils.hexZeroPad('0x0', 32), // the wards slot is 0th in the DAI contract - the mapping variable
    ]),
  );
  console.log('Storage Override Location', overrideStorageLocation);
  const mint = (
    await axios.post(
      `https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/simulate`,
      // the transaction
      {
        save: true,
        save_if_fails: true,
        simulation_type: 'full',
        network_id: '1',
        from: '0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2',
        to: '0x6b175474e89094c44da98b954eedeac495271d0f',
        input:
          '0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000',
        gas: 8000000,
        state_objects: {
          '0x6b175474e89094c44da98b954eedeac495271d0f': {
            storage: {
              '0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03':
                '0x0000000000000000000000000000000000000000000000000000000000000001',
            },
          },
        },
      },
      {
        headers: {
          'X-Access-Key': TENDERLY_ACCESS_KEY as string,
        },
      },
    )
  ).data;

  console.timeEnd('Simulation');
  console.log(JSON.stringify(mint, null, 2));
  // TODO: extract token transferred
};

mintDai();

Вычисление слота хранилища

Ключевая часть задания переопределения — вычисление правильной позиции хранилища. Оно зависит от структуры хранилища контракта и конкретной переменной, которую вы хотите переопределить. Для контракта DAI позиция хранилища для маппинга wards для нашего адреса вычисляется в соответствии с правилами раскладки хранилища Solidity и задаётся следующим выражением:
example.js
const fakeWardAddress = '0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2';
const overrideStorageLocation = keccak256(
  concatenate(
    hexZeroPad(fakeWardAddress), hexZeroPad(0x0)
  )
)
В нашем примере позиция хранилища: 0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03.

Изучите

Как переопределять контракты Gnosis Safe