<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>-</title><generator>teletype.in</generator><description><![CDATA[-]]></description><link>https://teletype.in/@vktio?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=vktio</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/vktio?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/vktio?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Sat, 04 Apr 2026 09:40:29 GMT</pubDate><lastBuildDate>Sat, 04 Apr 2026 09:40:29 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@vktio/reentrancy</guid><link>https://teletype.in/@vktio/reentrancy?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=vktio</link><comments>https://teletype.in/@vktio/reentrancy?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=vktio#comments</comments><dc:creator>vktio</dc:creator><title>Reentrancy attack для чайникoff</title><pubDate>Sat, 25 Jun 2022 16:04:30 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/29/87/298788db-490c-4a99-a110-3cb6c2fbcb9e.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/1d/75/1d75c2e4-3742-4876-ae1e-f5d8cfb6db71.png"></img>Несколько слов о том, что это за атака, как оно работает, к чему может привести и пара примеров из жизни.]]></description><content:encoded><![CDATA[
  <figure id="vKgo" class="m_original">
    <img src="https://img2.teletype.in/files/1d/75/1d75c2e4-3742-4876-ae1e-f5d8cfb6db71.png" width="606" />
  </figure>
  <p id="h3F7">Несколько слов о том, что это за атака, как оно работает, к чему может привести и пара примеров из жизни.</p>
  <h2 id="ZtDS" data-align="center">Концепт</h2>
  <p id="b5r5">Давайте представим, что мы наткнулись на этот прекрасный псевдокод.</p>
  <figure id="aoFn" class="m_retina">
    <img src="https://img4.teletype.in/files/3f/cc/3fcc5b0c-1a7a-4d74-9bd7-c5ce5fbbaf24.png" width="366.5" />
  </figure>
  <p id="DMEW">Разберем что тут происходит.</p>
  <ol id="amMq">
    <li id="5Gpt">Если переменная <strong>timesCalled </strong>&gt; 0, то функция завершается ошибкой</li>
    <li id="Ah4s">Вызываем какую-то функцию из стороннего контракта</li>
    <li id="m0Iz">увеличиваем переменную <strong>timesCalled </strong>на 1</li>
  </ol>
  <p id="NMql">По логике вещей, эта функция может быть вызвана один раз, после чего каунтер <strong>timesCalled </strong>увеличивается и становится равен 1. Все последующие попытки выполнить эту функцию будут заканчиваться ошибкой.</p>
  <p id="9YqX">Так ли это? Зависит от того, что происходит в <strong>externalContractFunction.</strong></p>
  <figure id="xtSF" class="m_retina">
    <img src="https://img4.teletype.in/files/73/9f/739f06c8-f4af-4983-91ea-87ee007a8b1b.png" width="329" />
  </figure>
  <p id="96C1">Для наглядности того, что мы получим в итоге, в <strong>badFunction</strong> заменим вызов <strong>externalContractFunction</strong> на код самой <strong>badFunction </strong></p>
  <figure id="6fr2" class="m_retina" data-caption-align="center">
    <img src="https://img2.teletype.in/files/d0/0f/d00f9981-7cd7-4fb6-8a31-36ee4e1214b8.png" width="440" />
    <figcaption>Вместо многоточия код повторяется столько раз, сколько externalContractFunction будет вызывать badFunction</figcaption>
  </figure>
  <p id="oDdB">В итоге в начале мы имеем набор одинаковых проверок каунтера <strong>timesCalled</strong> и все они пройдут успешно, т.к. все увеличения каунтера происходят в самом конце. Как итог, в случае если <strong>externalContractFunction</strong> будет вызвана 2 раза - значение <strong>timesCalled </strong>после выполнения всего кода будет 2, если 6 раз - то 6 и т.п.</p>
  <p id="fV3V">Был ли способ этого избежать?</p>
  <figure id="Tcri" class="m_retina">
    <img src="https://img2.teletype.in/files/5b/23/5b23ce47-70f1-4d14-8365-a6fcdee2760b.png" width="368.5" />
  </figure>
  <p id="0BSK">В примере выше мы сделали одно небольшое изменение - мы поставили вызов <strong>externalContractFunction </strong>в самый конец. Попробуем развернуть код и посмотреть, что у нас выходит теперь.</p>
  <figure id="83bn" class="m_retina">
    <img src="https://img3.teletype.in/files/65/7f/657ff7cc-aa2e-4af9-bbc9-c4d8c7cf4dee.png" width="372" />
  </figure>
  <p id="hLak">После перестановки мы имеем последовательные блоки проверки каунтера и его увеличения, как итог - вызов <strong>goodFunction </strong>завершится ошибкой в строке 4.</p>
  <h2 id="r8E3" data-align="center">Ближе к реальности</h2>
  <p id="6tre">Теперь разберем самую частую ошибку в контрактах, которая приводит к появлению уязвимости.</p>
  <figure id="aup0" class="m_retina">
    <img src="https://img3.teletype.in/files/20/df/20df8c67-d56e-4592-9b5f-5bb02108d9d5.png" width="623" />
  </figure>
  <p id="Qx3H">В функции <strong>mintPublic </strong>можно заметить знакомый паттерн.</p>
  <ul id="d8Mz">
    <li id="0Uoj">проверка каунтера</li>
    <li id="uOow">вызов функции</li>
    <li id="UBbX">увеличения каунтера</li>
  </ul>
  <p id="hrqQ">Но в примере с <strong>badFunction</strong> мы добились эксплуатирования уязвимости за счет кастомного кода в вызываемой фунции, что же не так с <strong>_safeMint</strong>?</p>
  <p id="wAd9">А не так с <strong>_safeMint</strong> то, что слово safe здесь обозначает только то, что её невозможно использовать для минта токена на контракт, с которого будет невозможно этот токен вывести. </p>
  <p id="u6Jn">Эта &quot;безопасность&quot; обеспечивается проверкой контракта на поддержку определенного интерфейса, а проверка, в свою очередь, пытается вызвать на контракте вызывающем <strong>_safeMint </strong>функцию <strong>onERC721Received.</strong></p>
  <p id="cbHj">Таким образом, атака на подобный контракт выглядит так.</p>
  <ul id="VV0f">
    <li id="20B1">Деплоим контракт с кастомной функцией <strong>onERC721Received</strong></li>
    <li id="btVt">С нашего атакующего контракта начинаем минт уязвимого контракта</li>
    <li id="3Ite">Уязвимый контракт во время выполнения <strong>_safeMint </strong>вызывает на атакующем контракте <strong>onERC721Received</strong>, эта функция в свою очередь снова вызывает <strong>mintPublic </strong>на уязвимом контракте</li>
  </ul>
  <figure id="ZDCm" class="m_retina" data-caption-align="center">
    <img src="https://img1.teletype.in/files/cd/58/cd58ebd3-617e-4489-a0b2-1bd0382e14f8.png" width="478" />
    <figcaption>кастомная функция onERC721Received, вызывающая publicMint на уязвимом контракте 6 раз в цикле.</figcaption>
  </figure>
  <h2 id="4HaM" data-align="center">Примеры из жизни</h2>
  <p id="Fb6i">Reentrancy остается большой проблемой до сих пор для многих контрактов. Не так давно Azuki добавили дополнительный слой безопасности в их новейшую версию ERC721A, которая закрывает дыру в <strong>_safeMint</strong>, но опасность существует всегда, пока разработчики ставят вызов сторонних функций перед внесением изменений в контракт.</p>
  <p id="V1T6">Ниже несколько примеров из реальных контрактов недавно созданных коллекций. Для удобства самостоятельного разбора: </p>
  <p id="ZgVo">- проверки выделены синим</p>
  <p id="aj4o">- вызов внешней функции красным</p>
  <p id="Qx8E">- необходимые изменения в контракте зеленым</p>
  <h3 id="X88K" data-align="center">SPOODEEMOONWTF </h3>
  <figure id="qgNE" class="m_column">
    <img src="https://img1.teletype.in/files/84/15/841561d1-b134-4d74-908f-a815636c5ff8.png" width="1565" />
    <figcaption>https://etherscan.io/address/0x973e7cf876932fa820139287244b66e21b9030bc#code - SPOODEEMOONWTF</figcaption>
  </figure>
  <p id="d1v5"> При минте SPOODEEMOONWTF с контракта, можно не только обойти ограничение в количестве токенов на кошелек, но и глобальное ограничение количества токенов. </p>
  <h3 id="RVnj" data-align="center">LACOSTE</h3>
  <figure id="Fv2F" class="m_column">
    <img src="https://img1.teletype.in/files/8d/7b/8d7b12e1-600d-47a5-a1b8-0837a355b093.png" width="2945" />
    <figcaption>https://etherscan.io/address/0xcd041f40d497038e2da65988b7d7e2c0d9244619#code - UNDW3Collection</figcaption>
  </figure>
  <p id="NnAG">Функция <strong>_mintNFT </strong>внутренняя, но вызывается при минте токенов. Аптейт количества сминченных кошельком токенов происходит в функции <strong>_updateClaimedNFT. </strong>При минте с атакующего контракта возможно обойти ограничение по количеству токенов на кошелек.</p>
  <h3 id="xdGm" data-align="center">GossamerSeed</h3>
  <figure id="G2tc" class="m_column">
    <img src="https://img1.teletype.in/files/ce/50/ce50c283-e6ae-475c-955a-232d54d67f53.png" width="2061" />
    <figcaption>https://etherscan.io/address/0xc7c962e44316e0c052448a0fdd1da15ea24fa9a9#code - GossamerSeed</figcaption>
  </figure>
  <p id="dP6M">Здесь так же можно обойти ограничение на количество минт мест на кошелек.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@vktio/nft-metadata</guid><link>https://teletype.in/@vktio/nft-metadata?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=vktio</link><comments>https://teletype.in/@vktio/nft-metadata?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=vktio#comments</comments><dc:creator>vktio</dc:creator><title>Немного о метаданных: Оффчейн, ончейн, ревил и динамические коллекции</title><pubDate>Sat, 18 Jun 2022 18:55:49 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/90/a4/90a48df5-7bc3-4a2d-9b77-e8de33d8586d.png"></media:content><description><![CDATA[<img src="https://www.theartnewspaper.ru/media/images/1_sg4n9Se.width-1290.jpg"></img>Решил написать эту статью, т.к. каждый раз сталкиваюсь с одними и теми же вопросами.]]></description><content:encoded><![CDATA[
  <figure id="QYI8" class="m_column">
    <img src="https://www.theartnewspaper.ru/media/images/1_sg4n9Se.width-1290.jpg" width="960" />
  </figure>
  <p id="hc0x">Решил написать эту статью, т.к. каждый раз сталкиваюсь с одними и теми же вопросами.</p>
  <p id="6T4z">Итак, для того, чтоб осветить все темы указанные в заголовке, сначала надо понять, что же такое метаданные.</p>
  <p id="C1fZ">Метадата в NFT - это список всех характеристик конкретного токена, включая traits, которые влияют на рарность токена и, непосредственно, контент (ЖПЕГ которым ты торгуешь, а может и не ЖПЕГ).</p>
  <p id="03GK">Как же ОС и остальные маркеты определяют метаданные и где они хранятся? Давайте разбираться.</p>
  <h2 id="lCRE" data-align="center">Оффчейн</h2>
  <p id="4yJQ">Самый распространенный способ хранения метадаты - оффчейн. Другими словами, где угодно, кроме блокчейна. Это может быть IPFS, это может быть свое облако или даже яндекс диск. В момент, когда маркету\тулзе\боту\пытливому юзеру надо получить метадату конкретного токена, он обращается к контракту и просить выдать ему даные токена с <strong>ID #</strong>. В ответ получает JSON файл с трейтами, именем, кратким описанием коллекции и всем всем, что ты можешь видеть на ОС, открыв страницу конкретного NFT токена. Среди прочей инфы в JSON хранится ссылка на контент - JPEG или что еще.</p>
  <figure id="cvmY" class="m_column">
    <img src="https://img3.teletype.in/files/e4/2f/e42f6caa-5cb4-4a92-80e6-60d40ffa5a2f.png" width="1646" />
  </figure>
  <p id="i9uS"><strong>Плюсы </strong></p>
  <p id="rmwK">- <strong>Гибкость </strong>в изменении метадаты. В случае с хранением на IPFS будет сложнее, но если же у тебя поднят свой сервер с кастом API - ты можешь менять данные на основе погоды, курса битка (F) или своего настроения. В любое время в зависимости от логики.</p>
  <p id="vBcx">Можно хранить любые файлы в любом количестве и любого размера.</p>
  <p id="Gk4v">Ревил проводится путем простой подмены base URI (был yandex.ru стал google.com) с указанием на актуальные данные или путем манипуляций с API.</p>
  <p id="80zu"><strong>Минусы </strong></p>
  <p id="xqp2">Оффчейн метадата должна быть загружена на сервер до начала минта, иначе вместо токена на ОС ты увидишь иконку незагруженной картинки и абсолютный ноль трейтов. (Если ты видел такое раньше и фаундеры говорили про проблемы с ОС - значит они обосрались с метадатой или своим API и теперь прикрывают себе задницу). Это так же значит, что команда знает порядок токенов в коллекции и, если будет нужно, они могут её подсмотреть и сминтить нужный токен. Да да, тот самый инсайд трейдинг.</p>
  <p id="TsCk">Забыл оплатить облако, взломали сервер, API написано левой пяткой? Холдеры увидят пустые странички вместо своих токенов. (ОС редко обновляет метадату, так что с ОС может все будет ок какое-то время). Фаундеры оказались негодяями? Получи изображение МПХ себе на ПФП.</p>
  <figure id="urpd" class="m_column">
    <img src="https://img1.teletype.in/files/cb/18/cb1889f3-50c6-45fb-9dc9-423e543fc876.png" width="992" />
  </figure>
  <h2 id="Tm02" data-align="center">Ончейн</h2>
  <p id="Mpim">Об ончейне я могу говорить долго и нудно, уж очень люблю его, но постараюсь быть краток.</p>
  <p id="rb8F">Тулзы и маркеты написаны таким образом, что вместо ссылки на JSON файл с метаданными могут принимать сразу JSON контент с содержимым. Откуда же он возьмется? В случае с ончейн - он будет сгенерирован прямо в блокчейне контрактом. Существуют смешанные типы хранения метаданных, когда в блокчейне хранится хэш токена, в котором заложены конкретные трейты. А сами названия трейтов, картинка и остальные данные по этому хэшу достаются с оффчейн API. Но мы тут не за этим. Фулл ончейн коллекции хранят все данные включая контент прямо в блокчейне.</p>
  <figure id="Vt8J" class="m_column">
    <img src="https://img3.teletype.in/files/23/70/2370948b-dfea-4652-b00e-81180d7f74b4.png" width="1894" />
  </figure>
  <p id="ndtW">Из примеров - Anonymice, EtherOrcs, CyberBrokers (сделали финт ушами и сделали оффчейн коллекцию, собрали денег и на 91 эфира загрузили весь арт в блокчейн).</p>
  <p id="t7WW"><strong>Из плюсов</strong> - никто никогда не потеряет свой токен из-за того что уборщица выдернула провод. Все что было в блокчейне остается в блокчейне включая твой ЖПЕГ. Пока эфир жив - жива и коллекция.</p>
  <p id="2RKO">Флекс. Флекс был основной причиной всплеска популярности ончейн коллекций. Рядовому юзеру абсолютно пофиг где хранится картинка. Не пофиг только девам задротам.</p>
  <p id="NIhW"><strong>Минусы</strong></p>
  <p id="V4Xf"><strong>- Дорого</strong>. Если прайс на деплой обычной коллекции колеблется в рендже от 0.2E до 0.5E в среднем, то коллеция 24х24 пиксельных пфп на 70 трейтов может обойтись в 3 эфира на деплое. (Перечитай про CyberBrokers еще раз выше, они потратили<strong> 91 эфир </strong>на загрузку данных)</p>
  <p id="K1AX"><strong>- Ограничено </strong>(потому что дорого) хочешь запилить 1 токен 1мб фоточку? будь готов заплатить сотни тысяч. Поэтому ончейн ограничен по контенту. Говоря о смешанных коллекциях - есть ребята, которые хранят JS код в ончейн, который при прогрузке на маркетах уже генерирует контент. Но это не тру и мы на такое не ведемся.</p>
  <p id="cRUb">- <strong>Ончейн предсказуем</strong>. Рандома не существует для блокчейна. Любое рандомное число может быть сгенерировано заново кем угодно если ты знаешь входные параметры. Если в случае с оффчейн метадатой ты можешь замешать свои данные как тебе угодно и никто не будет об этом знать (кроме тебя и внимательного сталкера который выследит куда ты загрузил данные), то в случае с ончейн данные следующего токена будет способен просчитать каждый, у кого есть на это достаточно знаний и времени.</p>
  <p id="ZTs4">Кроме того, в случае, если ты хранишь хэш или сами метаданные в ончейне, - их можно прочитать с контракта, а это открывает новые векторы для атак на подобные конракты.</p>
  <h2 id="UDNk" data-align="center">Итог</h2>
  <p id="XDLA">Оффчейн\ончейн\смешанная модель. Что выбрать для своей коллекции или что куда подходит лучше?</p>
  <p id="z6N2">Если вы делаете свой <strong>кэшграб без дальнейшего развития и вообще</strong> &quot;no roadmap, no utility, no discord etcetcetc&quot; (ну или это просто пфп\арт проект) - просто загружайте данные на IPFS и забудьте о них, <strong>оффчейн </strong>- ваш выбор.</p>
  <p id="yEJJ">Если вы хотите сделать <strong>коллекцию динамическую</strong>, которая будет меняться со временем или в зависимости от условий - <strong>оффчейн с кастомным API</strong> вместо IPFS - ваш вариант.</p>
  <p id="fm9Z">Вам <strong>нужно сделать логику</strong> зависящую от конкретных трейтов? Или может вам надо не только менять метаданные, но и отслеживать эти изменения с другого контракта? <strong>Смешанная модель для вас</strong>. Храните хэши трейтов в ончейне чтоб их можно было читать с контрактов, в зависимости от хэшей грузите метаданные с API.</p>
  <p id="6NoN">Хотите <strong>пофлексить </strong>и убедить холдеров, что их коллекция будет так же востребована и через 50 лет (агада) - <strong>ончейн </strong>выш выбор.</p>

]]></content:encoded></item></channel></rss>