buddy buddy tag nfts

the contract creation

this will be a review of the creation of 0xC321Aa2Cb7a0336aa829eA153B4eA422F5d806c4, the buddy buddy tags contract. experienced solidity writers may not find this interesting, but i hope it will be useful to some. as you could guess, this is a loot derivative with special features, so the contract framework starts there. 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! 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 "F---" where --- 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 soruce of randomness so that i couldn't know what emojis/colors would come out. oof, i also forgot to mention confirmations. it's late. 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 confimrer 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! thanks.
socials https://twitter.com/beautifooldata https://discord.gg/hBPTbG2sq4