Своп-транзакции очень распространены в нашей экосистеме web3, поскольку обмен одного токена на другой действительно полезен. Тем не менее, я начну эту статью с небольшого рассказа о моей прошлой неделе. Я работал над проектом MELK, проектом Учись зарабатывать, который требует текстовых доказательств (таких как адреса, хэши транзакций, значения и т. д.) в качестве подтверждения завершения миссии. Подробнее о проекте можно читать здесь (контент в ПТ-БР). В любом случае, я работал над миссией, и мне нужно было проверить, какое значение было заменено, например: человек обменял 1 USDC на X MATIC. Мне нужно было получить доступ к этому значению в 1 USDC. В простой транзакции передачи, такой как я, отправляю вам 1 доллар США, все, что нам нужно знать, это адрес контракта токена, и мы можем получить доступ к значению, используя такие библиотеки, как ethers.js и web3.js. Однако в данном конкретном случае значение не возвращается ни в стандартном объекте транзакции, ни с использованием контракта токена. Это происходит потому, что используемая DEX (в данном случае Uniswap) использует функции множественного вызова для выполнения обмена.

После того, как я обнаружил, что не могу получить значение свопа по умолчанию, моей первой мыслью было прочитать входное значение объекта транзакции. Но как я мог это сделать? Давайте немного кодировать!

Погружение в код

Прежде всего, мы собираемся использовать библиотеку web3.js В этом примере кода я покажу, как поменять местами значение в uniswap. Давайте импортируем все необходимые нам библиотеки. Если они у вас не установлены, вы можете установить их с помощью npm i /package-name/.

const Web3 = require('web3'); 
const web3 = new Web3(`https://polygon-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`); 
const abiDecoder = require("abi-decoder"); 
let abi = require('../data/uniswapAbi.json');

В этой статье я использую Alchemy API для подключения к основной сети, и мой ключ API alchemy хранится в ENV. Переменная ABI получает данные ABI, которые я сохранил в файле JSON. А вот ЛПИ можно найти здесь. AbiDecoder — это библиотека, которая поможет нам декодировать входные данные. Теперь займемся транзакцией.

let transaction = await web3.eth.getTransaction("0x40d84ef37bc95ce4d563df0630e1ea68132a9386064a03b41a66e8a82cace56b");

Хороший. Теперь у нас есть объект транзакции для этого хеша. Теперь приступим к расшифровке.

// decode transaction input
const data = abiDecoder.decodeMethod(transaction.input);

//filter input data, find transaction value
let filteredData = data.params.filter((param) => param.name === 'data');

// select hex data with transaction value
let hexDat = filteredData[0].value[0];

// decode hex data
let decodedData = abiDecoder.decodeMethod(hexDat);

// select swap value
let amount = decodedData.params[0].value.amountIn;
amount = ethers.utils.formatEther(amount);

Давайте пройдемся по этому коду. Сначала мы получаем декодированные данные из ввода транзакции. Ввод транзакции выглядит так:
0x5ae401dc00...

Да, мы не можем прочитать это правильно? Итак, мы передаем ему команду do abiDecoder, и он возвращает другой объект, например:

{ имя: 'multicall', параметры: [ { имя: 'крайний срок', значение: '1650201409', тип: 'uint256' }, { имя: 'данные', значение: [массив], тип: 'байты []' } ] }

Хорошо, теперь мы приближаемся к нашему решению. Здесь у нас есть имя и параметры этой функции мультивызова. Нам нужно получить доступ ко второму параметру с именем data. Внутри него живет наша ценность. Давай сделаем это.

//filter input data, find transaction value
let filteredData = data.params.filter((param) => param.name === 'data');

// select hex data with transaction value
let hexDat = filteredData[0].value[0];

Что ж, здесь мы выбираем второй параметр с именем data с помощью функции фильтра. Это вернет данные параметра объекта выше. Это вернет другой объект, например:

[ { имя: «данные», значение: [«0x04…»], тип: «байты []» } ]

В приведенном выше коде я удалил значение, потому что это будет огромное шестнадцатеричное значение. Но теперь мы должны прочитать это шестнадцатеричное значение. Итак, в строке hexDat = filteredData[0].value[0]; мы выбираем значение поля наших filteredData, выбираем массив значений и получаем первый элемент в массиве, то есть значение, переданное функции подкачки. Но значение по-прежнему закодировано в шестнадцатеричном формате, поэтому давайте его расшифруем!

// decode hex data
let decodedData = abiDecoder.decodeMethod(hexDat);

// select swap value
let amount  decodeData.parms[0].value.amountIn;
amount = ethers.utils.formatEther(amount);

Вот и все! Теперь мы расшифровали шестнадцатеричные данные, и в переменной amount мы получаем доступ к значению amountIn, которое присутствует в нашем декодируемом объекте. Это значение представляет значение, замененное в транзакции. Теперь мы просто форматируем его с помощью formatEther и Готово!