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

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

</content></entry></feed>