Своп-транзакции очень распространены в нашей экосистеме 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 и Готово!