Это первая часть серии статей о разборе контракта Cyberkongz NFT.
Cyberkongz — это генеративный НФТ, который имеет коллекцию из 1000 уникальных и случайно генерируемых 2D пиксельных горилл (известных как kongz), торгуемых на открытом морском рынке. Эти первые 1000 отчеканенных конгзов известны как genesis kongz, которые имеют возможность размножаться друг с другом, чтобы расширить новый набор конгзов, известный как baby kongz.
Эти пикселированные конгзы размером 34×34 не просто потрясающе подходят для использования в качестве фотографии профиля на любой социальной платформе, но также имеют огромное сообщество и много полезного вокруг них!
При размножении двух генезисных конгов рождается детеныш конга, а также 3D воксельный конг, который может быть использован в качестве социального аватара в метавселенной.
На странице «О сайте cyberkongz» дается подробное объяснение всей экосистемы токенов и токеномики.
Мне просто нравится, как им удается изысканно сочетать свои NFT с DeFi и metaverse.
Контракт Kongz
Важно отметить, что все функции, описанные здесь, относятся к контракту kongz. Любое упоминание функции, которая не входит в контракт, будет отмечено.
Каждый конг проходит через 2 основные фазы роста
Инкубация: Есть два основных места, где конг проходит инкубацию в контракте. Первое — во время начальной чеканки генезисных конгов в конструкторе, второе — после выведения нового конга.
Эволюция: Это часть, в которой конг развивается; это означает, что конгу присваивается новый ген, который имеет другое значение.
Контракт kongz в основном содержит 5 ключевых функций
- Возможность чеканить новых конгов.
- Разведение между генезисными конгзами
- Восхождение конгов
- Возможность эволюции конгза
- Изменение имени и биографии конгза
Конструктор
В начале контракта владелец контракта начинает с того, что добывает для себя 3 токена генезиса, как показано на фрагменте ниже
contract Kongz {
...
// data structure representing each kong
struct Kong {
uint256 genes; // the value 0 implies that it's a genesis kong
uint256 bornAt; // when the kong got minted
}
constructor (
string memory _name,
string memory _symbol,
string[] memory _names,
uint256[] memory _ids
) public ERC721Namable(_name, _symbol, _names, _ids) {
// cyberkongz uses heroku to host their metadata
_setBaseURI("https://kongz.herokuapp.com/api/metadata/");
// owner minting 3 kongz for themselves.
_mint(msg.sender, 1001);
_mint(msg.sender, 1002);
_mint(msg.sender, 1003);
// saving state of newly minted kongz
kongz[1001] = Kong(0, block.timestamp);
kongz[1002] = Kong(0, block.timestamp);
kongz[1003] = Kong(0, block.timestamp);
// emiting events for newly minted kongz.
emit KongIncubated(1001, 0, 0);
emit KongIncubated(1002, 0, 0);
emit KongIncubated(1003, 0, 0);
// handling the total supply of kongz.
bebeCount = 3;
}
...
}
Выращивание конгза
Эта функция отвечает за создание и вынашивание детенышей конгза при условии, что у владельца есть два генезисных конгза, которые будут служить в качестве отца и матери.
Более подробное описание добавлено в комментариях.
function breed(
uint256 _sire,
uint256 _matron
) external {
// confirm that msg.sender owns both NFT's serving as sire and matron
require(ownerOf(_sire) == msg.sender && ownerOf(_matron) == msg.sender);
// classified breedManager attempts to breed kongz if breed is successful
// then function continues execution else it terminates.
require(breedManager.tryBreed(_sire, _matron));
// the equavalent amount (600 BANANA) required for breeding gets burned from
// msg.sender balance.
yieldToken.burn(msg.sender, BREED_PRICE);
// update token supply after birth of baby kong
bebeCount++;
// create ID for new baby kong
uint256 id = 1000 + bebeCount; // 1004
// nice! Baby kong is also from gen 0
kongz[id] = Kong(0, block.timestamp); // should confirm this from team.
// msg.sender is a proud owner of a baby kong
_mint(msg.sender, id);
// msg.sender announces to the world his new baby kongz alongside the parents.
emit KongIncubated(id, _matron, _sire);
}
Заметные изменения
- Было сожжено 600 БАНАНОВ для того, чтобы произошел процесс размножения.
- Обновлен запас жетонов
- Малыш Конг добавлен в реестр kongz с новым сгенерированным идентификатором.
- Малыш-конг наконец-то отчеканен и назначен своим новым владельцем (msg.sender)
- Малыш-конг инкубируется
Эволюция kongz
После инкубации детеныша конга остается последнее, что необходимо сделать для его полного перехода — это эволюционировать. Эта функция обрабатывает процесс эволюции малыша конга, передавая его ID
в качестве параметра методу tryEvolve внутри breedManager (который держится в секрете от всего мира), который возвращает новый ген для конга.
// @param _tokenId uint256 is the Id of kong to evolve
function evolve(
uint256 _tokenId
) external {
require(ownerOf(_tokenId) == msg.sender);
Kong storage kong = kongz[_tokenId];
// kong must have a gene of 0 before it can be evolved.
require(kong.genes == 0);
// breedmanger tries to evolve the kong at the given tokenId.
uint256 genes = breedManager.tryEvolve(_tokenId);
kong.genes = genes;
emit KongBorn(_tokenId, genes);
}
Предположения: Поскольку у меня нет возможности увидеть, что именно происходит внутри метода breedManager.tryEvolve
, я предполагаю, что метод использует tokenId для создания нового гена, который будет переназначен развивающемуся конгу.
То, в чем я все еще не уверен: Я все еще не до конца понимаю, что происходит после изменения гена конга после его эволюции. Получает ли он какую-то особую полезность или что-то еще? Пожалуйста, дайте мне знать, если у вас есть лучшее понимание этой функции.
Восходящий конгз
Поскольку первое поколение kongz было создано и хранилось непосредственно на Opensea, команда cyberkongz решила перевести все свои токены с Opensea на собственные смарт-контракты. Миграция токенов, созданных и хранящихся непосредственно на Opensea, на смарт-контракт известна как — восходящая миграция
function ascend(
uint256 _tokenId,
uint256 _genes,
bytes calldata _sig
) external {
// confirms that the tokenId conforms to Opensea's ID format (REF 1)
// Opensea ID format -> 73424079983647210902572285069973579475843508985221180214989722260978404425729
require(isValidKong(_tokenId), "Not valid Kong");
// extracts currentId format from opensea tokenFormat (REF 2)
uint256 id = returnCorrectId(_tokenId);
require(keccak256(abi.encodePacked(id, _genes)).toEthSignedMessageHash().recover(_sig) == SIGNER, "Sig not valid");
kongz[id] = Kong(_genes, block.timestamp);
_mint(msg.sender, id);
OPENSEA_STORE.safeTransferFrom(msg.sender, burn, _tokenId, 1, "");
yieldToken.updateRewardOnMint(msg.sender, 1);
balanceOG[msg.sender]++;
emit KongAscended(id, _genes);
}
REF1 — isValidKong: isValidKong
— это служебная функция, используемая для проверки соответствия идентификатора токена kong формату идентификатора Opeansea. Поскольку объяснение этого формата выходит за рамки данной статьи, я оставлю здесь пост, который объясняет его правильно.
function isValidKong(uint256 _id) pure internal returns(bool) {
// making sure the ID fits the opensea format:
// first 20 bytes are the maker address
// next 7 bytes are the nft ID
// last 5 bytes the value associated to the ID, here will always be equal to 1
// There will only be 1000 kongz, we can fix boundaries and remove 5 ids that dont match kongz
if (_id >> 96 != 0x000000000000000000000000a2548e7ad6cee01eeb19d49bedb359aea3d8ad1d)
return false;
if (_id & 0x000000000000000000000000000000000000000000000000000000ffffffffff != 1)
return false;
uint256 id = (_id & 0x0000000000000000000000000000000000000000ffffffffffffff0000000000) >> 40;
if (id > 1005 || id == 262 || id == 197 || id == 75 || id == 34 || id == 18 || id == 0)
return false;
return true;
}
REF2 — returnCorrectId: Эта функция отвечает за получение tokenId в формате Id от Opensea и извлечение из него только tokenId. Если эта функция вас смущает, ознакомьтесь с этим постом на Medium, где прекрасно объясняется формат ID Opensea.
function returnCorrectId(uint256 _id) pure internal returns(uint256) {
_id = (_id & 0x0000000000000000000000000000000000000000ffffffffffffff0000000000) >> 40;
if (_id > 262)
return _id - 5;
else if (_id > 197)
return _id - 4;
else if (_id > 75)
return _id - 3;
else if (_id > 34)
return _id - 2;
else if (_id > 18)
return _id - 1;
else
return _id;
}
Изменение имени и биографии kongz
Cyberkongz — это действительно первый проект NFT, который я лично видел и который позволяет владельцам изменять имя и биографию NFT, давая им свою собственную уникальную историю. Тем не менее, эти возможности также приходят ценой нескольких БАНАНОВ, как мы скоро увидим.
Фактическая реализация контрактов changeName и changeBio находится в другом контракте под названием ERC721Namable, который был унаследован от контракта kongz.
function changeName(uint256 tokenId, string memory newName) public override {
// msg.sender pays the cost of changing the name by burning the equivalent
// amount of BANANAS required (300 BANANAS)
yieldToken.burn(msg.sender, nameChangePrice);
super.changeName(tokenId, newName);
}
function changeBio(uint256 tokenId, string memory _bio) public override {
// msg.sender pays the cost of changing the bio by burning the equivalent
// amount of BANANAS required (100 BANANAS)
yieldToken.burn(msg.sender, BIO_CHANGE_PRICE);
super.changeBio(tokenId, _bio);
}
Дополнительная информация
Контракт ERC721Namable довольно прост для понимания, потому что он существует только для того, чтобы решать функциональные задачи по изменению имен и биографии ваших токенов Kongz NFT.
Вы можете ознакомиться с контрактом здесь, который начинается со строк 1872 — 1920
Далее
В следующей части серии мы собираемся изучить контракт YieldToken (BANANA), который является токеном ERC20 в качестве газа для расширения функциональных возможностей и вознаграждения держателей токенов.
Этот контракт показывает, как Cyberkongz внедрила DEFi в свои NFT.
Заключительные мысли
Изначально я писал этот разбор для себя, поэтому, пожалуйста, дайте мне знать, что вы думаете об этом стиле разбора. Какие вещи я упустил и как их можно улучшить?
Несмотря на то, что это мой первый пост, пожалуйста, не сдерживайте себя. Спасибо