Develop fully on-chain, generative Ethereum NFTs using Solidity: Buddy Buddy TagsšŸ¤

BeautifoolData
8 min readSep 25, 2021

--

view all buddy buddy tags on opensea

buddy buddy tag NFTs are a brand new project put together to experiment with completely on-chain generative development. you get one for yourself, for your friend, foreveršŸ˜¹. as of this point, there are 13 minted tags, with 7 of them confirmed!

what does that mean? well, buddy buddy tags are on-chain, generative NFTs that require a minter to submit a buddyā€™s address when minting. the minter receives a randomized set of emojis along with his wallet address and his buddies. think of it like a fortune cookie, you donā€™t know what youā€™ll draw, and the interpretation is up to the minter and his buddy.

buddy buddy tag #2 ā€” 0xd9e3ā€¦ tagged 0x89d6ā€¦(BeautifoolData, i set up an alias!) and received this NFT immediately.

if the buddy heads to the website and confirms the tag using the token id, the NFT gets snazzed up with a colorful border. as a bonus, by confirming the tagged buddy gets added to the allow list and can then tag their own set of buddies.

once a tag is confirmed, the colorful border is added and the tagged user is added to the allow list for minting

this article will be a review of the creation of 0xC321Aa2Cb7a0336aa829eA153B4eA422F5d806c4, the buddy buddy tags contract.

i hope that newer ethereum contract devs find this useful. experienced solidity writers may not find this interesting, but i would love to hear any feedback or suggestions!

as you could guess, this is a loot derivative with special features, so the contract framework starts there. for a great write-up on loot, check out the Phemex medium post. on line 1314 we have the start of the meat of the contract. everything before this is boilerplate erc721 contract stuff.

here are the variables and purposes:

launched ā€” prevent anyone from interacting before i am ready
emoji_list ā€” the list of emojis that will be randomly pulled for the NFT
allow_list ā€” addresses that can mint
buddy_tagged ā€” addresses tagged by token ids aliases ā€” pretty strings for addresses
alias_unique ā€” check to see if string is taken
confirmations ā€” address to address confirmation check
CONSTANT values are self explanatory

first, i wanted to enforce an allow list. this was done in the can_addr_mint function

it checks an inputted address against three lists ā€” the contracts internal allow_list, the owners of acclimated mooncats, and the owners of fourierpunks. simple! (if you want to mint but are not on the allow list, reach out to me on twitter and tell me youā€™ve read this section. iā€™ll tag you as a buddy, and when you confirm youā€™ll be able to mint)

next, i needed a mint function

to make it slightly more interesting than the typical click-and-mints, i wanted a way to enforce some social aspect. so, the minting function requires someone elseā€™s address as input! the first few requires make some standard checks.

  1. contract must be launched
  2. you canā€™t tag yourself
  3. you have to pay the right amount of eth
  4. theres a supply limit to tags
  5. you must be allowed to mint

once those conditions are met, the usual _safeMint is called, and the buddy_tagged data is updated

so what does a mint do? it creates an NFT and transfers it to you, the minter. but what does that NFT look like? thats what i needed to deal with next. thanks to loot, i had a good base for a way to make something thats completely on chain. the display of the token comes from the tokenURI function.

this function overall will be hefty to explain because there are some functions in here that were created while tuning the output. iā€™ll try to give a brief overview of everything in here first, then go into more detail of those later.

first, if the token is not minted as checked by the totalSupply, it returns an error. an easy way to prevent lazy contract URI sniffing.

the parts string contains segments of the svg. these are concatenated at the end. harmony is randomly drawn for the type of color palette, and four colors are drawn. then the 17 segments of the SVG are built out. the SVG is two squares atop each other, the one behind has a pattern activated once confirmed, giving the colorful border. this is the check for segment [9]. the current NFT owner always shows at the top of it, as shown in [11]. the randomly drawn emojis appear in [13]. the tagged buddy is filled in at [15].

concatenation of all the string parts happens by a few abi.encodePackeds. then this is base64 encoded, and put into a metadata json object which is also base64 encoded. genius! had no idea opensea would be able to natively read this. note that in this space, you can write more data that you can make conform to the opensea metadata standards and have that show as attributes in their UI.

ok, now to fill the detail in some of those functions. lets go in order

getHarmony, getColor, and getTokenColor work together.

the first really just pulls a random number 0ā€“4. as you can see, those determine the relation between the colors that will be drawn. i took some very basic color wheel relations ā€” complementary, monochrome, analagous, triadic, tetradic. these are easily incorporated into SVGs since they can be set with HSL(). getTokenColor is used to get the initial HSL values, and the other colors are relative to that.

getAlias makes a quick check into the contract data to see if the address has saved value. if not, it shows the 0x address.

really quick, the aliases are set through a payable function set_alias

makes a few standard checks as weā€™ve seen so far, as well as tests for validity and uniqueness.

back to the functions from before, getEmojis is the function that randomly draws the 1/2/3 emojis for the NFT

random numbers are all over the place here. so, emojis. theyā€™re unicode characters. theres a large list of them on the unicode website. the more space you use on a contract, the most gas it costs to launch. for this reason, i launched with an empty emoji_list and updated the values via transactions later. in order to save a bit of gas, i only used emojis that fell in to the format ā€œ&#Fxxxā€ where xxx is three letters or numbers. this way, i could upload a long string made of concatenated 3-char emoji codings. so thatā€™s what happens in pullEmoji. we pull a random number, mod it by the emoji_list string divided by 3 to get the Nth emoji. then we look back in the string to get the three characters. it works, probably could be better, but i was happy with it.

the rest of getEmojis is the rarity. 1 / 200 times you get a single emoji. 4 / 200 times you get a double emoji. emojis also pull with replacement, so it should be possible to get a triplet, but i doubt it. its also possible that clones will come out. weā€™ll see.

how was the emoji list updated?

add_e! before the contract launched, i left a function to update the string. if i messed up, i left something to clear out the list. luckily, i didnā€™t have to use reset_e. i attribute that to ā€œgood, non-automated methodical testingā€.

everything up to here had be done before launch. what happens when we launch?

it flips the boolean and sets the LAUNCH_BLOCK.

at this point i realize iā€™ve been referencing a random function

it concatenates a string and the LAUNCH_BLOCK, hashes it, and casts it as a numeric. it was important to me to have this LAUNCH_BLOCK as an extra source of randomness so that i couldnā€™t know what emojis/colors would come out.

oof, i also forgot to mention confirmations.

to ā€œproveā€ the minter is a buddy of the person they tagged, i added this. it makes sure that the caller is indeed tagged in the token they pass. if so, it will update the confirmations mappings. as incentive to confirm, i also add the confirmer to the allow list. now they can mint! how fun.

sort_addresses was included in there to save some space. i think it saves space, you only have to store two addresses once this way.

that function is used in check_if_confirmed as well. i look back at this function often since launching. i ask myself, what was i thinking. why is this internal. this would be such a useful external function. poor design decision here. fortunately, theres a way to back into whether a pair of addresses is confirmed. you can check the tokenURI data!

the final function is withdraw.

in this contract, 25% goes to a donation, the rest goes to my wallet. im not sure why more contracts dont do this, bake it right in the contract if youre donating.

lastly, probably the most important thing of all when writing your own custom contract.

ascii art

thats all! any questions or comments, let me know on twitter @BeautifoolData! thanks.

--

--