vl\xd3\x1e\x8e\xf5~\xac\x97\x80\x8a\xef\xea\xa6\xea\x1e\xa1\x12\xcb\xf2R\xc6\x1e\x11G\xd8\xe1\x81\x91(\x1f\xfb<\x1a|\xa8\xf9D>\xf5L\xde\x84;~z\x11J\xff\xa2y<\x83_\xdbO\xec#\xeb\xf0b\xdc\xf7Wo(p\x11\xed\x80\xb8\xa0\x97*\xac\xdf<\xb0\x03\x83\xbf>\x0c\xc3\xf1\xaf\xcf\xf0\xbf\xfd_\x9fA\xdbA\xd3\x8a\xbf>\xe3#j\\M\xe4.\x1d\x8f\xe3\x84\xba\xd3q<\xc5\x91\xb7\xef\xf29\xf0\x91_\x03\\\x0dp\xa8\x8e=\x0e\x8f\xb1\xe6cK\xa5\x1f\x8f;$k\\r\xab^x-\xfa\xe7\xe4\x97\xf9;\xb8\xb8\x9f\xeb9~Ny\x9f\xf2\xd4\x14a\xf7N\x07\xb6%/\x07\xff;x\xd1\xc0\xaf77\x97\xf0\xcb\xf9\x0d\xb4\xcdt\xe0\xe1\xd3\x0d\xaf\x13\xae\xccKFo\x1e\x8f\xec\xdf\xfe\xd7\xbf\x11\nAzW\x1b9\x12\xd0n\xf2>=v\xed\xf6\xb4\x19\xf7\x8b\xc0\xba\xae\xed\xa8\x9b\xa3\xff\x0e^\x1c\x8f\xfbzS\x89>\xe8\xd88\x96\xdaO\xb8\xfb\xdeT\x9bq.\xb7\xed\x87\xd3qrb\xe2J\xdc\x9a7\xff\x83\xc5\x1c\x01^\xa9\xdcv|;3>tP\xc6\xf2\x16\x07s%\x1b0\xfe\xfbc[o\xa1\"\xae\xfc\x07~\x02\x19\xab\xc3\xa7k\xc7\xee\xdb\x8e=\x93\xaf\x8e\x1a\xab\xa1\xbe\xab\xf7\xf5\xf0\x08\x0dc[\xe9.\xe6f\xa5\xfbh \xdch\x9b\xd1|5;\xc6\x1f\xe7\xb3\xe6\x0c~x\xdf3\xf8\xc8\xba\x1e\x9dM\x83\xbc_\x1a\xc7O\xd5T;\xba\xadw\x1d\xab>p\xff\x12\xaa<\xfb\x91\x1a o\xdb\x81=\xc7\xb3\xc6\xfd\xa9\xd9\xe0H\x1fk-\xac\xc4\xe6\xd4u\x1cUP\xbd\xf1\x8d\xa5\x83[\x0eB\x98nx\x90\xd6\xfe\xeet?\x1e\xf0X\xd5\xb3g\xfc\xee\xdez\x90\x05\xf1\x0d\x1b\xf7\x0bO\xf3\xe3\x8e\xed\xea\xa6\xa1\xc3\x19\xc6%\x964\xce\xe6\x15\xb6\xb4}\xbb\xe6\xb3\xa7G\x08`\x9c\x9e\x8dn\x0f\xe0\x07\x91\x0e\x87\x18\nN\xb7\x1f\xe1P\xef\x1e(+pGN\x7f\xde,\x1e\xbeP\x1f\x8e{v\x98\xae\x1b\x16~\xd8\x0d\xf4\xecP5C\xbd\xe9\xf5iA\xec\x15\xc0\xb7\xc4[\xb1)\x08Z\xff\x7f\x1b\xa7\xff\x1d\x83j,\xbc\xde*\x0b\xb8\xb1Z\xcb3\xe0]\xfb\x91\\\xfa\xa7\x00\x1b\xfeE\x16O\xb8\xeb\x81\x97\x8b\xcfH`\x03UwW\x0f\xfcnxG}\x84%\xd5\x94U\xfb\xb6\xd9\x89\xbb\xc4\xf5\xcf3\xda;n\x96\xb1>w\xe6\x96F-O\xeeN\x8c\xc1t\xa9_`/\xacq\x0f\xfd\xe9xl;\xbe\xa2\x1d\xab\xcd\x87\x9f\xf1^\xf9q\x1d\xc3o\xdb\xd33\x89Z\xca\xdb{8\x0dhN\xe44\xedGC\xa6\\\x86\xce\x1d@\xdc]1\xddS\x8f\x8dxa\xd8/\xfc$z\x19\xf2\xa2\xe9\xbf\x7f\x0e\x97c=\xc7\xf9)\xaa\\M\x0bU\xdd\xc0\xcb\xff\xfa_\xc9E\xe5u\xdb\xc2}\xdb\xc2?\xc2\xd9\xd9\x19\x05/\x8d\x0d\xaf\x9aG\xeaOU\xf3x6\x16\xfa\xbak\x0f?\xdc\xb7\xed\x8f\xd4Cgg\xd4\xcaQ\xdf\xc3\x0f\xe3\xeb\xefyUo\xda\x1f\xfe\xcb\xf8\xfe\x8f\x96\xeb\xaei\x1d\xc6%\xe4\xb2/\xfe\xec\xe9\x8b\xffV}\xac\x92:\x03\xfe\x91\xefkF\xcd\x91\xed\xae\xfb\x1f^\xb7\xed\xd9f_\xf5\xbd\xb5\xd9X\x95\xf1ql\x85\xf2\nU\xa2\xb5?\xfe\xc1\xd3\x1f\x97\x8f\xc3\xc3x\xd4&tb\x15^\xb7\xed\x0fgggT\x1a\xc7\xd4\x1b?X\xfe\xcaG\x08\xef\xa55\x9d4\xbex\x81}\xf4\xea\xfc\xfa\xe5\xd5\xc5\xe5\xcd\xbb\xab\x1fM\xbb\n\xa2\x10\x1cG\xb6b\xb0 K\xef\xfc\xdf\x9e\xde\xf9\xa5%\x03\xdc\xc6\x9ey\xfe\x8f\xf0_\x8ewg\xaf\xdb\xf6\xff;;;\xd3oG\xe7R5\x8f\xcf\xc6\x0d\xd4\xf8l\xd5<\x1e\xef\xce\xde\xb2O\xd6z\xd6\xf7\xfc\xd9\xff\xeb\x1f\xa1\xa9\xf7\xb6\xbb\xdf\xe9N3.gw=\xabU\x9e|U\xd4\x05\xeb}\xf6\xbe9T]\xffP\xedoZ\x1c\xf2)\x15\xd5~\xbd\xe1\xae\x9b\xcd\x87\xc9\x06\xca\xad1\xdc=\xce[\x10i\xa9\x11\xa7{\x94\xf0\xeb\xb8X\xeb\n\xffDl(~\x1eO\x7fg\xfc\x0f\xe3F\xecO\xe3\x8e}Z9\xc6Ue\xfc\xfe\xa6]\xc7\x11\xa1\x170\x19\xebf\xff(\xcf,\xc6\xf1r\xda\xf4Au?0\xdc\x95\xf0\x93\xed\x9f~\xfe\x93\xaeP\x1c\x9cde\xf0\xa4\xc4\xc4\x08\xfd\xfe\xbem\xcf\xee\xaa\x8e7\xe3\xf3\xcf\x8fg\xff\xe7{\xec\x05\xdc\xf3S\x87\x18^\xec\xf7\xe3\x93\xe3\x92\xa1\xfd\xf1\xbf]\xbf{k\xfc8~\x84\xf1\x0f\xf31\x1a\xb7<\xed8\x19\xc5\xfa\x8e\xc7\x85S\xcfdx\xc0\xee\xb4\xaf:]\x93\xa9\x00q\x8fy]~6G`\x88\x89\xf6L,\xf7\xc6\xe1[Y+\xefyk\xff\xfa\xff\x8e\xed\xfd\xab8/.\xdc\x7f\xb2\xf7\xce\xe4\xdc~N\xeei\xab\xcd\x87qj\xcf\xe7\xa4\xfbz\xcf(\x83*\x8d\xc0%\xeb\xfa\xb6\xb1\x8cr\xe1\xf2\xb8\xaf\xbb~\xb8\xe5\xddn\x8b\xd3\x90\x8f\xf2 1\xf1\xe4\x9f\x83\x0c\xf9(t\xe9\xdf\xf3\xde\xf8\xfe9|O\x0d\xf9e\x13\xcf\xb0\x1d\xdf?\xa35\xf1\x16\xbc\xad\x0e\xa3\xb6\xff\x07\xab\xfaO\x96G\xc7\x16hO\x864\xe3\xe2^\xec\xcc\x97\xdf\x1e\xbf[\xdd\xc3'\xb6\xdf\xff\xf4\xa1i?5|2>pO\xf0\xe6\xd4\x0f\xedA\x8c\xd9\xa5\xbe\xe5@{\x86\xfbBm\xf4\xcd!U\xa2\xd0q@5;\xc31\xc3\x07\x97^\xc0_\xf9\x80\x97c\xed\xa1\xddoq\xa0)u\xe2N\x1e1FA\xb8U\xc4\x10\xd5\xb5\xf1\"\xa6\xb1 ?\x8cS\\v\x81qj\x97\xae\xa5\x7f\xfb_\xff\xf6#9\x8c\xd3\xc6\xc3\xb2\x10\xdb\x90\xe0\xcd\x1f\x95\xfd\xfd\xd9\x9f\xff\xfe\xcf\xfd\xf7\xe4G\x9e\xff\x9d~\xaf\xa1t\xc6~\xa7=%\x9d\xf2\x1a\x94Z\x0d\x15\xe2\xa8Jt\xe1R\x87x\xff?\x161O\xba\x8b?\x84We\xaa\xc1\xd5\xe5K\xa1K\x0fq\xb2c =\x82\x15\xab!\x85\xa3\x82g\x91\xaf\xd0\xaf\xc1\x97\x03\x14\xc6N\x9a<\xf8\xfd\x84\xc0T\x1d\xc3\xc3\xa8\xa2\xa6c\xbb\xba\x1fX\xc7\xb6\xb7&T\xa2\x94\"\x80\x1d\x05R\xbe\x9a\xde\x94\x1d\xaa\x01^D\x03W\xb9\xe6\x0d P\xca@\x02\x82\xd3_\x97\xc0\xa0\xb7\x9aV\x88p\x94\xfb\x8e\xb1[\x93\xc1\xc4\xda)\x16\xec\xef\xdb\xe8\x8a\xb5\x18\xa9\x14\n+\x9d\x8a\xb0\xa2=\xe0D|\x002a\xa8Rt,u*$\x0d\xc6Y\x87\xb1\x12\nb\xc1VB\xd5\x12~\x05p\xc7\x85hf\x8e\xc4d\x95\x91+\xcd5a\xad\xbe\xfb\xce\xac\x03\x0ei\xa7!\xd7\xeckx\xf8\xeaR\x01\x19\xb8\xea\xb0\xea\xab\xed\xf9\xa4K7\xe9Aq\x1f\xbf\x13\x9a,\x1f\xa4lM\xd3\xcbV\xb7\x08h\xd9\xce\x8e\x95\x81\xb3+\x00b\xb68\x8e\xad\x06\xdc\x7f\xe9\xb1-\xf1\xdei\xa5B\xecTv\xc0\xd9\x079\xe7\x06\x9d3\xc3\xce\x1e\xe09\x19z\xce\x0b>\x87\xc0\xcf \x00t^\x08:\x08\x84\xce\x0bC\x07\x00\xd1\xd9\xa1h\x0f\x18\x1d\x07G\x93\x8a\x9c\x10u\x16\x90:\x10\xa6&\xdf\\\x05]'\x83\xd7\xb9\xe1k;\x80\x9d\x19\xc2~\n\x10;3\x8c\x1d\ndg\x86\xb2\xdd`vv8\xdb\x0eh\xaf\x80\xb4\xe3AmR\x19\x07\xba-\xb0v\x12\xb0m\x85\xb6\xbd[\n'\xbc\x1d\xb6\xe3\xc8\x07q\xbbAn\x7fm\xb2\x02\xddn\xa8;\x1b\xd8\x9d\nw\x1b\xea\xf8\x8e\x86\xdc<\xe4\x85\xbcm\xa0w:\xec\x1d\x80\xf5:\xa1\xef@\xf0\xdb\x8a\x81\xad\x04\xc0\xedz\x08\xac \x19\x06_\xd39!P\xb8\xbf\x17\x82\xe0\xf0\xd5\x80\xb8\xb3w\x12@\xf1\x00X\xdc\x07\x8c\xfb\xa1qg\xa7\xad\x81\xc7\xc3\x00r\x12\"O\x06\xc9\x83a\xf2\xb5@y Tn\xefD\x1a.w<\x1f\x02\x99\xe7\x00\xcd\x9dU6~O\x82\xce\x0dm\x04\x94\x9e\x15L\xb7\xc1\xe9\x89\x80\xbaYe\x13`\xcf\x0f\xb1{@v\x0b\xcc\x9e\x13h\xcf\x0c\xb5?\x05\xd8\xbe\x06n\x0f\x04\xdcWA\xee\xe1\xa0\xbb\x05v\xb7\x01\xad\xe1P\xab\x1fz_\x05\xbe\x07\xc3\xefd\x83rC\xf0yAx\x0b\x0c\x9f\x1b\x88\xcf\x0d\xc5\xa7\x8f\x91 8>\x0c\x90\xd7W\xb6<\xec\x9e\x060\x9f\x0e\xcd\x17\x06\xa0\xc2\x00T\x18\x80\xbe\x06\x03\x90\x05D\x0d\x82Ou\xe0te<\x8c\x1e!\x11\x01\xa1:\xa2HV\x18\x8f\xaf\x10\x1f\xc3iNultn\x8f\xa2\xf1\x89\x12]eI\x945\xef\x1f\xdanx\xa8\x9a\xed\xed\xa6\xdd\x92\xc9\xae\xf6\x18.q\x0f\xd0\xa4\x02F\x15\x14\x15\x03Q\xae\xe4\xdf\xb0\xe6\xd8\xda\x8b\xc5\xc0\x08\xbe\xdf\x98a/\xa1\x8fm\xc7\xf9\xdb\xc0\xa9G\xf2\x01\xaap\xca\xc0\x99#\xd4\xc1\xde\xe1\xeeQ\x01\xf6.\xdczS\x9c\x03\xd8g\xa3m\x8a\xac\x89j0\x1a\x11\x1a\xd9\xa0\xbf\x18\x13\xe3`L\xd0?R\xb8C\xdc\x84~\xe2\xf0\x02\xd70tO\xed\x90\x9d`\xca\xf4vN\xf0\x90\xc2S'\xb9m\x1f\x93q\xa2\xd3S]\x9b\xece\xb7[v\xbbe\xb7\xfb-\xecv\xadK\\\xca\x02\xdb\xfbW\xd8\xab\xc5v5`%\xcd\xc5\xe5\x95\x1a\xb4\xf7\x044]\xe1\xc1y\xd9x\xba\xca\x9a/\xd6\xdc\x18z\x1a\x1b5\x0dm\xbb!$6+\x9d\x8e\x86\xa2\xa2!g\x00x\x91\xffu\xf43\xcbwc\x99g\xa6\xf6\xa1B\xdd\xa8'\xf2\xcb\xe8\x01\x18\xa4=:\xb6\xdd\xc0\xad\xd1Z\xe33T\xcd\xb6\xea\xf8\xc4\xef\x9f<\xc4\xe9I\xa3\x87\xc6\xfa\xab\xd7\x01i\xdd\xa6\xbeGV\xc2\x15\xbes3\xfegY\x94uL\xaeVN\xa4\xe7*\xb7s\x9d\x7f\x1eX3\xee\xfd\x952\xb4F\xcb#\xfa\xa6\x1a\x0f\xa9\xb5\x99HT\x1d\x8f{\x8e\xb4\xaa!.t\xe9\xd7b@\\\xa1j\xdbFW<%k\xc0\xe7\x1bN\x8b\x85\xb6c\xbd\xf90N\x92F\x03o\x11\x1e\xe8\xda\x03l\xeb{~\x9d\xdd0}/\xc5\x90\x125|S\xe3E\xfa3n!F.\xaa\xc3\xd5\xaea\x9ffu\xbc^cE\xc66\xd4\xea \x93\xf9c\xcaOm\xc3\x16DM\xd4T=\xb4\xdb\xd3^\xccX6\x1a\x04r\"\xf2\x10\x8b\xaf@\x98g\x9d\xd0`\x99\xd4@Ml\xb0On\xf0\xb0\xf6\x98\x93\x1c<\x13\x1d<\x93\x1d\x12&<\xd8&=\xb8\xe7&\x84O~p\x1b\x00\x08.\xc8\xc2\x1a\x90`\x0cH}\xf5@\x1b\x04p\xd6$\xcd0\x18\xea\xee\x98\xd58@\xb0\x81\x00G\x8ds\x1a\n\xa0\x8d\x05\xa8\x06\x03\xd6\x1b\x8d\xe9\x05\xc2V\xd8\xa3\xb80vK\x94A\x87k-\x8d\x10~\xb1\xd5F\x08\xbb+\xce\x08}\xad\xac\x84\x1e\xc3\xaft\xce\xce'JD\x080@\x84\xe9\xb1\xf6A\x90\xb9\xb1\x1a\x1a\x9fZc\x82$\x98\x15s\x8fA\x18\x14Q\xae\xd5^\xc8+\xf5q\xb8bh\xe6\xd4Xub,\xc7\xafob\x88\xf2\xc2'\xc6\xfa\xe5\xf9\x8b\xec\x95\xbfr:\x80e&\x95=<\xad\xfc\xeb\xee\xe1\xe3\xe6X\x89\xe5/\xb1\xfc%\x96\xffk\xc4\xf2S\xabO\xd8\xba\x16\xba\xe3\x1b\xff?b]\x1b_\x8b2\x05_a\xc7\xc7D}gpj\xd3\xb1\xa0\xabV\x13k\x83\xc5*\xcf\x88\xfd\xc0m\xbdM?n\xaf]Fm\x8b\xa8X\xc7\xc9\x0eR\x10\x10\x98\xe7D`O\x89\x02\x95\xd5\xed7\x19\xfb\xc7o(_`kFG\x8d?\xb0\xb5w+,o\xe4\x96J\x94G\x87\xaa\xdb1\x9dq\xde\xe6\xbd\xb0\xfb.\x86r\xb5\xad!\xd9R#\xc0\xc70<\xc4\xa7H@\xc64 \xf0\xa6J@l\xba\x04\xa4\xa4LP=\xe6g\x15\x8eN\x9d ty\x19\x85SR(\x08u\xe5j\xdb\xb4\xb4\nHK\xad\xa0\xa7(Y\xc9l \x17\x90=\xe9\x02r&^@P\xf2\x05\xe4L\xc0\x80r\xb5-ULd\xe2\x06\x94\xabm\xe3\x12;4e\xe5j\xdbr\xb5m\x80\x8er\xb5\xedR\xca\xd5\xb6\xe5j\xdb\xc0g}i'\x90\x9cz\x02\xe5j\xdb!09\x052'\xa8\x80\x8fu\xb7\\m\xebO\\\x81\x90\xe4\x15\x08O`\x81\xc0$\x16(W\xdb\x06%\xb8@j\x92\x8b\xa6\xab\\mK(\x0b`\xd2].\x1d\x12\x0c\xe5\x1eS\xabG\x9b;\x8a\xeb\xb6\xb9\xddVC\xa8cX\x9e\x91\xc6W~\x1a\xea\x03\x05\xda\xc2M}\xe0K\xb6\xe1\xc4\xffT\xf5\x86\xff~B7\xf9\x13\x96\x88#\xbe?\xaf7j\x1b\x96(\x8b\n\x85\xf8A\x16^T8\xc6\x12\x13<\xc0\xdf\xfb\x83\x06\x0f\x84\x802\xf6\x18\x82\x0c\xb52\x86\xb2\x03\x9e\xb1\x034\x16\x88\xc6\x0d\xd2\xc4E;\xac\x00j,PMH|B\x14\\c\x03lB\n\xf4\x8364l\xe3\x00n\xdca\xa7C\xb9,\xb2\\\x16Y.\x8b\x8c\x01v\x02\xa0\x9d\xec\xe0N\xb9,\x12e\x15\x18\x94\x0c\x07\xe5\x06\x84\xcae\x91\xaa\x84AC\x99\xc1\xa1rYd\xb9,\xd2\x03\x1a\x95\xcb\"\xd7\x03H\x86\xba\xa1\\\x16\x19\x06'\xf9\xafI\x0c\x83\x94\xcae\x91\\VBL\xe5\xb2H\x94\x10\xc8\xa9\\\x16\x89R.\x8b\xd4\x7f\xcc\x08O\x95\xcb\"\xb3BW\x99\xc1\xab\xa7\x80\xaf\xd6\x00X\x81\x10\xd6*\x10+\x1c\xc6*\x97E\xc6\x80Zya\xadrYd\xfe\xcb\"Ca.'\xd0\x15\x04J\xd0`W\x1c\xdc\x15\x0fx\x95L\xbe\x92\xc9W2\xf9\xbeb&\xdf\x02\n\x0e\x03\x99\xbd\x99|W\n\xff\x821\x95\xbf\x1a\x8d\x9c5;<#\x11\xa4'\x0b\\\xcb\xff&\xdb\xe8\xcd\xf9&\xb3\xbd]\xaa\x16\x937-\xb7[\xe6r/\x0c\x8d:\xaab\xf2\xb9\xa9\x01t\x9c\xd29\xbf\x9d\x01\xe4\x8b\x10\xc8V\xfab}$c\x00\x86\xf0\xc8\x0b\x13\xf1_7\x9a\x03Q~\x03\xdfw\x8d\xc8\xd5\x98\xbe\x89\xe6\xbb\xd4\xbb\x11|\x1d\xbb_\xb1\xd9\x18\xcaMS\xe9(|\xb9i*\x0ca\xe7\xed-7MM\xae\x8b\xa8\xf4\xc8D\x8c<\x08\x1d\x0fG\xc2\x130\xf0\x04\xf4\x9b0\x18\x191\xee\xbc\xe8v6\\\xdb\x8fhg\xc3\xb2\xcbMS\xe5\xa6\xa9\x15\xa8r\xb9iJt\xb4\xaa1\x05!\x0e\xb9|\xa9\xdc4\xa5H\xb9i\xaa\xdc4\xb5\xe2R'\xff3>\xb45\x0dg-7M \xc9\x89\x89\x96\x9b\xa6\xb8\x84\xe1\x9d^\xa43\x10\xe3\x0cA7\xcbMS\xb3\xae$\xb4\xb2\xdc4\x15x\xd3T\x08\xe6hA\x1b\xc9\x1d\xbd\x1da\\\x8b-\xc6\xa1\x8a\xa6?y\xc9\x00\x1e\xecW.\xc0@>``*+\x8d\x8d}f_\x17\xea\x14\x0ev\x1f\xf3\xba\x18\x17\xe3\xf9g\xff8eC\xbe\xac\xbb\xcdi_\x0du\xb3[\x9d\x11\xb9\x99\xdf\xbdE\xb5\x8e\x8e\xd7;\xc2V\xba\x1d\x84\x1b\x7fE NyMh\xbb\xba|)\xfbAG\xe6\xb4\x06\xdf\xb4C\xb5_\xddT\x0e\xd1\x067\xd2,(\xa4U\xfc\x05\x15\\\xfc\xff\x03\x00\x00\xff\xffPK\x07\x08f\x87\xc3\x1c\xc7\xc8\x00\x00\xcb\x95\x0c\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(f\x87\xc3\x1c\xc7\xc8\x00\x00\xcb\x95\x0c\x00\x0c\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00swagger.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00C\x00\x00\x00\n\xc9\x00\x00\x00\x00"
+ data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00 \x00swagger.yamlUT\x05\x00\x01\x80Cm8\xec\xfd]w\xdc8\xb2\xe7\x0b\xdf\xfbS\xc4S\x17S\xae\x19[\xae\xee\xea\xf5\\\xf8\x9c=\xeb\xb8mU\xb7f\xdc\xb6\xb6,\xf7\xac>\xb3\xf6RS\x99\xc8\x14\xc7\x99d6\x89\xb4\xad]\xbb\xbf\xfbYx#A&^\x02H\xa4\xa5]\x15qS.%\x19\x04\x82 \x08\xe2\xffC\xa0\xffR\xad\xd7\xac{ \xdf\xff\xfe\xec\xc7\xef\x9f\xd4\xcd\xaa}\xf9\x04\x80\xd7|\xc3^\xc2\x1b\xd6o\xdb\x1e\x9e\xc3\xd5\xf9\x87k\xa8\x9a%\xac\xaf._\xc3\x9f*\xce\xbeT\xf7\xb0l\x17\xfd\x13\x80%\xeb\x17]\xbd\xe3u\xdb\xbc\x84W\xea\xd8\xba\xe1\xac[U\x0b\x06\xab\xb6\x83\x9eW\x9c\xc1?\xf6\xac\xab\x998\xe33\xebzy\xf4Og?\x9e\xfd\xf8dW\xf1\xbb^\\\xf6\xc5R^\xf0\xc5\xaekW\xf5\x86\xf5/>\xff\xf4\xa2\xda\xed\x9eo\xea\xe6\x93<\x00`\xcd\xb8\xfa\x07@\xbf\xdfn\xab\xee\xfe%\xfc\xf7\xe7\xfa/\x00\xafv\xbbM\xbd\xa8DY\xde\x8a\x93\xccE\x81\xdf1\xa8\xc6\x1f{\x90>\xa1\xea\xfbvQW\x9c-\x81\xb7\xf2\xa0u\xfd\x995O\x06\x87\xfb\x9eu\xcf\xa0^\xc1\xaek?\xd7K\xb6<\x83\xf7\xfc\x8eu_\xea\x9e=\x83\x9a\x0f\x17\xa86\x9b\xf9E\xd45\x06W=o;\xb6<\xd3\xff\xdf\xeeX'\x8f\xbaX\xbe<(\xb6>\xa6c\xfd\xaemz\xd6\xbf\x1c\x9c|\xff\xfb\x1f\x7f\xfc~\xfc\xdf\x83\xe8\xf7\xfb\xc5\x82\xf5\xfdj\xbf\x19\xce>\xb3\x8e\xee\x17wl[\xd9\xe7\x03\xf0\xfb\x1d{ \xed\xed\xffa\x0b>\xf9a\xd7\x89B\xf2\xda\xbe\xbe2\xeb\x86\xd8\xa6\x83\xeb/5\xe7\xe2 \xfaS\xcd\xff\xbc\xbf}\x06\x8c/~\xf0:\x14\xf7R\x9c[\xaedV\xd7\xe0\xb2\x8f\xfa\x82\xd06\x07\x0f\xaf]~\xe0\xd5\xda\xd4!\xe8P\xf7\\\x81z\xca\xbe0\xd2b>\xc8\xfeR\xb6\x0e\x16o\x13\xc1X\xb0f\xbf\xf5\x87\xf39\xbc\xba\xbc|{\xf1\xfa\xd5\xf5\xc5\xfbw7o/\xde\xfd\xcf\x9b\x0f\xd7\xaf\xae\xcfo.\xde]\\_\xbcz{\xf1\xff\x9e\xbf\xb9\xf9\xf8\xee\xc3\xe5\xf9\xeb\x8b\x9f/\xce\xdf\xa4;\xfa\xeb\xf9\xd5\xc5\xcf\xe6\xef\x1f\xae_]]\x1f\xed\xe5\xfc\xea\xea\xfd\xd5\x91>>||\xfd\xfa\xfc\xc3\x87t/\xd7\x17\x7f9\x7fs\xf3\xfe\xe3\xb5\xe7\xd4%[U\xfb\x0d\x7fY&\xb0\x93\xce=\xd0\x96S\xef\xa3xS\x04\xda\x15\xc0]\xd5\xc3\xff\xd9\xf7\x1cn\x19k\xa0nj^W\x9b\xfa\xdf=\x9d\xa04d\xe0U\x130\x05\x18/\xd4\xf3\xaa\x13\xaf\xe1[V7k1B\xa8W\xf5\xf1\x97\x93mer1\xd6u\xe25\x0c\xcb\xbdxj\xe4\x13\xa6.\xa6\x1f\xfb]\xd7\x8a\x17\xe8\xb1\x17\xd6\x0dlr\xe9i\xd5\xacW\xf5\xe6\xf0u\x19\xbf\xdc\xd0\x12'\xd7\xe0\xf5\x96-\xa1\xdds\xf1*\xdb0\xf8R\xd5\\\\U\x0c\xc3\xe6uu^\xb4\xed\xaa\xc5\x86\xddt\xec\x1f{\xd6\xf3HG\x15h\x92\xef\xa5\x9f+\xe5\x06:\xb6\xebX\xcf\x1a\xae^\x80\xda;\xf0\xbb\x8a\xeb\xd0\xb0\x06\xb6\xd5\x92y\xfd\xe9\xe1\x99*\xdei\xdf\x91\xf5\xb2\xc0\x0bh\xd5v\xdb\x8a\xbf\x84}\xdd\xf0\xff\xff\x1f\xe2o\xaa\x8b7b\xb4!\xaax\xf1\xc6t\xfd:L\xde\x93\xf5\xbdR}\xc4\xcd\x83\x14[\xdd\xe6\x0f\xb2\x04\xaa\n\xaa\xf8U\xa3K\x07\xaat^O\x8bj\xb3\xb9 \x8d\xa2\x00\xd5\xdc\x84\xbd\xae6\x1b\xc7`K\xfce\xdf\xab\x11\xfe\x8eu\xa2\x86\xe2\x87\xa0+]\xf6\xd8\x0d@59@6;\xc0\x0f\xcf\x00\x7fC\x01\x1d>a\xf3\x91\x9c\xe86\xc61q\xfb\xa5a]\x7fW\xef\xa0\xbfk\xf7\x9b@\xef\xac\xed\x96!zrl#\x80SV\xfa\x8e}\x05\xd6,\xda%[\xca\xd2\xa8f#\xfb'UWQ\x17\xdd\x8a\xa2\x0ee\x9d\xef\xe7#\xca'\xd1\xf3\xaa\xc5\xa2\xdd7|\x8c\xb3\xff\xa9\xd9\xd4\xac)\xf4\xc8\xe3\x02\xf5Z^\xf1\xe2\xcd\xbc+\x1f{+U\xa6\xa1O\x8f\xdep\x11\xcb\xa1G\x0fu\x13\x1d\xeb\xc5\x90\xca\xe3P\x17\xff?\xfc\xc5\xd7}\xc2V\xbe\x07\xbbv\xab\xfbV\xe1\xd4\x94\xdd~-\x9ey\x1d\xbdo6\xf7P}\xae\xeaMu+\xde\xadwL}6\xa8\xf9\x8d\xba\x07\xf5f\x0e\x0f.\x11=\x06\xa6\xb7\xd0\xa3\x07D\xaf\xf9A\x1di\xdf\xb8\xca\x9e'\x88\x0e \xb4\xb7x\xb9\x01Yva\x9f\xab\xcd>\xf8\xa1\x07\xf8\x16\x0c\xe8V,\xec\xcf\xec\xebs\xf3\xa8\xcbR\xd8\xc3\x10\xe8\xebu#\x86\xa1\xf7\xd1w\x04\x8c\xdf{\xc1\xe3\x84\xc7\x8a\xef\xbb\x87\xaf\xedP\x92\xd9\xc0k\xd7\xb5\xcb\xfdB\xd6:\xeaO\xf8\x18\x06\xce\"z\xde3VU\xbda\xe1\x0eJ\xd5\xe2gy\xe0\xb4}\xaa\x93\x1f\xa8m\xcao\x84\xf2w\xeb\\\xb8U\xa1\x9fL,\xc1\x97\x9a\xab\x17\xac\xa8\xf5\xbes\x87t\xd11\x19\x86\x1b1\xc4?b\xc6\xcb\x8c\xf0\x96\x15g\xcf\x85\xafp\xbf\x1ahb\xafu\x89\xae\xeb-\x9b\xbf\x16\x84c\xa8\x9b\xf9\x84\xda\x97\xc0{AV\xd03P`_wu\xf7\xc8\xaa\x7f>\x94 \x1f\x00\xaf\xb3/\xf5f\xa3j\xe9*P\xb80\xb3\xd9c\xc7\xf0W\x0c\xc8\xd5\x0d\xe0-T\xb0`\x0d\xef\x02\xdf\xf3\xf6\xa0e\xfa\xcb\xaeZ\xd7\x8dgx\xaa\x0by9\x1c\x02K\xb6\xaa\x1b=\xf9>\x9e9LL\x1fz\x08=\xc6\xe1\x87\xb7a_\xf9\xcd'v\xefn\x19\xd1vaZ\xc5\xed=w7\x88\xc9L\x8cw\xacaJa>\xe5\xc4?y+^+\xbb\xaa\xd7\x9f \x97\xd5\xda|\x16\x9f\xa9\xdf=\xce\xfe\xb1g\x9d\x1aD\n\xb7\"\x80\x0c\xb6m\xcf\x81\xadV\xf5B\x8c\xb56\xf7gp\xc1U\xdb\xb9e\xc0\xb6;~\x0f\xf5\xca\xd7\xa2\xefX\xc7\xa0\xea\x184-l\xdb\xce\x8c\x81z\xd7\x80\x87\xb7\xbc\xda\x1c\x19\xcc\xc07d\xec\xe9\x92\x97\x97Q\x94\xffh\xf6\xdb[\xd6\x89f\xac\x8bl\x8d\xc2\xbc\xf5\xb5\x03-\xc7\xd57\xd2\x99o\x18\xfe\xa5\xea\xa1g\xfc\x19\xd4\xbc\xd7\xe3\x82\xba\x87}\xa3\x1a\xf1\x12Z#\xfb\x1c\x9c\x1f\x9f\xa5SEQ\xad^\xd5J\xdd\xaf[\xb6\x14\xef\xe5\xbaQ\xc2\x9ay0`\xcb\xfa\xbeZ\xb3^\x0c1\xdd\x1d\xc2\x1ds\xd5c\xd1v\xca\xc7R\xbc\xa2\xcd,\x8b\xf6&\xdf\xf7\xf2\x13\xc6\x8e\x8c3\x1c\xe6\x8c\x0f\xedv,\xf7/\xde\xceK\x8e\xd1w\xb2\xf7\x86?V\xddp\x93\xfe\x05~\xf7\x7f\x85N\x9a\x84E\xb6\xef\x7f\x81\xdf;\xcf\xf8\xe7\xe4\x8f\xbe\xd6\xf3\xaf\xe2\x89\x99ki\xc3\x05\x0e\xe6\xa0\xf4\xdf\xf5\xcc\x92|\xdcf\x0eE\xb4\xe6\x01\xe2-\xac\x19\x9f\x7f\xdci=Q|/W\xa3\x9e\xb3\xefY7\x9cn\xa6\x86-\x7fS\xf1\xae\x81}\xc3\xbe\xee\xd8B\xc4Q\x8eAN\xa9\xe1y\x069\x81\xa7[\x0c\"}'\xd4\x0dgk\xab\xb2\xc6L_P7\xfc\xa7\xdf\xcf~\xd5\xad,\xa9\x0cZ:\xf3\x9d\xf3m\xb5Gq\xfa\xcd\xbe\xf3\xf4\x93\x88\x9e\x12\xd3w({\x05\x1f\xaf\xde\xbe\xe8X\xdf\xee\xbb\x05S\n\x9b\x1cI\xee\x9b\xfa\x1f{\xb6\xb9\x87z\xc9\x1a^\xaf\x8c\xd8-\xae\x0d\xad\xafo\x04\xf5\xdd\xca:3\xad\xef=n\xd7\xb5\xbc]\xb4\x1b\xb8\xdd\xafV\xac37\xed\x0c\xae\xa5r)\xeb\x06\xdb}\xcf\xcd\xb0\x03*\xff\x10|\xc3\xaa\x9e\xfb\xaf\xd56\x0c\xbe{\xf1\x1d,\xee\xaa\xaeZp\xd6\x9d\xc9\xb9\x99M\xd5s\xe8\xd9z\xcb\x9a\xe1\x83\xfd\xe3\xd5\xdb\xef{\xd8U\xfc\xce\xebM\x16jx\xe8\xfdW\x95\x03\xef\xfdfs\x0f\xff\xd8W\x1b57\xafD\xc1\xd5\x18\xc9\xa7U\x0fu`\n\xe7\xef\xa2(/\xd6m\xbb\xde\xb03\x19\xb3\xdb\xfd\xea\xec\xcd^\x0d\x10\xff\xfe\x83\xaa\x89t;\xce'\xd5\xfeo\x9a\n\x16U\xd36\xf5\xa2\xda\xc8g\xc8\x7f\xe5\xa7\xecl}\xf6L\x84Vv\xfa\xdf\x9d}'\xde0M\xcb\xa1Z,\xd8\x8e\xb3\xe5\x0fgO\xfc\xa7_\x88\xcf\xbfj\xc1\xeb\x05{\x06\x9cU[\xf1\x8e\xd8W\"\x1c\xbb\x8e-\xda\xed\xae\x16/\xd9F\xf7\x92\xb7uS\x1dt\x93\xa3I\n\xe2~'\xdb`%;\xc9{\xff\xa5U_\x075\x17}\xea\xbe\x97\xc3e9\x91\xd46\\\x8cx\xda\x15\xbcj\xee\xcf\xe0\xcf\xed\x17\xf6\x99u\xcfD \xbc\xce>^\xbd\xed\xf5`[\xb8\xf2\xbc \x95\xc9\x1e\x94\xc1\xdf\xef8\xdf\xfd\xfd\x99\xfao\xff\xf7g\xd0vbh\xa4~}&[\xe3\xa2j\xa0\x95Og\x15Riz\xc6a\xbf\x83J\xd6=p]\xd6}f\xfa\x0bp[\xedz\xd5\xb4d\xc9y;\xbct\xe5\x80\xa3\xe6\x92R |.\xad\xda\xcd\xa6\xfd\xd2\xbf\x0c\xdc\xdb\xff\n\x17\xab\xb1F\xa2Y\x18\x8ae\xa8\xb4\xfe\x12\xddo\xd9\xd2=\x14\xd0\x8e^5\xf0\xe7\xeb\xebK\xf8\xd3\xf9\xb5\x11\xcb?^\xbdU\xcf\xd8}\xcd6K\xa8\xbcg\xff\xef\xf9cq}\xbfc\xff\xf6\xbf\xff\xcd{\x02\x98\xf1W\xa3\xdb\x9b~\x8d\xc8;\xa4'+\xa0j\xd4+\xcc?]\xf7_\xed/\xa3^\x8e|+\x1135\x0e_T\x0b\xd1\xb7\xb4\xed\xa7\xfdn\x18\xb1\xdcV}`\xaaZU6DG\xd1\xcc{)x\xaa\x95\\\xf5\xb5\xa7\x1em?R\xb3\xad\xd7w\x1cn\x03\x9d\x92\xac\xb4\x9c\x81\xaa\xb7\xbb\x0d\x13/Y5\xa2\x1dF\xb1=\xdbV\x0d\xaf\x17\xce\x0f\xc5\xc8L.b\x08\x14\xfd\xf6\xc6\x8f\x92\xfe\xa2\x90\x05\xa8D\xa1\xea\xa55\xc09\x18\xc7\xe8\x97{u\xdb~\xf6\xb7i\x1d\x02\xfd(\xb8\xaa\x8f)\xd9\xdf_5\xf7\x7f\x1fge\xaa\x06\xaa\xee\xb6\xe6\x9dx\x88\xfd%t\xba2\xef\x88j\xd3\xea\xa6\x07\x95\xfb\xd6\x8a\xdeY\xbehT o\xa7\xc3\xc2\xd9\xf0o\x18\xd5y\x9a\xe6\xa5yp6\xf5\xad,\xb6~\x8f\xf4\xd0\xefw\xbb\xb6\x93o\xf0]\xb5\xf8\xf4b\xdf\x88\xff\x88\xf7\xb6j\x17\xee'H\xbf\xe8\xfd\x03\x9bv\x05{\xae:6\xd3=\xf4\xa2c\xad\x96\xcbZ\xf5\x15\xb0f\x0d\xeb\xe4'\xe7\x96\xf1\xbbv\xa9q:\xf7\xcd\x14\xe5Q\xb7\xd0}\xbd\xf3\xaf\x95h\xfc\xf0\xbb\x97p)\xca/\xfa\x05]\x95j\x08z\xdd\xc0\xeb\xff\xf6\xdf\x02\xaf\xc9\x9f\xdb\x16Vm\x0b\xff\x02ggg\xfe/_Q\x98\xaa\xb9\xf7\x1fP5\xf7g\xa2\x18?w\xed\xf6\xe9\xaam\x7f\xf0\x1fzv\xe6\x7f\xff\xd5+x*\\}\x94\x15\xb9n\x9f\xfe\x17\xe1\xeb\x87\xe0\x87|\xc8\xdf?\xc3\xb1\xfb}$v\xff\xa3\xfa\\\x15\x0b\x1e\xfc\x8b\x1c\x1b\x8a\xab\x14\x88P\xdd?\xfd\xb9m\xcf\x16\x9b\xaa\xef#\x01RE\x14'\xa9:Z'\xfa\xcb\x10\x89\xdcO\x91\xc8]\xde\xf3\xbb\xb6 \xc4N\x15\xea\xe7\xb6}zvv\xe6\x7f\x19\x0cq{\x1a\\\xe0\xf6~\x1c\xc6\x99\xb7\x91\x9a\x00w\x8f\xa2\xf5\xac\x9b\x18\xe6\xb8/\xf5\xbdc\x88\xf6b\xb5\xdfl\xce\xe4\x0fb\xf8\xfb=T\xd6\xdbS\xbcYE\xdb\xf2\xbdkT\x9bs_lxU5\x9b{\xf3\x9dz0\xf90\x0c\xbb\xa1ZI\xf8\xd8s!9/\xf2\xfd\x8b\xef\xdd\x97\xd2\xefXSd\xf9\xf5\x0cL?#\xdf\xad\xda\xf6\xec\xb6\xeade\xbf\xbe\xb8?\xfb\xf7\xefT\x14\xe5\xb7\x9c\xd3\x9f\xff\xd3V\x16\xf5;\xe1C\xbc^\x9d\x87\xfc\x8f\x0f\xef\xdfy~\x12\xb7Z\xfc\x9dz\x02oUw=\xbf\x91722i\xafO\x10m\xcf\x1c\xef\x9e\xb2\x97\xe6y\xbc\x85\x85J\xf5\x9d\x8c\xe5w/\xe1;\xd7\xc39\x0d\xc3\x99\xaa\xe5w\xcfB\xfed\xfd\xdeU[\xe1\xf3\xffVU\xf8\xef\xc1\x13D\xfdf\xc7\xa7V\xf2b\xa5\xbf\xd3\xa6mM\xb5\x86\xba\x87/l\xb3y\xfe\xa9i\xbf4\xb2;\xb9\xabz\xa8`\xb1\xefy\xbb\x0d\xfd\xf8\x88\x1b\xd5\xb1~\xc2\xa3\xcc\xe7Y\xe5\xad\x9eF\xd5\x00\x0d\xd6lv\xcf\xaanq\xa7N\xb7{\xba\xbayy \xaeu\xec\x1f\xfb\xbac\xcb\x97\xb0\xaa6\x13I\xd59a`B\xe0\x06\x13\x90\x91\xb0&\x03\xe7Z\xe0\xc1\xe2%\xcf\xaa/\x03\xeaLc!\xe2p&\x02\xbdT\xef\xf5z%\xef\x95\x9cZ\xdd\xf4-\xf4\x8c\x9f(\x1ef\xc1\x142\x18\xc3r\xa7Y\xf5\x8d\x1b\xa8\x9b\xbe^\xb2\x83\x90\x1c\x1b\x0d\xdb\xd7\xe9\x832\x92&g\x9f\xd8\xbd/43\x86CC\x1b\x95\x1ejt\x8c\xef\xbbFI\xf3\xb6J}6\x10\x1er\xa6u=\x9b\x92\x9450t\\\x88\xda\x90$i\xdb\xc8\xa9\x98v\xb5\xea\x19\x87\xb6\x83iqa\xf2l\x95\x8e\x96g\xde\xcd\x11DU>d\x13\xd3\x95\x91\xa1l\xf6[\xd6\xd5\x0b\xf37\xf9VZT\xcd@RK~V\x07~\xdf\x0c\xf3\xbc\xb3\xcf\xbb\x0b\xe9m#\xba\xa8!\x84jft\xdf\x8bP\x7fb\x89\xf1\x9c\xba?qpg\x0c\x8c#\xbc\x9bz[c\xa3+\x8f5p\x91\x0f\x8dQ\x1a\x80\xdd\x82\xf9\xc0;O\xbc\xed\xd4\x8c\x9f\xfd\xa7\x8b\x15l\xd8\x8a\x1b\x94H\xb3E\xe6\x1bJ\xca\x17\x1a\x99mu\xe4\xc4\x07\x19\xab\x16w\xe2!\x7f\xc0(\xda\x80\xcfx~(\x96\xd6\x19\"\xa2\xb2\x85\xb6\xc0\xbb=\x93\x8cG\xdd,E\x97\xc5\x06!\xd3\x10\xe3\xe2@\xc7\xf2\x87\xbaYl\xf6\xcbY_X\x81\x06\xfaW\xce;&\xb9\x04K\xe0\x10C\x1c\x8b\x93\x9bu.\x1f/\xfa\xd9\xdd\x9aUAv\xb7\x1d\xeb5A\"\x1f\xaf\xf1y\x14\x8f\xdc\x99~\x9a\xeau\xd3v\xf3\xde\\?\x8d\xd3K\xa8\xc8\x1c{co\xdbv\xc3\xaa&t\x03;\xf6\x99u=\xf6M\xa6\x8f\x9e\xdf\xb8\xda\x02\xc4:\xe6~\x12&~\xc45\x98\xa2\xa5\xdan\xc9\xba\xf9\xfc\xf0\x87\xbaY\xb0\x97\xb0h\xc5\x90\xe9y\xbf\xfc\x04?\x9e\xfd\xe1\xa7\xa2\xd1\xe0\xd5z2\xd8\xfbW\xed-\x98\x0c\xe1\x85Zl\xd1\xbf\xf8eX \xf2O\xe5\xc4\x95 \xe1?\xbc \x12\xfex?\xac\xeb\x18\x12\x19\x80\xe8V7\x87t\x93\x86\x9bT\x82\x04\xe3O/\xfa\xa8Qy\x0d\xc6\xab\xe9\x83\x1fu\x82\x03\x1fc\x94\xc5\xa7\xfa\xd3\x14D\x15\xb2\xe3R\x14\xf8WU\x15LO\x10\x0c\x0cD\x83\x03\xd8\x95o\xd1X\x01\x14NJ\x10OI\x90R\xa6\xe0z\x8d\xc2\xc9\x08b\xa9\x08\x02\x89\x08\x92\xd2\x10D\xeb\x1fJA\x90\xbap=\xd5MB\xfa\x01\x9c\x8fP\xf2\x01\x9c\x87\xf0\xea\xb0\xf8ro\xe7\x89%\xd3\x0e`$\xed\xf4;\x17I9\x90\x9cp\x00\x19\xec2\xe9\x06p\x17+\x9el\x00w\xd9B\xa9\x06\xe2-\xafd\xa2\x01L\x9a\x81X\xa7\x99\x99b\xc0\xbf\xe2\x82\x87\x13\x0c\x14x\xcb\x85\x16\xecF;R\xc0\xad\xad\x008\"\xa9\x00>\xa5\xc0 \x8a{d2\x01\xc4*\xf2X\x93\x82\xe44\x02\xe1\x8c\x14\xa0?\x07BI\x04\xa2\xcd\nPM\x0b\xb0\x83(\xc0\xde<@\x06\x0c\x92\x13\x07\xc0mx\xc9,D{d\xdc\xed\x86SU\xb5`\xba\x80\xbcd\x01\xd8T\x01\x88D\x01\xa8\x00a\x82S2E@B\x82\x80Pz\x80Xr\x80B\xa9\x01\n$\x06\x88\xf6\x02\xf1\x1e \x9a\x12\xa0dB\x80hy\x01Uf\x88\x01\xa4\xcaPm\x14\x90\xed\x14\x8a&\x01\x88\xa7\x00@&\x008i\x1d\x8f_\xfa\x8f]\xf8\x1f[\xf6_h\xd1\x7f\xc1\xf6\x87X\xee\x9fzo\x8eY\xea\x8fX\xe8\x1f-\x0en\x95{\xac!\x15]\xe0\xef_\xde\x8fZ\xdc\xff\x8d\xaa\x9c\xb3\xa8_\x8a$\x1e\x7f\x9e%\xfd\xa1b\x14^\xce\xef[\xcc\x9f\xb2\x88v\x9c\xb8\x1dV\xd3\x1e\x96\xca\xcc\xb3\xcf\xfc\xe9Nt\xf6W\xf3y&\x17\xe4\xc8nE-\xab\xad\x9a\xc3yg\xa5\xf3U\xe3\\\xf3\xe0\x8b\x96\xd4\xce~\xa5%\xb5\xd8\xf9+ZRKKj}G\xd2\x92Zi\xb4\xa4\xf6\xd0hI--\xa9\xf5\x19-\xa9\xa5%\xb5\xd2hI--\xa9\xa5%\xb5\xb4\xa4V\x19-\xa9\xa5%\xb5\xb4\xa4\x96\x96\xd4\xd2\x92ZZR;5\xff\xa7--\xa9\xa5%\xb5\x1e\xa3%\xb5\xb4\xa4\x96\x96\xd4\x96YR;\xd01\x96\x97\xd0\xe7'\x9ep1\x9c\xbeZ\x14\x0b\xdcEPO\xe7^\xeb\xe6\xe5|\xf2}\\H\xc2\xbb}d\xb9T\xde2\x12\x85\x0f\xa9\xf3\xd2V\x8e\xbc\x97g\x0e\xabF\x0c\xf6\xa9\x1c\xca7\x8d=Q(\xaf6\xde\xd7q\xfeW\x07\xc8\x90\xa3V\xee\xdc\xc9\x02T1\xee\xd4x\xf0\xd1.2\xb1\x83o\x1b\x7f\x00\xdd\xed\xa8m4\x11\xd8a\xd4Gl\xa1E\xc4AR\x86|\xd9<\xde\xe8U.\xbe\xa5/f\x1dT\xac\xc6\xee\xaeQ\n\xd7\xf2\x06\x1f\xde^U\xd4Wj\xdd=\xeb\x87y\xb3\x83l\xd2\xad\xdd\x8c\x8dQ\n~J\xc1\x8f\x0e&\xa5\xe07F)\xf8\x9d\x96\x9b\x82?\x1cp\x17C\xa4\xde\xc9\x08~\xe8p\xda\x81[\xcb\x16f?\x1d\xf0CsxH\xf5\xa1\xe3\xf3E\xc8\xd0\xecWB\x86b\xady4B\x86\x08\x19r\x1b!C\xd2\x08\x19:4B\x86\x08\x19\xf2\x19!C\x84\x0cI#d\x88\x90!B\x86\x08\x19RF\xc8\x10!C\x84\x0c\x112D\xc8\x10!CS\xf3\x7f\xda\x122D\xc8\x90\xc7\x08\x19\"d\x88\x90\xa12\xc8P\xb1\x14\xf4J\x1e\xc7\x82\x82H\x16`\x8a\x16x\xca\xca\xe1Y\x80A\xb4\xe0\x08L\xcb\xebP\x1c\x18B\xb5\xa04\xae\x05Qd\x0br\xb1-\xaf7\xf5\xfd\x1b\x9e\n@\xe0[\x10F\xb8 \x0b\xe3\xf2\xba\n\xe2]\x90\x8bxy\xbd\xa9ad`\xd2\xad\x1c\xea\x05(\xdc\x0b2\x90/H\xc3\xbe \x07\xfd\x82d\xfc\x0b\xc2\xdd\x08D\x90\x1cH\xc0r\xb0(\x18\xe4\xe0`\x90\x8a\x84A\xb8\xe29h\x98\xd7\x99\x05^a\x1f\x19\x1c\"\x16| \xe4\xbe\x18\x01L\x0c\xca\xa2b\x10\xc3\xc5 \x8c\x8cy\xcf\xc9E\xc9\xa0`\xdbM@\xca +\x83\xc0\x08%\x96\x85\x03L\x9e\x18\xf760\xb2\xfd~\xe5\xac\x13\xdd\xa2\x92E\xb5b\xe6\xf5\xa7f\xfa\x16m\xd3H \xdf\x1f\xdba\xab\xa27R$\x86\xd0FW\xbb\xaemW^\x91.Z\xc7Kq\xfa\xb4\x86\xe3\x96\x81\xd2\xb7\xbb\xb2\xa1g#\x10\x84B\xaa\xe1n\x7f\xeb\xcfw\xa3\x0cq%@^\x0d\x90:%\xe0'\xc3 a\x0e\xc9XQ\xcd\x122uKH\xd1.\xa3\x9eFm\x13\xa3_Bi\x0d\x13rtL(\xa9eB\x92\x9e\x19ue\xa7VH\xd44\xa1\x8c\xae e\xb5M8B\xdf\x84\x0c\x8d3\xeaP\xbcRSuN(\xaauB\xb6\xde X\xcd3\xeae\xaa\x89buO(\xad}B\x11\xfd\x13\xf25P(\xa9\x83B\x92\x16\x1au5I\xe9\x80\xd3C\xe1t\x9a(\x9cX\x17\x85\xd3i\xa3\x90\xa1\x8fB\x8aF\x1a\xf5d\xa7\x80\x00\xbcN\n\xa9Zi\xd4\x9b\xec'R\xf4R\xc8\xd3L\xa1\xb4n\n\xc9\xda)\x94\xd2O\x01\xa3\xa1B\xda\xd0\x11\xa9\xa5B\xc6(3IS\x8dz\xe3&\xc5\x04RW\x85\xc4\x12'\xea\xabA_\xd3\xd1,Rc\x85T\x9d5\xe8 \xa1\xc1Bi\x1d\x16\x12\xb5X\xc8\xd4cc\x0e\xb1\x9a,\x14\xd5e!M>\x04\xac>\x0b\xe9\x1a-`D\x89#\xb4Z@\xfa\x8fLX\x16\xd4m!;\xf8i\xfa- k\x9e\xa9\xe3\xc21Z.\xe0#^D\xd3\x854]\x17\x12\xb4]H\xd2w\x01\x7fS\xf2t^H\xd6z!\xa6\xf7BI\xcd\x17rt_8B\xfb\x85t\xfd\x17\x907)\xa6\x03\x03\xd6O\x9a\x1e\x0c\x855a\xc0\x163\xf2\xc4\xa6\xeb\xc3Aw\xb7\xf78\x8d\x182u\xe2\xa0C\x9d\xea#\xaa\x15C\xa6^\x1ct\xc8\xcd\xdc^P3\x86\x0c\xdd8\xe8lL\x05\x82\x98VA\xea\xc7\x10\xd7\x90\xa1\xac\x8e\x0c\x18-\x19\x12\xf5\xe4\xc8\xfd\xb7\xd2\x89\x045e\xc8\xd0\x95\x83\xce>^\xbd=Cj\xcb\x90\xa9/C\xba\xc6\x0c\xb9:3di\xcd\x10\xef\x9a\x00\xa1\xdbA\x82v\x97\xa2;C\xae\xf6\x0c9\xfa3\xc4\x83QT\x87\x06\xac\x16\x0d\xa5\xf5hH\xd0\xa4!K\x97\x8ev\xd2Qm\x1a2\xf5i8R\xa3\x86\xc2\xed=Q\xab\x86d\xbd\x1a\"\xa3\xa9\xb8\xa6+\xecr\x7f\xfb?\xd9\xfd|\xb7\xa4\xdd\xfevS/dV\x06ke\xb0\x91\x9bc\xedK\x8a\xe1\xe2\x9e\xa9v\xc3#\x03\\1\xe6Q#\x80AQ\xf6\x1e\xdf\xd7\xeb\xa6\xe2\xfb\x0e\xb1\xd0\x80$\xdd\x80\x91\xa4K\x92\xee\xc4H\xd2\x8d:$I\x17H\xd2\x9d\x18I\xba$\xe9:\x8d$]e$\xe9\x92\xa4K\x92.I\xba\x11\x87$\xe9\"\xf5\x04\x92tI\xd2%I\x97$]\x92t\xa5\x91\xa4;1\x92tI\xd2\xb5\x8c$]\xb7\x91\xa4\x1b1\xac\xc4E\x92\xaem$\xe9\x86\x0e$I\xf7\x9bI\xba\x1f\x8cF:Wu\xef\xd8\xd7\xe7\xacY\xb4\xa2\xb9\x0fBjl*G\x9cx\xb9\xa9\xea\xe6\x9a}\xe5\xea\xf5\xe3=~'\x8e\xbb\xe1\xec+\x8f\xcb\xb3\xd1\xc9.\\e\xc7\xa2\x05*\xab^\x9a\xa2\xcar\x1e;\xe8P&\x00\x97\x93(z\x06?\xaa\xf1\x0c\xf1v\x1e%WA\xdf,\xdafU\xaf\xf3\x97_\xcbD\xd9\xaf\xa5\x93\xe9\xcbQ9\xd6Mr\xbe\x04\xdb\xeb.\xb44\x1b\xa1\x9ec\x94s\x99\xda\xfd\xb8V\x80\x9d\x0d\xf4\x06\xc7^\x83\xaf\x96\xde\xcb\xc1\x89\xec\xfb\xbc\xde\xe4\xb8\xb3n>\x89\x97\xc0\xb6Zz\xe6+\x17\x1d\x93!\xbf\xe1\xb5\xbf\x9e\x88:\x9a\xe9\xdce\xc5\xd9s\xe1+\xbf\x89\xe8\x12]\xd7\xdb\x83'_8\x16\xdd\xba\x062L\x0d\xef\x02yjo\x19kT-\x9do\x9fpy\x86\xbc\xee\x8e\xfb1\x14M\xbc\xb3X-\xa7\xf8\xab\x06\xea\x86\xb3\xee9xR\x07/\xba\xb6\xef\x9f\xebv\xeb<\xc2\x91\x9f|\xdcW\xc0\x91\xb8]\x95\xffr\xdcg\xc1\xce\x85nm\xbf`r\xca\x1fz\x08='\xe1'\xc4l\x1f\xe3n7\xd1V\x13\x95\x00B[\xdc\x8c6lb\xa3w\x12\x19\xf6\xb3\x81]\xd5\xf7JvT\xbb\xde\xfcc\xcfz~\xa6~\xf78\x93\xbb\x1fH7\xa1\x0do.\xb85\xb2\x91{\x8b\xf8^>\xa2Y0)\x826-l\xdb\xcel\xbc\xe1T[\xe4\xde\x17G\x06s\xb6\xb9\x88m\xb1go\xd8z\xc3\xb7\x17\x8b%\xe2\xf9\xeak\x07\xda\xde\xc5\xc4s\xf8\x97Jny\xf1\x0cj\xde\x1b\x19\xba\x87}\xa3\x1a\xf1R)g_jG\xb3\x8dw\xaa\xf6NG\xaaV\x93\xfd\xd0\xea\x06\xd6W\x97\xaf\x87\x07\xc3\x0c\xe3z\xf8\"\xee\x99\xeb\xf6\xb8\xdf\xa0\x8b\xb6S>\xe4\xe8\xb5S\x95\x1f\xc6\xe1b\xc0-E8;2\xcep\x983>\xb4\xdb\xb1\xdc\xc1q_\xc7v\xb2_\x83?V\xddp\x93b_f\x93\xb0\xc8\xf6\xed\xfb&\x9b\x0e\xe1\xc2\x0f\xa3\xdc\xe7c\xdc\x06c\x12w\xb5\xdb\x8c\xfa\x7f\xf9Y\xb2j]Y\xd3\xa4\x8b\x17\xd6&\x1f\xe2\xee\xa8\xe9\x98\xf1a\xd1\x93H\xf6C2\xddG\xa3\x81}\xa3\x90\x19\xb6T\xc8\xc2)\xb7\xd3\x90\x17p\xf4\xca\xfe\xa7U\x8c\xe4|'\x88w\xc7\xda1\x9dd\x9e\xed\xba\xe1?\xfd~\xf6\xabn5IeX2^\xd5\x9bG\xb2\x0d\x08\x8f`\x89\xd1\x9e\x0f\xd3\x17(K\xc5\x0f\x8d\xfe\xe9u\xc8\x91\xd8!\x1a7\xc4\xec~\x11\xc1\x0cS\xf1B\x89\x0fz\xbda\xb1B\xd7,i2N\x88\xc7\x08'\x98\xa0\xd7\x1f\x1e\x1f<\x12\x1bL\xc3\x0552\xe4/v\n&\x98\x8a\x07\x06w\xb5H\xc1\x02\x93p@\x0b\xf7\xf3;\xc4a\x80(\xfc\x0f\xbb\x8b\x05\x02\xfb+\x86\xfb\x1d\x87\xf9e\xe0}E\xb0><\xce7\xc5\xf5\x02\xfd\x93\xe3\xddo\xdb \xf0\xbdSa{'\xc0\xf5R0=4\x9e7\xc1\xef\x82}x\x1c\xcbK\xc2\xf1 \xb8\x1b\x05\x16\xc3sLg\x86z\xe3T\xec\xce`u\xfe\x97`\x14\xb7;\x1a\xb3\x0b\xe2u\x88!P\xf4[\x1a?JJ\xc2\xe7d\x14%\x1e\xe7\xef\xb0\xa3\xd8\x1c\xa6d\x89\x98\\hg <\x1e\x97\x84\xc5\xc5\xd0\xb7\x1c\xe4McmN\x7fQ\xd4-\x07q\x83\xc0\xc6!1\xb4\xad\x0c\xd2\x86\xa4\xa9\xa2\x08[\x02\xba\x16\xe2%rP\xb5\x90?\x8f\x9cW\x08IK\x0b\x1e\x1eA\xc3D(\x019\xcbB\xcd\"\x91;\x1a-C\"e\x18\x94\x0c\x87\x90E\x82\x9a\x8e\x8c\xe1Q1/\"V\x04\x0dKB\xc2rP\xb0\x04\x04,\xc8B\x05\x8a\x15<\x0f\x8fx\x95B\xbb\"\xd5p\xfe\x96\x8ep\xf9vm\x88\xa2[9\xc8\x96\xc1\xb2\x9c\x0eC\xa8V\x0e\xa2\xe5\xbdP\x18\xcdJE\xb2,\xec\xca\xe9\xcf\xffi\x8b@\xb0\x02\xe8U:r\x15\xc6\xaaRp\xaa)2\xe5t\xe7\xc3\xa8R\xf1)\xf0\xee\xb4\x10\xc7\xa6Rq\xa9\x04L*\x19\x8fJ\xc3\xa2\x02\x04P\xa8TX\x1c\x04\x8b?%cOI\xb8\x93\xa7\x929x\x93\x86\x80\x02\xcf\x90\xeb\x97\x1c\x9cI#K\xee\xb6\x1d\xc2\x98\xd2\xf1%o\x07\x16\xc4\x96\x02\xb8R6\x92T\xa2\xcd% Hx\xf4\xc8\xbc\x86wUWm\x19g\xddd'z r\xc8M\xc7-\x07\xa1/\xcf\xa7f\xce\xf2\x07\xb5\xc9\xf8\x8cE\xd0{\x15\x98<\x11\xba\x8b\x14\xfe\xc5\xdd:d3zVu\x8b\xd9$\xc7@0\xac\xa6Sp9\xfb\xf0\xcf\xa6\x07L\x85\x15\xb73[z\x8f\xac\xb6\xd4\xe2\xc4\x03<\xed\x89\xedy}\x85\xa3\x98\n\x9bJ\xda\xfe\x0e&\x91\x87\n\x8b\xb0.\xd5[\xbc^\xa9\xc8\xd5=T\x9b\xbe\x85\x9e\xf1\x13\xc5\x83W\xdd\x9a\xf1\xf4X\\\xcb\xf3\xa6\"\x0d\xb7\xb7\x89\xb0\xf6\x98W\xb1\x98\x17x\xbc\xdd\xd3x\x1c\x86B\x05U\x86\xf9\xf4\x01\x19\x19\x91\xb3O\xec\xde\x17\x98\x99\xe0\xabq\x8bJ\x0f*:\xc6\xf7\x9d\x82\xd2&\xfa\xf2\xd9\xc0f\xc89\xd5\xf5l\xf2Q\xd6@\xbc\x08c\xbc\xc5{\x11\x98\xb6\x91\xad\xae]\xadz\xc6\xa1\xed`Z\\;uD\xf9hyf\xd8\x1cAT\xe5C60]\x19\x19\xcaf\xbfe]\xbd0\x7f\x93\xef\x9fE\xd5\x88\xfa\xa8\xe9\xc5;\xd6\x98\xc0\xef\x9baFw\xf6!w!\xbdmDK\x1cB\xa8\xe6@\xf7\xbd\x08\xf5'\x96\x18\xcf\xa9\xfb\x13\x07wF\xaf8\xc2\xbb\xa9\xb756\xba\xf2X\x83 \xf8\xa0\x165\xdbo\xb7`\x0d,\xec73\n@\xcd\xed\xd9\x7f\xbaX\xc1\x86\xad\xb8\x81\x804\x15d\xbe\x96\xa4P\xa1\x1e\x10u\x11\x11\xe7\xdb{`\xd5\xe2\x0e\xaa\xdd\xee\x01\xa3h\xa39\xe3\xf9\xa1XZg\x88\x88\xca\x16\xda\x02\xef\xf6Ln\xf4S7\xcbzQq6H\x96:\x82\xf2@\xdd\x90lwu\xb3\xd8\xec\x97\xb37C\xa5\xae2h\xc6\xb3;& \x04K\xca\x10\x83\x19\x8bp\x9bu.\x1f/\xfa\xd9\xdd\x9aUA\xf6\xb6\x1d\xeb5+\"\x1f\xaf\xf1y\x14\x8f\xdc\x99~\x9a\xeau\xd3v3!\xc8<\x8d\xd3K\xa8\xc8\x1c{co\xdbv\xc3\xac\x1c*\x8e\x1b\xd8\xb1\xcf\xac\xeb\xb1\xeft}\xf4\xfc\xc6\xd5\x16\xda\xd51\xf7\x930\xf1#\xae\xc1\x14\xe7$\xf9\xe2\xf9L\xf0\x87\xbaY\x881G+\x86F\xcf\xfb\xe5'\xf8\xf1\xec\x0f?\x15\x8d\x06\xaf\xd6\x93a\xdd\xbfjo/\x96r<\xf6B\x7f\xd0\xf4/>\xff\xf4B\xbeG\x9f\x8b7n\xffB\xe5\xe4Rg\xae\xd9\x00\x10\xf5\xfb\xed\xb6\xea\xee'\x11\x1b\x10\xa4\xf7\xf2\x1cY\xe0\x9a\xf5\x06[\xd2\xe9\xbdD\x8bT/jy\x81g.\x89[\x0d\x87\xc4C:\xfcI8\xa9FY\xc8z\xd5\x8b\xef\x97\xf9hB\x9f\xd6\xee\x98\x1a)_,_\xce\x8b\xa7\x0f1\x90\x93\x15\x9c\xef\x7f\xff\xe3\x8f\xdf\xfbI)\xe8\xf7\x8b\x05\xeb\xfb\xd5~sJD\xca\x0e\xbcm\xfc\x01X#1\xc6\xcc\x16\xd9\xc6\x91t\xb6\x0b5\xf8\xcc<\x1d\x89*\xcbV\xf1F\x11^3lY\xff\xb1]\x89\x16X7\xeb\x0d\x0b\x10\xf4r\x98.o\xde\xe1\xadS%\xd1\xfb\xc5\xb1~\xfa1 \x1f\x07}\xdbg\xa7\x12\xc9L$3:\x98D2\x1b#\x92\xd9iEH\xe6\x83\x80OIf\xf5\x8e\x1d\xae\xe2Z\x06\xa2\x87K\xb7\xf7\x8e):\x1d\xc3y\x94$A%?|[1\x18\xb1:N\xddo\x12\xf0L\xc03\x01\xcf\xda\x08x&\xe0y4\x02\x9e9\x01\xcfn#\xe0\xd9\x18\x01\xcf\x04<\x13\xf0\x8c\x1c%\x11\xf0<\x18\x01\xcf\xb6\x11\xf0L\xc0\xb3\xc3\x08xv\x1dC\xc03\x01\xcf\x04<\x13\xf0<5\xff\xa7-\x01\xcf\x04<{\x8c\x80g\x02\x9e x.\x03<\xa7\xf3\xbf\xef\xa7\xfc\xafV\x0dZ\x0d\xb3\xd8*\x15\xb4\xab35Y\xc8\xc7\x8f\xdei\xd5\xaa\xcdlfE\xd1\x01=o;9\x81\xa0\x9a\x97r-'\xbbz\xce\xaa\xe5\xb1\x00U\x11\xf6w\x8c\xc3\xb9\x83\xf1\xd5\xd1\x90\xf0\xae\x145d]\xe4\x1b\xda\x00\x9b3i\xf9`ry\x8a\xf9\x8e\xb3\x06\x95z\xcaO\x14\x04\xe2}1\xd1\"\xdeW\xd9\x89\x83\x1b'U\x89\xf7-\x11E\xe2}\x89\xf7\xfd\xf5\xf3\xbe\xfaA|n(\xda\xe7\x95!\x14\xfd\xdc\xaf\xf5\xa2{\xa3N7o\xfb\x11o4\x00\xb0\xfa$T\x0f\xfb\xe0\xda\xdeYx6j]\xd7\x9fY\xa3\x166\x89\xb1\xfa\xd3Q\x13\xfe\xc1z\xf9\xeb\xc3'\x8c\xaf\xaf(\xfa\xd8G\x0b\xfb\xca\xd1\xdd\xe3\xc0l\x8eb}\x07\xc4P\xae\xfc\xb3A\xd4%\xeb\xb9\xe9\x81t\xdb\xb3\x87\x85\x8e\xf4\xbd\xca\xf41\xe1\x12\x1d\x9d\xad\x9a#\xf6xFT\x1e\x90\x1a\x95\xb1T\xce(\xe8\xcc-6\x05f_K\xf3F\x10g\x8e \x83;\nW u\x0f\xe7\"\xfc\x11d2HA\x87\xc9\xfb6\x1f\xc9\"A2\x8f\x14t\x95\xbbWsQ. \x12\xd9$H\xe5\x93\xc2-{xOa\x19%(\xcd)\x01\x8eU\x82\x92\xbc\x12\x1c\xcd,A\x1e\xb7\x04\xa5\xd8%\xc8\xe2\x97\xc2\x8f\x03~\xef\xe5\x13pLpB\x96 N\xc33A\"\xd3\x04y\\S\xac\x0b\xc6\xb1MP\x96o\x82\x04\xc6 \xd29'\xc8`\x9d\x10]&v?\xe5\xa3\x99'\x88qO\x80\x1f\x9e!\xf8'H\x1c\xc5%sPAo){&cKY\x90\x89\x82$.\nJ\xb3Q\x90\xc9G\x85\xdb\x15r\xff\xe3LN\xca\xeb\x8f#\xf7=.\xc3K\x01\x1e\xfb\x01\x0c7\x05i\xec\x14\xc4\xe0\x84L\x86\n\x10~\x03\xc2h!\x9e\n\xb2\x82\x8b\xe7\xaa\x00Q\xcb\x0c\xbe\nr\x19+\xc0E\xf5h\xd6\n\xf0\xbc\x15 \x99+@sW\x80\x0bz:\x7f\x05I\x0c\x16\xc4\xf6&.\xc2bA*\x8f\x05\x99L\x16\xa4qY\x80\xb8 \xb1=\x88\xa3\xe7\xe39-(\xc8j\x01\xa6h\x81\xa7\xac\x1c\xb7\x05\x18v\x0b\x8e\xe0\xb7\xbc\x0e9b\x7f\xe1\xa2\x1c\x17DY.\xc8\xe5\xb9\xbc\xde0\xfb #\xb8.\x88\xef#\x9c\xcewy]E\xf7\x0f\xceb\xbf\xbc\xde\xa2\xfb\x06\x97c\xc0\x00\xc5\x81A\x06\x0b\x06i<\x18\xe40a\x90\xcc\x85A|;\xdcX)\xb1\xbc\x0e\x96\x11\x83\x1cN\x0cRY1\x08W<\x87\x19\xf3:C\xed\xff\x9b\xc3\x8e\x05\x1f\x08\xcc\xbe\xbf\x05\x192\x88qd\x10f\xc9\xbc\xe7\xe42fP\xb0\xed&\xb0f\x90\xc4\x9bA`\x84\x12K\xc9\x01&]\x8c\x7fk\xd3!\xf9\x91\xd2F\xa7)\x90\x0eM\xcd\xf4-\xda\xa6\x91\x82\xbe?\xb6\xb2G\xe6c\xcaN\xfdx;\x8f\xdfum\xbb\xf2\x8at\xd1:^\x8a\xd3\xa75T\xa8\xdc]\xbdS\xbe\xdd\x95\x0d=\x1b\x81 \x14R\x0dw\xfb[\x7f\xf2\x1be\x88+\x01\xf2j\x80\xd4)\x01?\x19\x06 sH\xc6\x8aj\x96\x90\xa9[B\x8av\x19\xf54j\x9b\x18\xfd\x12Jk\x98\x90\xa3cBI-\x13\x92\xf4\xcc\xa8+;\xe7B\xa2\xa6 etM(\xabm\xc2\x11\xfa&dh\x9cQ\x87\xe2\x95\x9a\xaasBQ\xad\x13\xb2\xf5N\xc0j\x9eQ/SM\x14\xab{Bi\xed\x13\x8a\xe8\x9f\x90\xaf\x81BI\x1d\x14\x92\xb4\xd0\xa8\xabI\xae\x07\x9c\x1e\n\xa7\xd3D\xe1\xc4\xba(\x9cN\x1b\x85\x0c}\x14R4\xd2\xa8';7\x04\xe0uRH\xd5J\xa3\xded?\x91\xa2\x97B\x9ef\n\xa5uSH\xd6N\xa1\x94~\n\x18\x0d\x15\xd2\x86\x8eH-\x152F\x99I\x9aj\xd4\x1b7\xb9'\x90\xba*$\x968Q_\x0d\xfa\x9a\x8ef\x91\x1a+\xa4\xea\xacAO\x08\x0d\x16J\xeb\xb0\x90\xa8\xc5B\xa6\x1e\x1bs\x88\xd5d\xa1\xa8.\x0bi\xf2!`\xf5YH\xd7h\x01#J\x1c\xa1\xd5\x02\xd2\x7fd\xc2\xb2\xa0n\x0b\xd9\xc1O\xd3o\x01Y\xf3L\x1d\x17\x8e\xd1r\x01\x1f\xf1\"\x9a.\xa4\xe9\xba\x90\xa0\xedB\x92\xbe\x0b\xf8\x9b\x92\xa7\xf3B\xb2\xd6\x0b1\xbd\x17Jj\xbe\x90\xa3\xfb\xc2\x11\xda/\xa4\xeb\xbf\x80\xbcI1\x1d\x18\xb0~\xd2\xf4`(\xac \x03\xb6\x98\x91'6]\x1f\x0e\xba\xbb\xbd\xc7i\xc4\x90\xa9\x13\x07\x1d\xea\x1c Q\xad\x182\xf5\xe2\xa0Cn\xe6\xf6\x82\x9a1d\xe8\xc6Agc\x8e\x10\xc4\xb4\nR?\x86\xb8\x86\x0ceud\xc0h\xc9\x90\xa8'G\xee\xbf\x95g$\xa8)C\x86\xae\x1ct\xf6\xf1\xea\xed\x19R[\x86L}\x19\xd25f\xc8\xd5\x99!Kk\x86x\xd7\x04\x08\xdd\x0e\x12\xb4\xbb\x14\xdd\x19r\xb5g\xc8\xd1\x9f!\x1e\x8c\xa2:4`\xb5h(\xadGC\x82&\x0dY\xbat\xb4\x93\x8ej\xd3\x90\xa9O\xc3\x91\x1a5\x14n\xef\x89Z5$\xeb\xd5\x10\x19M\xc55]a\x97\xfb\xdb\xff\xc9\xee\xe7[@\xee\xf6\xb7\x9bz!s4X\x8b\x80\x8d\xdc\x1ck_R\x0c\x17\xf7L\xb5\x1b\x1e\x19\xe0\x8a1\x8f\x1a\x01\x0c\x8a\xb2\xf7\xf8\xbe^7\x15\xdfw\x88\x85\x06$\xe9\x06\x8c$]\x92t'F\x92n\xd4!I\xba@\x92\xee\xc4H\xd2%I\xd7i$\xe9*#I\x97$]\x92tI\xd2\x8d8$I\x17\xa9'\x90\xa4K\x92.I\xba$\xe9\x92\xa4+\x8d$\xdd\x89\x91\xa4K\x92\xaee$\xe9\xba\x8d$\xdd\x88a%.\x92tm#I7t I\xba\xdfL\xd2\xfd`4\xd2\xb9\xaa{\xc7\xbe>g\xcd\xa2\x15\xcd}\x10RcS9\xe2\xc4\xcbMU7\xd7\xec+W\xaf\x1f\xef\xf1;q\xdc\x0dg_y\\\x9e\x8dNv\xe1*;\x16-PY\xf5\xd2\x14U\x96\xf3\xd8A\x872\x1d\xb8\x9cD\xd13\xf8Q\x8dg\x88\xb7\xf3(\xb5M\xc9\xa2mV\xf5:\x7f\xf9\xb5\xdc\xb0\xe4\xb5t2}9*\xc7\xbaI\xce\x97`{\xdd\x85\x96f#\xd4s\x8cr.\x13\xbd\x1f\xd7\n\xb0\xb3\x81\xde\xe0\xd8k\xf0\xd5\xd2{98\x91}\x9f\xd7\x9b\x1cw\xd6\xcd'\xf1\x12\xd8VK\xcf|\xe5\xa2c2\xe47\xbc\xf6\xd7\x13QG3\x9d\xbb\xac8{.|\xe57\x11]\xa2\xebz{\xf0\xe4\x0b\xc7\xa2[\xd7@\x86\xa9\xe1] O\xed-c\x8d\xaa\xa5\xf3\xed\x13.\x8f\xbc#o\xc5%\x0e\xef\xc7P4\xf1\xceb\xb5\x9c\xe2\xaf\x1a\xa8\x1b\xce\xba\xe7\xe0I\x1d\xbc\xe8\xda\xbe\x7f\xae\xdb\xad\xf3\x08g~\xf2P)\xdf\xd6\xbd\x14\x81\x0f\xf2\xde?S\xfbZ\xb4\x8d\x15F\xf5\xf9\xe6\x1eq[\x84\x8c\xb7|\x8e\xd2\x8d{ \xf8\xd2\xca{\x9e\xc1\xf0\xd3g6\xaaq\xb7\xc9h\x8b\x8c\xca\x0b\xa1\xcdtF\x1b\xb6\xcb\xd1{\x96\x0c;\xe7\xc0\xae\xea{%i\xaa\xfdu\xfe\xb1g=?S\xbf{\x9c\xc9}\x16\xa4\x9b\xd0\xd6:\x17\xdc\x1a5\xc9]L|/6\xd1\xe4\x98\x14X\x9b\x16\xb6mg\xb6\xf8p*9r\x97\x8d#\x839\xdb\xc6\xc4\xb6\xd8s=l\xf2\xe1\xdb\xf5\xc5\x12\x08}\xf5\xb5\x03m\xef\x97\xe29\xfcK%7\xd7x\x065\xef\x8d\xc4\xdd\xc3\xbeQ\x9b\x05,\x95*\xf7\xa5\xee\x0f\xdbG\xbc\xc3\xb6\xf7TR\xb5\x9al\xc2V7\xb0\xbe\xba|=l\xe4`\x86\x88=|\x11\xf7\xccu{\xdco\xe7E\xdb)\x1frd\xdc\xa9\xca\x0fc|1\x98\x97\x02\x9f\x1d\x19g8\xcc\x19\x1f\xda\xedX\xee\xe0\x98\xb2c;\xd9g\xc2\x1f\xabn\xb8I\xb1\xaf\xbeIXd\xfb\xf6}\xefM\x87\x87\xba\xf5\x1c<\x86r/\x11\xdfV\x1b\x93\xf8\xab\xfdm\xd4\xff\xcbO\x9f\xc3\xe4\xed\xd2\xd9\x0b\xef\x1e\"\xe2~\xa9\xc9\x9f\xe1<\xdd\xa5\xdaO\xcdt\xd3\x8e\x06\xf6\x8d\xe2s\xd8R\xf1\x11\xa7\xdc\xbbC^\xc0\xd7\xc9:\x1f_1l\xf4\x9d ^Tk\xc7\xdc\x95y\xd8\xeb\x86\xff\xf4\xfb\xd9\xaf\xba\x19%\x95a\xc9xUo\x1e\xc9\x9e#<\xc2@F\xbbBL\xe7\xa0,\x95u4b\xab\xd7!G2\x8eh\xb6\x11\xb3\xd5F\x84iLe\x19%\xab\xe8\xf5\x86e\x18]S\xb2\xc9\xec\"\x9eY\x9c0\x89^\x7fxV\xf1HF1\x8dM\xd4|\x92\xbf\xd8)Lb*\x8b\x18\xdcB#\x85ALb\x0f-\xb6\xd0\xef\x10\xc7\x1c\xa2XC\xec\x96\x19\x08\xc6\xb0\x18[x\x1cS\x98\xc1\x12\x16a\x08\xf1\xec\xe0\x94\x0d\x0c\xf4O\x9e\x8f\x1dc'`\x05O\xc5\x08\x9e\x80\x0dLa\x02\xd1,\xe0\x84\xf5\x0b\xf6\xe1q\x060\x89\xfd\x83\xe0\xd6\x17X\xe6\xcf1w\x1a\xea\x8dS\x19?\xc3\xf0\xf9_\x82Q\xb6\xefh\xa6/\xc8\xf2!\x86@\xd1\x8fk\xfc()\x89\xd5\x93Q\x94,\x9e\xbf\xc3\x8e2z\x98\x92%2y\xa1m,\xf0,^\x12\x83\x17\xe3\xecr\xf8:\xcd\xd09\xfdE\xb9\xba\x1c\x9e\x0e\x02\xbb\x94\xc48\xba2\xfc\x1c\x12\xdd\x8a\xf2r \x9c\\\x08\xce\xc8\xe1\xe2B\xfe<\xdaa!\xfe--xx\xde\x0d\x13\xa1\x04\xbe-\x8bk\x8bD\xeeh\x8e\x0d\xc9\xafa\xb85\x1c\xaf\x16 j:\x9f\x86\xe7\xd2\xbc\x8f\xe2\x91;\xd3OS\xbdn\xdan&)\x98\xa7qz \x15\x99co\xecm\xdbn\x98\x95\xfa\xc3q\x03;\xf6\x99u\xbd\xf7\x0d3\xbby\xfa\xe8\xf9\x8d\xab-j\xa8c\xee'a\xe2G\\\x83)\x84Fb\xb1\xf39\xc5\x0fu\xb3\x10o\xc0\xb6\xdf\xb6\xfd\xf3~\xf9 ~<\xfb\xc3OE\xa3\xc1\xab\xf5d\x80\xf0\xaf\xda\xdb\x8b\xa5\xdc\xf2\xe8\x85\x1e\x1a\xf7/>\xff\xf4b\xc9\xab\xf5s\xdeUM\xbfb\xdds\x0d\xfd\xe8\xb3\xd7l\xc0Q\xfa\xfdv[u\xf7\x93\xa8]4\x8bv[7\xeb7\xd7\xd5\xfaZ{\xd0`P/kP\xb3^I\xbew\x0c\xc4A`\xae\xd3\x1b\xbah\x9c\xff\x15\xcf\xc4\x18(\xa9\x90I\x9ar[-E\xd4\xbfT\x9d\x1eH\xee{\xd6\x8d\x9b8\xad\xeb\xcfl\xbe9T\xbbcj\xf4u\xb1|\x19,\xa4>\xdeP4V\xcc\xbe\xff\xfd\x8f?~\xefGq\xa0\xdf/\x16\xac\xefW\xfb\xcd)\x19\x9c\xe9\xdd\xb0\x8d?\x00\xce\"Z\xca\x0dooxW\xb9@\x1fe\x08A'F\xef\x81l+\xd7\xed\xb5\xb8\xce\xf4KY\xbd(t\xd7\xa7Z\x94\xe8J\x1d}\xa7m\xb7lhx\x1d[\xc2\xaak\x03\xbc\x84\xea\x96\x17\xac\xfe<\xaa@\x06\x85\xd3\x90C\xcf\x9a\xa5G\x80Q?\x9d48\x1f\xe4%\xe6\xb0\xb2\x19\x03\x1b\xddj\xa1\xde\x12*<\xac\xf137V\xfd\x9c\xc7\x98X\x9c\xb4NW&\xe0\xb3Zyo\xc4]\xc5\x9f\xf9\xd9M\x18\xd8\x96gj\xb1\xa8\xf78\xd1}Lo\xab\xf8g\xdd\xc9\xc6\xe5z\x84\x82Uq\xf41c\x8d\xa0\x9a\xf6\x81\xc1\xa8\xdf2\xfeE\xf4~\xfc\x8bD_\xbaC\x9d,T\x92\xa1\x13\x1e/\xce%\xbe\xae\xe0mg\x97|\xd8#\x8ff\xf7\xc1\xaeX\x9any\xf6\x13\xc1\xda\x04k\xc7\x1e\x19\x82\xb5 \xd6\x9e\xda\xa1\xe4\xa4[\xd0\xe5\xf8\x19\xa3n\x94N\xbb:\xfe\xd9\x04u\xe2!|\xcb\xe4\xd084FL\x05\xbf\x1d\xb7K\xb1\xe0\xc1\xd1\xf2\xc8\x83\x8fO(\x01\xe1\xb3_ \x08\x8f\xb5\xe6\xd1\x08\x08' \xdcm\x04\x84K# \xfc\xd0\x08\x08' \xdcg\x04\x84\x13\x10.\x8d\x80p\x02\xc2 \x08' \\\x19\x01\xe1\x04\x84\x13\x10N@8\x01\xe1\x04\x84O\xcd\xffiK@8\x01\xe1\x1e# \x9c\x80p\x02\xc2\xcb\x00\xe1\x06\x9e\xb0\x9c\x84\xbe>\x9f\xb6\x03p\xed\xc31f\x90\x89$\xb1x\xebH\x0d8\xe8\xd9\xd3\xd0\xd6Z\x02\x1bX\x03B\xb6\xd1\x88\xf1\xa9\xa9bB\xb6O\x18\xdc8lL\xc8v\x89(\x12\xb2M\xc8\xf6\xaf\x1f\xd9\x96/\xfd\x00\xa2})\x7f\x1f\x08l \xc9\xe8\xd3a\xdb.\xf7\x1b\xa6\xc6\x0dN^\xfaR\x1fyi\x1f\xf1h i;\x14\xb6y]A\x14\xd3h\xea\xc5\xa7\xc6\x9b\x879\xe8\x18\xa2\xceAJ,\xcd\xcd\x865k~\xe7\x9f\xe0B\xc8 (!d[}\xfdF\xd7\x8aa~\xeftXu\xe3\x9c\x12\\f\x10\x0b\x1d\xdb\xc8)\xfa\xe0V\xb7Cc6\xb7\xca%\x1a,y\xe5I\x1d^\xe0\x16vl}\xc3\xbe\x1e\x15\xd2_u+xs]\xad\xd17Z\xdfNy\x92\xebN\xde\xd6\xed\xc9n\xe47\x8f\x8c\xf7\xf9\xf8c\xdd\xa6F\xcc\xe3\xe9\xb6n\xd7]\xb5\xbbs\xcdo\xb6]\xb5\xd8\x9c\xaekSo\x80\x9bzY(\x9a\x01\xac\x18\x101\x05\xa9\x90\x8b\"]\xbc\x99\x7f\xcb^\xbc1\xe31\x15\x13]v5Z\x08\xf8[T\x9b\x8d\xba\x0b\x9fYW\xaf\x1c_\xb9S[V\xdcG\xbaT\xfd\xa7\x1b9x{<\xd1z\xd5\x7fz-\xc7\xaa\xb3h\x8d\xa3U\x15\xad~\xf8\xee\x17\xb5\x088\x9c\xc4)\x14\x8c\xad\xdc\xf0\xe1Q\x05\xe3/u\xe3\x0c\xc6\xb6n\xea\xed~;\x8e\xea\x87\x98\xdc\x05X\xc6\xe1C~\xd1nw\x1b\xc6\xa3\x9bd\xc8\xb8i\x1a\xc9\x1aNy\xb1\xb2]\xc7vU\xc7n\xd6U`\x87\x89o\x1d\xc3KU\xa8?U\xfd\xc1d\xd2\xd6\x84o]\x99\xcf\xd5}\x88\xa6\x02X\xee;3\xad\xa2*+c\x13\x8aa\xcf\xab5s>\xe8\x9e\x93\xd8W\xb6\xd8\xf3G\x16\xc4sU\xa8\xe2AT\x95U\xaaG(\x8a\xd8\xe0\xad\x18\xbbQ%\x8a\xc5\xce\xcd]\x1b\xf3\xf2\xd7\xc6\xa2\xaf*e\xf1\x17\x96\xb2%k\xdam\xf8\x10\xe4M\x17\x16\x8b\x802\xa4;\x0c/d\xecu[\x8fK#*\xe0\xed'\xa67o\xa9T\x15\xcd\x94B\xd5,\xa1\n\xef\xe8\xa3*\x11\xdbW\xf8\xdd\xfb\xeb\xf3\x97R\x91\xd3\x0dQI[\xb5\x84\x96.\x1a\xae_\x11\x03(\x16\xde\xd4\xd8R\x05tn\xfc\xe0\xe1\xc3nL\xfd\xf0\xad\x0d\xb7\xf7\xb0n\xd7\xad\x9cv\xf7\xab\xc8\x98g\xeeg\xc6^m]\xbd\xff\xf8\xc8\xad\x18\x1b\x08\xca\xea>\xb6K\xd2\xb0G\x92\xeebb\xdd\x7f\xfc\x81\xf3md`\xec\xbd\xf4\x80\x1eU\x8eO\xb9\xc7\x9f\x9c\x033\xba\xd0\xbe\x9f\x8f\x82T\x06\x9a\xfe\xae\xdeI-\x16\x16\xac\xe1]p\x13\xefj\xc4m\x87\x15\xab\xb7\xf7P\xc1\x1b9\xe9\x11\x18\xe7V\xbb\xdd\xcd\xa6n>\x05\x16Z\x1c9\x94\x95@`\xcd\xefo\x96Z\xb9\x89ui\x91G\x19\xd3\xe6\xf4v\x11\xc3\xb5\xc1\\\x1bn%\xca+Q@+f\xce\xed{Fc_wu\xe7!\xeab\xc5y\xb5\xdb\xbd\x15\xf1\x9d\xb4\x9eH\xe3\xa9v;\x8f7y\xab\x0e~\x1b\xd6u\xc9KL\x14p\xeb\"+\xcd\xaf\xce\xa6\xb0&\xde\x10k\xbc\xd4U\xa2\xab\xb9\xe4_\xd5\x82-]\xae\xab\xcb\xd73\x7f\xb4P\x8b\x16jE\x9fx\xec\x8b\x9b\x16j\xd1B-\xb7\xd1B-i\xb4P\xeb\xd0h\xa1\x16-\xd4\xf2\x19-\xd4\xa2\x85Z\xd2h\xa1\x16-\xd4\xa2\x85Z\xb4PK\x19-\xd4\xa2\x85Z\xb4P\x8b\x16j\xd1B-Z\xa855\xff\xa7--\xd4\xa2\x85Z\x1e\xa3\x85Z\xb4P\x8b\x16j\xe1\x17j%\xf1\xdd\xe6\xdf\xbf\xec{\xd6\xfdS\x9d\x16\xc9\xc5\xad\xb1m\x17\xf4\xad\xd4\xd8\xe1\xa3_\xae\xdaRi\xb3\xc7T\xb7`M\xc0\xeaU^\xd6`L7x\xe5{\xa9<,[\xa6\xe6V\xe4\x94Xe.\xf6L\xf7\xe3\x1a\xb6\x1f\xf4\xb5i\x1a\xdeAQ\x80f\xbf\xd9\x98sM/\xea\xe2\xd1\xf5O\x8f\x17DW\xc5\xf4\xc9WY$zX\x85\x8aN\xc0`f0\x8a\xebO8\xf5)G{\nkLY\n\x93\xbc\x84\xc7aT_*\xa0.ejK\xde\x19y\x9c\xb2t\x94\xae\x94\xa5*A\xb5\xd9\xf8\xa2\x88\xd3\x94r\x14\xa5\xd0\x87VH\xa99N\xab\xc9Pk\x8a\xe85\xc5\x15\x9b\xa8fs\x02\xd5\xe6T\xba\xcd \x94\x9b\x14\xed&W\xbd \xf6\xe11\xfd\xa6\xa0\x82\x83\xd5p\x12U\x9c\xe2:N\\\xc99Z\xcb\xa1\xf5>\xd1\x92\xe5i;NW\xb4\xde'G\xe5\x89\xe9\xb4\xdb\xb1\xdc\xc1i\x9d\x8e\xed\x98\x9c~\xffc\xd5\x0d7)\xf2\xf18\x0d\x8bl\xdf\xbe\xcf\xc7\x00\xbbu\xf00\xca\xf1\xa6\x1a\xa5\xa5\xa0[zhI\x99\x9a\x89\xdcr\xfd\x1e\xec\xf1N z\x86\xbe\xfd\xaca\x91\xaf\x11\x0e\x03\x91\xc9\xf7\xde\xf8\xadd7\xc4\x9c\x8d\x07\x0f\xaa\x13\xd9_S\x7f\x03\xf8\x8a;\x8c!\x9dc\xdf\xee\x1b\xd5bZXolg]|\xb8\xecK\xc7g\xb7\xfcEv?O\x0f\xf6Y\x91s\xc1\xf5l'\x15;\x14\x16\xb7\xf3\xc3\x89\xeaO\xdb\x1ec\xa2\xe5\x195\xd2\xb6\xc7e\x83\x1b\xdf\xb0\x97\xb6=.\x11E\xda\xf6\x98\xb6=\xfeun{|0\xe1<\xf9\x83\x7f\xde\xd9\x8a\xd8\x95}\xc6d\xd2x\xe2\x0b\xf4\xdc\x8a\x9e\xfe\xb12f\x0d\x9e\xcc\xc0L\xffa2\xb5<\xb9\x8a>\xe0\xd1\xce0;\x82h\xdbC\x8c\xe3\x17\x1d\xab\xb8\xebcH\x19b \xae\xe7%^+G\xf3\xc92\xed\xdftFv\x04\xdc\xe5\x11\xbd\x0b\xebvU\xc7=\x13S)\x85\nLM\xbe\xb6.t0=+\xe7\xb2\xe5 \xb4n>\xb7\x9b\xcf\xee\xe7\xd8\xb6h\xc5\xfe\x93\xcc\xcb\xd6\xcb\x83yY\xf1\x82\x90\x93\xaf^w\xf3;\x0bu\xef\x9f\x98\x95`\xa9\xe3\xd7\xf8\x94\xd0\xb4O1#\x92\x9ew\xfb\x85|\xdbU\x93Bx \xa7\x0bK\xd8U-\xb4m\x16l'\x1d|\xa7\x96\x04|'C\xd0U\x03\x9f\xd0\xb7\x12\x8bv\xb9k\x18\xff\xd2v\x9f\x0e&\x04i\xce\x96\xe6l\xe3O\"\xcd\xd9\xd2\x9c\xadm\xc9s\xb6\x93\xe1\x0fj\xea\xd6\xe5\xe5\xc5t\xacFko\xb5\xd1\x0c\xee\xafa\x06w\xfa\xb9Q~\n4:c\x10\xe2\x8e|3\xa1r\xf09\x8c\xbc\x02\x15*\\\x19Sf{\x1c\xee+;rR\xd7v\xe5\xfa\x0e\xe8\xd5\x94\xae\x9cI\x988\xacW\x87/\x005(\xa7\xf9\\\x9a\xcf\xa5\xf9\\\x9a\xcf-\x1cE\x9a\xcf\xa5\xf9\xdc_\xe7|\xae\x19\x19\xc9}\x0e\x86\xff\xf1\xcf\xe3\x9a \xa2\xfe\x90\xf6\x1d~\xd1\x13\xb6j\xcfx\xed`27;8yb\xaa\xf4H\xe7eg\x01\xb1\xed!F\xe6\xdfl\x86\xf0\xa3L}\x03\xf5R\xf5O\xb3\x048\xb3i\xf7\xa9\xc9G/\xbf\x94\xba\x00\x7f\xdeo\xab\xe6y\xc7\xaa\xa5\xec\xba\xec\xa41\xc1\xab\xdb\xad\xe4\xe8B\xbc7\xe3V\xcb\xab*\x85\xe8\x18B\xc5\xe0\x1d\xab\xfa}w\xda\x89\xea\xab\xe9\x04\xad\xb9&T\x0b\xf5ZP\xb7N&!i\x17\xb5\x9c\x9c\xf0\xae\xc5\x02\x80xp\xdb/M \xf2\xfc\x95\xe3\x83F\x96\xb5\xfd\xd2 \x1aX1abV\x8e\xe1\x1bQ_\xc0\x7f\xf1\xbamnx}T37\x0f\xe3\xb2\xe2\xec\xb9\xf0\x15.\xea \x98\xc8e?5\xf2a\x18\x17\xd2\xdd\xac\x18\xbb\xe1\xed'\xd6xz\x96p\x7f\xa6\xcc\xdb\xab)\x8b\xf4m\xcab=\x9c\xb2%k\xdam\xe8\x00T\x8c\x85U[\xf1(\x14p\x15\x9f\xe34\xf6\xba\xad\x1b\x95iH\xbc\x1cAF]\xe7\x93P\x153\x03\xb0\xaaYB\xe5\x07\xfaM\xe1CIM\x00\xde\xbd\xbf>\x7f)Wz\xaa\xa3\xf5\x92\xc9Z&\xc3\xb8h\xb8\x9e&\x18\x12\x90\xf4\x8e\xa9>\xdb\xf4Z35\xcb\x17\xbap_\xaf\x9b\x8a\xef;\xd6\x0f#\x121X_\xb7\xebV.\xe5\xf2\xadL\x8e\xf7ko\xeb^\x8eiW\x8c\xe9\xe8\xc9\xb0\xf5*\x88\xe6ka[7\xf5v\xbf\x85u%\xbe\xf9\xeb\x85'\x1f\x06\xa8\xd4Y25\x91\x1e\x97\x84\x13X\xf9\x1f(]\xf2\x0fC?1\xa4\x1d\xd1#\xa0e\xc5+%7\xa9\xa1\x8f\xcf\x19I@$\x01\xc5\x1f\x04\x92\x80H\x02\xb2m*\x01\xf9Z\x8f\xfc\xbe\x1b\xbe\xaf\xf2\x85\x9f\xf1;\xef\xea\xf2\xf5<@\xfa\xed`\xfe\x97$\xa0\x83\xf8\xfc\xd6% L\x8f\xa0\x8c\xd2\xafR\xfaU\xb7Q\xfaUi\x94~\xf5\xd0(\xfd*\xa5_\xf5\x19\xa5_\xa5\xf4\xab\xd2(\xfd\xaa\xbfMS\xfaUe\x94~\x95\xd2\xafF\x93\x88R\xfa\xd5\xdc\xc8Q\xfaUJ\xbf\xea0J\xbfJ\xe9W)\xfd*\xa5_\xa5\xf4\xab\x94~\x95\xd2\xaf\xfe\xe6\xd3\xaf\x12R\x9f\xc6+\x13R\x7f\xc2\xe0\xc6apB\xeaKD\x91\x90zB\xea\x7fKH\xfd\x8b_\xacu\x8a\xffT\xe7G\x12\xa5\x0c\xa4\xd9\x1c\xb5\xaf\x1b\xf5\x9cI\x8a\xf0\xb6\xdd\xab\xe6>\x00\xacr\xf6\x94\x9b\xb4\xb2\x83\xbbay\xa4\x93\xc5\xd7\xbf=z\x14\xbf(2\xe7\x03\xea\xa3\x93\xfbx\xbe+\x1b\xa5\xf7\x83\xf4\xd1\xd2\x1d\x05\xd1G\x11z\xec\xe5s\xf1\xf90<\x8f\xbd\xbaWL)\x8b\xcdG\x82\x19@\xe6\xb1\x159\n\x97\x0f\xc2\xf2\x99%@\x80\xf2\x08L>zq\x1c\"\x7f\x04 \x9f\x80\xc7\xf3\x08\x1c\x1fD\xe3\x83\xfd\x93\xb2\x18\x8f\x05\x18(>\x1aQ@\x01\xf1\x087\x18m\x10\xd2Qx\x04\xf0^\x16wG\xc2\xeey\xa8{\xac\x1f*\x8c\xb9\xe3!w\xef#Q\x08pG\xa1\xa9(2u\xc6\xa2\xce\xfc\x11\x85J\x14j\xa9\x9e\x8a(T\xa2P\xddF\x14\xaa4\xa2P\x0f\x8d(T\xa2P}F\x14*Q\xa8\xd2\x88B%\n\x95(T\xa2P\x95\x11\x85J\x14*Q\xa8D\xa1\x12\x85J\x14\xea\xd4\xfc\x9f\xb6D\xa1\x12\x85\xea1\xa2P\x89B%\n\xb5\x0c\x85\x8aH#~q\xb8\x07\x8a\xc9\xbfm\x9dr\xe4\x96\xff\xe0\xc3KJpG/\xe4\x0c]\xd5\x88\x1f\xd6]\xbb\x0fm\xd9\xf4'\xf1\xfb\xab\xe1x\x8de\x8dj\xe0\xba\xab\x1a.\xf3\xa6\x83re\xfa\xdf _4\xf3\xf2\xc4\x04\xe4\x91bF\xaaR\x8fC\xff\xb2n\\\x81\xf9\x1fL\xde\xcf\xc0\xdc\x8f\xa3\xedk\xa9y\xb6\xff\xfa\x97\xaaWQd\xfe1\x1c\x18\xc1Z\xb4 \xe7\x11\xcaC\xf9,\x8f#>c\x95\x11U\x96\xc8\x1c\xdc\xd1|\x07\x8f\xa8\x9d\x80\xab6 g\xcd\x8c\xa5*\x9fAg\xee\xe9\xaf\xc0\xf7`i\x05\x14\xe2*(d(\xa1\xe1\nT\xfc\x0e\xad\x86B)E\x142U\xd1\xa0C\x11\\\xb42\n\xc7\xab\xa3\x90\xac\x90\x06]i\xe5&I%\x85\xd2J)$\xaa\xa5\x90\xaa\x98\x86[\xf6\xa0\xa6bUS(\xad\x9c\x02N=\x85\x92\n*\x1c\xad\xa2B\x9e\x92\n\xa5\xd4T\xc8RT\xc3\x8fC\xa5vO\x89>7'QV\xe1\x84\xea*\x9cFa\x85D\x95\x15\xf2\x94\xd6X\x17\x8cS[\xa1\xac\xe2\n \xaa+\xa4+\xaf\x90\xa1\xbe\"\xba\xcc\x1f\x10\n,\x94Pa!\xa6\xc4\x02~x\x86Pd!q\x14\x97\xac\xcc\x06\xbdI\xd5\x16\xa1\xceBB)\x0b\xaa\xb4\x90\xa4\xd4Bi\xb5\x162\x15\xdbp\xbb\xea\xe3\xaa-\xe4+\xb7^\x7f\xe2\x8a1\xf5\x16\x8a)\xb8\x80\x17\"\x01\xa3\xe4B\x9a\x9a\x0b1\xb9$S\xd5\x05\x84\xdf\xc0Tm!\x85\x17\xb2\x82\x8bWz\x01Q\xcb\x0c\xc5\x17rU_\xc0E\xf5h\xf5\x17\xf0\n0 U`@+\xc1\x80\x0bz\xba\"\x0cI\xaa0\x84\x94a(\xa5\x0eC\xaaB\x0c\x99*1\xa4)\xc5\x80\xb8 !\xc5\x180\xe7\xe3\x95c(\xa8\x1e\x03\xa6h\x81\xa7\xac\x9c\x92\x0c\x185\x19\x8eP\x94\xbd\x0e\xe5\xbc`@U\x86\xd2\xca2D\xd5e\xc8U\x98\xbd\xde\xd4\xf7ox*\x00\xa14CXm\x86,\xc5\xd9\xeb*\xa8DC\xae\x1a\xed\xf5\xa6\x86\x91\x81I\xb7r\xaa4\xa0\x94i\xc8P\xa7!M\xa1\x86\x1c\x95\x1a\x92\x95j\x08w#\x10Q\x0f!AA\xc4\xaa\xd6\x90\xa3\\C\xaaz\x0d\xe1\x8a\xe7\xa8\xd8^g\x96F\x8c}dpjv\xf0\x81h\xd6aE\x1b\xca\xaa\xda\x10S\xb6!\xacn{\xcf\xc9U\xbd\xa1`\xdbMP\xbf!I\x01\x87\xc0\x08E+e\xd7U\xb7V\xa99FEo\x10\xc6d\xcb\xbbe\xac1\xba\x99\xd3\xd7p8ie\xd1I\x16\xd2\xcaH+#\xad\x8c\xb42\xd2\xcaH+\x03\xd2\xcaH+\x9b\x18ie\xcaH+#\xad\xcc6\xd2\xca(\xe1\xf41\x88G\n\xde\x91\x81vP\xc2iJ8M \xa7\xb1hFQ,#\x07\xc9\xa0\x84\xd3\xbe\xc3\xa2\xe8E\x02v\x81I\xa7\x9c\x82[P\xc2iJ8\x8d@'(\xe14d\xe2\x11 h\x04%\x9c.\x80>D\xb1\x87\\\xe4\xc1\xfb\xae\xa1\x84\xd3\x87F \xa7\xfd\xb8B\x1cUH\xc5\x14\x12\x10\x85d^\xe3\xf3(\x1e\xb93\xfd4\xd5\xeb\xa6\xedf:\x85y\x1a\xa7\x97P\x919\xf6\xc6\xde\xb6\xed\x86Y\xcb\xbc\x1d7\xb0c\x9fY795t\xf3\xf4\xd1\xf3\x1bW[\x14S\xc7\xdcO\xc2\xc4\x8f\xb8\x06SHO\xdb-Y7\x9f\xa8\xfcP7\x0b\xf6\x12\x16m\xbfm\xfb\xe7\xfd\xf2\x13\xfcx\xf6\x87\x9f\x8aF\xa3\xf4~\x11\xe25\x1d\xd8.\xe2c\xcf:\xdcn\x11\xd2\x91\xb9\xf3\x93\xcd\"\xa6>\x9e\x98Z\xd3^\x11Qt\x85\xf6\x8a\xa0\xbd\"\x0e\x8fA,\xb0\xc6\x08^\xc6R\xa1\xa5\xa03\xb7r\x15\x98\xca-\x0d/A\x1c`\x82\x0c\x88)\\\x01\xca\x7f\x93\x0b6A2\xdc\x14tE\xf9o(\xffM.\x00\x05y\x10\x14\x94\x02\xa1 \x0b\x86\n?\x0e\x94\xff&\x0d\x8e\x82D@\n\xf2 \xa9X\x17\xccQ\xa0\x14\x94\x85\xa5 \x01\x98\x82th\n2\xc0)D\x97I\xf9o\x94%CUAo\x94\xff\x86\xf2\xdf\xcc\xac\x0c|\x05x\x86\x080\x10\x16\xa4\x81X\x10#\x1d2\x81,@\xf8Edj9\x12\xce\x82\xac\xe0\xe2!-@\xd42\x03\xd6\x82\\`\x0bpQ=\x1a\xdc\x02<\xbc\x05H\x80\x0b\xd0\x10\x17\xe0\x82\x9e\x0esA\x12\xd0\x05\x94\xff&\x9e$&\xf8s\xf4|<\xf4\x05\x05\xc1/\xc0\x14\x8d\xf2\xdf\x94\x85\xc2 \n\x86A.\x1c\xe6\xf5F\xf9o(\xff\x8d\xcf\x92\x013H\x86\xcc\x80\xf2\xdf8-\x07@\xf3:\xa3\xfc7\xc6(\xff\x8d\xc3h\xaf\x08\x87a\xe7/\x80\xb42\xd2\xcal#\xad\x8c\xb42\xd2\xcaH+#\xad\x8c\xb42\xd2\xca\xe6FZ\x99\xc3\xb0\xa5$\xad,\xdc\xaeH+;4\xd2\xcaH+\xcb\x9e~#\xad\x8c\xb4\xb2\x90\x91Vf,Z4\xd2\xcaH+\xb3\x8c\xb42\xd2\xcaH+#\xad\x8c\xb4\xb2\xc1J\xb5\xdd\x87\xd4\xcah\xaf\x88\xc1=\xed\x15a\x19\xed\x15A{E\xd0^\x11\xcaN\xbbW\xc4\xc1=\x93K\xe1\xa7\x8b\xcd\xf3\xb7\x8a\x98-Z\x9f\x1dC\x1bG(\xa3\x8d#h\xe3\x88\xd1h\xe3\x08\xda8b\xb4\xa2\x9cH\n#\x92\xc4\x87\xd0\xc6\x11\xc7\xb2 \x19\x1cH\x11\x06$\x9d\xff\xa0\x8d#\x8e\xe1=RX\x8f\x0c\xce\x836\x8e\xa0\x8d#h\xe3\x08,\xa7Q\x94\xd1\xc8\xe13h\xe3\x08\xdfaQ\x0e#\x81\xc1\xc0l\x8b\x90\xc2^\xd0\xc6\x11\xb4q\x04\x82\xa3\xa0\x8d# \x93\x95H\xe0$h\xe3\x88\x02\x1cD\x94\x81\xc8\xe5\x1f\xbc\xef\x1a\xda8\xe2\xd0h\xe3\x08?\xbb\x10\xe7\x16R\x99\x85\x04^!\x99UH\xe3\x14h\xe3\x884\x16\x816\x8e\x18\x8c6\x8e\xd0\xf6\xeb\xd98B\xe6\xc8\xf5\x95\xfd\xe9\xfb\xe0F\x11\x07[D\x88\xcfh#\xbf\x9d\xcd*\x92\x9aA{V\x15\xda\xf9!%Z\xb4\xf3\x83\xb2\x13\x077\xbeg\x01\xed\xfcP\"\x8a\xb4\xf3\x03\xed\xfc\xf0\x1b\xde\xf9A\xc2}\x81\xfd\x1e\xac\xf0}\xecY\xf7'y\xb8,x\xcd\xecm\x1f\xe4\x9fe\x8b\x17\xf1\xd3CQ\xb3\x1f\x80=B\x19\xdcM\x85\x14\xe9\xa5\xfe\xcc\x1a\x18\x86;\x07{F\xa8\x8b\xeb_\x1f\xf1~\x11cDm\xe3\x0f@\xac|\xfb\xfd\".\xde\x1c\x0cIg\x9bB\xd4\xbd\xde\x05\x8c}\xad{\xcf\x1a\xfc\x9eI\x0d Zl\x1f\x86\xa4\xcc\xbd\xcf\xd7\xd4b\x08\xe3dG3k\xbc\xad\n\xe8\xad[\xed\x97\x12\xa5T\xe5\xfc\xf5\x9b\xd5\xf6\xa3\x84\x88\xa0^\xaa'v\x86\x12\xb9x_e\xb2\xef\xcdoG\xfa\xea\x7f\xdeo\xab\xe6y\xc7\xaa\xa5|w\xd9\xec\xcd\xb8G\x9c\xd3\x85\xfd<\x1f]\x0c\xf3\xfda{U\xe50w\xd1y\xfe\x8eu\xdb\xba\xef\xeb\xb6\xf1f#\n=\xe9\xca\xbc\xcf\xbb2|%\x02\xed\xf6r,\xa8\xfe\x9a\xd2S\x02\xc36'\xed\xd0\x7f\xcb\xbdz\xc4\xd7\xa8\x9f\xe5\x8aE\xc64-\xd3O\xcf\xc1r\xf1\xa7v\xa5\xae4;\x9d`r\x82\xc9\xe3-\x9a`r\x82\xc9m\x9bJYQ\x06\\\x0d\x1e\x8f\xe3\xbf\xf5\xe8\xf7\xea\xf2\xf5\x9b\xb7\x99\x01\xbb'<\xb8\x96\x1c\x06\x873@\xdcM\x7f\xeb\x1f\x1f7\xfc]\x94b\x8c\"\xdcQ\xd9\x05\xcf\xde\x95\x80\xb7c\xe8\xb6o\\6Z\x1cd\x8e\xa1\x82e\x91m\x1f\xb0\xfd\x0dj\x98\x07j\xfb1\xedh[9\x12\xd1\x8e\x02\xda\xd8\x02d\xc1\xd9Q4\x9bG\xc0\xec \x96\x8d-\xba\xb7U\xe6\x01\xd9~\xb0+\x10\x8bl\x18\x1b\x87Q\x16\xa0( \xa2$\x88\xd2\xf9;A\x94\x96\x11DI\x10\xe5h\x04Qr\x82(\xddF\x10\xa51\x82( \xa2$\x88\x129J\"\x88r0\x82(m#\x88\x92 J\x87\x11D\xe9:\x86 J\x82( \xa2$\x88rj\xfeO[\x82( \xa2\xf4\x18A\x94\x04Q\x12D\xf9\xa0\x10\xa5$\xcd\xec\xdeh.\xb1\x9d\x1e\xa24$F\xbc\xd8J\xca3\xf0\xe4Q\x85\xc4\xa1\x92\xa7!N^l\xc5c\xd6\x05\xf2\x10\x0e\xc2\xe1_\xd4\x91\x07)\x08\xb5\x07\xf9\x1a\xd4\xc0\xc9\x81T\xedfJ\xb4\xc7'&J\x8f\x14-\x99\x84\xc86\x9e\xaf\xe49\xe7\x99(\x1d\x17\xa5\xe3\x8a\xd3\x0c\x94\x8e\x8b\xd2q\xd9\xe6L\xc7u\xf0\x18N9\x12\xdd\xf3\x16\xc0I\xcc[\x81\xf6aVF\x18 a$\xa3\x11FB\x18\xc9h\x84\x91p\xc2H\xdcF\x18\x891\xc2H\x08#!\x8c\x049J\"\x8cd0\xc2Hl#\x8c\x840\x12\x87\x11F\xe2:\x860\x12\xc2H\x08#!\x8cdj\xfeO[\xc2H\x08#\xf1\x18a$\x84\x91\x10FB\x18 \x16#\xb1\xd2vL\x12q\x19\xa8\xe2\xd8,b8\xb6\x84\xd2p\xe5\xe68\xa24\\'\x0cn<\x81\x14\xa5\xe1*\x11EJ\xc3Ei\xb8~\xc3i\xb8\xac\xec5/~\x11/\xe4@\x1e\xae\x8f=\xeb\xect2v\n.\xcb\x8fag,\x1eR{: !-oOL\x08\x1e)\x08\x19L\xf3\xc3\xf3y\x94\xc7\x0f\xbeDs\x81% ?\xa8\xed\x9c\x0fG\xb6\xa2MY\xc9\xb3\xc6\xbb!\x9ed\xff\xe6\xc6\xb1\xbc`1\xa2IY1Qx\xa4\x14\xd44:\xb6\xf1\x07P\x1a\x1f\x05\x994\xdb\xd9o|\x0c\xbc\x9b\x15\xfa7\xf1\x8b\xe9\xc6\xca0\xb8\x84\x0f\x950\x85\x13#;=\x16\x9e\xb5\xcb\xa9\xed\xaa\x8e5<\x1a\xe0b\x05vn\xa2\xa8\na\xca\xeet\xd0xw\x1fD\xb5\x01}\xf5w\x96\xc0\x99\x1a)\xfby<\xba V\x18\xde\x8c~gEs=P\xeat\xb3\xc5\xf9\x14\x0b\x19p\x90\xben\xd6\x1b\xab \xbb\xfdQFF\xca\xc8H\x19\x19)##\x1c\x9f\x91\xd1\xbd\xb3\xa7\x19\xf4\xc431*\x92l\x18x]]\xbe\x9e\xf9#\xa8\x8c\xa0\xb2\xd8\xab5\xfe\xf0+#\xa8\x8c\xa02\xb7\x11T&\x8d\xa0\xb2C#\xa8\x8c\xa02\x9f\x11TFP\x994\x82\xca\x08*#\xa8\x8c\xa02e\x04\x95\x11TFP\x19Ae\x04\x95\x11T65\xff\xa7-Ae\x04\x95y\x8c\xa02\x82\xca\x08*{4P\xd9\x00\x01\x15\x06\xb3\xa2D\x19e1LK\x11GY\x0cO\x18\xdcx\x1b\xa5,\x86%\xa2HY\x0c)\x8b\xe1o \x8b\xa1\x1b\xd5}\xf1\xcbH)\x07\xd2\x17\x1a(n\xbe\x87s\xdd\xa8\xe7L\xfcV\xdd\xb6{n\xbf\xbf\xd5\xc4/\x1f\xb2\x19\x0e\x03\x02\x17\xbe\xfb\xc4\x04\xe0q\xd3\xbbEq\xbe(\x83\x1b\x95%\xf0\x10Z\x11\xfa6\\\xce\x10\xc8\x1a\xc7X\xcbP\xb7\x11\xe6\xb6`A\xf3h[?k\x1b\xbd\xd7Gs\xb6Q\xca\x16[\x84<\xc2\xb6\x1c_\x8b!\xe3\x12\xc18\xe2\xe2\x88\x8bs\xfdN\\\x9ce\xc4\xc5\x11\x177\x1aqq\x9c\xb88\xb7\x11\x17g\x8c\xb88\xe2\xe2\x88\x8bC\x8e\x92\x88\x8b\x1b\x8c\xb88\xdb\x88\x8b#.\xcea\xc4\xc5\xb9\x8e!.\x8e\xb88\xe2\xe2\x88\x8b\x9b\x9a\xff\xd3\x96\xb88\xe2\xe2m\x1a\xb5\xd6\xf1\xc6\xd0\xac\xa6\xf3\x899{\xb0\xcc\xc2B{\x87\xa5D\xcb3\x1bD{\x87\x95\x0dn|\xd7+\xda;\xacD\x14i\xef0\xda;\xec\xd7\xb9w\x18\x8a\x18\x0f\xa0\xe1\x1f\xf4\xb123\x96\x93\xfd\xeeMr\xb3J#\xdb\xb3\xad\x8b\xa6\xbb\x84\xd9\xee\x9e\x98\n?R>\xdb\n\x8dm\xfc\x01\x08\x96o\x0fg\x07\xd4\x9bh~;\xc3\xbd\x8aO\xa5[\xc6\xfcr\xe0\xa2\x93d\x9a\xbb\xcaC\xae\xbap\x8dC\xa9\xc80\xc9\xc8\\8\xba\xdeL*\\\xab`\xe9c\xa5.v\x9f\x0e6Z\x13et\x9e\xc2\xber\xd65\xd5&\x1aQ\x0c\x0dn\x8d\xfa\xcf\xb5_Q\x06\x85\xe3\xd5\xbd\xbf\x14\x9c}\xe5E/\x7f\xad\x11\x94p\xdd\x1b^\xfb\x9f.g\xad\xf4\x19\xb0h\x9bF\xbd\x12\xe5\x17r\xa8n\xd1\xc7\x1c\x10\x8f\xba\xb0\xbb\xaa\xbf\x9b\xf6\xec\x87\x16\xea\x86F\xf3vH\xa3!\xca\xac\x0cSre=\xaf:\xef]\x1e\x0dq\xbfGC=\x14\xa3\xc5\xbb\xb1\xd1.\x9a%\xfbj\xda\xd0\x00\xb5\x99\xc7_\x8e\xbdD#\x0b\x00s\xa3\x8d\x9d\x05\xaf\xd6*\x10~:A\x19k\xbc\x0f\xe4h\xbf\xfeX\xb1f\x19\x8b\x14\xaf\xd6\x85#\x95R\xf5\xebj\x0d\x1d[\xb1\x8e5\x0b\x06O\xe54\x86\x9e\xdaxf\x1eY\xf5\x81\x13\x9al2\xc6\xf8\"$\x9f\x0dE\x13\xfd\x9b\xba\xb2\xb5\xb4\xe2g\xb4\x9c\xda\xdb\x1bg\xabk\xe9y\xed\xb1\xae\x98\xee\xf3c\xb7\x99m\x9f\xad\x16MI\xa4Bq\x93\xf5\"\xe2L\xbbz[7\x9f2\xfa\xcd\xd07f\xbc\xbf\x8c\xf4\x95\x88\xc8\x9a\xf7\x88\x1a\x8fn\xaa\xf9W\xf63\xd1PV{\xb9\xb4\x06\x16\x15g\xeb\xb6\xab\xff]\xce&:=V{~\xe7Z,\xa7\x0c_\xa0W\xd2OtVa\xd16\x9fY\xd7W\xa8y\xaab3>\xce\x1d\xeb\xdb\xae^\xd7M\xb5\xd1\x0b\xceuwc\x95\xcf\xe9s\x182,o<\x93\x9d\xca\n\xb5\x85\xc8;\x13\xf7\xbe\x94\xae0\x8f\xd7\xb5F\xa8\x87J\x86O\x8a\xdf$e\xac\xd9oc\xdd\xcas\xb8|\xff\xe1\xfa\xe6\xea\xfc\xe7\xf3\xab\xf3w\xaf\xcfo\xae\xffvy~\xf3\xf1\xdd\x87\xcb\xf3\xd7\x17?_\x9c\xbf\xc9:\xff\xea\xfc\xf2\xed\xdf\xb2\xce\xfc\xd7\x8f\xef\xaf\xcf\xb3\xce\xbc:\x17\x7f\x0d\x9ej\xd6\xcb\x1eUg\x0c\x9f?Z4\xbe/\xe1]\x8b\xbc\xf1\xd6\xea\x81X\xff\x1f\xb8-/\xd5:\xcbq\x0c>\xcbf\xb1\xdb\xdc\x9bt\x13\xe3b\x85\xc0Db\xe4\x9a\xf2\x86\x86\xaf\xf9\x8f}\xcb\xc7%\x04\x05\xae\xa9\x9aB\xb4\xa2V\xff\x83\xbej$\x85\x8b2\xf4\x13\x8a\xeaL\x95\x1dL\xfd\x8f=\"\xa2\xcc\x12\x9cz\xb0BG\x9f\x92K]\xc2\x83\xba\xcd\x07\x08\xdf\xf7r\x88\x10\xc0\x7f\xa5\xa9\x95\xc4\x03U\")\x8c\xb6\xd9\x04V\x8e*\x1bR:\x89`\x18\xc5\x15\xd1/\x99\xac%m\xcf\xaf\x86\xa2\xfb\x07L\xb2\xe5\xc5\x9e\xf9x\xec^\xa9\x1c.\xc3\x18\xc4j\xeb=\xfa\x85#\xf1q\xb6\xac9[~\xe3rZ_9o\xab\x9e\x83*\x04\x88\x13\xa3\xa5\x96\xf9\xb6\x8e(\xaa.\x82L\x19\x13\xb9\x98\xf5\x8e\xb2V\x8ej<\xa6nT\xa5E\xb8\xab\xdbv\xcf\xa1\x82\xben\xd6\x1b\xa7\xbb`\x02\xaf\xd0\xf7\x13\xa5\xdf\xa2\xf4[\x94~K\xdbo;\xfd\x96\xaf\xf5H,p\x02\xdf\xa1\xb68tyy1e\xf8f\x87\x8c \xb5\x86\x1f(\x9f\xd6\xecW\xca\xa7\x85\xe9\x19\x94Q>-\xca\xa7\xe56\xca\xa7%\x8d\xf2i\x1d\x1a\xe5\xd3\xa2|Z>\xa3|Z\x94OK\x1a\xe5\xd3\xa2|Z\x94O\x8b\xf2i)\xa3|Z\x94O\x8b\xf2iQ>-\xca\xa7E\xf9\xb4\xa6\xe6\xff\xb4\xa5|Z\x94O\xcbc\x94O\x8b\xf2iQ>\xad\x87\xcc\xa7\xc5[K&V\x1c\xd7t\x1a\xf5\xf4\x89\xb4(\x95TZ\x9e\x1eJ%u\xc2\xe0\xc6\xdb(\xa5\x92*\x11EJ%E\xa9\xa4~\xcb\xa9\xa4^\xfc\xa2a\xfe\x7f*_\xae\xa4R\x92\x963\xb9\xa4D[\x9b`q)\xf9\xa4.G\x8c\xeeQ\xa7\x91*J\xf4E\x93AE\xc5\x07<\x87\x96\x9c\xeb)\x90\x15)\x96\xd1\x89\x07\x89\x1a@es:,\xf6\xb1\xb9\x9c\xbeA\x90\xb19\x9c\xa2\x19\x9c\xa2e:\x84kS\xb27\xf9s7e\\\x18\x91\xb7)\x9c\xb5\xc9Q\x97\x8c\x9cM\xc1\xe7\x10\xa2\xcf\"\xa0\xb25\xf1\x00\xa4e,\x9ay ZRe\xf1\xf2*C\xe5\x1c\x88\xde\xd7\xd1\x10\xcd}\xb4\x18\xe5:\x1az\xfd\xbc#\x9d\xb0\xcb8>\xfd \"\xcf\xc0\xaf7>\xd1\xec\x02\x88\x84' \xd1\x19W\x82c\xf3\x98\xc4\xb2\x94\x14\xcdQ\xa2\x9d\x1d\x97<)\x9e:\x89\xfa\x89\x99=\xfcs@\xfd\x84\xdb\xa8\x9f\x088;&\x93Q8\x8f\x11\xf5\x103{\xd8'\x00\x99\xb3\xe87\xdbC\xa0\xf2\x14!\xf2\xf4$D\x07_\xe1r\xf9\x89\xf0\xd9\x89\x12r\x13\xa5\xd7\xf9\xb8\xbcD\xe6c\xf4\xe8\xacD\xdaQVN\"\xffwT\xac\xe7\x0b\xf6z\xd1X\x8e\xef\x95\x12\x99\x88By\x88\xb0EA\xe5 Be \x8a^\x11\xd1\xa1\x1c\xb6\xb0\xe3r\x0f\xe12\x0f\x15\xb8\xe7G\xe7b\x96n\xe2\x8fMJ\xb6\xa1\xe8\x0dQ\x16\xcf4\x14\xcd\x83\x93qv<\xcbPn\x8e\xa1\xcc\x0cC\x05\xf2\x0bahec\xd1\x98&\xe4\x16Bdn\x80\x07\xc9+\xf4\xed\xb3\n}\xe3\x9cB\xbbxF!\xe4S\x88\xe8\x1c\x95\x1dL=cs a2 \x9d\xa8\xb0\x91g\xa1l\x06\xa1\xbc\xfcAy\xd9\x83\xac\xbc\x0c\x85r\x07\xc5\"\x96\x997\x08\xbck\xfb\xbcY\x8309\x83tiS3\x06E\x9bY\xe8\x9d\x94\x9e+(1SPj\x9e\xa0\xa4,AI9\x8223\x04\xe1re\x14\xce\x0et\xd2\xdc@\xdf>3\xd0\x03\xe4\x05\xfafY\x81\x109\x81\xa2O(.\xcf\x8e\xee\x1fR\xb2\x01\xe1r\x01\x95-_~\x16\xa0@\x0e\xa0h\x11\xf5\xc5\xe3\xf9\x7f\xac\xb7\xccq\xd9\x7f|/\x17\xc9`\xa8\x97\x18\"]\x87N\xd0!\x8bsu\xf9z\xe6\x8b\xd2rPZ\x8e\xd8\x18\x12\xfb\xa1Di9(-\x87\xdb(-\x874J\xcbqh\x94\x96\x83\xd2r\xf8\x8c\xd2rPZ\x0ei\x94\x96\x83\xd2rPZ\x0eJ\xcb\xa1\x8c\xd2rPZ\x0eJ\xcbAi9(-\x07\xa5\xe5\x98\x9a\xff\xd3\x96\xd2rPZ\x0e\x8fQZ\x0eJ\xcbAi9\x1e2-\x87{\x19\xe6\xa6\x9e|\x99}\x83\xd4\x1c\n\x0f\x8a\x17\\\xb1\xaa&\x97\xc8\xb7\xc9 Rz!\xf6\x8b\x8a\xf3jq\xb7e\x8d\xe1,}\x8b\xb2_\x8d\x07\x0e\xeb\xb3\xe5\xb4\x84\xf5w;0w\xd5g\x93dD\xad\xd5\x1eBz\xb0J\xdbr\xfd\xc4\xc4\xea\x91.\xd8>\x08\x97m\xfc\x01\x84\xbd\xe8\x82\xef\xa4\x89-\xcc\x1e\xa3\x81I-\xf4C\xcd\xdb\xe1/\xb5\x7f\xe6r\x0c\xb6\xc5\xdb{\x8f\x1e\x17\xf6z\\F\xb8\xbf\x87\x08\xd3a0\xdc\x95\xf6y\x1a\xea\xec<\"V\xd7\xd0rz\xcc\x82\xfa\x91k\\\x0d\xf8\xd8X|\xe7)R0k\xbc+\x82\"\x0f\x03 \x1e\x08@\xa8\xdd\x80\xbb\xdd\x80\x9c55\x96\xaa|\x07\x9d\xb9\xa7?\x03\xf3\x01\xa5\x15p\x88\xab\xe0\x90\xa1\x84\x87+P\xf1;\xb4\x1a\x0e\xa5\x14q\xc8T\xc5\x83\x0eEp\xd1\xca8\x1c\xaf\x8eC\xb2B\x1et\xa5\x95\xbb$\x95\x1cJ+\xe5\x90\xa8\x96C\xaab\x1en\xd9\x83\x9a\x8eU\xcd\xa1\xb4r\x0e8\xf5\x1cJ*\xe8p\xb4\x8a\x0eyJ:\x94R\xd3!KQ\x0f?\x0e\x95\"\xd2\xa3\xcf\xcdI\x94u8\xa1\xba\x0e\xa7Q\xd8!Qe\x87<\xa5=\xd6\x05\xe3\xd4v(\xab\xb8C\x82\xea\x0e\xe9\xca;d\xa8\xef\x88.\xf3\x07\x84\x02\x0f%Tx\x88)\xf1\x80\x1f\x9e!\x14yH\x1c\xc5%+\xf3AoR\xb5G\xa8\xf3\x90P\xca\x82*=$)\xf5PZ\xad\x87L\xc5>\xdc\xae\xfa\xb8j\x0f\xf9\xca\xbd\xd7\x9f\xb8bL\xbd\x87b\n>\xe0\x85h\xc0(\xf9\x90\xa6\xe6CL.\xcbT\xf5\x01\xe170U_H\xe1\x87\xac\xe0\xe2\x95~@\xd42C\xf1\x87\\\xd5\x1fpQ=Z\xfd\x07<\x01\x00H\n\x00\xd0$\x00\xe0\x82\x9eN\x04@\x12\x15\x00!2\x00J\xd1\x01\x90J\x08@&%\x00i\xa4\x00 nB\x88\x18\x00\xcc\xf9xr\x00\n\xd2\x03\x80)Z\xe0)+G\x12\x00\x86&\x80#\x88\x02\xafCq`\x88*\x80\xd2d\x01D\xe9\x02\xc8%\x0c\xbc\xde\xd4\xf7ox*\x00A\x1a@\x986\x80,\xe2\xc0\xeb*H\"@.\x8d\xe0\xf5\xa6\x86\x91\x81I\xb7rT\x02\xa0\xc8\x04\xc8\xa0\x13 \x8dP\x80\x1cJ\x01\x92I\x05\x08w#\x10Q\x8f!AA\xc6R\x0b\x90C.@*\xbd\x00\xe1\x8a\xe7P\x0c^g\x16#\x80}dp4C\xf0\x81h\xd6a\xa2\x01\xcaR\x0d\x10#\x1b L7x\xcf\xc9\xa5\x1e\xa0`\xdbM\xa0\x1f \x89\x80\x80\xc0\x08\xc5\xac\x9dV2\xd7\x80\xee\x87D1\x93\xd3i\xd4\xfd\xa6\xb9(*^\xa9D\x14v\x1et\xaf\xc71[\xbdO\x9f\xce\xca+n\xb6\x14\xf1\xaf.E-\xec\xf6N\xd3\xe0\xb2\x1f\x0c\x1b\x9b\xe8\x95\xce\xc3\x1e'\xb0\xab\xfa^M\xed\xaa\x9dP\xfe\xb1g=?S\xbf{\x9c\x8d[\xc0\x846A\xb9\xe0\xd6\x13,\xf7\x9b\xf0M\x00p\x99!\xae\xea\x184-l\xdb\xcel\xc6\xe0\x9c\x05\x93\xfb!\x1c\x19\xccxJ.\xef\x1c\xd2\xb0\x1d\x83o\x7f\x0ek)\x93\xaf\xbev\xa0\xed\x9d-<\x87\x7f\xa9\xe46\x08\xcf\xa0\xe6\xbd\x99\xe6\xefa\xdfH%\x84-\xd5L\xe6\x97\xba?l\x1f\xf1i1{\xf7\x1bU\xab ?V7\xb0\xbe\xba|=.\x8c\xd7\xddW\xaf\xb2\xfa\xb9n\x8f[eZ\xb4\x9d\xf2!{\xeaNU~x\xcf\x88\x17\x8a\x9c\x1c\xb5#\xe3\x0c\x879\xe3C\xbb\x1d\xcb\x1d\xec\xf7:\xb6\x93I\xe8\xe1\x8fU7\xdc\xa4\xd8\xc8b\x12\x16\xd9\xbe}\xe3\x8aiw\xa6[\xcf\xc1c8d\x1c\xb0\x80\x1dT\xf2\x01\x97\x9f\x17s\xa8H\xdc!\xcaC@y\x08b\xd3\xfe\xf1\xee@Y*\x8da\xa6\xb0\xbd\x0e\xb1$FQ\n\x83\xf2\x10P\x1e\x82\xd1\x8a\xd2\x15)dE\x12UAy\x08\x8e%(2\xe8\x89\"\xe4D:5Ay\x08\x8e\xa1$R\x08\x89\x0c:\x82\xf2\x10P\x1e\x02\xcaC\x80\xa5\x1b\x8a\x92\x0d9T\x03\xe5!\xf0\x1d\x16\xa5\x17\x12\xc8\x05\xcc*\xfb\x14b\x81\xf2\x10P\x1e\x02\x04}@y\x08 \x930H\xa0\x0b(\x0fA\x01z J\x0e\xe4R\x03\xdew\x0d\xe5!84\xcaC@y\x08fVbM8V\xd1OV\xf3\x93\x94|\xcaC\x90\xa9\xd8S\x1e\x02c\xa7\xcbC0n\xf5%\xa5v9g?\xdb\x89\xfeT\xcb\xfb\x8f\xceB\xc0g+\xf2\xbfMV\x02pm\x96\xff\x89\xdd\xfbJ?S\x16\xb5\xa2_\xe9\x97\x96\xbd\x17\xbe-a\x9e\x0d\xf2\xbf\x9c\xb3[\xcf&\xb7d\x00L\xca\x81\x90\xa4/\x13\xf3\xb7\x8d\xfc\xa8oW\xab\x9eq\xf1\x9d<-.X\x9aC\xcf&\x9b\x97\xe4\xec\xa6\xef\x0d\xe3d\x06\xc7\x11DU>_\x1cg\xb3'\xba22\x94\xcd~+7c\xd3\x7f\x93\xfd\xdb\xa2j\x86\xcdU\xbe\xdc\xb1\xc6\x04~\xdf\x0c3\x86\xb3\x0f\x85\x0b\xe9m\xc3\xfa~\x0c\xa1\x9ac\xdb\xf7\"\xd4\x9fXb<\xa7\xeeO\x1c\xdcx\x1b\xdd\xd4\xdb\x1a\x1b]y\xac\x91\xb9}\xdc\x84\x9aM\xb6[\xb0\x16\xc5\xf7\x9b\x99\xca\xac\xe6\x8e\xec?]\xac`\xc3V\xdcp&\x1a<1\xa3q9\x11\xae\x1e\x10u\x11\x11\xe7\xdb{`\xd5\xe2\x0e\xaa\xdd\xee\x01\xa3h\xd3\x1f\xe3\xf9\xa1XZg\xc8\xfeU\xed\xdc#z \x10\xff\xa8\x9be\xbd\xa88\x1b$1\x1dAy\xa0nH\xb6\xbb\xbaYl\xf6\xcb\xd9X\xbbRW\x194\xc9\xd9\x1d\x93\n\xb75U.^\x96c\x9d\xe63\xe7\x1f/\xfa\xd9\xdd\x9aUA~\x9et\xac\xd7,\x82|\xbc\xc6\xe7QV\xeab\xf7\xe9\xa3\x84\xae\xa0\x9e|\xb78Oa_9\xeb\x9a*\xce\xd1c\x88\xec\xa7\xed0\x92?\xd7~E\x19\x14\xa2X\xf7\xfeRp\xf65\xb2'l\xe2\xe5\xaf5\x9f\x13\xae{\xc3\xeb\xd0F\xb2\x8eZ\xe93\xac\xcd\x86\xcdZ\x05\xefU\xa2\x8f9 \x1euawU\x7f7\xed\xec\x0f-\xd4\x0d\x8d\x16Y;\x00\xb82+\xc3\x94\\Y\xcf\xab\xce{\x97GC\xdc\xef\xd1P\x0f\xc5h\xf1nl4\xb9\xe0\xc1\xb4\xa1\x81\xf8\xb3\x17\xb4\x1cj\xb4\x9c\xda[\x9f\x89o\xd4\xb5\xf4T\xf7XWL\xf7\xf9\xb1\xdb\xcc6\x80R+\xca$o\xa2\xa0\xd2z\x11q\xa6]\xbd\xad\x9bO\x19\xfdf\xe8\x1b3\xde_\xa2\xd6\xa5c\xbe\xe6\xaf\xd5xt\x92\xe3@\x96\xfc\x99h(\xab\xbd\\w\x04\x8b\x8a\xb3u\xdb\xd5\xff^Y\x93\x8bS\xab\xf6\xfc\xce\xb5\x92P\x19\xbe@\xaf\xa4\x9f\xe8\xac\xc2\xa2m>\xb3\xae\xafP\xf3T\xc5f|\xac\xd67NY\xb5]\xbd\xae\x9bj\xa3n\xb8\xe9n\xac\xf29}\x0eC\x86\xe5\x8dg\xb2SY\xa1\xb6\x10yg\xe2\xde\x97\xd2\x15\xe6\xf1\xba\xd6|\xf9P\xc9\xf0I\xf1\x9b\xa4\x8c5\xfbm\xac[y\x0e\x97\xef?\\\xdf\\\x9d\xff|~u\xfe\xee\xf5\xf9\xcd\xf5\xdf.\xcfo>\xbe\xfbpy\xfe\xfa\xe2\xe7\x8b\xf37Y\xe7_\x9d_\xbe\xfd[\xd6\x99\xff\xfa\xf1\xfd\xf5y\xd6\x99W\xe7\xe2\xaf\xc1S\xcdb\xe2\xa3\xea\x8cY\xbc0Z4\xbe/\xe1]\x8b\xbc\xf1\xd6\xd2\x8aX\xff\x1f\xb8-/\xd5\"\xd4q\x0c>\x19\x13wl\xb7\xb9\xd7\xf8\x91\xb5\x92#0\x91\x18\xb9\xa6\xbc\xa1\xe1k\xfec\xdf\xf2q}E\x81k\xaa\xa6\x10\xad\xa8\xd5\xff\xa0\xaf\x1a\xc9F\xa3\x0c\xfd\x84\xa2:Se\x07S\xffc\x8f\x88(\xb3\xa4\xca\x1e\xac\xd0\xd1\xa7\xe4R\x97\xf0\xa0n\xf3\x01\xc2\xf7\xbd\x1c\"\x04\xd8hij\x99\xf5\x00\x9aH0\xa3m6\x81e\xb5\xca\xe4\xda n\xe0S-\xc2\"\xfa%]\xcf\xcb\xb6\xe7WC\xd1\xfd\x03&\x9d))\xfc\xcc\xc7c\xf7\n6\xb5$\xbf\xf4\x18\xc4j\xeb=\xa3Tc\x94jL\x1a\xa5\x1a\xa3Tc\x94j\x8cR\x8d)\xa3Tc\x94j\x8cR\x8dQ\xaa1J5F\xa9\xc6\xa6\xe6\xff\xb4\xa5Tc\x94j\xccc\x94j\x8cR\x8dQ\xaa\xb1\x87L56\xc9$\xa5(\xaec\xf3`\x1d|\xcdG2\xce\x8c\xf9\x0b\x10\xe5\xd6 \x07NP\xec\xd4\xf4]\x94\x12+-\xdf\x10\xa5\xc4:ap\xe3\xc9\x9c(%V\x89(RJ,J\x89\xf5\xebL\x89\xd5\xb1J\xa7\xbc\xfa\xfc;\xff\xd6\xceb\xfc\x11\xc8\x80ue\x9c\\\xca\x03\x95\x9e\xd6\xc3,]\xa5:F\x0df4\x92n\xae\xa2\x1dN2a\xcd\x9c>1\xb5\x7f\xac\xc9\xb0\xac\x18\xd9\xe6u\x05Q\x94$\x9a\xd4**\xa3\xe0y:\xc7\x18Q\xf4\x17\xc3\x8aZ\x89Y\xa9\x9b\xdb1\xa5\xa38\xdcul]\xf7\x9culyc\xee\xb7\xa7\xe4\x1a\x0bU.\xad\xa5\x8dW\x83\x07s\xff\xff\xaa\x87L\xba5\x04\x02\x11X>\x17cv\x00X#:\xc9\xc0\x12\x1bw_spTt\xfd\x04\xc0\xff\xbacR\xc8\x8b\xd6\x15\xb5\x02X\x17\xdcq\xc4\xaac\xec\xc6\x9fO\xca{\x0f~\xee\x18\xbbf_\xf9\x7f\xca\xc8\x9b\xe0z*a\x8dz\xfd\x81\x13\xb6\xad\xbe\xdelX\xb3\xe6w\xb1\x82\x95\xd8Y\x1a\xd7p\xae\xef\x98(\x17\xa8r\xa9\xb1C\xb8\x9e\x01gw\x95W\xe2\xec\xd8\xfa\x86}\x8d\xd5;\xb2h\x0b\xa3\x81*\xbbb\xeb\xf3\xaf\xaa6r\xac7\xad\x92\xbeaz\xe4\x11\x90;\xf4\x02,5\x18\x11\xbd=o\xf5\x90\xf6\x8b\xfc60\xf8X?\xf9L\x08\xb04 \xd70\xa9\x80\x1eN\xd2\x85n\x98\xd9\xb1~\xfeb\x9c\xcc\xff\xed\x0e\x1e<\x17,\xa3y\xf8\xe1M\xe9\xecu]O\xa8.\xdf\x01q.G\x00\xb3\x82\xe5\xd3\xaeSGpu\xf9\x9a\x00W\x02\\\xa3\xdd\x03\xb6s \xc0\x95\x00W\xb7\x11\xe0*\x8d\x00\xd7C#\xc0\x95\x00W\x9f\x11\xe0J\x80\xab4\x02\\ p%\xc0\x95\x00We\x04\xb8\x12\xe0J\x80+\x01\xae\x04\xb8\x12\xe0:5\xff\xa7-\x01\xae\x04\xb8z\x8c\x00W\x02\\ p}H\xc0\xd5\x82\x17&\xcc\xa8\xcd\x92@\x160\x8a\xc3\xc8\xcap7r\xbf\xba_t\xf6\xcf\x7f\x8e'!P\x9c \x84#\xb1\xd9a;:+\xd7\x96\x9b\xc0yb\x02\xf3H\xd9\x9bY\\l\xe3\x0f\xa0\x8fE\xb9\x9d\xa4\xf9!L\xca\xeb#7\xa3\x1b\xd8\xac\xe8\x86t\xbb\xbd\x87\xf2\x08'\xa8-\\\xdd\xf9&\xd4\xad\xab.j\x86\xbf]\xd4\xd9{\xd0\x9d`\xe7\xbc\xa1t\xb3\x1d@\x9c\xe7#f\x18\x8f\xdeI\x8dG\xb4\\\xc0\xdd=@\xce \x1aK\xd5u\x83\xce\xdc\x93{\x81\xaf\xdd\xd2\xfa.\xc45^\xc8\xd0y\xc3\x15\xa8\xf8\x1dZ\xeb\x85Rz/dj\xbeA\x87\"\xb8h\xdd\x17\x8e\xd7~!Y\xff\x0d\xba\xd2\xbaT\x92\x06\x0c\xa5u`H\xd4\x82!U\x0f\x0e\xb7\xecA+\xc6j\xc2PZ\x17\x06\x9c6\x0c%\xf5a8Z#\x86<\x9d\x18Ji\xc5\x90\xa5\x17\x87\x1f\x87J\xe5=\x8f>7'\xd1\x8d\xe1\x84\xda1\x9cF?\x86D\x0d\x19\xf2t\xe4X\x17\x8c\xd3\x92\xa1\xac\x9e\x0c \x9a2\xa4\xeb\xca\x90\xa1-#\xba\xcc\x1f\x10\xfa2\x94\xd0\x98!6\n\x04\xfc\xf0\x0c\xa17C\xe2(.Yw\x0ez\x93\x9a4B{\x86\x84R\x16\xd4\xa0!I\x87\x86\xd2Z4d\xea\xd1\xe1v\xd5\xc75i\xc8\xd7\xa5\xbd\xfe\xc4\x15c\xda4\x14\xd3\xa7\x01/\xb3\x02F\xa7\x864\xad\x1abbP\xa6f\x0d\x08\xbf\x81\x89\xe8B\xfa5d\x05\x17\xafc\x03\xa2\x96\x19z6\xe4j\xda\x80\x8b\xea\xd1\xda6\xe0\xf5m@j\xdc\x80\xd6\xb9\x01\x17\xf4t\xbd\x1b\x924o\x08\xe9\xdePJ\xfb\x86T\xfd\x1b25pH\xd3\xc1\x01q\x13Bz8`\xce\xc7\xeb\xe2PP\x1b\x07L\xd1\x02OY9\x9d\x1c0Z9\x1c\xa1\x97{\x1d\x8a\x03C\x9a9\x94\xd6\xcd!\xaa\x9dC\xae~\xee\xf5\xa6\xbe\x7f\xc3S\x01\x08\x1d\x1d\xc2Z:d\xe9\xe9^WA\x9d\x1dr\xb5v\xaf75\x8c\x0cL\xba\x95\xd3\xdc\x01\xa5\xbbC\x86\xf6\x0ei\xfa;\xe4h\xf0\x90\xac\xc3C\xb8\x1b\x81\x886\n \xfa(V\x93\x87\x1c]\x1eR\xb5y\x08WxG(\xa7\xd9\x89\xd8\xc8k\x8es\x86-\x02\xb5\x007\xdd'\xb1\xe2\x95\xceH1n?\xe5\xf3F\xfbP\xd1>Tq\xe5\x9d\xf6\xa1\xeai\x1f*\xcb\x12\xf6\xa1\x1a\xb8\x9b\x02\xcb\xf2i\x07*Z\xa0\xef\xfc\x9d\x16\xe8[F\x0b\xf4i\x81\xfehE\xc1\x8c\x14(# \xc8\xa0\x05\xfa\xc7\xc2\x17\x19\xe0E\x11\xe8\"\x1d\xb8\xa0\x05\xfa\xc7\x00\x16)pE\x06XA\x0b\xf4i\x81>-\xd0\xc7\x82\x11E\xa1\x88\x1c \x82\x16\xe8\xfb\x0e\x8b\x82\x0f \xd0\x03f\xf9y\n\xec@\x0b\xf4i\x81>\x02\\\xa0\x05\xfa\x90 '$\x80 \xb4@\xbf\x00x\x10\x85\x0er\x81\x03\xef\xbb\x86\x16\xe8\x1f\x1a-\xd0\xa7\x05\xfa3+\xb1X\x1a\x0b\x03$\x83\x00I\x10\x00-\xd0\xcf\x14\xfbi\x81\xbe\xb1\xd3-\xd0W\xfb\xf5L\xf2a\xeb\xb5\xbc\x9e\xe5\xea\x96\xefS-\xda\xb7\xb6\x0eQ\xeb\x99}\xf5\xb9\x14E\xad\x97\x8f\xa5\xb4\xfb~\xf2Y\x1eR\xfa\x9f\x9aY\xe3\x1f\xe0c/\xde\xe1\xde\x1aL_\n&\xb5\xfa\xba\x95\x1bP\xb5C\xfet9b\x10Cku\xff\xea\xa5\xdc5e\x98\x9f\xa8T\x7fr6\x8bG\xea\xa6&\xb3\x88\xd0f\\)\xd1\xf2Le\xd1f\\e\x83\x1b\xdfF\x8a6\xe3*\x11E\xda\x8c\x8b6\xe3\xfa-o\xc6\xe5K\n\xf4\xe2\x17\xf3O\xf1wu\x9dP\x8a\xa0\xf06]r\x0e_\xfcE\xe5\x08\x1a\xc6B\xce\x0cAOL4\x1ey\x82\xa0\xa2\xc0f4\xcdOTc\xc2c\x86YY|<\xd9z\x82\xb9zNR\xe6#\xb2\xf4\x84\xcb\x19\xca\xd0\x13\xcf\xcfsLv\x9e\x80\xb8\x18lR\x10mV\x80\x00\xb8\x10\xf7 \x90\"\xa0\xb2,\x8c+\xe0/)\x1bOQ\x94+\ns\x95\xc6\xb9\xf0@W!\xa4+\x0f\xea\n\xb8K\xcc\xbfs$\xd8U\x1a\xedJ\x84\xbb\n\xe3]i\x80W\"\xe2\x15j\xc3\x19\x19w\x8ab^(\xd0\xab \xeau,\xec\x95\x85{\x15\x02\xber\x90\xaf\x803t\x86\x9d\x13`_\xa7\x03\xbfN\x82~\xa5\xc1_\xc5\xf1/,\x00V\x14\x01\xc3C`\xc9\x18X:\x08\x16\xed\nq\xb9t\x8e\x86\xc1\xa2ytP\x03*\x04\x12\x962\xeaJ\xc6\xc2B/At\xf6\x1c\\\xf9\n\xc2a)xXa@,\x0f\x11\x0b\xb5 T\xc6\x9cLL\xcc\xe3\x8d\xa3\xb2\xe5\x94A\xc5\xd0\xbc\x13\x02\x17K\x02\xc6b !r\xa0\xb1\x98O\xaf\n\\\x08\x1dK\x0f&\x1e\x1f\x8b\xd5-\x03!\xcb\x84\xc8\x10q<\x1a$C\xa3d8\x98\x0c\x8b\x93!\x82\x9c\x8e\x94\xa5@e\xc1\xfc7E\xc0\xb2D\xb4,\x0f.K\xc2\xcbbA\x0f\xe7\xbc\x89\x9c\x8b\xc7\xcc\xca\x81f\xd1By\x9f\x9fr\xb8\x19\x028\xcbG\xce<\xeex4\xc3MQ\xec,\x06\x9ee\xa2g\x1e_\xf1\xcc6\x08\xfc,\x92\xd5\xa64\x82V\x1cB\xf3ch%A4\x0c\x8a\x96\x0e\xa3%\xe1h\x19@Z*\x92\x16\xc9T\x13.\x1d\x16\x12\xc2\x82i\x19hZ\"\x9c\x16\xa8n\x0e\xa0\xe6q\x85\xc8M\x93\x03\xa9\x05\x9a|\x1f\x8e\xf7\xaf\x83\xb5\xe6<\xae\x86\x13\xc7\xc4\xf9\xee\xd5\xb1\xe6\xb81H\xe2m?82\xc5\xd1\x7f\x98-\x96=\xb8\x88>\xec\x11\xaf\x9b5E\xbe\x99\x05\xd46\xfe\x00\x02ft\xf1m\xd2\x04^`1k|\xa7\x0fpu^\xa2\xcf7\xcb]\xeb\xfepm\xae\xd7\xd7\x18r\xe7!\xb1\xea\x86V\xc4b\xd6\xc4:W\xc5\x1e4z\xf7-\xb9k;~W5\xcb\x1b\xb7\xdc\xae\x0cqWt >J}xt+U|k\xb5\xb0z?[\xc1u\xba[\xd6\xfdnS\xdd\xdf\x1c;\xd7\xab\x0b\xf5W\x95\x12C\xbc\x9aF5T_\xc3$bP\xc9JbE\x0b7\xab\xc3\xceb\xc6\x91(\x9a@\xa1$\x9163_\xd6\xec\x99w\x9d\xf5]\xc6h\xbb#\xda\xee(\xd6Zi\xbb#5\xbc\xa7\xed\x8e\x8c9\xb7;:x\x0c5\xcav0.BQm._/\\#9q\xa7\x88h#\xa2-\xf6\x8a\x8fw\x0b\xca\x88h#\xa2\xcdmD\xb4I#\xa2\xed\xd0\x88h#\xa2\xcdgD\xb4\x11\xd1&\x8d\x886\"\xda\x88h#\xa2M\x19\x11mD\xb4\x11\xd1FD\x1b\x11mD\xb4M\xcd\xffiKD\x1b\x11m\x1e#\xa2\x8d\x886\"\xda\x1e\x94h\x8b\xb0D\xdf\x9e\x0b\xa3\x0dt\xd2v'\xa1\x0dtN\x18\xdcx\x1b\xa5\x0dtJD\x916\xd0\xa1\x0dt~\xc3\x1b\xe8\xb8(a\xfc\xde9\x07X^\n'\x9c\xc8\x07?11\xfa\xcf\x83\x07\x17e\x04\xa3\x90oT\xd1(@\xb6\x95\x83{\x83\x98\xe67\xde\xe8\x06\x83\xf4b\x80\xde\xe8\x1d(\x08\xf3\"P^lqJa\xbc\xa1\xc6S\x0e\xe1E\x01\xbcN|7\x11\xbc+\xc9\xdd\x11v\xa7\x8d\xb0;\xc2\xeeF#\xec\x8e\xb0\xbb\xd1\x08\xbb\xe3\x84\xdd\xb9\x8d\xb0;c\x84\xdd\x11vG\xd8\x1dr\x94D\xd8\xdd`\x84\xdd\xd9F\xd8\x1daw\x0e#\xec\xceu\x0caw\x84\xdd\x11vG\xd8\xdd\xd4\xfc\x9f\xb6\x84\xdd\x11v\xe71\xc2\xee\x08\xbb#\xec\x8e\xb0;\xc8O\xc7v\x08\x13\x0cU:\xb6\xe4\xa5\xb2\xb3\x89/`I]\xc8\xdb\x17\xc8\xbav)\x7fwR\x13\xdbv\xb9\xdf0\xab\x01\xe8sg\x94\x84\xbc\x94r\xf3\xc4\xd4\xf1\x91\x02\x12v0l\xf3\xba\x82\xa8\xe4\xd6\xf3\xaaYV\x9d$.zg:6\x88H\x81\x10\x92\x03!V:e1Y\x10\x82\xa9\xca \xaa\x99\x8e\x16\xa7\x1b\x949\x18\x07\x11\x1f\xa8\x9b\xbe^\xb2I\x87\xe0\xf5\xa2|D\x0b\x1d\x9c\xca\x83\xb1,\xd7\xe2?\xd3\xe2xO\xb2\x9bg\xb1\x02\x047\x9f\x07xjd\x9d\x1f\xe0\xfc+g\x8dx%Z\xe5\x98\x05\xd2\xbf\xcf\xab2\xf9\x02\xac\xc4\xf8\xb4\xe6P\xedv\x1b)\xc3\xfare\xc5K\xf8A7\xf4+uy\xdf.z\xfa\xa8X|m\xa0vW/>\x89\xb7\x7f\x13\xd0|\xd5\xce\xab\xc5\x9d\xd2*\xac\xc2yn\x81d\x90\x1dr\xbe2|A^-\x97\x1d\xeb\xfb\xe9m\xf7\x84\\\xf5I\xe1k\x1e\xbd\xc9\x1f\x8fh\xec\x80\xab\x1e \xe7j\x8d\xa5\xea\xedAg\xeeI\xd7\xc0,Di\xdd\x1d\xe2\xda;d\xe8\xef\xe1\nT\xfc\x0e\xad\xc1C)\x1d\x1e2\xb5\xf8\xa0C\x11\\\xb4\x1e\x0f\xc7k\xf2\x90\xac\xcb\x07]i\xbd0I\x9b\x87\xd2\xfa<$j\xf4\x90\xaa\xd3\x87[\xf6\xa0\xe1c\xb5z(\xad\xd7\x03N\xb3\x87\x92\xba=\x1c\xad\xddC\x9e~\x0f\xa54|\xc8\xd2\xf1\xc3\x8fC\xd5\xb3e\\\xcb\x87\xd3\xe8\xf9pBM\x1fN\xa3\xebC\xa2\xb6\x0fy\xfa~\xac\x0b\xc6i\xfcPV\xe7\x87\x04\xad\x1f\xd2\xf5~\xc8\xd0\xfc\x11]\xe6\x0f\x08\xdd\x1fJh\xff\x10\xd3\xff\x01?\x01\x107!\xc4)\x00\xe6|<\xaf\x00\x05\x99\x05\xc0\x14-\xf0\x94\x95\xe3\x17\x00\xc30\xc0\x11\x1c\x83\xd7\xa180\xc42@i\x9e\x01\xa2L\x03\xe4r\x0d^o\xea\xfb7<\x15\x80\xe0\x1b \xcc8@\x16\xe7\xe0u\x15\xe4\x1f \x97\x81\xf0zS\xc3\xc8\xc0\xa4[9\x16\x02P<\x04d0\x11\x90\xc6E@\x0e\x1b\x01\xc9|\x04\x84\xbb\x11\x88h\xd6\x90\xa0[cY \xc8\xe1% \x95\x99\x80p\xc5s\xd8 \xaf3\x8bL\xc0>28\x86\"\xf8@4\xeb0G\x01eY\n\x88\xf1\x14\x10f*\xbc\xe7\xe4\xb2\x16P\xb0\xed&0\x17\x90\xc4]@`\x84b\x96YK\x95\x0b\xa1\x83J\xb1\xb0n\x9b\x9be\xc5\x8f\x91\x04\xcd<\x88p\xf3\x9c\xd7[\xdf\xad\x19\x96\x81o\xe5\x10\xf2@J\xfeR\xf5\x01\x15yXE-\x8f=\\E-^K\xea\x9b\xb9^\xb8kM\xbb\x06\xd1\xaeA\xf1\xec\x00\xb4kPO\xbb\x06Y\x96\xb4k\x90D\x86\xf0y\x18\x149E\x89\x18(\x11\x83\xe3wJ\xc4`\x19%b\xa0D\x0c\xa3\x15\x05=R \x8f$\xc0\x83\x121\x1c\x0bsd\x80\x1cE \x8et\x80\x83\x121\x1c\x03l\xa4\xc0\x1a\x19\xa0\x06%b\xa0D\x0c\x94\x88\x01\x0bZ\x14\x85,r\x00\x0bJ\xc4\xe0;,\nR$@\x14\x984\x03)\xf0\x04%b\xa0D\x0c\x08\x10\x82\x121@&\xec\x90\x00:P\"\x86\x02 C\x14b\xc8\x05\x18\xbc\xef\x1aJ\xc4ph\x94\x88\x81\x121\xcc\xac\xc4\xa2x,\\\x90\x0c\x16$A\x05\x94\x88!\x13\x1e\xa0D\x0c\xc6N\x98\x88\xc1\xb3\xca\xdfr\x91\x9e\xd3 5\xfd\x82Z\xb2{f\xf4\x12_\xf1g\xdf\xcf\xb9\x9a\xc7A{\xf3\xe9\x1c9\xda\x86K\xc3\xc8\xd2-\x82\xfaD\xb6&\x91\xa9C\xd8z\xc3\xc4\xdf\xe1'z\x86\xde\x90\xa51\xcc\xb4\x84yxf\xbaB\x8e\x960\xd3\x0c\xe6W\x98^ W3\x18\xb4\x81\xa9\xfb\x83\xc1T\x8e6\xe0\xd3\x00\x8e\x9a\xf7G\xcd\xf5c\xe7\xf4\xb3\xe7\xf1s\xe7\xee\xdds\xf4\x85\xe6\xe5K\xce\xc5\x17\x9a\x7f\x8f\xcd\xb9\xe7\xcd\xb3\x07\xe6\xd3\xd3\xe7\xd0\x83s\xe5\x88\xf9\xf1\xdc9q\xcf\xdcw\xe6|w\xce\xbeC\xb3\xd7\xe4\xecm(\x9f\x0d\xeb\x9c\xd0\xab0sb{\x9c\xc0>\x0cz\xd9\xbay\xa6\xf1\xc7\xfcK\x07 =B@\x9c\x95\x97\xe4cozE\xedc\xa9^\x80*\x86\x93\xb3\xe4[\xbb\xeea\xdd\xca\xdd\x94\xdaa\xdf;\xf9\xb5]\xaf\xac\x13e\xf7\xb7\xe9\xdb1@\x85\xe3A{#\xa6D+\xd2zho\xc42\xc1\x8d\xef\xeaG{#\x96\x88\"\xed\x8dH{#\xfe\x86\xf7FT\x87\xfc\xa2\xfe\x11\xdb\x1bQ\xaa\xc7\xfe\x8c\x84\xf8\xbd\x11\xad\xa5\x02\x8f<\x1ba\xd1u\x0b\xd1\x9c\x82\x9e\xdel4rjyj\xd9\x13\xcdSK\x9fv\x0e8Cg\xfc+4==\xb5\x92\x93\xd5S+4u=\xb5\xd8D\xf6\xd4\xf2\xa6\xb5#=~\x1c \xcf\x99\xfe\x0e5\x10$D\x8e\x9a&\x9fZ\xea\xa4y\xb4+\xc4\xe5\xf6\xcb\x9c\\\xb7-\x92\xd7\x0f5\xa0B \xe5)\xa3\xae\xe4\xd9\xf7\xd0K\x10\x9d\xcd\x0fW\xbe\x82py\n^^\x180\xcfC\xccC-\x08\x95\xc1/\x133\xf7x\xe3\xa8\xec}ePs4/\x8d\xc0\xcd\x93\x80\xf3X\x82\xaa\x1c\xe8<\xe6\xd3K\x91\x15B\xcf\xd3\x83\x89\xc7\xcfcu\xcb@\xd03!tD\x1c\x8f\x06\xd1\xd1(:\x0eF\xc7\xe2\xe8\x88 \xa7#\xe9)Pz0\x1f_\x110=\x11M\xcf\x83\xd3\x93\xf0\xf4X\xd0\xc39\xf8\"\xe7\xe21\xf5r\xa0z\xb4P\xde\xe7\xa7\x1c\xae\x8e\x00\xd6\xf3\x91u\x8f;\x1e\xcd\xb8W\x14[\x8f\x81\xeb\x99\xe8\xba\xc7W<\xd3\x1e\x02_\x8fd\xd9+\x8d\xb0\x17\x87\xd8\xfd\x18{I\x90\x1d\x83\xb2\xa7\xc3\xecI8{\x06\xd0\x9e\x8a\xb4G2\xe7\x85K\x87\x85\x8c\xb1`{\x06\xda\x9e\x08\xb7\x07\xaa\x9b\x03\xb8{\\!r\xe5\xe5@\xee\x81&\x1f\xcf\x93W\x10t\x8f\xe6\xc8;\x05\xec^\xaa-&\x00\xef)\xc8\xbbo|\x90\x96\xff\x0e\x91\xfd.:c\x80\xcb|wL\xde\xbbc\xb3\xde!\xf2U\xa5\xa5\xab\xa2lU\x94\xad\xca\xf5;e\xab\xb2\x8c\xb2UQ\xb6\xaa\xd1\x8a\xca\x91)bd\x92\x14I\xd9\xaa\x8e\x15 3\xe4\xc7\"\xe2c\xba\xf4H\xd9\xaa\x8e\x91\x1cS\x04\xc7\xc2r#Nl,(5b\x85F\xc7\x87\x00e\xab\x9a\x1aBZ\xc4\x8e\x92\x92eE\xcaV\x85\x12\x13s\xa4D\xcaV\xe5;,*\x1f&\x88\x87\x98\\L)\xc2!e\xab\xa2lU\x08y\x90\xb2UA\xa6 \x98 \x07R\xb6\xaa\x02\xf2_T\xfc\xcb\x95\xfe\xbc\xef\x1a\xcaVuh\x94\xad\x8a\xb2U\xcd\xacD\xe6 \xac\xa8\x97,\xe9% z\x94\xad*S\xc4\xa3lU\xc6N\x98\xadJ\xb4\x85\xf1.iimX\x17\xfa\xad\xf3V\x0dKX\xe3Ux\x98\xa2\xc6\xd6\xeb\x8a\x0f\xde\xcd\xfd\x8b\xcf\xbf{\xb1\xa8\xbb\xc5~S\xf1\xbaY\xbf\xf8e\xc9\x9av\x1bX\x96kM\x0d\xbc\x1e\xcf\x9353\x8aM\xb5\x1d\x16\x93\xb7\x9fX\xd3\x83u\x01\xf36\xdfV\xdd'\xa9\xe1\x0e\xde&\x13\xddj=\xaf,\x8b\xfe\xdbdI\xafue\xfd\xf3\xa3]\xd7k\xd5\xfdF\x85\x08@\xb6\xab\x1aqS\xae\x14\x19\xf8 \x10\x00\xe2\x89x\xc0\xfe\xd0\xcf\x1d9\x0fM\xd9U\x8b\x05\x07K\x16\x7f\xf9fW\x1c\xb5+UYT\xe6\xb8\xec\xc4\x81;\xbde\xe3\xe5\xfa\x8b\x1dd\xba\x0e\x9dF\xc9m\x92\xf8\xf4\xbd\x9d\x02}\xef\x993\xce7\x89\xab\xc7A\x06\xe3\xab%\n\xaf\xab\xfd\xcfA\xf6\x0f\xc4\xd9:QM\x08\xd6?\xcaK4\x91rf\xf0a/\xb0zl\x8ed\x80\xab\x9a-\x8f\x06\xe0\x95s\x85\x85\xbc}ws\xfe\x9c{\xca\x85\x93\x00]\xce5\x0f&\xb8h\x06\xe1\x8c\x9b\x028z\xab\xcb\x0d\xe6\xd4\x00\xb1\xef\xb0>\xd6\xd7\xbb\xa6\x1aN\x1d\xeb'\x9b\x04w\x8f\xb0kw-w\x83\xa5\xe4X\xbf\x1a\xbb\xe1\xa5\x18\x80\xaf\x19[\xcc\x81{&\xb9\xdd6\x0fU\xb7\xf31>\xc8q\x8c1\xdf\x0d\xfbdk\x12\xef{\x0c\xc4\x1c\x8b\xa8{\xd8v\xd5\xa7\x06\xee\xbbq1[8\xcd:V\xf5\xdd\xc9\x16\x97Um85\x1f\x0f\x1f\xb2\x15v\xc7I\xe9\x96}\xe4\xee\x1bn\xa2\xd1\xba\xad\xd9V\nKl\xdbW\xce5(\x1bK\xa3\x83\xca\xc62\xfb\xc62j\xe3ac>\xe43\xd6\xb1!QX~\xaf\xc5{\xdc\xb2\xb87&\xe3\xfc\xb9g\xc3\xe6\x81\xa7,)y\x8e\xaaq\x9eROF\x9bW)\xc1p\xa2\x1cro\xb2\xac\x84x\xe2\x9b\xdd\xa3\xa8\xbd\xabJ\xc2P&F\xcb\xd6A\xcb,\x8c\xde\xb2\xdb&\xe3\xa7\xae \x93\x05\xec\x87\xee\xb4\x19W%\x8b\x7fV\x1b\x07\xf3\xd7/\x86\xb0\x18\xc2/i\x08\xd7Cy\xc9'\xe4UgA%\xcd`\xfc#r\xe0\x9e\xddU=;\xc3\xf9\"\x8c\xf3\x19\x12\xa4\xff\xfb\x89\xf5b\xca\x18\x1f\xcb\xfc<\x1f\x98\xe2g#\xebJF\xfd\xdamE\x0e\x02\xf7\xac\xf4\xed$y;>\x1c\xd8r\x0d!\xb5\xb7=\x0b\xe9\xfa\xac.\x8dq\xdd$\xc7^v\x01\xe70\xcf\xd0\x03\x8b\xedqNbt\x83\x16=\x0f)\xbaB\xd6\xad\xb7^\xa7\xc7\xb6\xb73+i9AY\x9eJXn\x90\x94\xa7R\x94\xf3G\x96\xd3\\U\xbf\xa4'\x17\xac\xe0\xf1\xdd\x9b@+\xce\xb5\"\x95\xb8:3MRq\x07\xa58=\xc3eL\xcbu{\x90f\xd6\x8cSy\xdd\xb6\xd0\xb7\x07v;\xad*d\x9c\x96b\xaa\xd5\x0f\xa7\x86ha\xa4\x89@6\xa6J\xa8/\xd6r*M\xa1Bu\x83\xfb\xa8N}!\xc3\xa7\x8a\xcf\xacgP\x0f\xbd\xcc\xe1\x1b\xd7!\xdc\xc9o1\xad\xe9S-\xbc\xda\xf4\xc7P\xc7\x84s,\x8aG\xa6\xc1\xf8\x89w\xde|\"\xd8\xb4\x1d>\xc4\xf3b\xb5\xb1\xcb\xc3\xed\xf8\x8a\xa9\xb6j\x9a\xc8\xcbI)J2fe\xc7\x8exD\xfcs\xd5M]f\x9f\x97B\x0d\x1f\x19\xfa\x94T\xe7\x88\x9c\x1d/\xdb\xba \x9e\x15\x9a\x9f\x94\x18\x19\xba\x0f\xd4x\x84\xfe\x1e\xd1\xdeL|=\xcau\xa9\xb9(QS\x88?\x12\x9d\x0c24\xb4?\xfb\xf8\xf7g/\x8e\xc77u\xf3\xa1\xbfT<\xf2\x01\xfd\xc9\x13\xc8\xea\xe1\xf1v+\"\xfd<\xb3NL\xafWb\xdb _\x07\xf9:\xdc\xf1\xecM\xdeCsV*\xec\xeb\xe6\x03\xb0\xcf\xc7\xba\x13\xa9R\x92\x90}Qk\xd1\xff:B\xd0\xb1=\x1f~\x02o\xaa\x8eG\xae\xaf\xb7v\x84,vT\x1d\xdc\x13\xa7^e\x97w5\x1eo\x96j\x15&\x0f\xdeP\xeb\x9a\x06\xae^\x02\xfb\xda\xb6\xbc\x82\xa8\xfd\xd4\xb0\xae\x7f\xa8\x8f\x8a\xcf\x8b\x9a\xe4\xf6N\x82\xc0z\x90p\xf1X\xb9\x07\xf6\x19X\xb3i\xc7\x03\xc0X\x06\xf6,\x1f\xe3s\x9ddG\xf3\xba=Z\x00c\xa5\x99\x14r+\x90\xdd\xb9\xcd\xea\x08\xd8\x8f\x87\xb8\xf0ahk\xd4K\xae\xe7\xe2\x95>u\xe7\x99\x80%\xcdsxl2\xa3\x12\x0e\xa9\xc1\x8a\xe7\x10\xc3j,\xce}b\xa8\x1e\xb8!\x92\xe8\xb7\xf0%\x89:\xa8vI\xf5\x1er\xc7\xe0|,\xe4.\x1e\x8e\x9d\xf3\x0dE\xdd\x03\x1aC}E]af\x84\xe1%\xe7\xd95\xfeM\xed\xbaJE\xcb\x08k\xea(\x1f\x9c\xd3\xca\x82 \x84\x0c\xe4_\xd8\xe7\x9f\xe4x\x15\xbe\xc3\xd9\x1e\xf3\x83\x0c\x9e]\xf8~\x1e\xf7l\x9a\xaa\xe9\xb0\x13W\x01r&\xa9\xb5\x9a\xf4k+\x85`\xd7\xe1\xb5\x1b\x9f\x11\xcb.\xa1M\xbfl\xf7\xbe\xaa\xf7L\x9f\x1bX\x9b\xd7\xfcO\xcb\x8f\x86\x8fg\xfd`\xeb\x81\xb4\xa9\x86\xe7\x1c\xe4\xe3]\xb18\xad\xe0Aw\xfcLc}O\xdd\xe4[\x90\xb1S\xe3\xf2\xee9\x1c\xd9\xc9\x10\xe5\xe2!\x94q\"D\xcd(\x0c\x069\"\xdfY\x98\xd4\x88\xfc\x0c\x99\xafN\xe7\x93\xba5\xb5\xaa\xf7{q\x98\xfdN\xd1\xa5\x1d\x99\x84\x15\x13y*\xf1\x7f\xf9\xb4\x13\x89\xbe\x83\xa0f\xdc\xe4!\xe4\xec\x1f\xa6\x8b\xabVn\x0f\xd9\xef\xdbO\xb31\xe2\x1b\x87\xfe9\x88\x0f\xf8\x0cxg\x8f\xff\x10\xdd\xf1\xfa\xfd\x9bg\xa0\x0eb\xc2\x08\xfc\xb9nW\xfa\xf7\x0e\xd5\xe7\xdb=kv\xc3C\xa0\xa9\x9cp\x04\xd1\x1dS\x91\xb6\xf8]\xc5;'\xaa\nwu\xbb\xeb\xaa\xe3\xc3#\xdd\x8a\x97\x0fU\xdd\xbcl\x9b\xfbz\x17\xdc\x8e\xa5g\xc9\xed\xd3U\xf4\xd3F\x98o>\xc7\x87p\xdd3\xfdx\xe3A\xd2\xe2g\xe5\xca\x9f\xd2\xb1\xa8v\xf3\x96\xf5\x83\xf4;\xcb\xce\xad\xc4\x1d\x96C\xab:\xb4\xc4\xafz9A;\xd1\x81\x0c+\xb1\xee'\xdc\xe1\x9d\xb1\x0c\xae\xc4\xf6\xcb\xce\xdb\x1a\xc3\xd6J\xb3\xb2Fq\xb1r\xd5\x9a\"+\x03\xeb\x10\xcf\xbb\x1a\xc9\xb6jpT\xba9V\xa3\x98U\xa3\xf8Tg\xdeT\xa3\x8f,,\xaa1\xdc\xa9\nG\xaa\xa6\xcd\xc2\x98\x1a\xcb\x93:\xf1\xa1\xea\xad!(db8Q\xed\xdc\xa7\x89\x8c\xa7A<\xa7\xe1|\xa6 ,\xa6\xb1\xdc\xa5\xb6P\xf9l\xcc\xa4y\xf9H\xb3\xb1\x90\xfa\xb9G\xe3\x18G)\xa7\x8b\x85gt=\xbb\xa8\x87Et\x08\xe1\x0e\x8de\x0c\xb52\x83F\xf2\x81\x12\xde\x12\xaf\x83\xd6`\xfct\xaf\xa0\x91\xec\x9e3\x8b'\xd5\xbf\xdf\xf9\xcb\x8e\xe3\xef\\ru*\xeaL\xd6\xce\x0c\\\x9d \x0c\x9d\xe6(\xd7\x17\xc3\x046N\x0b\xf3f\n\xdf\xa6\x93(\xd2\xc2\xad\xe9e\xd44I\xea\xc2\xd93\xcdw\xffF\xb55\x8a\x1f3\xa4\xb1>.L{\xdb\xbc\xbc\x97+\xd8.\xc96G2[:\xf9,\xed,\x96.\xeeJ\xb2\x13By*}\xec\x94\x1a'e\x02\x13e\x00\xffd8\xeb\xa4\x97k\x92\xa0f4\x8a#\x9e\xf1qH\xa61G\x92\x95R~\x89\xe2\x86\xb4\xf1@\xa6\xb0?\x12L\x8f\xb1\xfc\x8e$\x97c\x02\x83\xa3\xb1\xd5\xb6r4\x1a\xcc\x8cy\xf8\x18\xe3Y\x185\xc6\xc5(\x9eE+\xa7b\x18\x93\xa2\x97?Q\x1c\xa4}\xac\x89\xe21'W\xa2A\x1eh\x96\x16\xcaL\xe7fC\x0c\xe4@\x0c`>\\T9\x81\xe5\xd0`4L\xe01\xa48\x0b\xe3\x98\n)V\xc2|\\\x84\xf1_\xd7\xcb;\xe8c\x1b\x9c\xcd4\x05\x12\xbe\x10\xde3\xab_\x90}\x1eX7NCt\x10*\xde\xb6;u\xef\xb8i\x9b\x86G\xff\xab-\x9fP\xb4W\xdco\xa8a\x9e\xc7\xaem\xef\x0d\xc0\xfar\xfcuY\x9d9\x12\x81\xbf\xe2\xae\xd9\xac/\xdc\xe1w<\xdd-#\xcb\xad\xef\xdbu\x80\xd5q\x08>0\xd2\x9f;\x9f\xd1\x89\x08NG\"du&BN\x87\"\xb8\x9c\x8a\x90\xe6X\x84\x8c\xceE\xf0:\x18!\xd6\xc9\x08)\x8eF\xaa\xc7\xfcW6E;\x1c ]\xdek\x9aR\x1c\x8f\x84:\xe7\xd5Ly\x1d\x90\x90\xee\x84\x84\xfc\x8eHHsFB\x9aC\x92\x9e\xa2d%\xb3\xb9)!\xbb\xab\x12r\xba+!\xc8e 9\xdd\x96\xe0p]B\xa2\xfb\x92\x9a\xe3\x96k\x91\x82\x9c\x9a\x90\xec\xd8$\x14\xd2\x97 E\xba;!2@\xccy\xd9\x91\x7f\xfd\x8ft\x81Rf\xcfq\xb5\x91\xbb\x1ei\xeePM\x99\xeb\"\xa3\x0cnQ\xc8\xed\x1a\x05\xc2=\n\xe9.RM\x9b\xe90\x85D\xa7)\xf8|\x89\xe0\xbe\x98\xc8\xeb@\x05\xebM\x1f\xe1\x8eT\xbb\x0e\xe3\x94\x9c\xe4T\x85\x15\x9d\xe1s\xae\x82\xb7\xdd^'+\xacs\xb4\x82\xab?\"\x1d\xae\xe0s\xba\x82\xf7\xfa \xdf\xc5A\xd6N\nu\xc2B\x80#\x16\xe8\x0b\x82\x12\x1c\xb2\x10\xe6\x94\x85U\x8eY\x08q\xce\x82\xfd\xee\x1c\xb2x\xcb\xb3>g-$;l\xc1UQ\xed\xd7\xac\xce[Ht\xe0j\xaa\xe8\x8b{\xb2\xbat!\xb3[\x17|\xd7\xef\x90\x17\xef\xe4q\xf1BN7/dw\xf5B\xb0\xbb\x17B\\\xbe\x10\xee\xf6\x85@\xd7/\xd8\xee\x8e\xa1K\x0fu\x14\xfa/\xc5 t\x05C\x98;\x18\xa8f\xe4t\x0bC\xaakX\xd3E^n\x93\xcfY\x0cY\x1d\xc6\x90<\x1e\xbc\x8ec\x08p\x1e\x83\xb1\xea\xd82M.Ow\xff\xc4\x1e\xf5\x8c\x81\xe3\xe9n_o8\xab\x06\x95]!\xbd\xc9\xe6\x0d\xdf\"\x06\xd5X\x98\xc7u\x03m1\x95\xabeI\x9f)\xae\xdc\xe2\xca-\xae\xdc\xe2\xca-\xae\xdc\xe2\xca-\xae\xdc\xe2\xca\x9d\xa4\xb8r\x8b+WJq\xe5z]\x9a\xc5\x95[\\\xb9\x86\x14Wn\x9c\x874\xfc\xd9\xe2\xca-\xae\\\xe2#\x14W\xeeB\x8a+\xb7\xb8r\x8b+7\x87+\xf7z\" \xd2\xbc\xb9\x0f$\x93\x900\x19\x97\xfb\xaann\xd8g\xfd\xe0\xa6\x13\x06\x1d\xc7\xe7n\x07\xf6y\x08L\x1b\xb4z\x9ce\x81\xaej\xa2\x11\x14\xd4Ku\x83\xd4\xdd\xfa\xc5~0\xf9O\x08o\xd6\xd4\x1b\xe2w\x1em|\xbbQ\xe8\x1a`\xae\xa4\x95j\x01\x9f\x97\xa4\xb0d\x04\xf3\xac+\xa5\xb3\x0f\x0e\xa2\xc6\xdd\xc2\xdfx\xdf\xb5\x0b\xff\x02\xee\xbf6\xac\xfe8\xbbu&\xca\xceV\xf8A\x9a\xedt\x7f\x0f\xfeOXC\xae\xf9\xb3\xfa\xb4\x90\xc8\x9a\xf4! \x82C\xac=\xb2\x0c\xeaD\x94\xb2\x82a\xc5.\xfa\xefJ\xb6\xcd\xa0%\xb5\xb4\xf9\xa1\x1a\x9e\xf1\x93\xa3@H\xd4%m\xdc\x04\xa8\x9d\xb7\xab?\xb2e/\x8d\xff\xac;\xfe=\xf4\x11\xa9\x8d\xa0\xb9BP\x89\xef'\x1e\x98\xear\xc7\x86O\xa3\xa1\x18>qT\xa4\xb3\x0d\xcf\x89\xde2`<\x92\xd4\x9c\xae\xde\x8c\xa3\x996)\xa6]ed\xa2\x96\xd6\x8d\xa8\x85\xffT\x81{\xb9\xcb\x7fr\xc6\x8e\x95\xe6\xe7\x14\xf1>w\xaa\xd5\x9bu\xecoo\xeb\xcd\x87\xb1-+m\xeaW5\x98\xcb:\x07\x18\xcda\xa6\x89\xec\xa1\x11o[\x86'R\xdb\xae\xec\x0e\x82a\xd7\xd9\"\xfaj\x89\x85%\x98\xc8u\xad,\xa7\x0b\xf2R\x81\xd9H\xa6S\xc9\xe2\xaa(\xd4vd\xe3\xd8\x92\x93\xac\xffp\xbb\xf1\xdcm\x00\xebj\xff\xa2\xff\xf0\x92\xdbI\xad\xf6\xf3\x0d\x19X{\x0e\x0c\xe1\x8e\xa3\xea?\xcc\xf5\x86\xa5KB\xa9\xec\x81\xef\x19\xb3V\xf6\xd7q\xe3FT\xf6P7\xf5\xe1t\x98\xef\xfc\x99\xea\xac,]\x9bv<\xd6,\x10\x0c\xad\xa7\x17\xbcv\x04\xff\xdc\xb1c\xc7\xaac\xb7\xbb\xca`\xd9\x8ao\xd3%*\xfdK\xd5\x1b\x0b\xdaA6gWI\xa8\x8f#?3\x15\x9f\xa2\x07+\x87\x06D\xf9\xb9\x1f\xc6\xe3\x1b5\x10\xc5C\xec3\xdb\x9c\x86\xcc\x8d:G\xa5\xc9\x8d\xc2\xca\xe1\xf9A\xdf]P\x8d\xb9g\xec\x96\xbe\xfdcyM\x9fq=\x1fy\x00\xb1G\xcb\x90w5[\x8fs\xf4\x9d\xcc\x96\xc7]\xb7H&\xdeP\x82\x92\xef\x9e\x12\x94\x90\xdbJP\xa8\x91\xf2\x9a\xb1\x17\x07jN\xcf\x03\xe5\x9e\xb1 \xe9\xae\x1e\xad\x87Z1\x90\xf5Im\x0e\x13}9W\x17\x92\xc05jA-\xcf\x8d\x8c\xf4\xe3\x98\xdc\xdcJ2/g\x7fUV}\xa1@\xdd\x96\xc8m+\xbf\x8b\xd5H\"\xb6.\x83k\x8f\x0f\x89\x0b\xa0\x9f\xd5\xde\xceh\x1fQ\xdcJ\x16{\x82\x9c]\x1e\xab\xb3\xb0\xd7\xaf\xf0Vd\xbb\x85%\x8a\xa9\xdeF\xe5\xef)\xdb\xf0;\xe5c\xa6w\xb0\xd2\xdb\x18\xe9 6\xfa\xe0SZ\n\x03\xbd~\x7f\x04\xf8m\x08qUE5\xed\xf6\x97\x83Iv\x95x?\xc8\\P\x86\"\xc0(\x9c\xc9Q\x1fl\x1d\x92\x0fs\xabG+5eC?r\x9e\xd1i\x19\x99\xf4\xa84\xdcjv\xbb\x82\xae\x07~\x1e\xac\x9b\xdd^7*\xc4b\xb6~,\x04/\x16+OK\xf2\x04\xa6\x7f\x94 \xd3G\x9d<\xc1e},aF\xd4)t\xbd\x1ej\xe0$\x1dO\x15=\xeaA\x15\x84o.\xaa\xcbt\x07(\xd8\x9b\xf9M\xf6n\x92\x83t\x94\xbb\xba\x8d\xea\xb8\xac\xadH\xe5\xbb\x86i\xfb\x13\xd5\x96\xfc\x97\x00\x99pW\x9a\xcbB\xd3\xa6\x98S\xed/\x8a7\x00h\xf7\x05dnY\xaa;CS'\xad\xb86\xf1\x9e\xb8\x11)n\x0eM\x95tzP_\xc7\xe7\xee\x00\x9b\xcb\x032\xb77\x97\x0b\x04\xecn\x10\x08q\x85\x80\xcd\x1d\x02\x99\x1b\x9c\xcb=\x02\x0e\x17 \x80\xddM\x02\x16W \x00\xed.\x01\xcae\x026\xb3\x86bO4\xb2\xb8O\xc0\xd5\xc9`u\xa3x^\xf3\x85'G\xbbT\x0cM)~\x15C\x99\xf0\xb3\x18\xbf\x87;[\xc01\xfe\xf29]\xc0\xeex\x01\xcb\xb5]\xe6\xee\x1928a\xb8\xdeTG\x0c\xacr\xc6\x88\xa7o\xf9M\xb4QK\xbd\xe3\xea]p\x0dk\xb9\xddJ\xbd\x82W\xd1\x95z\x0d\xef\xa4H(X\x1c\x84\x14\x15\xf2\xee\x95 \xe59\xb4\xdb\x93\xf5\xb82\xf3\xe5\x05\x9cV\x0cN\xbb\x15_b(\x17`\xa4'9Z\x13\x1c\x87\xf8\xe4\xc6\\\x89\x8d\xe5\x02\x0c\x94r\x01\x86|=k\xb2bB\xa2bB\x92\"a02\xa6#\xe6ME\xcc\x96\x86\xe8OA\xcc\x96~X.\xc0(\x17`\xacH\x13,\x17`\x88\x8eV5\xa6$\x00\x86\xdc Q.\xc0P\xa4\\\x80Q.\xc0Xq\xd7\x84\xff\x19_\"^Z\x12^\xb9\x00CH\xce\xf4:kj]\xb9\x00\xa3\\\x80\xb1x\xcc\x9f&W.\xc0\x88O\x82\x8b\xff\xba\xde\xe47_\xe2\x9b\xfb\x02\x8cT\xde2\xc3\xaf\xb0\xe4,\xb3\xf3\x95\x11\\e\xc5EW\\tR\x8a\x8b\x0e\xa5\xb8\xe8\xe4\xeb\xc5EW\\t\xc5EW\\t\xc5E\xa7\xfc\x7fq\xd1\x15\x17]q\xd1\x15\x17\x9d\x94\xe2\xa2+.\xba\xe2\xa2+.:(.\xba\xdf\xb2\x8b.\x91\x82jA:E\x11N\x91\xfb\x7f\xd2=\x98B2%\x8f\xd2\x8a>\xcd\x95\xb1$\x97\xd2CA\xb5\xbbp\xb9'F\xcf\xd0\x14\x11\x9d\x95\xd2\x1b\x9f\x1e\x18\x867\xf2\xc1'\xc9\x89&\n\x98\xf18\xed\xcc\x8b\xfa\x1f'\xd6=*g\xfc7u\xf3\xe1\xcf\x8f2{\xf0\x8a\xf5\xc7\xb6\xe9Yp$\xe2X\x93(\x1f\xe7\xa9W\xc9h \x06\xf4}\x8f\xfdn\xb0T)^\\\xe5=\"\x19T\xc4\x92\x9a\xd9kl\xa8\xea\xbd8:\xd4\xfd|\xe2\x14\x9cO\x8a\n[\x14\xb8=\x06\xdc\x9a\x17\x0b\xae6\x03X3\x0e\xc3\xe8c\xa4\x9842\x93\xfa\x80\xb2I\x96\xeb$\xae\x19B\x9fV\xeb~\xa8\x06\xfdD/\x0c\xc7\xf8\x17i\x0f,\xdf\x86h\x0fkNF\x08\xfeO\xf0\xe2\xf2\xf2\xcd\xc5\xcb\x177\x17\xef\xde\xde\xbe\xb9x\xfbO\xb7\xd77/n\xceo/\xde^\xdc\\\xbcxs\xf1\xbf\xce_\xdd\xbe\x7f{}y\xfe\xf2\xe2\xf5\xc5\xf9\xab\xd0\xd7\xff\xf9\xfc\xea\xe2\xb5\xfc\xfd\xfa\xe6\xc5\xd5M\xe4\xbb\xe7WW\xef\xae\xa2\xde\xbc~\xff\xf2\xe5\xf9\xf5u\xe8\xbb7\x17\xbf\x9e\xbf\xba}\xf7\xfef\xf1\x82\xd8\xb6>O\xe9&\xb7We\xed\x17x\x0e/f\xf6\xb7\x7f3\xfd\xfd\x9c\x10\x8e;k\x8d@wwy\xd4'3\n\x1bGe7\x8c{}6\x1a^\"\x03}E!\xfc\xdb.\x8a\xe0\xbe\xd8E\xee\xcb2g\xe9\xd8\xb5\x1b\xf5\xfe\xf3U\xc5\x89\x01\xb1(p\xd9\x0c[N\x94\xbb\x90i\xe4,4\x0f\xf5\x81m\xa1=\xf1,\x90=\x83OU\xcdi\xf6d8\xbc\xda.\xa5(\xc1\xa2\xd0\xa9\xec\x0eRl\xc6\xc8\x9a\x8a>htd3c\xe0\xa1\xdaJ\xbe1M\x99\x91c\x11e\xed\xf5\\F\xf0\x19ZGN\x15\xccM\xf7\xb3PH\xb1\xb3QL*\xd3\xab\xb3\x92\xa5B\x8a\x85\xa4AQ\x9c\x87\xb5bRj\xcf\xd9rgmyVm\xf0\xf5\"\xd8W\xef\xd5|\x01\xaa8:\x10V\xd4\x89\\\xd5!\x9e_\xc0\xa2M\xdb\x1b\x18>'!66\x0c)\x04+\x86\x94\xe8-L\n[\x06\xa1\xce6\xe8\x11\"#\x8d\xd9\x7f\xe8\xd5\x12\xc3\xfe\xc0me\xd7\x1e\xc4\x1c\x1f\x15\xc8:\xa9\xa6S\xf7U\xbdk\x16\x08\x12?&\x8c\xef\xf0\xdd\xd48C\xd1fS\x9b\x83\x083'\xd6\x0b\xeb<\xbe\xc6\xbf/\xe9A\xe6E\xc6\xb6\x08\x08\x1d\xf1\xd3\xd6r\xe7\x0b\xf8\xc6\n\xccU\xff\xc58\xfb)+\x88<\x07\xdea\x9a\xdd2CN\x15\xcbeuSY\x81\x95\xb1\xce\xd4_\xc8\xa3\xf2r\xad\x13\x07T^\xdb\xf1\x19[Y8Su*\xe7Q\xee\xabz\xcf\xa8y\x87\xb5{\xcd\xff\xbc\xfc\xc8\xf8\xca\x93}`\xbeQJ\xeb\xd3\xf3Q\x05v\xd5\xe2\xec8G\x00\x8dm\x989\xa1\xc1\xcex\x0c\xaeb\xef\xad\xcc\xc7\xa0,x\xeb\xd9\x8f?\x8d\xd6H!>F\xe1\xc9\x8eOS\xc7\xf3I\xf5\x9aZ\xd6\xfb\xbd\xc8\xc0\xfcN\xd3\xb7de\\\xba\"h\x9e\xea\nur\xef\x867\x97\xf5; \xacl\x98\xdb\x83\xa4\x8a\x1eN\xdd<\xe5\x85:\xb9\xb9\xe4\xb1\x11|\xc7\xde\xc2\x8e\x0dd\x1a\xea\xa9G?\xa1XQ\xeam\xb8c\xe6\x1d_\x12W;ep%\xd5\xdd2yy\x01M\xff\x8dc\x989vS\xd6wl\x8e\x0b\xcb\x0b\xb4\xc1\xa4\xfa\xf3\x95\xf0\xf6\xd8\\@\x92\xbe\xc8\xd6\x82\xe5>\x86\x7fb\xde\xe1\xfa0\x7f\x81q\x84l\xe6i\xd6FF/>\x94x\xf1X\xedD\x06\xfes]\xd5\xe5\xf4'-a|\xfa\xb9\x13cD\xfb\xe6A\xae\xb8\x86}\x1e\x96\xe9\xc4\x10b3\xdcQ\x14\xc6\xfeF\x96\"\x0f2\xe3?e\xd6}/\xb6\x93\x97\xd5n\xa2\xf2\xc2\xbfkJ\xfe}\x9c$\xfc\xf5Q\xdd\xd8\x01\x0c\x0em?\x00\xe3\xc11<\x8e\xe6\x0c.\x06\xc5]\x7f\x1c\x1e\xa1\xbe\xd7\x07\xd1\x03\xeb\x18\x8fnjZ8\xb4\x9d\xdci-BK\x86v\xa8Bc0#\xa81\xb8z\xde\x1b\xfc\x1f3o\x8b\x0c\xacR\xe2\x81\xf4\xfa\xab\x1d\xc5w\xcf\xb7\\\x89\xbe\xc5\x1e\x97\x8a\x9e\x0d\xcf\xa0\x1ez\x19\x15\xd6\xc3\xa9\xc1A\xb4\xc5\xe0\x9dO\xb52l\xec\xdf\x10\x8b\x14v\xb2\x96!V\x13\x1cR7\xb0\xbb\xba|9\x0dD\x89S\xf4\xe3V\xb4c\xda\xc6y\xd3v\xf8 \xc7P\xa45\x95\xd0\xc6\xb8\x83\xe1'\x0c\xb5\x99\x8b\xb6\xc9'\xaf\xdb\xc3\\)\xf2\x1e\x8e\x8e\x1d\xf9R \x7f\xae\xba\xa9gm\xf7\xc2,\xda\xc8\x07\x97\x8e\x9c!\x98A{\xb6\xfc\x16|\xed\xfa\"\xdbl,3\xfa\x1a\x83\x86\xe4,ve9s\xd8\xc9\xe0Ug\xb9\"\x103%\x94k/\x80\xf9\\_\xdeS\xec\xbc\xb1w\xd0\x8cz`\x8f\xae_\xa6I6\x8d\xaf\xb8J\xc7\"-\x14\xd6\x92\x05mq\x9e\x14\\\xe7\x04\x8f\xf7\xc6sR\xc8\x82\xbb\xb8\x90\x97\xd0\x1aXN\x7f1\xf8\x0b\xa9\xc8\xe4\xff\x97B`0\x81(\x8c\xb3m\x14\x12\xb3\x1e \x08W\x10\x80\xc6\x84\xbeM\xe11\xa1\xef\xd2\x88L\x88g]{%\x07*\xe3\xc3e\xd6\x7f\x0f\xd5\xfdo(\xfb7\x0c\x9euc3\xc1\x1d\x99\x86\xce\x84\x16\x93\x0d\x9f -0\x11\xa1 \x19I90\x1a7J\xe32[Y\x91\x1a\x12\xab\x89^+(\xbf\xb2\xd7@{`\x92\x08\xdc&\x04\xb9\xc9V\xadH\xfc\xc6\x03@\xb8W\xad(h\x87\xd4d\xeb@\x8fc\xd1\xe7Z\xf4n\x1b\xc0\xff\x01\xc0\xdb\x0d `\x96U\x98\x90U\x93\x03+\xf2\xa2E\x19\x1b\x93\x0f3\nF\x8d\xfc\xb8\x91\x139\xf26\xde3\x98\xf3\xe2Gv\x04\x89\xc6\x90\xec(R2\x8e\x94\x84$E\x1ba+\x9e\x94\x07QJ\xb6\x0c\x0e\\\xc9;\x92\x002cK^tiM\x95\x1c3;/\xc6dG\x99\xec8S2\xd2\x94\xfc\xe1\x1dx\xd3\x9a^\x8e\xc1\x9c\x9c\xa8\x93\xb3p7\xaa\x93\x1f{\xf2\xa0O9\xea\x9a\x0d\x83\nD\nbq(\x13\x89*\x8e\xfd\xe2\xd8/\x8e\xfd\xdf\x88c?\x046\x9e\xbd\xf9\xc69V\xfc.\x12:pL\x8f\xed\x15\xda\x84\xf7^\xf7\xd9!\x168\x1e\x02\xb4\x9b4\x1d\xce\xe7\xe9v\xe3\xdf\x0bD\x8c7o\xaf\x00|\x01\x86\xaa\xdb\xb1\xd0\xcb\xe6\xe8\xc9\xba\xec\xc6`\xc4\x00\xef\xcc6|\x9fa0\xf0\xfcr\x01\x80\xcb:Q\xd6\x89\xdf\xe2:A\xf7\x92\xcb2\xfb\xa1_\xcd\xaf\xa5c\xbe\xb3\xd9\x08@{-\x958#\xed]\xf0\xa2\xe1Ex)#N<\xa6\x1bn\xe3\x11y@I\xb5\xce^\x0cw*a\xfd\n\xfam\xa3\xb7\xea*\xb1e\xfd \x97 y\x8b\x93$9\x1bZs\x19\x13\x7f\xa3K^\xed\xda\x19H2\xb3\xf9o\x8e\x03\xb4\x0f\xa8\x82\xbc\xf4f(v\x923\x94|Tg(\xd9\x08\xcfP\xac\xb4g(C<\xf9\x19J.\n4\x147\x11\x1aJ\x14\x1d\x1aJ4)\x1a\xddw\x8fGy\x03\x15E\x8d\x86\x12M\x90FjC\x87\x86\x85&\x0d%\x85,\x8dTx:B\xc5\xdbj)/\x828\x8dTd%SCI\xa4T\x93J\x02\x88\xd5\xc87\x83\xc9\xd6P\x12(\xd7P\x12\x88\xd7l\x13\xddR\xd5l\x94l(y\x89\xd9P\xb2\xd1\xb3\xa1\xf8I\xdaP\xb2Q\xb5\xa1\xd8\x08\xdbPRh\xdbh[AP\xb9\xa1\x0c!\x84n(\xb1\xb4n\xa42\x1b\xd5\x1bJ$\xe1\x1b\x8a\x15\x90\xf1l)\xacgW\x94\x90\x1dG$\x1d\x1cmL%E\x1c~#\xed\x19_m\xd2\x08\xe2\x0cu\x9c0\x8e\xa0\x89C\xc9@\x16\x87\x92F\x19g\xa8\xe3;\x1ar\xf3\x90H\x1fg\x96d\xd0\xc9\xa1\xa4\x90\xca\xa18\xd9\xd6P,\x04s(^\x9a9\x14\x82\xe1\x8aK8\xe5\x1c\x8aM\x8f\xc1)\x94HB\x87\x12\xde9>B:\x14_/x\xc9\xe9PVP\xd4\xa18z'\x92\xae\x0e\xc5IZ\x87b\xa7\xae\x9b\xfen%\xb0CqtZ(\x99\x1d\x8a\x8f\xd2\x0eE#\xb6CI\xa0\xb7C \xb9C \xa7\xbaC\xf1\x12\xde\xa1\xd8:\xd1$\xbfC\xb1>\xef#\xc2CI\xa3\xc3CqT\xd9\xf8=\x89 \xcf\xd0\xa6\x11\xe6\xa1\xa4\xd0\xe6\x99%\x184z(Idzf\x95\x97\xe4z(9)\xf6P\xacD{(\x06\xdd\x1eJ\x1e\xd2=\x94l\xd4{(y \xf8P\xc2h\xf8P\xbcd|(\xc2\xe1\xe3\xa3\xe4[<\xec$\xe6C!\xa6\x17\x904n(\xa1dnn\xaa>\xa1+\x8c\xb0O<\xec\xa7\xedC!\x1a\x94\x93\xc2\x0f%\x89\xc8\xcf\xd0f\x10\xfb\xa1\xe4\xa3\xf7S\x8a\xc9B\xf2\x87\x92:F\xbc\x84\x7fB\x9d\x87\xf6\x0fE_\x9a\xecQg\x02'\xa5\x03o\xf88\xf9<\xb0n4\x07\xe8\xdfW<\xd8D\x80\xec\xa6m\x1a\xb6\x19('\xee\x14rE^s\x8cr\x9c\xaf\xe3\x9dEb\xb1K\xe2\xbea\x11\xb2\xcb_t\xd7X\xd7\x1a\xe7V7\xae\x00\x9e\xc5\xa1\x11\x02B\xde\\\x0e{\xf0\x9f\xb0!\xe0\xe0*%\xd6yoU\xb8<\x85Z\xb6\x81\x91N|\xab2\x87s\x1fb\x1d\xfcVm\xbcR\x1e'?\xe4p\xf4C\xbc\xb3\xdf\xaa\xaf\nr\xf8C\x9a\xd3\x1fb\x1d\xff\xf6j\xcb[R|\xce\x7f\x88\x04\x00\xac\xca\xe6\x9bS\xdc \x00$\x00\x01v\x85\xe26\x15\x07\x18\x009\x01\x01\xf0\x83\x02\x90\x0b\x18\x80$p\x00\xd6\x03\x04\x90\x03$\x80\x04\xa0\xc0a\x9f\xc8\xd3\xd1,\x99\x01\x03x\"\xd0\x00\xf2\x03\x07\xb0\x02<\x80h\x00\xc1i\xc3\xdd \x02D\x01 VU\xee{afY\x01(@nP\x01\xbc\xc0\x02\xa4\x82\x0b\x90!\xe3\xc3\x034\xc0\x8a]RN\xc0\x01\xbc\xa0\x03\x04\xd6,\x0e| U\xc95\xc2\x0d@@N\x10\x02\xd2\x80\x08R\x9fX\xe8\xed\x1b\x9b\x04@\x82\xd4G\xdfq3K:0\x01a\xfew\xf0\x01\x14\x10\x0eR\x80\xcb\xeb\x18\x01V\x80G\x9f\xc5\xd3\x92\x01\xb8\x80\xd5\x9d\x17\x06`@`\x0f\x05\x02\x19\x10\x03f\x80\xbf\xe7\x92@\x0d\x08\x036 \x00\xdc\x80 \x80\x03\xfc\x9d\xba\x0e\xe8\x80`\xb0\x03l\x80\x07\xe4\x00=`\x0d\xf0\x01\x11\xe0\x07\x84\x03 \xe0\xe9d\x1b\x10\x02\xbe\xf7\xc2\x00\x11\xc8\x04\x8a\x80\xaf:\x96\x99\x11\x05\x90\x90\x9a,\xb7\n\xcd\x92\x15(\x01\x07X\x02 \x80 \xa9\x8c\xba\x91h\x96\x04\xe0\x84\xd4g?\xdaz\x00\x15\xb0\x83*\x90\x19X\x81$p\x85TG\x01.\x10\x0b\xba\xb8\xec\x96\xc5G\x0d+\xc1\x18\x08\x07d`-(\x03\xab\x80\x19\xb0Oop8\xdfa\x85\x03>\x04\xa4\x81\xb5@\x0d\xac\x01k\xc0\xde\xc8\x04\xd0\xc61\x87\xa8\xbf$\x809\xf4\xd8\xb6\x01:\x10\x0d\xea\xd0\xaal@\x0f<\x01\xd8\x03\x99\xc6\\ \xe8\x03\xc1\xc0\x0fXVs7\xed@\xca\x85\xedt\x7f\xcf\xd4*\x96\x0d\x92\xfd\x12\xf7Y\x9c|\x0b\x05n)p\x0b\xb9\xe3)pK\x81[\n\xdc\xa2(*pK\x81[\n\xdc2K\x81[&)pK\x81[\n\xdc\x02\xab\x11\x83\x02\xb7,d]\xe7\x15\xb8E\x95\x02\xb7\x14\xb8e\x96\x02\xb7(R\xe0\x96\x02\xb7\x14\xb8\xc5*\x05n\xa1_(pK\x81[\xbei\xb8\xe5zbu\xd6\x10\x97\x07\x92\xfa\xd9q6\xba\xdcWus\xc3>\x0f\x16\x86\xe7\xe3\xf8\xf7\xdb\x81}6\xf8\x1a\xc1\x7f\xc0\xf7`FS\xd1\x8eF\xa0\xf1\x17\x0c\xdbu\x03m\xb7\xb5\x9c\x8e\xc7C\xa8\xf0\x0eZ\xfd\xbfS\xbf-\xfe\x8a\xecg\x9b\xb6\xb9\xafw\x96\xdc!\xce7\xf6\x92?\xb1\xb4\xee\xf8\x96\x18\x0ft\xfe\x90\xae1.o(\xe2F\"\x9f{\xc2\xda(5{\x0bS\xa0\xb8\xb3a\xc9\xd2l\xa8\xab{~\xf7\xc7r\x9d{b*l\xaaU\xeb\xd9\xb1%+\xba\xa1\xcb\xa4\xcc\xf6\xd0\x91\x92l\x81\xa2\n\xe3\xca\xc9j\xee\xdb\xab\x1a\xa8\x9b\x81u?A\xdb\xc1\xa6k\xfb\xfe'sD.\x87\xb0\xd2\xe5\x85o\xb4\xf0\x8d\x16\xbe\xd1\xea\xf7\xc27\xda/:k\xc1G=n\x16\xc5\xddN\xea\xab?\xcf\xef\xc2\xd8\x95x\xb0\xee\xce\xc2\xc0Y\x188\x0b\x03\xe7\xd30p\x8aO\xba\xe4\xde\xf4\xd1aZ\x998U%?\xdb\xb4(\x9c\x9c\x0eJ\xce\x8bf\xd3\x1e\xeaf\xf7\xea\xa6\xda\xddtU\xd3\xdf\xb3Nt\xe0zZN\xf1Y\x9e\x98\x99s;T\xbb\xdb\xa1\xbd\x1d\xbaj\xbbf9\xb2/7\xbc\xf1\xed\xcd\xa8oi\xfcq\x06\x88Ut|\nw\xe6S\x00\x80\xa1i\x10]\xd8\xb1-\xdcw-\xe1\x1c\xc7\x0f\xbaa\xf5\xc7\x19\xc3\x90\xc3Y@\xf4=k\xf4\x0d\x12\xfe\x94\xa5\xb1\xd7\\\x95\xbe\xaa\xca\x8cn\x89\xaal\xb8\x91\x10\xcde\xfc_fkE\xbd\xbf[\xfe\x86m\xcbR\xd7+\xd9QZm\xad\x1d\xf8P\x8d\xb6\xcb\x0c\x9a\x91\x91\x14\xcf\xb8\x9d7\xbf\xca\xae\xfe\xc8\x96\xdd?\xfe\xb3\xee\xf8GW\x87*YUb\xf2\xcc5\x86J\x8c\x1c\xf1\xc0T\xd9;6|b\xac\x81\xe1\xd3r\xd1:\xf5\xac\x9bQ\x17\xaaD9A\x95Bp\x1f\x84\xbb\x07\x1e\xc31\x0dX\xadX\xdc\xe0\xa9Mj?U\xdd\xb6W\xfbd|y\xac\x85\xf8\xa9\xec\x16\xcan\xa1\xec\x16\xbe\xe0n\x01 \x96J\xdf\xc1\xf4\x1d\xb2\xd2\x87s\x7f\xbb\xb4\x05\xb2\x81_V]uX\xbf\xc98\xf2\xd7\xe2,Q\xbd\xf9`\x9e\xc1\xad\x87y\xfbQ\xfeP7\xb7{\xd6\xec\x86\x07\xf38\xef\xf4\xa78B%\x0e\xd5\xe7\xcc:m\x96\xe1\xad\xe8\x06\xfc\x00\xda\xb8\xea\xaa\x03\x1bX7\xae-{>\xb0\xc5\xaa(?\x9f\xa6Kv\xa9\xfa\xfb\xb8AK\xee\xe2\x8e\xedn\xd9\xe7U]\xf1\x9b\xfa*\xe3\xc4 \xfe\x00\x929~|I\xed\xe9\xbb\xbaM\xee\xe8'k\xe1\x9f\xebvu\x03\xef\xeav\xd7U\xc7\x07\x15\nl\xbbj\xb3O\x9f\xb3h\x18o\xebmd3\x89\xa5\x17\x9c\x9bW\x80k^\xe4\xc5+}\xfbz\xf1Jn\\\xb1m\xa2nb\xcd\xdbT\xfb=\x19\xf97\xb4\xf0\x91u\xf5=\xdf\xa1P\xce\xd1m5\xe8A\x9cU\xff\xe1\x96\xaf\xe0_\xae\xd5/\xfa\x0f/\xf9\xe1Ak\xf5\xbc\x03\xc1V\xf35\x1e\xbdxU\xff\xc1\xdc\x8c\x8d2\xb7\x97j\xdc\x81\xbb|\xbfh\xe3~\xad\x1b\xb2q\x87\xba\xa9\x0f\xa7\x03\xe0\xb1Im\xe3|`$\xd4m\xda\xc3q\xcf\x06\xab\xbb\x9b\xb7_\x04\xc0B\x7f\xdalX\xdfs\xb4Y{\xf6\xd8\xb1c\xd5\xb1\xdb]e\xb9\x05\xe2)\xfa\xe2\x12\x0b\xfdK\xd5\x1b\xa7\xc9\x83\xec\x86]%\xb7r|\xc7\xb5=q\xf0\x91F2\xb0\x11\xbc\xadT_\xf4\xc3\xb8c\xa2&\x8e\xf60\xfb\xcc6\xa7\xe1\x0bw\xc69\x16\x9a\xad3\xb0\x11\x08HP\xbd\xe1\xeb\x84{\xc6n\xb1d[\x1f,\xbd2R\x0c\xef\xcc\xf2\xa5\xe8T\x9b-k\xda\x03\xfd'\xcfG\x19\xc5\xd6\x12\x14\xcf\xeb>\x0ce\x94\x97m=\xef\xb0+\x18\xda\x0fL &\x15V]n\xb7\xabf\xcb\x03@y\x85l\xd1\x88o\xdf\xdd\x9c?\xe7\x81\x11\xe2\xe3c\x84A\xcdcG/\x9aA\x98\xbd)^W\x05`I\x85\xc2\x8fG\xfem\xc2\x08{~\xd2\xa9;\x04\x06v\xed\xae\xe5h\xa7\x19\xb4\xe0\x1a\xc7\xaf\x19{q\xa0,\xdc<\x8c\xef\x19\x9b\x02\xd5\xabG7\xa67\xb4r:\xdaL\x9c}\x10\xebNS)\xef\xf8\x1b\xc1{\x8by\xa6hz\xb8i\x96'\xf9\x93\xf0\x11(+\xce\xcc,\xd8\xdeC\x05\x1b\xd6\x0c\x9d\xc8\xb6Z*\xaa\xe6,\x85\xc9sv\xf7\x08\x95\x9d\xe5\xb0:\x1eo\xb5K\x8c n[\xc3\xe3\xa4\xeb\xe1\xf1v+\x00\xcc\x95&OnG\x05\xe2$\xd5\x81T\x07w\xf0T\x8b?\xac?\xd6\xe2{Q\xe7\xda\x81Lc\xb4~v\xb7=\x8cMT$\xd61{zbLR\"\x9d|\x18\x95rH]\xe1dM4\x1c\xe2\xd3\x0b#\x93\n\x8dT,w*aT\x02aT\xda\xe0\x9c\x1eH\x99R*Y0&EPI\x05\xd4\xb4Y\x12\x03c\xd3\x01\xa7\xb4?\xbd5D\xa4dL\xea\x9f=\xc5/1\xb1/(\x9d/\x94%\x85\xcc\x9b8f\xe6b\x84'\x89\x99\xef\xfe\x8djkT\x1aXHc})_\xf6\xb6y\xd3\xbbV$u\x91m\x8eL\xe0r\xa6m\xd9\x93\xb5\\)Zd'\x84\xa6c\xf9\x92\xb0\xb4\xd4\xab\x84\x84\xab\x804\xab\xf0\xe4*oJ\x15\x91\x81d\x14G<\xe3K\x95JK\x90\"+\xa5\xfc\x12\x95\x02eKwJIr\"\x12\x9ab\xd3\x98\xc8\x94\xa5\x84D%c\xabmME2\x12\x90\xf2\xa4\x1d\xc5'\x1bi\x89EQ\xe9D\xd6\xd4\xa1\xb0\x84!o\x9aP`rPHJ\x90\x91#c\x96\x16\x9a\x80\xe1N\xfa L\xf5 H\xf0YT9!\x99\xc7H\xdcIH\xd7\xa1Rs\xe2\x12r\xa8\xe4\x9b|)7\xf1_\xd7\x9b^\xe3K\xaa!\xee\xac\xff\xc7E$\x8b\xe6b\x0br\xe1 |\xf9\xea\xf2\xa5\xd0\xe4\xf4\xe4]\xf1\xa3j\xb0\xefN\x00s\x8a\xef\x0e\xfd\x96\xd7\xf8\xbb\xeaI\xaf\x14\x14o\x81\xee\xcd\xef\x86\xfb\xfd\xd6\x9ceD\x95~1\xf2r\xf8\xe0\xc5\xec \x99\xa3#\xd2\nL\xb7\xb5\x85\x8bmM\xa1sF\x93R0k\xa4\xa3\x81\x17>>#\x90\xb1EJ\xd3}U\xef\xd9\xd6\xe8\xe6\xd7\xfc\xe7e/\xe3\xa3\xc9=\xcc}\x1e\xeb\x1a{>\xbe\x82\x8dC\x0f\x8e\xc1\x997\xd6M\xe6/\x89\x97p\xc0-\x9b\xb0\xc0~\xd1y\xe2\x1a\xadg\xaf\x95\xde \x18\xb4Z\xd3\xc8f\xa54i\x82m\xc4\xe7\x99g\xe9\xd8R\\\xa5\x0dDa\x1a\x13M;\xe0\xb8\xc0^\x98\xce\xc4\x1a\x06n\xef\x8dkuN\x06t\x876\x97\\\xdd\x11?\x87\x88\xf9\x13ZP\xfc\xbc\xd1\xbf\x87\xb4J\xeb>\x08/\x8c\x88@P\xbe\x8f\xbe\x06\xbch\x1e\x83\xbb\x7f0 \x0c\xb2c\xec\xce\x8fL\xb0\x05\x0dY\xe4\x81+\xe2\xa1\n\x1a\x9c\xa0\xf6\xec\x01\xc0D4(1\xc3\x10\x8a6\xdd\x07\xb3\x1a\x8cH\x04\"\xf8G^v\xca\x02\x84H\x04 \xf89l\xa1]U\x9e\x0c<\x18\xe7\x9f|0C\x02\xc4\x90\x11^\x88\x84\x16r\xc2\nY \x85|pB\x16(\xc1\x0d#\xc4C\x08$h\x90\x150 \x0e\x0fK{\x90\x15(\x88\x00 B6\x02$0`_\x9br\x02\x02\xc3\x04\x06\xd0\xe5\xe5\x00\x01T2\x95 \x00Ht\xfe\xa7;\xfe\x17KM^W\x7f\xac\x9b\xdf\xea\xf5&\xdc\xfbN\xd7\xfe\xd2\x97\x18\xe6\xd2_\xbe\xf37\xbd-\xab\xdd\xf8\xbe\xc6\xb8\xdc\xf7t\xfd\x9dn\xfb@\x97\xbd\xd1\xae\x08W\xbd\xd5MO\xbb\xe8m\xeey\xa3\x91!ny\x97K^q\xc7G\xba\xe2=n\xf80\x17\xbc\xd3\xfd\xaey\xb9\x17\xea\xb5\xbf\xb9\xdc\xed\xf1\xaev\xa3\x02\xe2\xff\xb2\xb9\xd73\xba\xd6\xb3\xb9\xd5s\xb9\xd4Iw\xfa\xc2\x95\x9e\xeeF\xcf\xe2B\xcf\xe7>\xf7\xbb\xce\x9dn\xf3\x00\x97\xb9\xcf]\xbe\xf0;/\xb5\x87:Q\xed.\xf2\x00\xf7\xb8\xc75>U/\x97K<\xa3;<\x8f+<\x8f\x1b<\xee\xcb9\xdd\xdf.\xd7\xf7h.w\xddqs\xb6\xab\x06\xf6\xa9z<\xebN\xcdP\x1f\xd8\xd9\xf9\xec\xa8\x0bp\xa4\xf8\xddz\x9bvk\xeco\xebf`;%\x80[np\xebf\xf8\xfb?\x89_E\x0f:uo\xd9P\xd5\xfb'&\x80\x18,\xb7v\x90[u\x14_\n@&\xef\x11\x8a\xebV\x8e<\x9e$\x94x\x7f\x92\xa1\xca\x1a\xfc\x1a\xedeB\x89\xf65\x19\x9a|\xb7i\xac\xf6;\xa1$z\x9f\x88\xde\xb2\xde\x9a\x91\xe8\x892\xf4Yo\xc9H\xf4J\x19\xfal\xb7b\xe4\xf3U\xa1$x\xac\xa4\x82l~+\x94H\xef\xd5T\x9bl>,\x94,\x9e,\x94|\xfe,\x94,^-\x14\x7f\x88l\x8a\x87\xcb\xb4\xa2\xd6['R\xfc^\x862\xfa\x96 b/AY\xa94\x9f\x98\xa1\x8e\xbaM\"\xc2S\x86B\xde\x1e\xe1X\x8a\x1d\xc9\xf6\xbeU:\xd2\x8ff\x1a.\xeb\xad\x10\xae\x1a\xe4\xf0\xac-\x14R\xb7?$z\xd9P\xd2}m\x0bu\xe6\x82\x9b\xe8}[\xe8\x1a\x88[\x1db\xfdq(\x9e\x8b\x08\xac\xb77x\x83oi*\xf40o\x9d\xfd\xfd\xbf\xd1m_\xed\xbfC k\xbc/\x14\xd7\xddRo8n\xb0w\x0f\xc5\xd2\x03\x11\x9e>\x14\xcfm\n\xae[\x14\xdc\xb7'X:%\xc4\x0f\x88\xe2\x0b\xd0%nG\x88\xf4\x0c\xa2\x04\x84\xe9\x86z Q\xbc\xa1\xba\x96\xeb\x02\x88b\xc9\xe7|!\xbb)\x9eD\x14K\xf5\x16\xbfe\xf3-\xa2d\xf40\xa2d\xf33\xa2\xe4\xf26\xa28n\x13 n\x11H\xf7?\xa2d\xf1B\xa2\xe4\xf3E\xa2\xf8=\x92(\xdep\xde \xef\xe4\xe2A\x0f\xcb?A|O\x95\x1a\xea\xfb\xf2\xb1\xf9\x07\xf8.\xc5\x83\xfe\xe0^\xa3\xf2\xb9\xbc\x99(\x19}\x9a(y<\x9b(y\xfc\x9b()\xdf\xdb\x1b\xea\xeb\x0f\xf6\xc5\x95A\x84\xf1q>\x80q/\xf9P\x1f\xfb\xb3\x8f\x7f\xc4\x8c\xfa?\xef\xdb\xcd\x87\xf5\xac\x1a<\xfe\xb5\x0d#*a\xd0Z\x85Vb\xeb\xa9\x84P\xaf\x12\x82\xa2t\xac\xeaM\xba G\xb1\xbe\xe3\xdd\x15W\xa8WG\xfa\xa7Dys\xc7LQ\x89\xa2\x86\x86\xbe{\xdd+\xd3\x9f\xee\xfac\xb5a\x04 X\xc0\x81\x95$\x05r\xf0\xec\x8a\xc2\\\xf4_\xb2BP7}\xbde3\xa7\xbd\xa1\x8d\xb7X\xd0H\x19\x7f\xbccD\x1f\xb8z\xfb}\xcf:\xfe\xf5\xf5\xaa\xddW\x9ba\xf2aN\xa3\x94\xf7\xb4\x18\x06\xe3\xef\xbb\xfa\xa3F\xb4/\x86\xd2\xb2jc\x9d\xe5\x07(\x1c\xb2\x85C\xb6p\xc8~A\x0eY\x07\x13\xecrA\x0dI\x9f\xc17\\\xd93\xf4\xd2}\xa5\xfe\xbaz\x05_\xe8|\xda\x85\x9c_\x12\xa2gz\x84\xac\xa1/\xf1E\xdd\x8e\n}0\xf1\x93\xcf-Y\x96;N\x0c\xd6\x1d\xabnx\\_8us\x8a\xa2\xd0XI\xf9\xf6\x81\xaf$u\xf3\xb1\xdd\x7f\xc49A3\xbb[*\xfc\x8d,\xa1\xf5\xd6XB\xc7\x11k_?\xd5\x06A\xdd\x9bk(\xf7(+\xbf\xbaV\x8a\xab\xa52\xac\xc7\xd0\x9d6\x98F\xb1(l\xb9\x03\xb9P\x89\xe1\x07\x04\xff6\xec\xc8\xdf\xfb\x1e\xa1\xb1\xefy\xcb\xbaj:\x83\xf2l\x9b=4l\xf8\xd4v\x1f\xfa\xb2\xa2\x96\x15\xb5\xac\xa8\xdf\xd6\x8aJ\xaes\xce\x85U}\xfb\xe7\xc5\xeb4\xcf\x9c\xb1\xbe\xaa\xef\x04\xaf\xa8\xc62G\x0e\xed\xd4\xa5\x8d^\xd4\x9cEE\xadZT\xd9\xe4\xfaD\x16m\x99\xbc\xd4\xc4\xcd\xb2\x0e9V\xa0y\xed\xa1\xc7X\xccz\x93i\xa5\xb1\x0d\xbf\xe9\x04\x19<\xf6\x0c_\x89k@\xe4\xf1\x8f\x18\x9e\x11\x7f\x91\x11\xde\x10\xdd\x0fB\x16\xb2\xf8\xb0\x89\x8e\x0e\xd5\xb1\xf1\x85\xc7{\x80\xebBw\x14\x99N\x0b\xcd]A;*2\xba(4\xe7\x84tK\x88\xa1-\x1b\xc2\x93\x87\xff\xd2U\xcd\x9a\xbc\xff<\x9d\x7faZ\x11\xb2W\xf9\xd2\xbe\x1b\xab(Z\xc9\xe3\x89\xaaf#\x07<\xfe-p\x8e\xbd &\x13\xefW\x7f \x85\xa6tm\xb4f\xb6XMk\xa4\xe6\x10\x1f\xa7\x99+J\xb3\xd0\x94\xa2\x14\x9aR\xf9z\xd6x\xcc\x84h\xcc\xdc\xb1\x98\xd9\"1\xf3\xc6af\x8b\xc2\xf4\xc7`f\x8b\xc0,4\xa5\x85\xa64%\x8e\xb2\xd0\x94\xa6\xc5K\x860w\x16\x9aRE\nMi\xa1)]\xc1\x08\xea\x7f\xc6\x17\xf3\x98\x16\xf1XhJ\x85\xe4\x8cr,4\xa5\\\xc2\"\x1b\xbdq\x8d\x81Q\x8d!1\x8d\x85\xa6t\xd6\x959\x861_\x04c\xbe\xf8\xc5\xf8\xaf\xeb\x8d]\xf4E.\x1aW\xf5\xdeT\xdd\x8e\xf1\x8d\xe1\xec\xb5\x9b\xdcg\xb3\x13Wx\xd7\xc4\xdb\xd3\x03\xc5\xa3V\xa4\x1b\xc5`\xd1\x864\xd4\x1b%\x00\xfbF G\xc0Q\xbc88\x8a\xad\x13)vmp=\xef\xc3\xc7Q\xd2Pr\x14G\x95\x8d\xdf\x93psC\x9b\xc1\xc2\x0d\x89h\xbaY\x02\xc1\xcc\x0d\xa9\x18\xbbYe\x9d\xad\x1b2#\xef(\x0e\xd6n\xa0\x99\xbb!\x1b\x16\x8f\x92\x0d\x91G\xc9\x8b\xcb\xa3\x84\xa1\xf3(^\x8c\x1e%\x10\xa9_<\xeca\xf5\x06zz\x81\x85\xed\x19V`\xbc>\x86o\x08\xc7\xf1\xc5\xc3~4\x1f\x85hPNd\x1f% \xdf7\xb4\x11,\xe0\x90\x15\xf5W\x8a\xc9\x82\xfd\xa3\xa4\x8e\x11o\x1c\x80P\xe7e\x06\x07beK\xc9\xb5A!2n\x84\xea\xe2\x9f\x86\xe2\x9f.\xfe\xe9\xe2\x9f.\xfeiC\x8a\x7f\xba\xf8\xa7u)\xfe\xe9\xe2\x9f.\xfeiCQ\xf1O\x1b\x12\xee\x82-\xfei\xea\x91\xe2\x9f.\xfe\xe9\xe2\x9f\xd6\xa5\xf8\xa7\x8b\x7fZJ\xf1OC\xf1O\x17\xfft\xf1O\xfb\xc7\xc8\x97\xf0O\x13\x87\x99\x98\xfc5C\xc92\x9f\x8d0\xbbjZ\x1bJdr\x1b\x94+\x86\xca\x15C\xe5\x8a\xa1/{\xc5\x10\xcc\x9fu\x9e{\xe8kf\xdc\xff\xa1\xfc*o\xe8\x93\x1d\xf3\x9d\xf2\xf2\xf2v\"K\xce\x0d\x04\xdfO\xa4)Pn(r\xa5\xfa\\c\xf2\xe7\xea\x14\x1f\x914\x1aer\xac\xa9; \x13,$CG&\xba\xb2\xcfu?\xa8p\xc0S\xa6B\xcaB\xc7-\xa6\xd8p\xcb\x1a*\xef\x1f\xab\x8e5C\xce\x9c\xcc\x1f$\xc8\xf2\xa3\xd27X\x8c\xac\x93\xf2\xda\xb8\x0f\x0c\xfc\x16B\xff[\x05\xaf\x0bk\xa3j]V\x15\xa54\xe5\xd5\xacC+\\\x9f\x9ab`k\xc9\x9dSRg_7\xbb\xbd2XT-B\x035?\xec\xd3\x11'\xa1,u\xd5\xe4[\x9f`'j\xfb\x9bM\xb1K\x9a\xad\xe6|uM\x14\xd7TI\x9d\xb5\xd6y\x9b\\\xa1u\xb3\x97\x9a\xbf!\xe9\x83\xeb\xe7\xb0c\x16\x87\x14\xb8n&\xe7\x98\xcbe\x7f\\\xf6\xc7e\x7f\xfcE\xf7\xc7\xc4\xf2\x19\xb0\x9d]\xac\x9f\xc1\xbbW\xf1\xcb\xfa\x05T\xbc\x18e\x122n[\xe7\x85\x87{\x89\xb4(1\xc2\x00\xaf\xdf\xa8\xfdr:T\xcdO\x1d\xab\xb6|\xb6\xa8qV97i\xef\xe4\xc5\x88[\xdd\xb0\xd7=U\xd0\xd0\xb1\xaa?u\xa1v\xd5f'\xae\x96\xf7\x0eJ\xadPm\xf8\xcc\x17\xdd\xca#t\xdaM\xcd\xc7:w\xf6\x12t\xd8f\x1d\xdbO\xcdZ\xfe\x19kF~\xfb\xa9\xb1~T\xf2b\xf3\x95%M\xab\x9fP\xa6\xab\xaf\xdb\xe6v\xa8\x83\x07\x8f\x1c\xba\xdbj`?\x8d\xefQU\x18\xe4M\xb3\xdc\xc3X;\x07\xd6\xecI\xbf\xbdg\xecvh?\xb0\xa6\xa7*\xb3\x8a\x0dfu \xe8\x965\xed\xc1\xfc\xd9\xb9y\x01\xa8\x0e\xe3`Z\xf9\x9a/p\xe2e[+n\x03\xe0=\"\x90\x08\xac\xa6t\"T\xcd\x96\x87U\xf0JP\xd8\xff\xdbw7\xe7\xcf9\x8c\x82\xcf\x08\xac\xa2\xe6\xde\x88\x8bf\x10\xbb\xea\xc9q\xa9\xba\x8a\x0de\xc2\xea\x1a\xbf\xf7\xf5\xae\xa9\x86S\xc7z\xbe$\xd5\x1dBj\xbbv\xd7r\x7f\xedY\xd0|}S\xf7<\xfc\xf0\x9e1\xd1b\xde\xd4\x1e\x1b.\xe9\x15\x0fuS\x1fN\x07\xd8U=\x1c\xbbz3v\x10\xc6\xa1i\xea\xc4\xa9\x81b\x1d\xd7\x87\xa0\xdcKN\xf3d\x8aX\xd9\xef\xb5\x0d\xe5+\xbe\xde,U,V5m\xd9q:i\xe4\xca&\xcb\x0d_\xd5\"\xce\x85\xf2\xcd\xa7=\x18f?\x0f\xae^\x06cO<\xeb\x16\xc3\xc4\xd3\xce\xda%\xd1\xb6(\x86\x94E\xd8\x97|K#\xb98\x92\xcbcHU\xa3\x96H\xcb\"\x19Q\x9ec\xa1t.\x95A\x83\x9c^.\xa3\x16\xcc\xa0%\xd3\xbehZ\x96M\xe7\xc2\xe9^:\x1d\x8b\xa7g\xf9t-\xa0\x9eW}\x8bh\xcee4\xf3Bj_J\xd7.\xa6\xaeY\x9eyAu-\xa9\xa4\x19H\\V\x8b\x7f\xa6\xf8g\x8a\x7f\xe6k\xf8g\x96\xf0\xa3\xb1\xe7\\\xb3\xa7\xe5\xee\x1a\xa1\xcd\xbf\xbd}\xdf\xb3\xae\x90\x0b\xce\x12\x02\x86\x14r\xc1\x92\xbcY\x927)m%y\xb3$o\x96\xe4M(\xc9\x9bK)\xc9\x9b%y\xb3$ozkS\x927\xa1$oBI\xde,\xc9\x9b%y\xb3$o\x1aR\x927K\xf2\xa6\x94\x92\xbc %y\xb3$o\x96\xe4M\xff\x18\xf9\x12\xc9\x9b\x85\\\xb0\xf8\xa7\xc9?\x17\xfft\xf1O\x17\xfft\xf1O\x17\xff4!\xc5?]\xfc\xd3\xfe\x1dG\xf1O\x17\xff\xf4B\x8a\x7fZH\xf1O\x17\xfft\xf1OG9{\xc9\x9f\x8b\x7fZ\xff\xb1\xf8\xa7\x8b\x7f\xba\xf8\xa7M \xf5=\x16\xfft\xf1O\xfb\xc6\xc8\x97\xf0O\x17r\xc1\x92\x9cS\x92sJr\x8e\x90\xd0\xe4\x1c\x98?k\x12\xb9\xe0?.\xb2{\xe8\x94\x1bg\x8a\x8f\xfa\xfa\xcf\xcb\xf7e\x7f\x85q\xb4\x8c\xefrn\xc2_\xc7/\xda\xadO\xf89\xe0{Q\x19?\x8b\xf9T\x8cY1f\xc5\x98}AcFf\x1a\xda\xec\xc1:k\xa4jXk\x88V[ \xbe#\x8a\xb2\x19\xd6D\xc2\x84\x19r\xf1\xca\x97/X\xf7Xe\x93;QP\xe3=9\xd1\xa8d\x13\xb4U\xac\xee\x11ZP\x14f\xac\x92\x9bND\xdd\xdfBnJ-\xbe\x8d\xd6Kx\x1aR-\xbd\x94#\xeb\x0eu\xdf/yA\xc1\xb2^\x82\x9bW\xc9^%\xc3>_\xce\xc5bw\xcb5eJ\x13m'.\x80\xb1wz8V\xdd [\xa1)[\xb6I~N9q\xf5\xc3\xcb\xf8S{\x8fZ\xbf\x03\xaf\xc1 \xb24\xba\x8dY\x9d\xda\xcc\xdfZ\xbf\xcb\xe1\xad\x89\xdb\xe4|\x03i\xcd)6\xc9n\x95\\F \x9eF5\xc26=)\xe5l\x88\x8dz\x02\xbe#\xd2R=%\xe3\x91Y\x96\xd5b\xd9m\x96\x87\xd6\xc6U9\xc2#\x93\xd5v\xe9\xd6+\xc2~\x953J9\xa3\x943\xca7tF ;\x9d\xe8{\x86\xf5|(\x8a!Z\xbds \xad\xe8\x8a\xed\xc3b\nl\xd9P\xd5\xfb8E\xdf\xc2>\xc4\xe4R\x19\xbf\x91\xb2\xc6\xcf\xbdE/\xf4O\xb6\x1717 +k6.\x13F\x9d\xe6\x15f\xb9\xb8\xf0\xd5}\xd6H6eu\xa0;U\x03p\x7f4\xd0*\xa9\xb6\x99\xa9M\x16\xf6\xcdx}~\xc4^2\xb5M\x00\xfbV\x01\x82\xab\xac\xee\x0fz\xcc6\x90[\x81\xc5K\x9a\x83`\xa1\xc3\xba\xf2\xe7\xfe:\\+1n\xc13v\xc13~\x01\x88M\xab\x18\xca\xe6\x9e\x0e\xbe\xd1of\xd9\x9b\xcdO\xbf\xe2\x96O\xbbc\x00\xad\xa1\xc2a\xa7}4\xeb\xea\xb10\xe8\xb8$\xae\xf2o\xa9\xadp.!\xd7\xeaE<\x01\xab\x05i{\xc9\x9e\xb5\xd8[\xbb\xa5\x0d\xbb\xc2\xc3,W\x1f\x96\xf4`\xb4\x0dA\xff\xc5\x15\xc4%\x1d\x11\x05\x87_\xc9\xb1<\xaa\x91}+t\xae\xbb\x80\x83<\x9a\xb9\xd4\x87_\xb7!^\x88\xbeh\x83\x1c\x99\xe2\x7f\x82\x87f\xe2\x88\\E\"\x1c\xfe\x8d\xc2)\x83W\x7f\x9f5\x04\xc1&5\xb0Ks\x02\xf7\xaf^\xb0\xc6\xf2\xeb*u5\xb3\xaf\xc1\xe9\xbbB\xbb\x85\xc7\xd7\xc2\xe0\xeb\x1cO&k\xaf(r\x08\xe4\xeb\xf50\xf5>\xc5\x1e\x9a\xe4\xe2\xb5\xae\x904\xff\xae\xe5q\xd7\xe1?\x9amWU\x92B\xb3;\xaf\x86\xb3\x84\xb3\xe9R\x07\xf8\x8c\x0c\xba4w\xeeb\xb0HK;\x8d\xde5|\xb9\x94\x99\x9dN\x9d\xc1v6\xdb\x16 \xd2\xe9K\x1d\xb1r-\xc8+\x9d\xbb\x89U\x08w\xe2fYo\x0c\x97m\xde\x15g\x11\xf8\x95\xd3\xa9@\xee\xcd\xd7\xfb[\x95\n*G\xca\x10\xcf\xaa\x987\xc7\xb6\x1f\xf8\x9cy1\x0c\xd5\xe6a4-_|\xd2,LO\xc8&z\xac\xb4\x9a\xe6>Z\xc9\xa9\xfa\x8a\xae)\x17Y5=\x9b\xb6i\xd8f\xec\xd9\xc9]=\xeaKm\x84\xb2\x01v\xd5N\xc9\x8f\x9e*\x92g\xe2]\xdcO\x83\xc2\xe8\x0c\x9e\x87\xac.v\xe4\xcaJ\xaf\xab\x03\x99\xcao]V\xdd\xe9k\x19\x13\xf7\xed)\xfb\xf9\x92\xf5\xb3\xa5\xe9[\x13\xf4\x87\xf8\xd4\xfc\\I\xf9\xeet\xfc\xa8D\xfc\xe8\x14|\xde^\x1dE\xb2&\xdfG\xa7\xdd\xe3\xdc\xd4\xb4Y\x12\xeeSR\xedyZ\xbd\xde\x1a\"\x93#&\xbd\xde\x9eJ\x9f\x98D\x1f\x94>\x1f\x9e*\x9f\x90$\x9f\x90\x1eO\x18\x8c\x8cI\xf0y\xd3\xdf\xb3%\xbe\xfbS\xde\xb3%\xbb\xdb\xd2\xdcS\x12\xdc\xc9d\xf6!$\x8d=6\x81\xdd\x9a\xac\x1e\x99\xa6N$\xa8[\x17\xca0\xa0\xd9XA#\x13\xd1\xe7\xa4s\xaa\x7f\xbf\xf3\x97\x9d\x96v\x8ei\xe6\x8a:3\xe1X\xb5C'C\xc3\xdb\xae\xde\xd5\xe3\xf6\x9aw\xa4\x1c\xedJ\xa9B\xcbdn\xb7\xb7\xfc\xb0\x15\xd5\xc7\xc1\xde\x18\xfe8=\x8cnD4\xc8T!\xfd1\xfb(f\xcd\xc9H\x0e\x01\xf8 .\xdf]\xdf\xdc^\x9d\xbf>\xbf:\x7f\xfb\xf2\xfc\xf6\xe6_.\xcfo\xdf\xbf\xbd\xbe<\x7fy\xf1\xfa\xe2\xfcU\xe0\x1bW\xe7\x97o\xfe%\xf0\xd9\xff\xf1\xfe\xdd\xcdy\xe0\xb3W\xe7\xe3\xaf\xda\xc3\x02\xc6}\xbe\xb2\xeen\x06\x8b\x80\xbex\x0eo[e\xe9\xb5\x85\x14\xb9;\xe99\x86\xe1\xcej\x16+p\xc7\x8e\xfbG B\xcd\x17e,\x0eW\x9eRx\xf7\xbaK\xf9\xf7S;\xccQEQ\xa5\xe0\x87\xf16F\x99W\x8er\x8ez\x00:\x8ac4\x079\xf9\xa6y>\xcf_\xb2l\x8e\xfce.\x9c\xa2\x96\x11%\x19\xb5\xd2M\xfa\x1fzn\xd4e\xc0\xf6\x14\xc5\xaf+'o\xaf\xcfon.\xde\xfe\xc5b<\xf4\xa7\xce\xff\xf9\xfc\xea_\xde\xbd=w<\xf2\xfa\xdd\x9b7\xef\xfe\xe7\xf9\xd5\xb5\xe3\x99_\xdf\xdf\xbc\x7f\xf1\xc6\xf5\xc0\xf9\xdb\x9b\x8bwog\x1d\x93\xe1\xf3\xd7\xdan\xec\x1cm\x16\x06N\xe9\\\xda\xc8\xd9:\xe49\x9c\x7fd\xddc\xdb\xb0 \xa2\xe7!\xb4C\xab\x1a6\xfdK\xb9\xba\xef9\xbck\xf6\x8f\"\xd6\x9buSD!\xee/\x12K\xc1\x0f \x8aP\xb4\x1eN\xc3\x89\xa7\x03\xc8B\x13\x8b\x11\x9fQ\x14td\xedq\xcf\xa4\x0b\x80\x07^\x8b\xd9/t\xd1\xc5\x89\x12\xa6d\xd6m5\xf8\xd2\xc7\xbc\xc9\xac/e\"\xeb\xf8\x04\xb1\x01\xe3A+l[\x0fl\x9b\xa5\xbf#\xb95aiNc\xf85'\x05\x06L\x15\x96\x84\xd8\x0f\x01\xe8\xd75B3\xe3\xd3\xeb\xb3\x0d\x8fOO\xf6\xfety\x869P3;n&\xab\x17\x9f~\x97\x8e\x9e\xd9\x93\x02\xa3;\xcd\x87\xa29p4g\xd9\xe6\x84\x0d\xc1\xd2(4me1\x0eD\xcd\x86\xa9\x91\xb5]\x81\xaaE\x1f8\xed\xd8\x9a\xdb\xe7\xe8\xf0:z\xfd\x8e~\xcf\xa3\xc7\xf7\xe8\xddU\xa3x\xf7\xd6(~\x8f\\\x94O\xce\xa1\xcdr\xdf\xeb,\x0e?\xe4\xef\xa1\xedV\x8f\xa4\x13\x05\x08ly>$ \x13\x160\xa9\x89C\xe1\\8\\\x99\xa3\x8a\xe4\x1f\xa7e\x8e\xda\xfe\x9c\xd8\xf2ow\x8e\xc6 v6\xcc\xae\xccNE\xf2\x8eP\xcb\xad\xd5\xb3\xfc\x8eg\xa7\xf5\xce7\x14\x07x\x16\xd8\xf2\x90&\xc5\xc0~.\x8bC\x03\x82A\x90\xe0\xbaV\xc5\xc1\x82\xf3a%\x1a\x18\x9cT\xac\x82\x06)p\xd0eW\x9c\x14i\xae\xf3K\"HH\xc3\x84!\x05;\xa1B\x0fX\x98|\xf6L\x86\x0c}\xa0a\xf4\xb7\xb2\xd8~\xb7\xdd\xe7\xaf\xba\x86o\x08\x80\x180\x9f\xec b\x00D\xb6\xea=;\x90\xb8\x1eJ\\\x0d&&\xc1\x89>\x9f\xb7\x14o\x7f\xad\x83\x14\xbf$\xa8\xf8%a\xc5/\x06,:\xa1\xc5\x80\xb9\x11\xb0o0\xdcq>\x80\xd1\x0d1f\xad\x94u\xd5\xcf\x085\xae\x00\x1b\xd7\xc2\x8d\x0b\x0fy\"\xe0\xe8\xea\x93x\x14\xd2Pe\xa0\x92n\\r\xaaU(2\xe9\x1c\x1e\xb6+J\xc3P=\xeaY\x0bB\x19\x8eQ\x06\xa2\x94\x818\xe5j\xa4\xd2o\xb73\xa1\x95O\x84W~I\xc4\xf2\x8bb\x96_\x00\xb5t\xe2\x96A\x9bM\x1a;\x9c\xe6l\x08z\xe9\xc3/s\xd4c=\x8aI\xe2\x98!\xfb{;\x96\xb9\xb0\xd5qhf\xc9\xb1-9\xb6%\xc7\xf6k\xe4\xd8.Y\x8d)\xc8?$\xa2@}O\xe8\x0b\xe65\x96l\xfe%\xce\xa0\xc4\x19\x84tZ\x893\xa0\xd5\x948\x03\xdb\x1f\x03\x0e\xd6\xe0\x1b|\xb3\xf8\x0e\xd8\x90\x1d)(\x18\xe6\x7fJ\x0c\xb3\xc4\x19\x98\xf2M\x8f\xd32Gm\x7fNl\xf9\xb7;GK\x9c\x81*\xdf\xec\x08-q\x06%\xce`\x965\xad*q\x06\xba\xae\xc9\x16\x978\x83\x12g\x80\x0f\xfb\xe6S\x893\x08i\x93\x0f\xafB\xf1\xf6W\x893p\x95U\xe2\x0c\xf2V\xaa\xc4\x19\x98\xbaJ\x9cA\x893P\xa4\xc4\x19\xd8\xcb*q\x06%\xce`YT\x893(q\x06%\xce@Q\xf6\xbb\x8c3\xa00\xffp&\xef\xc5\xeb\xb2\xb1NB\x03\xbe\xf9\xb9\xc6%\x16\x87\xa91<\xd5\xad\x8d\x7fC\xe3\xd9\xc6\xf86/\xce-\x8bs\xa3\x12\xb8=\xa1\x07g\xe2V$\xeb\x06\xe4\xe9\xb7\x1d_`\xb3\xf1\x84[\x0cu\xdb.F\xee\xf2\\rl\xfb\xbe\x1e_\xeb\x96;{~c\x898\xa9l\xaa\x86_\xd2IL \x81\x80\xa8\xb3\xc1\x19i\xa3\x01\x04\xa4y\xb7\x98vy\x80]\xef\xd76q\xb5\x85\xa7\xfe\x0bVBq\xa1/\x00-\xb2\n)\x80U$@\xa5\x7f\xde\xf7\xd2\x03\xfe\xcd~Z\x03\x94\xf9\x1a\x9fVCG\x16\xb8\x81\xeb\xd3\xc6\x00\x1a\xf3\x1e\x8d\x84)\\\xc5\x85\xc2\x10\x11\xb0\x831pz\xd6\xbdh\xfaOr\xf3\x1e2~\xa8\x08\xb9\x88\x0f\xb8\xd8\xf7]O!q[K\xdc\x99\x0e?(W\xe2/u\x90e,\xf1#\xcd\x95\xf8eu\xfc\xb3T\x15\xe5\xcd\xf8Z7\x0e\xc9J\xcf\x91\xc3\xc7\x93\xea\xe7!\x11\x95,u\xc2\x1dSK\xd5\x852\x9c`\xbdh(\xf1\xda\xa3\xa9Tm7\xa7\xbcE\\ON~Zp\xc2\xbc\xe3+t4\x84\xb5?!\xe0\xdap\x80\x17\xe3\xf6\xe1\xe7\x8e\xf5\xed\xa9\xdb\x88kL\xf9T=\xf1\xf8\xec\xfd#\xd4\xdb\xf1\xd8s_\x9b\xb7|\x13\xea\xe6\x1b>)`\xc7r\xfd8bF\xa2\x0dp8\xcdn\xc1q\x93\xb7gU?P\xda\xc6s\xea\xf7?\x7f?\x9b\x823n\xe7\xf6U?@\xcfv\x07\xe5V\xc1\xf7Wo\xfe\xd0\xc3\xb1\x1a\x1e\xb8zB\xd9\xb4\x86P%\x0d\xc4\xad\xaf\xbc\xa7\x84z\xde'?T\xfcRr\xe2\xf5\xbf\x8e\x05[\xafU\xfc\xeb\x8fXo\xaep^\x07\xc6\xc6\x13\xba6U\xd36\xf5\x86\x9fs\xa9\x9b\xce\x01~`g\xbb\xb3gc\xb7qO\xd3\xf7g\xdf\x8fs\xa2i\x07\xa86\x1bv\x1c\xd8\xf6G\xfa\xca\xff\x8b\x06\x8ecG\xd6\x1b\xf6\x0c\x066\x1a\xc3S\x7f\xaa\xc6f\x1f;\xb6i\x0f\xc7z?\xd6K@\xc5wuSu\x8fP\x89ey)c\x8f\x88#\xec\xf0\xc0H\x94\x8f}\x1e\x0d>\xd4|\"\x9fz&o\xc2\x1d?\xbd\x08\xa5\x7f\xd1<\x9e\xc1/\xed'\xf6\x91ux1\xee\xfb\xab7\x14\xb8\x88v@\\\xd0K\x15\xd6o\x1e\xd8\x81\xc1_\x1f\x86\xe1\xf8\xd7g\xf8\xdf\xfe\xaf\xcf\xa0\xed\xa0i\xc5_\x9f\xf1\x115\xae&r\x97\x8e\xc7qB\xdd\xe98\x9e\xe2\xc8\xdbw\xf9\x1c\xf8\xc8\xaf\x01\xae\x068T\xc7\x1e\x87\xc7X\xf3\xb1\xa5\xd2\x8f\xc7\x1d\x925.\xb9U/\xbc\x16\xfds\xf2\xcb\xfc\x1d\\\xdc\xcf\xf5\x1c?\xa7\xbcOyj\x8a\xb0{\xa7\x03\xdb\x92\x97\x83\xff\x1d\xbch\xe0\x97\x9b\x9bK\xf8\xcb\xf9\x0d\xb4\xcdt\xe0\xe1\xd3\x0d\xaf\x13\xae\xccKFo\x1e\x8f\xec_\xff\xf7\xbf\x12\nAzW\x1b9\x12\xd0n\xf2>=v\xed\xf6\xb4\x19\xf7\x8b\xc0\xba\xae\xed\xa8\x9b\xa3\xff\x0e^\x1c\x8f\xfbzS\x89>\xe8\xd88\x96\xdaO\xb8\xfb\xdeT\x9bq.\xb7\xed\x87\xd3qrb\xe2J\xdc\x9a7\xff\x83\xc5\x1c\x01^\xa9\xdcv|;3>tP\xc6\xf2\x16\x07s%\x1b0\xfe\xfbc[o\xa1\"\xae\xfc\x07~\x02\x19\xab\xc3\xa7k\xc7\xee\xdb\x8e=\x93\xaf\x8e\x1a\xab\xa1\xbe\xab\xf7\xf5\xf0\x08\x0dc[\xe9.\xe6f\xa5\xfbh \xdch\x9b\xd1|5;\xc6\x1f\xe7\xb3\xe6\x0c~x\xdf3\xf8\xc8\xba\x1e\x9dM\x83\xbc_\x1a\xc7O\xd5T;\xba\xadw\x1d\xab>p\xff\x12\xaa<\xfb\x91\x1a o\xdb\x81=\xc7\xb3\xc6\xfd\xa9\xd9\xe0H\x1fk-\xac\xc4\xe6\xd4u\x1cUP\xbd\xf1\x8d\xa5\x83[\x0eB\x98nx\x90\xd6\xfe\xeet?\x1e\xf0X\xd5\xb3g\xfc\xee\xdez\x90\x05\xf1\x0d\x1b\xf7\x0bO\xf3\xe3\x8e\xed\xea\xa6\xa1\xc3\x19\xc6%\x964\xce\xe6\x15\xb6\xb4}\xbb\xe6\xb3\xa7G\x08`\x9c\x9e\x8dn\x0f\xe0\x07\x91\x0e\x87\x18\nN\xb7\x1f\xe1P\xef\x1e(+pGN\x7f\xde,\x1e\xbeP\x1f\x8e{v\x98\xae\x1b\x16~\xd8\x0d\xf4\xecP5C\xbd\xe9\xf5iA\xec\x15\xc0\xb7\xc4[\xb1)\x08Z\xff\x7f\x1d\xa7\xff\x1d\x83j,\xbc\xde*\x0b\xb8\xb1Z\xcb3\xe0]\xfb\x91\\\xfa\xa7\x00\x1b\xfeE\x16O\xb8\xeb\x81\x97\x8b\xcfH`\x03UwW\x0f\xfcnxG}\x84%\xd5\x94U\xfb\xb6\xd9\x89\xbb\xc4\xf5\xcf3\xda;n\x96\xb1>w\xe6\x96F-O\xeeN\x8c\xc1t\xa9_`/\xacq\x0f\xfd\xe9xl;\xbe\xa2\x1d\xab\xcd\x87\x9f\xf1^\xf9q\x1d\xc3o\xdb\xd33\x89Z\xca\xdb{8\x0dhN\xe44\xedGC\xa6\\\x86\xce\x1d@\xdc]1\xddS\x8f\x8dxa\xd8/\xfc$z\x19\xf2\xa2\xe9?>\x87\xcb\xb1\x9e\xe3\xfc\x14U\xae\xa6\x85\xaan\xe0\xe5\x7f\xfd\xaf\xe4\xa2\xf2\xbam\xe1\xbem\xe1\x1f\xe0\xec\xec\x8c\x82\x97\xc6\x86W\xcd#\xf5\xa7\xaay<\x1b\x0b}\xdd\xb5\x87\x1f\xee\xdb\xf6G\xea\xa1\xb33j\xe5\xa8\xef\xe1\x87\xf1\xf5\xf7\xbc\xaa7\xed\x0f\xffe|\xffG\xcbu\xd7\xb4\x0e\xe3\x12r\xd9\x17\x7f\xf2\xf4\xc5\x7f\xaf>VI\x9d\x01\xff\xc0\xf75\xa3\xe6\xc8v\xd7\xfd\x0f\xaf\xdb\xf6l\xb3\xaf\xfa\xde\xdal\xac\xca\xf88\xb6By\x85*\xd1\xda\x1f\x7f\xef\xe9\x8f\xcb\xc7\xe1azu~\xfd\xf2\xea\xe2\xf2\xe6\xdd\xd5\x8f\xa6]\x05Q\x08\x8e#[1X\x90\xa5w\xfe\xff\x9e\xde\xf9KK\x06\xb8\x8d=\xf3\xfc\x1f\xe0\xbf\x1c\xef\xce^\xb7\xed\xff{vv\xa6\xdf\x8e\xce\xa5j\x1e\x9f\x8d\x1b\xa8\xf1\xd9\xaay<\xde\x9d\xbde\x9f\xac\xf5\xac\xef\xf9\xb3\xff\xbf\x7f\x80\xa6\xde\xdb\xee~\xa7;\xcd\xb8\x9c\xdd\xf5\xacVy\xf2UQ\x17\xac\xf7\xd9\xfb\xe6Pu\xfdC\xb5\xbfiq\xc8\xa7TT\xfb\xf5\x86\xbbn6\x1f&\x1b(\xb7\xc6p\xf78oA\xa4\xa5F\x9c\xeeQ\xc2\xaf\xe3b\xad+\xfc\x03\xb1\xa1\xf8y<\xfd\x9d\xf1?\x8c\x1b\xb1?\x8c;\xf6i\xe5\x18W\x95\xf1\xfb\x9bv\x1dG\x84^\xc0d\xac\x9b\xfd\xa3<\xb3\x18\xc7\xcbi\xd3\x07\xd5\xfd\xc0pW\xc2O\xb6\x7f\xf8\xf9\x0f\xbaBqp\x92\x95\xc1\x93\x12\x13#\xf4\xfb\xfb\xb6=\xbb\xab:\xde\x8c\xcf??\x9e\xfd\x9f\xef\xb1\x17p\xcfO\x1dbx\xb1\xdf\x8fO\x8eK\x86\xf6\xc7\xff~\xfd\xee\xad\xf1\xe3\xf8\x11\xc6?\xcc\xc7h\xdc\xf2\xb4\xe3d\x14\xeb;\x1e\x17N=\x93\xe1\x01\xbb\xd3\xbe\xeatM\xa6\x02\xc4=\xe6u\xf9\xd9\x1c\x81!&\xda3\xb1\xdc\x1b\x87oe\xad\xbc\xe7\xad\xfd\xeb\xff3\xb6\xf7\xaf\xe2\xbc\xb8p\xff\xc9\xde;\x93s\xfb9\xb9\xa7\xad6\x1f\xc6\xa9=\x9f\x93\xee\xeb=\xa3\x0c\xaa4\x02\x97\xac\xeb\xdb\xc62\xca\x85\xcb\xe3\xbe\xee\xfa\xe1\x96w\xbb-NC>\xca\x83\xc4\xc4\x93\x7f\n2\xe4\xa3\xd0\xa5\x7f\xcf{\xe3\xfb\xe7\xf0=5\xe4\x97M<\xc3v|\xff\x8c\xd6\xc4[\xf0\xb6:\x8c\xda\xfeo\xac\xea?Z\x1e\x1d[\xa0=\x19\xd2\x8c\x8b{\xb13_~{\xfcnu\x0f\x9f\xd8~\xff\xd3\x87\xa6\xfd\xd4\xf0\xc9\xf8\xc0=\xc1\x9bS?\xb4\x071f\x97\xfa\x96\x03\xed\x19\xee\x0b\xb5\xd17\x87T\x89B\xc7\x01\xd5\xec\x0c\xc7\x0c\x1f\\z\x01\x7f\xe5\x03^\x8e\xb5\x87v\xbf\xc5\x81\xa6\xd4\x89;y\xc4\x18\x05\xe1V\x11CT\xd7\xc6\x8b\x98\xc6&\xfc0Nq\xd9\x05\xc6\xa9]\xba\x96\xfe\xf5\x7f\xff\xeb\x8f\xe40N\x1b\x0f\xcbBlC\x827\x7fT\xf6\xc7\xb3?\xfd\xf1O\xfd\xf7\xe4G\x9e\xff\x9d~\xaf\xa1t\xc6~\xa7=%\x9d\xf2\x1a\x94Z\x0d\x15\xe2\xa8Jt\xe1R\x87x\xff?\x161O\xba\x8b?\x84We\xaa\xc1\xd5\xe5K\xa1K\x0fq\xb2c =\x82\x15\xab!\x85\xa3\x82g\x91\xaf\xd0\xaf\xc1\x97\x03\x14\xc6N\x9a<\xf8\xfd\x84\xc0T\x1d\xc3\xc3\xa8\xa2\xa6c\xbb\xba\x1fX\xc7\xb6\xb7&T\xa2\x94\"\x80\x1d\x05R\xbe\x9a\xde\x94\x1d\xaa\x01^D\x03W\xb9\xe6\x0d P\xca@\x02\x82\xd3_\x97\xc0\xa0\xb7\x9aV\x88p\x94\xfb\x8e\xb1[\x93\xc1\xc4\xda)\x16\xec\xef\xdb\xe8\x8a\xb5\x18\xa9\x14\n+\x9d\x8a\xb0\xa2=\xe0D|\x002a\xa8Rt,u*$\x0d\xc6Y\x87\xb1\x12\nb\xc1VB\xd5\x12~\x05p\xc7\x85hf\x8e\xc4d\x95\x91+\xcd5a\xad\xbe\xfb\xce\xac\x03\x0ei\xa7!\xd7\xeckx\xf8\xeaR\x01\x19\xb8\xea\xb0\xea\xab\xed\xf9\xa4K7\xe9Aq\x1f\xbf\x11\x9a,\x1f\xa4lM\xd3\xcbV\xb7\x08h\xd9\xce\x8e\x95\x81\xb3+\x00b\xb68\x8e\xad\x06\xdc\x7f\xe9\xb1-\xf1\xdei\xa5B\xecTv\xc0\xd9\x079\xe7\x06\x9d3\xc3\xce\x1e\xe09\x19z\xce\x0b>\x87\xc0\xcf \x00t^\x08:\x08\x84\xce\x0bC\x07\x00\xd1\xd9\xa1h\x0f\x18\x1d\x07G\x93\x8a\x9c\x10u\x16\x90:\x10\xa6&\xdf\\\x05]'\x83\xd7\xb9\xe1k;\x80\x9d\x19\xc2~\n\x10;3\x8c\x1d\ndg\x86\xb2\xdd`vv8\xdb\x0eh\xaf\x80\xb4\xe3AmR\x19\x07\xba-\xb0v\x12\xb0m\x85\xb6\xbd[\n'\xbc\x1d\xb6\xe3\xc8\x07q\xbbAn\x7fm\xb2\x02\xddn\xa8;\x1b\xd8\x9d\nw\x1b\xea\xf8\x8e\x86\xdc<\xe4\x85\xbcm\xa0w:\xec\x1d\x80\xf5:\xa1\xef@\xf0\xdb\x8a\x81\xad\x04\xc0\xedz\x08\xac \x19\x06_\xd39!P\xb8\xbf\x17\x82\xe0\xf0\xd5\x80\xb8\xb3w\x12@\xf1\x00X\xdc\x07\x8c\xfb\xa1qg\xa7\xad\x81\xc7\xc3\x00r\x12\"O\x06\xc9\x83a\xf2\xb5@y Tn\xefD\x1a.w<\x1f\x02\x99\xe7\x00\xcd\x9dU6~O\x82\xce\x0dm\x04\x94\x9e\x15L\xb7\xc1\xe9\x89\x80\xbaYe\x13`\xcf\x0f\xb1{@v\x0b\xcc\x9e\x13h\xcf\x0c\xb5?\x05\xd8\xbe\x06n\x0f\x04\xdcWA\xee\xe1\xa0\xbb\x05v\xb7\x01\xad\xe1P\xab\x1fz_\x05\xbe\x07\xc3\xefd\x83rC\xf0yAx\x0b\x0c\x9f\x1b\x88\xcf\x0d\xc5\xa7\x8f\x91 8>\x0c\x90\xd7W\xb6<\xec\x9e\x060\x9f\x0e\xcd\x17\x06\xa0\xc2\x00T\x18\x80\xbe\x06\x03\x90\x05D\x0d\x82Ou\xe0te<\x8c\x1e!\x11\x01\xa1:\xa2HV\x18\x8f\xaf\x10\x1f\xc3iNultn\x8f\xa2\xf1\x89\x12]eI\x945\xef\x1f\xdanx\xa8\x9a\xed\xed\xa6\xdd\x92\xc9\xae\xf6\x18.q\x0f\xd0\xa4\x02F\x15\x14\x15\x03Q\xae\xe4\xdf\xb0\xe6\xd8\xda\x8b\xc5\xc0\x08\xbe\xdf\x98a/\xa1\x8fm\xc7\xf9\xdb\xc0\xa9G\xf2\x01\xaap\xca\xc0\x99#\xd4\xc1\xde\xe1\xeeQ\x01\xf6.\xdczS\x9c\x03\xd8g\xa3m\x8a\xac\x89j0\x1a\x11\x1a\xd9\xa0\xbf\x18\x13\xe3`L\xd0\xdfS\xb8C\xdc\x84~\xe2\xf0\x02\xd70tO\xed\x90\x9d`\xca\xf4vN\xf0\x90\xc2S'\xb9m\x1f\x93q\xa2\xd3S]\x9b\xece\xb7[v\xbbe\xb7\xfb-\xecv\xadK\\\xca\x02\xdb\xfbW\xd8\xab\xc5v5`%\xcd\xc5\xe5\x95\x1a\xb4\xf7\x044]\xe1\xc1y\xd9x\xba\xca\x9a/\xd6\xdc\x18z\x1a\x1b5\x0dm\xbb!$6+\x9d\x8e\x86\xa2\xa2!g\x00x\x91\xffu\xf43\xcbwc\x99g\xa6\xf6\xa1B\xdd\xa8'\xf2\xcb\xe8\x01\x18\xa4=:\xb6\xdd\xc0\xad\xd1Z\xe33T\xcd\xb6\xea\xf8\xc4\xef\x9f<\xc4\xe9I\xa3\x87\xc6\xfa\xab\xd7\x01i\xdd\xa6\xbeGV\xc2\x15\xbes3\xfegY\x94uL\xaeVN\xa4\xe7*\xb7s\x9d\x7f\x1eX3\xee\xfd\x952\xb4F\xcb#\xfa\xa6\x1a\x0f\xa9\xb5\x99HT\x1d\x8f{\x8e\xb4\xaa!.t\xe9\xd7b@\\\xa1j\xdbFW<%k\xc0\xe7\x1bN\x8b\x85\xb6c\xbd\xf90N\x92F\x03o\x11\x1e\xe8\xda\x03l\xeb{~\x9d\xdd0}/\xc5\x90\x125|S\xe3E\xfa3n!F.\xaa\xc3\xd5\xaea\x9ffu\xbc^cE\xc66\xd4\xea \x93\xf9c\xcaOm\xc3\x16DM\xd4T=\xb4\xdb\xd3^\xccX6\x1a\x04r\"\xf2\x10\x8b\xaf@\x98g\x9d\xd0`\x99\xd4@Ml\xb0On\xf0\xb0\xf6\x98\x93\x1c<\x13\x1d<\x93\x1d\x12&<\xd8&=\xb8\xe7&\x84O~p\x1b\x00\x08.\xc8\xc2\x1a\x90`\x0cH}\xf5@\x1b\x04p\xd6$\xcd0\x18\xea\xee\x98\xd58@\xb0\x81\x00G\x8ds\x1a\n\xa0\x8d\x05\xa8\x06\x03\xd6\x1b\x8d\xe9\x05\xc2V\xd8\xa3\xb80vK\x94A\x87k-\x8d\x10~\xb1\xd5F\x08\xbb+\xce\x08}\xad\xac\x84\x1e\xc3\xaft\xce\xce'JD\x080@\x84\xe9\xb1\xf6A\x90\xb9\xb1\x1a\x1a\x9fZc\x82$\x98\x15s\x8fA\x18\x14Q\xae\xd5^\xc8+\xf5q\xb8bh\xe6\xd4Xub,\xc7\xafob\x88\xf2\xc2'\xc6\xfa\xe5\xf9\x8b\xec\x95\xbfr:\x80e&\x95=<\xad\xfc\xeb\xee\xe1\xe3\xe6X\x89\xe5/\xb1\xfc%\x96\xffk\xc4\xf2S\xabO\xd8\xba\x16\xba\xe3\x1b\xff?b]\x1b_\x8b2\x05_a\xc7\xc7D}gpj\xd3\xb1\xa0\xabV\x13k\x83\xc5*\xcf\x88\xfd\xc0m\xbdM?n\xaf]Fm\x8b\xa8X\xc7\xc9\x0eR\x10\x10\x98\xe7D`O\x89\x02\x95\xd5\xedW\x19\xfb\xc7o(_`kFG\x8d?\xb0\xb5w+,o\xe4\x96J\x94G\x87\xaa\xdb1\x9dq\xde\xe6\xbd\xb0\xfb.\x86r\xb5\xad!\xd9R#\xc0\xc70<\xc4\xa7H@\xc64 \xf0\xa6J@l\xba\x04\xa4\xa4LP=\xe6g\x15\x8eN\x9d ty\x19\x85SR(\x08u\xe5j\xdb\xb4\xb4\nHK\xad\xa0\xa7(Y\xc9l \x17\x90=\xe9\x02r&^@P\xf2\x05\xe4L\xc0\x80r\xb5-ULd\xe2\x06\x94\xabm\xe3\x12;4e\xe5j\xdbr\xb5m\x80\x8er\xb5\xedR\xca\xd5\xb6\xe5j\xdb\xc0g}i'\x90\x9cz\x02\xe5j\xdb!09\x052'\xa8\x80\x8fu\xb7\\m\xebO\\\x81\x90\xe4\x15\x08O`\x81\xc0$\x16(W\xdb\x06%\xb8@j\x92\x8b\xa6\xab\\mK(\x0b`\xd2].\x1d\x12\x0c\xe5\x1eS\xabG\x9b;\x8a\xeb\xb6\xb9\xddVC\xa8cX\x9e\x91\xc6W~\x1a\xea\x03\x05\xda\xc2M}\xe0K\xb6\xe1\xc4\xffT\xf5\x86\xff~B7\xf9\x13\x96\x88#\xbe?\xaf7j\x1b\x96(\x8b\n\x85\xf8A\x16^T8\xc6\x12\x13<\xc0\xdf\xfb\x9d\x06\x0f\x84\x802\xf6\x18\x82\x0c\xb52\x86\xb2\x03\x9e\xb1\x034\x16\x88\xc6\x0d\xd2\xc4E;\xac\x00j,PMH|B\x14\\c\x03lB\n\xf4\x8364l\xe3\x00n\xdca\xa7C\xb9,\xb2\\\x16Y.\x8b\x8c\x01v\x02\xa0\x9d\xec\xe0N\xb9,\x12e\x15\x18\x94\x0c\x07\xe5\x06\x84\xcae\x91\xaa\x84AC\x99\xc1\xa1rYd\xb9,\xd2\x03\x1a\x95\xcb\"\xd7\x03H\x86\xba\xa1\\\x16\x19\x06'\xf9\xafI\x0c\x83\x94\xcae\x91\\VBL\xe5\xb2H\x94\x10\xc8\xa9\\\x16\x89R.\x8b\xd4\x7f\xcc\x08O\x95\xcb\"\xb3BW\x99\xc1\xab\xa7\x80\xaf\xd6\x00X\x81\x10\xd6*\x10+\x1c\xc6*\x97E\xc6\x80Zya\xadrYd\xfe\xcb\"Ca.'\xd0\x15\x04J\xd0`W\x1c\xdc\x15\x0fx\x95L\xbe\x92\xc9W2\xf9\xbeb&\xdf\x02\n\x0e\x03\x99\xbd\x99|W\n\xff\x821\x95\xbf\x1a\x8d\x9c5;<#\x11\xa4'\x0b\\\xcb\xff&\xdb\xe8\xcd\xf9&\xb3\xbd]\xaa\x16\x937-\xb7[\xe6r/\x0c\x8d:\xaab\xf2\xb9\xa9\x01t\x9c\xd29\xbf\x9d\x01\xe4\x8b\x10\xc8V\xfab}$c\x00\x86\xf0\xc8\x0b\x13\xf1_7\x9a\x03Q~\x03\xdfw\x8d\xc8\xd5\x98\xbe\x89\xe6\xbb\xd4\xbb\x11|\x1d\xbb_\xb1\xd9\x18\xcaMS\xe9(|\xb9i*\x0ca\xe7\xed-7MM\xae\x8b\xa8\xf4\xc8D\x8c<\x08\x1d\x0fG\xc2\x130\xf0\x04\xf4\x9b0\x18\x191\xee\xbc\xe8v6\\\xdb\x8fhg\xc3\xb2\xcbMS\xe5\xa6\xa9\x15\xa8r\xb9iJt\xb4\xaa1\x05!\x0e\xb9|\xa9\xdc4\xa5H\xb9i\xaa\xdc4\xb5\xe2R'\xff3>\xb45\x0dg-7M \xc9\x89\x89\x96\x9b\xa6\xb8\x84\xe1\x9d^\xa43\x10\xe3\x0cA7\xcbMS\xb3\xae$\xb4\xb2\xdc4\x15x\xd3T\x08\xe6hA\x1b\xc9\x1d\xbd\x1da\\\x8b-\xc6\xa1\x8a\xa6?y\xc9\x00\x1e\xecW.\xc0@>``*+\x8d\x8d}f_\x17\xea\x14\x0ev\x1f\xf3\xba\x18\x17\xe3\xf9g\xff8eC\xbe\xac\xbb\xcdi_\x0du\xb3[\x9d\x11\xb9\x99\xdf\xbdE\xb5\x8e\x8e\xd7;\xc2V\xba\x1d\x84\x1b\x7fE NyMh\xbb\xba|)\xfbAG\xe6\xb4\x06\xdf\xb4C\xb5_\xddT\x0e\xd1\x067\xd2,(\xa4U\xfc\x05\x12\\\x1c\xda\x0f\xac\xb9\xaf6C\xdb\x8d\x0d\xb9cC\xb5\xfa\xc2\x98-k\xda\xc3\xedd\xc8\xee\x991\xe1\x82\xf0\x94\xe0LV^\x9e~t\xb0\xba@\xaaC{j,\x19\x85\xc6\xe3\xae`\x85\x97m\xdd\xa0{\x94\x8d++\xef9\xb1\x8f\xc2*\x89h\x0b>\x85\xabF\x14\xac\x9d\xb8\xde\xbe\xbb9\xc7\xeb\x91\xf0\xcfb\xabUsg\xc7E3\x08\xd3=9\x84\x16\xeb\xe8\xf4\xf1f\xe9\xeb]S\x0d\xa7\x8e\xf5\x1c/\xaf;\xdc\x9c\xef\xda]\xcbW\xb2\x10\xaf\xcb\xab\xb1\xf2/\xc5\xe7{\xcd\xd8\xd4H\xbe\xa5fL\xf8\x167\x0f\xe3*&\xfd\x9e\xd3\xc2\xc5\xcd\x8a\xa2\xada\x9f\xbe[\xf4h\xd3\x1e\x10%\x18U\xd5=l\xbb\xeaS\x83\xe6D5\xd80\x8c\x16\xa9;=B\xb5\xe1\xc1\x0b\xcf\xf4k \xeeN]\xc3\xb6\xd8\"\xfaC\x89\x1b\x10\xd4\xfa\xcf\xb7+LsB\x1d\xf3\xe2\x12\x86\xe9+]\xd7\xcd\x86=\x87W|v\xc0\xffu\xf6\xdf\xce\xfe\x9bg\xb2\xe4\xbc\xd4e\xd1\xaa\xa3\xb31b\xd5\x90\xd5\x97\x1a\xc8\xa9DO$\xd7\xb4\x05\xcb\xd4\x057S\xed\xea\x0c]r*\x83k:\x83uJ{^\xf3e\xb4DOoCS\xca\x1c7\x94\x899o\xfc\x1e>\xf1\xc1\xdb\xf8d\x03\xa0\xe9\xabt#\x00y\x0d\x01\xb8\x8c\xc1\xaa\x8bS\xe6%\xd2\xb8<\xe5,d\xde\xcb{\xdcx\x0f\xae\x9f\xff\xbcO\xe2\xa2\x0f\x941N\xdbBG\xfd\x16\xdfw\xea\x94~\xe8N\x9bqD\xc9\x9e\x11\x8a\x96:0\x9e\x8bG\xdc\x9d}\xf7\xff\x05\x00\x00\xff\xffPK\x07\x08-\x0e\x16\x82&\xcc\x00\x00\x1e\xac\x0c\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(-\x0e\x16\x82&\xcc\x00\x00\x1e\xac\x0c\x00\x0c\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00swagger.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00C\x00\x00\x00i\xcc\x00\x00\x00\x00"
fs.Register(data)
}
diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml
index c78bc502fb..b658aec771 100644
--- a/client/docs/swagger-ui/swagger.yaml
+++ b/client/docs/swagger-ui/swagger.yaml
@@ -14797,6 +14797,125 @@ paths:
format: uint64
tags:
- Query
+ /desmos/tokenfactory/v1beta1/params:
+ get:
+ summary: >-
+ Params defines a gRPC query method that returns the tokenfactory
+ module's
+
+ parameters.
+ operationId: Params
+ responses:
+ '200':
+ description: A successful response.
+ schema:
+ type: object
+ properties:
+ params:
+ description: params defines the parameters of the module.
+ type: object
+ properties:
+ denom_creation_fee:
+ type: array
+ items:
+ type: object
+ properties:
+ denom:
+ type: string
+ amount:
+ type: string
+ description: >-
+ Coin defines a token with a denomination and an amount.
+
+
+ NOTE: The amount field is an Int which implements the
+ custom method
+
+ signatures required by gogoproto.
+ description: >-
+ DenomCreationFee defines the fee to be charged on the
+ creation of a new
+
+ denom. The fee is drawn from the subspace treasruy
+ account, and
+
+ burned.
+ description: >-
+ QueryParamsResponse is the response type for the Query/Params RPC
+ method.
+ default:
+ description: An unexpected error response.
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ code:
+ type: integer
+ format: int32
+ message:
+ type: string
+ details:
+ type: array
+ items:
+ type: object
+ properties:
+ type_url:
+ type: string
+ value:
+ type: string
+ format: byte
+ tags:
+ - Query
+ /desmos/tokenfactory/v1beta1/subspaces/{subspace_id}/denoms:
+ get:
+ summary: |-
+ SubspaceDenoms defines a gRPC query method for fetching all
+ denominations created by a specific subspace.
+ operationId: SubspaceDenoms
+ responses:
+ '200':
+ description: A successful response.
+ schema:
+ type: object
+ properties:
+ denoms:
+ type: array
+ items:
+ type: string
+ description: |-
+ QuerySubspaceDenomsResponse defines the response structure for the
+ SubspaceDenoms gRPC query.
+ default:
+ description: An unexpected error response.
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ code:
+ type: integer
+ format: int32
+ message:
+ type: string
+ details:
+ type: array
+ items:
+ type: object
+ properties:
+ type_url:
+ type: string
+ value:
+ type: string
+ format: byte
+ parameters:
+ - name: subspace_id
+ in: path
+ required: true
+ type: string
+ format: uint64
+ tags:
+ - Query
definitions:
cosmos.base.query.v1beta1.PageRequest:
type: object
@@ -23482,3 +23601,73 @@ definitions:
total_supply:
type: string
title: QueryTotalResponse is the response type for the Query/Total RPC method
+ desmos.tokenfactory.v1beta1.Params:
+ type: object
+ properties:
+ denom_creation_fee:
+ type: array
+ items:
+ type: object
+ properties:
+ denom:
+ type: string
+ amount:
+ type: string
+ description: |-
+ Coin defines a token with a denomination and an amount.
+
+ NOTE: The amount field is an Int which implements the custom method
+ signatures required by gogoproto.
+ description: >-
+ DenomCreationFee defines the fee to be charged on the creation of a
+ new
+
+ denom. The fee is drawn from the subspace treasruy account, and
+
+ burned.
+ description: |-
+ Params defines the parameters for the tokenfactory module.
+
+ Since: Desmos 6.0.0
+ desmos.tokenfactory.v1beta1.QueryParamsResponse:
+ type: object
+ properties:
+ params:
+ description: params defines the parameters of the module.
+ type: object
+ properties:
+ denom_creation_fee:
+ type: array
+ items:
+ type: object
+ properties:
+ denom:
+ type: string
+ amount:
+ type: string
+ description: >-
+ Coin defines a token with a denomination and an amount.
+
+
+ NOTE: The amount field is an Int which implements the custom
+ method
+
+ signatures required by gogoproto.
+ description: >-
+ DenomCreationFee defines the fee to be charged on the creation of
+ a new
+
+ denom. The fee is drawn from the subspace treasruy account, and
+
+ burned.
+ description: QueryParamsResponse is the response type for the Query/Params RPC method.
+ desmos.tokenfactory.v1beta1.QuerySubspaceDenomsResponse:
+ type: object
+ properties:
+ denoms:
+ type: array
+ items:
+ type: string
+ description: |-
+ QuerySubspaceDenomsResponse defines the response structure for the
+ SubspaceDenoms gRPC query.
diff --git a/go.mod b/go.mod
index 2b9f5eeb5e..e55fc29153 100644
--- a/go.mod
+++ b/go.mod
@@ -309,7 +309,7 @@ require (
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d // indirect
- nhooyr.io/websocket v1.8.6 // indirect
+ nhooyr.io/websocket v1.8.7 // indirect
pgregory.net/rapid v0.5.5 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
diff --git a/go.sum b/go.sum
index 6df9c81067..b3d5af653a 100644
--- a/go.sum
+++ b/go.sum
@@ -2236,8 +2236,9 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d h1:3rvTIIM22r9pvXk+q3swxUQAQOxksVMGK7sml4nG57w=
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is=
-nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
+nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
+nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/proto/buf.lock b/proto/buf.lock
index 9e70ac49f4..9cfb67e595 100644
--- a/proto/buf.lock
+++ b/proto/buf.lock
@@ -5,23 +5,29 @@ deps:
owner: cosmos
repository: cosmos-proto
commit: 1935555c206d4afb9e94615dfd0fad31
+ digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377
- remote: buf.build
owner: cosmos
repository: cosmos-sdk
commit: 954f7b05f38440fc8250134b15adec47
+ digest: shake256:2ab4404fd04a7d1d52df0e2d0f2d477a3d83ffd88d876957bf3fedfd702c8e52833d65b3ce1d89a3c5adf2aab512616b0e4f51d8463f07eda9a8a3317ee3ac54
- remote: buf.build
owner: cosmos
repository: gogo-proto
- commit: 34d970b699f84aa382f3c29773a60836
+ commit: 5e5b9fdd01804356895f8f79a6f1ddc1
+ digest: shake256:0b85da49e2e5f9ebc4806eae058e2f56096ff3b1c59d1fb7c190413dd15f45dd456f0b69ced9059341c80795d2b6c943de15b120a9e0308b499e43e4b5fc2952
- remote: buf.build
owner: cosmos
repository: ibc
- commit: d6a99e4eff7443b1b9c887d1feb2b23f
+ commit: e6b07bb8a5a548f6a5135067a2538ddd
+ digest: shake256:5794f44aeabb1402b4d52c98c16f34536aa3b536edfe37354723364da8e98c84c4ade83b0bc32a378e60870e9442222132dcc4bf47d3b4395956f4ab73ed32b1
- remote: buf.build
owner: cosmos
repository: ics23
commit: 55085f7c710a45f58fa09947208eb70b
+ digest: shake256:9bf0bc495b5a11c88d163d39ef521bc4b00bc1374a05758c91d82821bdc61f09e8c2c51dda8452529bf80137f34d852561eacbe9550a59015d51cecb0dacb628
- remote: buf.build
owner: googleapis
repository: googleapis
- commit: 463926e7ee924d46ad0a726e1cf4eacd
+ commit: cc916c31859748a68fd229a3c8d7a2e8
+ digest: shake256:469b049d0eb04203d5272062636c078decefc96fec69739159c25d85349c50c34c7706918a8b216c5c27f76939df48452148cff8c5c3ae77fa6ba5c25c1b8bf8
diff --git a/proto/desmos/tokenfactory/module/v1/module.proto b/proto/desmos/tokenfactory/module/v1/module.proto
new file mode 100644
index 0000000000..69818ce7af
--- /dev/null
+++ b/proto/desmos/tokenfactory/module/v1/module.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package desmos.tokenfactory.module.v1;
+
+import "cosmos/app/v1alpha1/module.proto";
+
+// Module is the config object of the tokenfactory module.
+message Module {
+ option (cosmos.app.v1alpha1.module) = {
+ go_import : "github.com/desmos-labs/desmos/x/tokenfactory"
+ };
+
+ // authority defines the custom module authority. If not set, defaults to the
+ // governance module.
+ string authority = 1;
+}
\ No newline at end of file
diff --git a/proto/desmos/tokenfactory/v1beta1/genesis.proto b/proto/desmos/tokenfactory/v1beta1/genesis.proto
new file mode 100644
index 0000000000..415bac256b
--- /dev/null
+++ b/proto/desmos/tokenfactory/v1beta1/genesis.proto
@@ -0,0 +1,36 @@
+syntax = "proto3";
+package desmos.tokenfactory.v1beta1;
+
+import "amino/amino.proto";
+import "gogoproto/gogo.proto";
+import "desmos/tokenfactory/v1beta1/params.proto";
+import "desmos/tokenfactory/v1beta1/models.proto";
+
+option go_package = "github.com/desmos-labs/desmos/v5/x/tokenfactory/types";
+
+// GenesisState defines the tokenfactory module's genesis state.
+message GenesisState {
+ // params defines the paramaters of the module.
+ Params params = 1
+ [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
+
+ repeated GenesisDenom factory_denoms = 2 [
+ (gogoproto.moretags) = "yaml:\"factory_denoms\"",
+ (gogoproto.nullable) = false,
+ (amino.dont_omitempty) = true
+ ];
+}
+
+// GenesisDenom defines a tokenfactory denom that is defined within genesis
+// state. The structure contains DenomAuthorityMetadata which defines the
+// denom's admin.
+message GenesisDenom {
+ option (gogoproto.equal) = true;
+
+ string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
+ DenomAuthorityMetadata authority_metadata = 2 [
+ (gogoproto.moretags) = "yaml:\"authority_metadata\"",
+ (gogoproto.nullable) = false,
+ (amino.dont_omitempty) = true
+ ];
+}
diff --git a/proto/desmos/tokenfactory/v1beta1/models.proto b/proto/desmos/tokenfactory/v1beta1/models.proto
new file mode 100644
index 0000000000..2ab0a49c67
--- /dev/null
+++ b/proto/desmos/tokenfactory/v1beta1/models.proto
@@ -0,0 +1,20 @@
+syntax = "proto3";
+package desmos.tokenfactory.v1beta1;
+
+import "gogoproto/gogo.proto";
+import "cosmos_proto/cosmos.proto";
+
+option go_package = "github.com/desmos-labs/desmos/v5/x/tokenfactory/types";
+
+// DenomAuthorityMetadata specifies metadata for addresses that have specific
+// capabilities over a token factory denom. Right now there is only one Admin
+// permission, but is planned to be extended to the future.
+message DenomAuthorityMetadata {
+ option (gogoproto.equal) = true;
+
+ // Can be empty for no admin, or a valid osmosis address
+ string admin = 1 [
+ (gogoproto.moretags) = "yaml:\"admin\"",
+ (cosmos_proto.scalar) = "cosmos.AddressString"
+ ];
+}
diff --git a/proto/desmos/tokenfactory/v1beta1/msgs.proto b/proto/desmos/tokenfactory/v1beta1/msgs.proto
new file mode 100644
index 0000000000..7880e12bfd
--- /dev/null
+++ b/proto/desmos/tokenfactory/v1beta1/msgs.proto
@@ -0,0 +1,218 @@
+syntax = "proto3";
+package desmos.tokenfactory.v1beta1;
+
+import "amino/amino.proto";
+import "gogoproto/gogo.proto";
+import "cosmos/base/v1beta1/coin.proto";
+import "cosmos/bank/v1beta1/bank.proto";
+import "cosmos_proto/cosmos.proto";
+import "cosmos/msg/v1/msg.proto";
+
+import "desmos/tokenfactory/v1beta1/params.proto";
+
+option go_package = "github.com/desmos-labs/desmos/v5/x/tokenfactory/types";
+
+// Msg defines the tokefactory module's gRPC message service.
+service Msg {
+ option (cosmos.msg.v1.service) = true;
+
+ // CreateDenom allows an account to create a new denom for subspace. It
+ // requires a subspace and a sub denomination. The (subspace_treasury_address,
+ // sub_denomination) tuple must be unique and cannot be re-used.
+ //
+ // The resulting denom created is defined as
+ // . The resulting denom's admin is
+ // originally set to be the subspace treasury account, and this can not be
+ // changed later.
+ //
+ // Since: Desmos 6.0.0
+ rpc CreateDenom(MsgCreateDenom) returns (MsgCreateDenomResponse);
+
+ // Mint allows subspace admins to mint more of a token.
+ //
+ // Since: Desmos 6.0.0
+ rpc Mint(MsgMint) returns (MsgMintResponse);
+
+ // Burn allows subspace admins to burn a token.
+ // For now, we only support burning from the treasury account.
+ //
+ // Since: Desmos 6.0.0
+ rpc Burn(MsgBurn) returns (MsgBurnResponse);
+
+ // SetDenomMetadata allows subspace admins to set the denom's bank metadata.
+ //
+ // Since: Desmos 6.0.0
+ rpc SetDenomMetadata(MsgSetDenomMetadata)
+ returns (MsgSetDenomMetadataResponse);
+
+ // UpdateParams defines a (governance) operation for updating the module
+ // parameters. The authority defaults to the x/gov module account.
+ //
+ // Since: Desmos 6.0.0
+ rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
+}
+
+// MsgCreateDenom represents the message to be used to create a denom for
+// subspace
+//
+// Since: Desmos 6.0.0
+message MsgCreateDenom {
+ option (cosmos.msg.v1.signer) = "sender";
+ option (amino.name) = "desmos/MsgCreateDenom";
+
+ // Id of the subspace which manages the denom
+ uint64 subspace_id = 1 [
+ (gogoproto.customname) = "SubspaceID",
+ (gogoproto.moretags) = "yaml:\"subspace_id\""
+ ];
+
+ // Address of user having the permission to manage subspace denoms
+ string sender = 2 [
+ (gogoproto.moretags) = "yaml:\"sender\"",
+ (cosmos_proto.scalar) = "cosmos.AddressString"
+ ];
+
+ // Subdenom name of the creating denom
+ // It can be up to 44 "alphanumeric" characters long
+ string subdenom = 3 [ (gogoproto.moretags) = "yaml:\"subdenom\"" ];
+}
+
+// MsgCreateDenomResponse represents the Msg/CreateDenom response type
+// It returns the full string of the newly created denom
+message MsgCreateDenomResponse {
+ // Name of the newly created denom
+ string new_token_denom = 1
+ [ (gogoproto.moretags) = "yaml:\"new_token_denom\"" ];
+}
+
+// MsgMint represents the message to be used to mint subspace tokens to treasury
+// account
+//
+// Since: Desmos 6.0.0
+message MsgMint {
+ option (cosmos.msg.v1.signer) = "sender";
+ option (amino.name) = "desmos/MsgMint";
+
+ // Id of the subspace which manages the denom
+ uint64 subspace_id = 1 [
+ (gogoproto.customname) = "SubspaceID",
+ (gogoproto.moretags) = "yaml:\"subspace_id\""
+ ];
+
+ // Address of user having the permission to manage subspace denoms
+ string sender = 2 [
+ (gogoproto.moretags) = "yaml:\"sender\"",
+ (cosmos_proto.scalar) = "cosmos.AddressString"
+ ];
+
+ // Amount of the minting subspace tokens
+ cosmos.base.v1beta1.Coin amount = 3 [
+ (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
+ (gogoproto.moretags) = "yaml:\"amount\"",
+ (gogoproto.nullable) = false,
+ (amino.encoding) = "legacy_coins",
+ (amino.dont_omitempty) = true
+ ];
+}
+
+// MsgMintResponse represents the Msg/Mint response type
+//
+// Since: Desmos 6.0.0
+message MsgMintResponse {}
+
+// MsgBurn represents the message to be used to burn subspace tokens from
+// treasury account
+//
+// Since: Desmos 6.0.0
+message MsgBurn {
+ option (cosmos.msg.v1.signer) = "sender";
+ option (amino.name) = "desmos/MsgBurn";
+
+ // Id of the subspace which manages the denom
+ uint64 subspace_id = 1 [
+ (gogoproto.customname) = "SubspaceID",
+ (gogoproto.moretags) = "yaml:\"subspace_id\""
+ ];
+
+ // Address of user having the permission to manage subspace denoms
+ string sender = 2 [
+ (gogoproto.moretags) = "yaml:\"sender\"",
+ (cosmos_proto.scalar) = "cosmos.AddressString"
+ ];
+
+ // Amount of the burning subspace tokens
+ cosmos.base.v1beta1.Coin amount = 3 [
+ (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
+ (gogoproto.moretags) = "yaml:\"amount\"",
+ (gogoproto.nullable) = false,
+ (amino.encoding) = "legacy_coins",
+ (amino.dont_omitempty) = true
+ ];
+}
+
+// MsgBurnResponse represents the Msg/Burn response type
+//
+// Since: Desmos 6.0.0
+message MsgBurnResponse {}
+
+// MsgSetDenomMetadata represents the message to be used to set the subspace
+// token's bank metadata
+//
+// Since: Desmos 6.0.0
+message MsgSetDenomMetadata {
+ option (cosmos.msg.v1.signer) = "sender";
+ option (amino.name) = "desmos/MsgSetDenomMetadata";
+
+ // Id of the subspace which manages the denom
+ uint64 subspace_id = 1 [
+ (gogoproto.customname) = "SubspaceID",
+ (gogoproto.moretags) = "yaml:\"subspace_id\""
+ ];
+
+ // Address of user having the permission to manage subspace denoms
+ string sender = 2 [
+ (gogoproto.moretags) = "yaml:\"sender\"",
+ (cosmos_proto.scalar) = "cosmos.AddressString"
+ ];
+
+ // Metadata of the denom
+ cosmos.bank.v1beta1.Metadata metadata = 3 [
+ (gogoproto.moretags) = "yaml:\"metadata\"",
+ (gogoproto.nullable) = false,
+ (amino.dont_omitempty) = true
+ ];
+}
+
+// MsgSetDenomMetadataResponse represents the Msg/SetDenomMetadata response type
+//
+// Since: Desmos 6.0.0
+message MsgSetDenomMetadataResponse {}
+
+// MsgUpdateParams is the Msg/UpdateParams request type.
+//
+// Since: Desmos 6.0.0
+message MsgUpdateParams {
+ option (amino.name) = "desmos/x/tokenfactoy/MsgUpdateParams";
+ option (cosmos.msg.v1.signer) = "authority";
+
+ // authority is the address that controls the module (defaults to x/gov unless
+ // overwritten).
+ string authority = 1 [
+ (gogoproto.moretags) = "yaml:\"authority\"",
+ (cosmos_proto.scalar) = "cosmos.AddressString"
+ ];
+
+ // params defines the parameters to update.
+ //
+ // NOTE: All parameters must be supplied.
+ Params params = 2 [
+ (gogoproto.moretags) = "yaml:\"params\"",
+ (gogoproto.nullable) = false,
+ (amino.dont_omitempty) = true
+ ];
+}
+
+// MsgUpdateParamsResponse represents the Msg/UpdateParams response type
+//
+// Since: Desmos 6.0.0
+message MsgUpdateParamsResponse {}
\ No newline at end of file
diff --git a/proto/desmos/tokenfactory/v1beta1/params.proto b/proto/desmos/tokenfactory/v1beta1/params.proto
new file mode 100644
index 0000000000..3ebdfcdc1f
--- /dev/null
+++ b/proto/desmos/tokenfactory/v1beta1/params.proto
@@ -0,0 +1,26 @@
+syntax = "proto3";
+package desmos.tokenfactory.v1beta1;
+
+import "amino/amino.proto";
+import "gogoproto/gogo.proto";
+import "cosmos/base/v1beta1/coin.proto";
+
+option go_package = "github.com/desmos-labs/desmos/v5/x/tokenfactory/types";
+
+// Params defines the parameters for the tokenfactory module.
+//
+// Since: Desmos 6.0.0
+message Params {
+ option (amino.name) = "desmos/x/tokenfactory/Params";
+
+ // DenomCreationFee defines the fee to be charged on the creation of a new
+ // denom. The fee is drawn from the subspace treasruy account, and
+ // burned.
+ repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [
+ (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
+ (gogoproto.moretags) = "yaml:\"denom_creation_fee\"",
+ (gogoproto.nullable) = false,
+ (amino.encoding) = "legacy_coins",
+ (amino.dont_omitempty) = true
+ ];
+}
\ No newline at end of file
diff --git a/proto/desmos/tokenfactory/v1beta1/query.proto b/proto/desmos/tokenfactory/v1beta1/query.proto
new file mode 100644
index 0000000000..5ccc9f2da8
--- /dev/null
+++ b/proto/desmos/tokenfactory/v1beta1/query.proto
@@ -0,0 +1,48 @@
+syntax = "proto3";
+package desmos.tokenfactory.v1beta1;
+
+import "amino/amino.proto";
+import "gogoproto/gogo.proto";
+import "google/api/annotations.proto";
+import "desmos/tokenfactory/v1beta1/params.proto";
+
+option go_package = "github.com/desmos-labs/desmos/v5/x/tokenfactory/types";
+
+// Query defines the gRPC querier service.
+service Query {
+ // Params defines a gRPC query method that returns the tokenfactory module's
+ // parameters.
+ rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
+ option (google.api.http).get = "/desmos/tokenfactory/v1beta1/params";
+ }
+
+ // SubspaceDenoms defines a gRPC query method for fetching all
+ // denominations created by a specific subspace.
+ rpc SubspaceDenoms(QuerySubspaceDenomsRequest)
+ returns (QuerySubspaceDenomsResponse) {
+ option (google.api.http).get =
+ "/desmos/tokenfactory/v1beta1/subspaces/{subspace_id}/denoms";
+ }
+}
+
+// QueryParamsRequest is the request type for the Query/Params RPC method.
+message QueryParamsRequest {}
+
+// QueryParamsResponse is the response type for the Query/Params RPC method.
+message QueryParamsResponse {
+ // params defines the parameters of the module.
+ Params params = 1
+ [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
+}
+
+// QuerySubspaceDenomsRequest defines the request structure for the
+// SubspaceDenoms gRPC query.
+message QuerySubspaceDenomsRequest {
+ uint64 subspace_id = 1 [ (gogoproto.moretags) = "yaml:\"creator\"" ];
+}
+
+// QuerySubspaceDenomsResponse defines the response structure for the
+// SubspaceDenoms gRPC query.
+message QuerySubspaceDenomsResponse {
+ repeated string denoms = 1 [ (gogoproto.moretags) = "yaml:\"denoms\"" ];
+}
diff --git a/x/posts/testutil/expected_keepers_mocks.go b/x/posts/testutil/expected_keepers_mocks.go
index cc73462020..05c80e7990 100644
--- a/x/posts/testutil/expected_keepers_mocks.go
+++ b/x/posts/testutil/expected_keepers_mocks.go
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
-// Source: types/expected_keepers.go
+// Source: ./x/posts/types/expected_keepers.go
// Package testutil is a generated GoMock package.
package testutil
diff --git a/x/subspaces/simulation/utils.go b/x/subspaces/simulation/utils.go
index fab1cffefa..8259be4fd4 100644
--- a/x/subspaces/simulation/utils.go
+++ b/x/subspaces/simulation/utils.go
@@ -36,11 +36,12 @@ func GenerateRandomSubspace(r *rand.Rand, accs []simtypes.Account) types.Subspac
simAccount, _ := simtypes.RandomAcc(r, accs)
creator := simAccount.Address.String()
+ id := RandomID(r)
return types.NewSubspace(
RandomID(r),
RandomName(r),
RandomDescription(r),
- creator,
+ types.GetTreasuryAddress(id).String(),
creator,
creator,
RandomDate(r),
diff --git a/x/tokenfactory/client/cli/cli_test.go b/x/tokenfactory/client/cli/cli_test.go
new file mode 100644
index 0000000000..2690f24361
--- /dev/null
+++ b/x/tokenfactory/client/cli/cli_test.go
@@ -0,0 +1,489 @@
+//go:build norace
+// +build norace
+
+package cli_test
+
+import (
+ "fmt"
+ "os"
+ "path"
+ "testing"
+ "time"
+
+ tmcli "github.com/cometbft/cometbft/libs/cli"
+ "github.com/cosmos/cosmos-sdk/client/flags"
+ clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
+ "github.com/cosmos/cosmos-sdk/testutil/network"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/gogo/protobuf/proto"
+ "github.com/stretchr/testify/suite"
+
+ "github.com/desmos-labs/desmos/v5/testutil"
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/client/cli"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func TestIntegrationTestSuite(t *testing.T) {
+ suite.Run(t, new(IntegrationTestSuite))
+}
+
+type IntegrationTestSuite struct {
+ suite.Suite
+
+ cfg network.Config
+ network *network.Network
+}
+
+func (s *IntegrationTestSuite) SetupSuite() {
+ s.T().Log("setting up integration test suite")
+
+ cfg := testutil.DefaultConfig()
+ genesisState := cfg.GenesisState
+ cfg.NumValidators = 2
+
+ // Initialize the subspaces module genesis state
+ subspacesGenesis := subspacestypes.NewGenesisState(
+ 2,
+ []subspacestypes.SubspaceData{
+ subspacestypes.NewSubspaceData(1, 1, 1),
+ },
+ []subspacestypes.Subspace{
+ subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9",
+ "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn",
+ "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ),
+ },
+ nil, nil, nil, nil, nil,
+ )
+
+ subspacesDataBz, err := cfg.Codec.MarshalJSON(subspacesGenesis)
+ s.Require().NoError(err)
+ genesisState[subspacestypes.ModuleName] = subspacesDataBz
+
+ tokenfactoryGenesis := &types.GenesisState{
+ Params: types.DefaultParams(),
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn",
+ },
+ },
+ {
+ Denom: "factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/utesttoken",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9",
+ },
+ },
+ },
+ }
+
+ tokenfactoryDataBz, err := cfg.Codec.MarshalJSON(tokenfactoryGenesis)
+ s.Require().NoError(err)
+ genesisState[types.ModuleName] = tokenfactoryDataBz
+
+ cfg.GenesisState = genesisState
+
+ s.cfg = cfg
+ s.network, err = network.New(s.T(), s.T().TempDir(), cfg)
+ s.Require().NoError(err)
+
+ _, err = s.network.WaitForHeight(1)
+ s.Require().NoError(err)
+}
+
+func (s *IntegrationTestSuite) TearDownSuite() {
+ s.T().Log("tearing down integration test suite")
+ s.network.Cleanup()
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+func (s *IntegrationTestSuite) TestCmdQueryParams() {
+ val := s.network.Validators[0]
+ testCases := []struct {
+ name string
+ args []string
+ shouldErr bool
+ expResponse types.QueryParamsResponse
+ }{
+ {
+ name: "params is returned properly",
+ args: []string{
+ fmt.Sprintf("--%s=json", tmcli.OutputFlag),
+ },
+ shouldErr: false,
+ expResponse: types.QueryParamsResponse{
+ Params: types.DefaultParams(),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ s.Run(tc.name, func() {
+ cmd := cli.GetCmdQueryParams()
+ clientCtx := val.ClientCtx
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+
+ if tc.shouldErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+
+ var response types.QueryParamsResponse
+ s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String())
+ s.Require().Equal(tc.expResponse.Params, response.Params)
+ }
+ })
+ }
+}
+
+func (s *IntegrationTestSuite) TestCmdQuerySubspaceDenoms() {
+ val := s.network.Validators[0]
+ testCases := []struct {
+ name string
+ args []string
+ shouldErr bool
+ expResponse types.QuerySubspaceDenomsResponse
+ }{
+ {
+ name: "invalid subspace id returns error",
+ args: []string{
+ "X",
+ fmt.Sprintf("--%s=json", tmcli.OutputFlag),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "denoms are returned properly",
+ args: []string{
+ "1",
+ fmt.Sprintf("--%s=json", tmcli.OutputFlag),
+ },
+ shouldErr: false,
+ expResponse: types.QuerySubspaceDenomsResponse{
+ Denoms: []string{
+ "factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ "factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/utesttoken",
+ },
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ s.Run(tc.name, func() {
+ cmd := cli.GetCmdQuerySubspaceDenoms()
+ clientCtx := val.ClientCtx
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+
+ if tc.shouldErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+
+ var response types.QuerySubspaceDenomsResponse
+ s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String())
+ s.Require().Equal(tc.expResponse.Denoms, response.Denoms)
+ }
+ })
+ }
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+func (s *IntegrationTestSuite) TestCmdCreateDenom() {
+ val := s.network.Validators[0]
+ testCases := []struct {
+ name string
+ args []string
+ shouldErr bool
+ respType proto.Message
+ }{
+ {
+ name: "invalid subspace id returns error",
+ args: []string{
+ "X", "utrytoken",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid subdenom returns error",
+ args: []string{
+ "1", "",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ args: []string{
+ "1", "utrytoken",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
+ fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
+ fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
+ },
+ shouldErr: false,
+ respType: &sdk.TxResponse{},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ s.Run(tc.name, func() {
+ cmd := cli.GetCmdCreateDenom()
+ clientCtx := val.ClientCtx
+
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+ if tc.shouldErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+ s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
+ }
+ })
+ }
+}
+
+func (s *IntegrationTestSuite) TestCmdMint() {
+ val := s.network.Validators[0]
+ testCases := []struct {
+ name string
+ args []string
+ shouldErr bool
+ respType proto.Message
+ }{
+ {
+ name: "invalid subspace id returns error",
+ args: []string{
+ "X",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid amount returns error",
+ args: []string{
+ "1", "X",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid sender returns error",
+ args: []string{
+ "1", "10factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ args: []string{
+ "1", "10factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
+ fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
+ fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
+ },
+ shouldErr: false,
+ respType: &sdk.TxResponse{},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ s.Run(tc.name, func() {
+ cmd := cli.GetCmdMint()
+ clientCtx := val.ClientCtx
+
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+ if tc.shouldErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+ s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
+ }
+ })
+ }
+}
+
+func (s *IntegrationTestSuite) TestCmdBurn() {
+ val := s.network.Validators[0]
+ testCases := []struct {
+ name string
+ args []string
+ shouldErr bool
+ respType proto.Message
+ }{
+ {
+ name: "invalid subspace id returns error",
+ args: []string{
+ "X",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid amount returns error",
+ args: []string{
+ "1", "X",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid sender returns error",
+ args: []string{
+ "1", "10factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ args: []string{
+ "1", "10factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
+ fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
+ fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
+ },
+ shouldErr: false,
+ respType: &sdk.TxResponse{},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ s.Run(tc.name, func() {
+ cmd := cli.GetCmdBurn()
+ clientCtx := val.ClientCtx
+
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+ if tc.shouldErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+ s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
+ }
+ })
+ }
+}
+
+func (s *IntegrationTestSuite) TestCmdSetDenomMetadata() {
+ val := s.network.Validators[0]
+ testCases := []struct {
+ name string
+ setupFile func() string
+ args []string
+ shouldErr bool
+ respType proto.Message
+ }{
+ {
+ name: "invalid subspace id returns error",
+ args: []string{
+ "X",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid path returns error",
+ args: []string{
+ "1", "X",
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "invalid metadata schema returns error",
+ setupFile: func() string {
+ os.CreateTemp(os.TempDir(), "metadata.json")
+ return path.Join(os.TempDir(), "metadata.json")
+ },
+ args: []string{
+ "1", path.Join(os.TempDir(), "metadata.json"),
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ },
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ setupFile: func() string {
+ file, _ := os.CreateTemp(os.TempDir(), "metadata.json")
+
+ bz := s.cfg.Codec.MustMarshalJSON(&banktypes.Metadata{
+ Name: "Mint Token",
+ Symbol: "MTK",
+ Description: "The custom token of the test subspace.",
+ DenomUnits: []*banktypes.DenomUnit{
+ {Denom: "factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken", Exponent: uint32(0), Aliases: nil},
+ {Denom: "minttoken", Exponent: uint32(6), Aliases: []string{"minttoken"}},
+ },
+ Base: "factory/cosmos1cyjzgj9j7d2gdqk78pa0fgvfnlzradat97aek9/uminttoken",
+ Display: "minttoken",
+ })
+ file.Write(bz)
+ return path.Join(os.TempDir(), "metadata.json")
+ },
+ args: []string{
+ "1", path.Join(os.TempDir(), "metadata.json"),
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
+ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
+ fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
+ fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
+ },
+ shouldErr: false,
+ respType: &sdk.TxResponse{},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ s.Run(tc.name, func() {
+ cmd := cli.GetCmdCreateDenom()
+ clientCtx := val.ClientCtx
+
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+ if tc.shouldErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+ s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
+ }
+ })
+ }
+}
+
+func (s *IntegrationTestSuite) TestCmdDraftDenomMetadata() {
+ val := s.network.Validators[0]
+
+ s.Run("draft denom metadata properly", func() {
+ outputPath := path.Join(os.TempDir(), "metadata.json")
+ cmd := cli.GetCmdDraftDenomMetadata()
+ clientCtx := val.ClientCtx
+
+ _, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, []string{fmt.Sprintf("--%s=%s", cli.FlagOutputPath, outputPath)})
+ s.Require().NoError(err)
+
+ out, err := os.ReadFile(outputPath)
+ s.Require().NoError(err)
+
+ var metadata banktypes.Metadata
+ err = clientCtx.Codec.UnmarshalJSON(out, &metadata)
+ s.Require().NoError(err)
+ s.Require().Equal(banktypes.Metadata{DenomUnits: []*banktypes.DenomUnit{}}, metadata)
+ })
+}
diff --git a/x/tokenfactory/client/cli/query.go b/x/tokenfactory/client/cli/query.go
new file mode 100644
index 0000000000..256debb956
--- /dev/null
+++ b/x/tokenfactory/client/cli/query.go
@@ -0,0 +1,92 @@
+package cli
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/cosmos/cosmos-sdk/client"
+ "github.com/cosmos/cosmos-sdk/client/flags"
+ "github.com/cosmos/cosmos-sdk/version"
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+ "github.com/spf13/cobra"
+)
+
+// DONTCOVER
+
+// GetQueryCmd returns the command allowing to perform queries
+func GetQueryCmd() *cobra.Command {
+ queryCmd := &cobra.Command{
+ Use: types.ModuleName,
+ Short: "Querying commands for the token factory module",
+ DisableFlagParsing: true,
+ SuggestionsMinimumDistance: 2,
+ RunE: client.ValidateCmd,
+ }
+ queryCmd.AddCommand(
+ GetCmdQuerySubspaceDenoms(),
+ GetCmdQueryParams(),
+ )
+ return queryCmd
+}
+
+// GetCmdQuerySubspaceDenoms returns the command to query subspace denoms of the given subspace with id
+func GetCmdQuerySubspaceDenoms() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "subspace-denoms [subspace-id]",
+ Short: "Query the denoms owned by the subspace",
+ Example: fmt.Sprintf(`%s query tokenfactory subspace-denom 1`, version.AppName),
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientQueryContext(cmd)
+ if err != nil {
+ return err
+ }
+ queryClient := types.NewQueryClient(clientCtx)
+
+ subspaceID, err := subspacestypes.ParseSubspaceID(args[0])
+ if err != nil {
+ return err
+ }
+
+ res, err := queryClient.SubspaceDenoms(context.Background(), types.NewQuerySubspaceDenomsRequest(subspaceID))
+ if err != nil {
+ return err
+ }
+
+ return clientCtx.PrintProto(res)
+ },
+ }
+
+ flags.AddQueryFlagsToCmd(cmd)
+
+ return cmd
+}
+
+// GetCmdQueryParams returns the command to query the params of the module
+func GetCmdQueryParams() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "params",
+ Short: "Query the module parameters",
+ Example: fmt.Sprintf(`%s query tokenfactory params`, version.AppName),
+ Args: cobra.NoArgs,
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ clientCtx, err := client.GetClientQueryContext(cmd)
+ if err != nil {
+ return err
+ }
+ queryClient := types.NewQueryClient(clientCtx)
+
+ res, err := queryClient.Params(context.Background(), types.NewQueryParamsRequest())
+ if err != nil {
+ return err
+ }
+
+ return clientCtx.PrintProto(res)
+ },
+ }
+
+ flags.AddQueryFlagsToCmd(cmd)
+
+ return cmd
+}
diff --git a/x/tokenfactory/client/cli/tx.go b/x/tokenfactory/client/cli/tx.go
new file mode 100644
index 0000000000..e7388d6b11
--- /dev/null
+++ b/x/tokenfactory/client/cli/tx.go
@@ -0,0 +1,225 @@
+package cli
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/cosmos/cosmos-sdk/client"
+ "github.com/cosmos/cosmos-sdk/client/flags"
+ "github.com/cosmos/cosmos-sdk/client/tx"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/spf13/cobra"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+const (
+ FlagOutputPath = "output-path"
+)
+
+// DONTCOVER
+
+// GetTxCmd returns the transaction commands for this module
+func GetTxCmd() *cobra.Command {
+ txCmd := &cobra.Command{
+ Use: types.ModuleName,
+ Short: "Token factory transaction subcommands",
+ DisableFlagParsing: true,
+ SuggestionsMinimumDistance: 2,
+ RunE: client.ValidateCmd,
+ }
+
+ txCmd.AddCommand(
+ GetCmdCreateDenom(),
+ GetCmdMint(),
+ GetCmdBurn(),
+ GetCmdSetDenomMetadata(),
+ GetCmdDraftDenomMetadata(),
+ )
+
+ return txCmd
+}
+
+// GetCmdCreateDenom returns the command used to create a denom
+func GetCmdCreateDenom() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "create-denom [subspace-id] [subdenom]",
+ Short: fmt.Sprintf("create a new denom from an account. (Costs %s though!)", sdk.DefaultBondDenom),
+ Args: cobra.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ subspaceID, err := subspacestypes.ParseSubspaceID(args[0])
+ if err != nil {
+ return err
+ }
+
+ sender := clientCtx.FromAddress
+ msg := types.NewMsgCreateDenom(subspaceID, sender.String(), args[1])
+ if err = msg.ValidateBasic(); err != nil {
+ return fmt.Errorf("message validation failed: %w", err)
+ }
+
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+
+ flags.AddTxFlagsToCmd(cmd)
+
+ return cmd
+}
+
+// GetCmdMint returns the command used to mint a denom to an address
+func GetCmdMint() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "mint [subspace-id] [amount]",
+ Short: "Mint a denom to an address. Must have permissions to do so.",
+ Args: cobra.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ subspaceID, err := subspacestypes.ParseSubspaceID(args[0])
+ if err != nil {
+ return err
+ }
+
+ amount, err := sdk.ParseCoinNormalized(args[1])
+ if err != nil {
+ return err
+ }
+
+ sender := clientCtx.FromAddress
+ msg := types.NewMsgMint(subspaceID, sender.String(), amount)
+ if err = msg.ValidateBasic(); err != nil {
+ return fmt.Errorf("message validation failed: %w", err)
+ }
+
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+
+ flags.AddTxFlagsToCmd(cmd)
+
+ return cmd
+}
+
+// GetCmdBurn returns the command used to burn a denom from the treasury account
+func GetCmdBurn() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "burn [subspace-id] [amount]",
+ Short: "Burn tokens from the treasury account. Must have permissions to do so.",
+ Args: cobra.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ subspaceID, err := subspacestypes.ParseSubspaceID(args[0])
+ if err != nil {
+ return err
+ }
+
+ amount, err := sdk.ParseCoinNormalized(args[1])
+ if err != nil {
+ return err
+ }
+
+ sender := clientCtx.FromAddress
+ msg := types.NewMsgBurn(subspaceID, sender.String(), amount)
+ if err = msg.ValidateBasic(); err != nil {
+ return fmt.Errorf("message validation failed: %w", err)
+ }
+
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+
+ flags.AddTxFlagsToCmd(cmd)
+
+ return cmd
+}
+
+// GetCmdSetDenomMetadata returns the command used to set the metadata of the denom
+func GetCmdSetDenomMetadata() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "set-denom-metadata [subspace-id] [json-path]",
+ Short: "Set a subspace token metadata. Must have permissions to do so.",
+ Args: cobra.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ subspaceID, err := subspacestypes.ParseSubspaceID(args[0])
+ if err != nil {
+ return err
+ }
+
+ var metadata banktypes.Metadata
+ bz, err := os.ReadFile(args[1])
+ if err != nil {
+ return err
+ }
+
+ err = clientCtx.Codec.UnmarshalJSON(bz, &metadata)
+ if err != nil {
+ return err
+ }
+
+ sender := clientCtx.FromAddress
+ msg := types.NewMsgSetDenomMetadata(subspaceID, sender.String(), metadata)
+ if err = msg.ValidateBasic(); err != nil {
+ return fmt.Errorf("message validation failed: %w", err)
+ }
+
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+
+ flags.AddTxFlagsToCmd(cmd)
+
+ return cmd
+}
+
+// GetCmdDraftDenomMetadata returns the command used to draft a denom metadata
+func GetCmdDraftDenomMetadata() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "draft-denom-metadata",
+ Short: "Draft a subspace token metadata for setting denom metadata",
+ Args: cobra.NoArgs,
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ output, err := cmd.Flags().GetString(FlagOutputPath)
+ if err != nil {
+ return err
+ }
+
+ var metadata banktypes.Metadata
+ bz, err := clientCtx.Codec.MarshalJSON(&metadata)
+ if err != nil {
+ return err
+ }
+
+ return os.WriteFile(output, bz, 0600)
+ },
+ }
+
+ cmd.Flags().String(FlagOutputPath, "metadata.json", "output file path of the draft metadata")
+ flags.AddTxFlagsToCmd(cmd)
+
+ return cmd
+}
diff --git a/x/tokenfactory/keeper/admins.go b/x/tokenfactory/keeper/admins.go
new file mode 100644
index 0000000000..b99ade0999
--- /dev/null
+++ b/x/tokenfactory/keeper/admins.go
@@ -0,0 +1,29 @@
+package keeper
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// GetAuthorityMetadata returns the authority metadata for a specific denom
+func (k Keeper) GetAuthorityMetadata(ctx sdk.Context, denom string) types.DenomAuthorityMetadata {
+ bz := k.GetDenomPrefixStore(ctx, denom).Get([]byte(types.DenomAuthorityMetadataKey))
+
+ var metadata types.DenomAuthorityMetadata
+ k.cdc.MustUnmarshal(bz, &metadata)
+ return metadata
+}
+
+// SetAuthorityMetadata stores authority metadata for a specific denom
+func (k Keeper) SetAuthorityMetadata(ctx sdk.Context, denom string, metadata types.DenomAuthorityMetadata) error {
+ err := metadata.Validate()
+ if err != nil {
+ return err
+ }
+
+ store := k.GetDenomPrefixStore(ctx, denom)
+
+ store.Set([]byte(types.DenomAuthorityMetadataKey), k.cdc.MustMarshal(&metadata))
+ return nil
+}
diff --git a/x/tokenfactory/keeper/bankactions.go b/x/tokenfactory/keeper/bankactions.go
new file mode 100644
index 0000000000..89c608d35d
--- /dev/null
+++ b/x/tokenfactory/keeper/bankactions.go
@@ -0,0 +1,40 @@
+package keeper
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func (k Keeper) mintTo(ctx sdk.Context, amount sdk.Coin, mintTo string) error {
+ // verify that denom is an x/tokenfactory denom
+ _, _, err := types.DeconstructDenom(amount.Denom)
+ if err != nil {
+ return err
+ }
+
+ err = k.bk.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
+ if err != nil {
+ return err
+ }
+
+ addr := sdk.MustAccAddressFromBech32(mintTo)
+
+ return k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, sdk.NewCoins(amount))
+}
+
+func (k Keeper) burnFrom(ctx sdk.Context, amount sdk.Coin, burnFrom string) error {
+ // verify that denom is an x/tokenfactory denom
+ _, _, err := types.DeconstructDenom(amount.Denom)
+ if err != nil {
+ return err
+ }
+
+ addr := sdk.MustAccAddressFromBech32(burnFrom)
+
+ if err := k.bk.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, sdk.NewCoins(amount)); err != nil {
+ return err
+ }
+
+ return k.bk.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
+}
diff --git a/x/tokenfactory/keeper/common_test.go b/x/tokenfactory/keeper/common_test.go
new file mode 100644
index 0000000000..2c28f3635d
--- /dev/null
+++ b/x/tokenfactory/keeper/common_test.go
@@ -0,0 +1,79 @@
+package keeper_test
+
+import (
+ "testing"
+
+ "github.com/golang/mock/gomock"
+
+ "github.com/cometbft/cometbft/libs/log"
+ tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
+
+ db "github.com/cometbft/cometbft-db"
+ "github.com/cosmos/cosmos-sdk/codec"
+ "github.com/cosmos/cosmos-sdk/store"
+ storetypes "github.com/cosmos/cosmos-sdk/store/types"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ "github.com/stretchr/testify/suite"
+
+ "github.com/desmos-labs/desmos/v5/app"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/keeper"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/testutil"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+type KeeperTestSuite struct {
+ suite.Suite
+
+ cdc codec.Codec
+ legacyAminoCdc *codec.LegacyAmino
+ ctx sdk.Context
+
+ storeKey storetypes.StoreKey
+ k keeper.Keeper
+
+ bk *testutil.MockBankKeeper
+ sk *testutil.MockSubspacesKeeper
+ ak *testutil.MockAccountKeeper
+}
+
+func (suite *KeeperTestSuite) SetupTest() {
+ // Define store keys
+ keys := sdk.NewMemoryStoreKeys(types.StoreKey)
+ suite.storeKey = keys[types.StoreKey]
+
+ // Create an in-memory db
+ memDB := db.NewMemDB()
+ ms := store.NewCommitMultiStore(memDB)
+ for _, key := range keys {
+ ms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, memDB)
+ }
+
+ if err := ms.LoadLatestVersion(); err != nil {
+ panic(err)
+ }
+
+ suite.ctx = sdk.NewContext(ms, tmproto.Header{ChainID: "test-chain"}, false, log.NewNopLogger())
+ suite.cdc, suite.legacyAminoCdc = app.MakeCodecs()
+
+ // Mocks initializations
+ ctrl := gomock.NewController(suite.T())
+ defer ctrl.Finish()
+
+ suite.bk = testutil.NewMockBankKeeper(ctrl)
+ suite.sk = testutil.NewMockSubspacesKeeper(ctrl)
+ suite.ak = testutil.NewMockAccountKeeper(ctrl)
+
+ suite.k = keeper.NewKeeper(
+ suite.storeKey,
+ suite.cdc,
+ suite.sk,
+ suite.ak,
+ suite.bk,
+ authtypes.NewModuleAddress("gov").String(),
+ )
+}
+
+func TestKeeperTestSuite(t *testing.T) {
+ suite.Run(t, new(KeeperTestSuite))
+}
diff --git a/x/tokenfactory/keeper/createdenom.go b/x/tokenfactory/keeper/createdenom.go
new file mode 100644
index 0000000000..c018228ddb
--- /dev/null
+++ b/x/tokenfactory/keeper/createdenom.go
@@ -0,0 +1,90 @@
+package keeper
+
+import (
+ "fmt"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// ConvertToBaseToken converts a fee amount in a whitelisted fee token to the base fee token amount
+func (k Keeper) CreateDenom(ctx sdk.Context, creator string, subdenom string) (newTokenDenom string, err error) {
+ denom, err := k.validateCreateDenom(ctx, creator, subdenom)
+ if err != nil {
+ return "", err
+ }
+
+ err = k.chargeForCreateDenom(ctx, creator)
+ if err != nil {
+ return "", err
+ }
+
+ err = k.createDenomAfterValidation(ctx, creator, denom)
+ return denom, err
+}
+
+// Runs CreateDenom logic after the charge and all denom validation has been handled.
+// Made into a second function for genesis initialization.
+func (k Keeper) createDenomAfterValidation(ctx sdk.Context, creator string, denom string) (err error) {
+ _, exists := k.bk.GetDenomMetaData(ctx, denom)
+ if !exists {
+ denomMetaData := banktypes.Metadata{
+ DenomUnits: []*banktypes.DenomUnit{{
+ Denom: denom,
+ Exponent: 0,
+ }},
+ Base: denom,
+ }
+
+ k.bk.SetDenomMetaData(ctx, denomMetaData)
+ }
+
+ authorityMetadata := types.DenomAuthorityMetadata{
+ Admin: creator,
+ }
+ err = k.SetAuthorityMetadata(ctx, denom, authorityMetadata)
+ if err != nil {
+ return err
+ }
+
+ k.AddDenomFromCreator(ctx, creator, denom)
+ return nil
+}
+
+func (k Keeper) validateCreateDenom(ctx sdk.Context, creator string, subdenom string) (newTokenDenom string, err error) {
+ // Temporary check until IBC bug is sorted out
+ if k.bk.HasSupply(ctx, subdenom) {
+ return "", fmt.Errorf("temporary error until IBC bug is sorted out, " +
+ "can't create subdenoms that are the same as a native denom")
+ }
+
+ denom, err := types.GetTokenDenom(creator, subdenom)
+ if err != nil {
+ return "", err
+ }
+
+ _, found := k.bk.GetDenomMetaData(ctx, denom)
+ if found {
+ return "", types.ErrDenomExists
+ }
+
+ return denom, nil
+}
+
+func (k Keeper) chargeForCreateDenom(ctx sdk.Context, creator string) (err error) {
+ creationFee := k.GetParams(ctx).DenomCreationFee
+
+ // Burn creation fee
+ if creationFee != nil {
+ err := k.bk.SendCoinsFromAccountToModule(ctx, sdk.MustAccAddressFromBech32(creator), types.ModuleName, creationFee)
+ if err != nil {
+ return err
+ }
+
+ return k.bk.BurnCoins(ctx, types.ModuleName, creationFee)
+ }
+
+ return nil
+}
diff --git a/x/tokenfactory/keeper/createdenom_test.go b/x/tokenfactory/keeper/createdenom_test.go
new file mode 100644
index 0000000000..0c8faa1148
--- /dev/null
+++ b/x/tokenfactory/keeper/createdenom_test.go
@@ -0,0 +1,228 @@
+package keeper_test
+
+import (
+ "fmt"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+ "github.com/golang/mock/gomock"
+)
+
+func (suite *KeeperTestSuite) TestKeeper_CreateDenom() {
+ testCases := []struct {
+ name string
+ setup func()
+ store func(ctx sdk.Context)
+ shouldErr bool
+ creator string
+ subdenom string
+ expDenom string
+ check func(ctx sdk.Context)
+ }{
+ {
+ name: "subdenom with existing supply returns error",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(true)
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ shouldErr: true,
+ },
+ {
+ name: "invalid token denoms returns error",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/",
+ shouldErr: true,
+ },
+ {
+ name: "existing denom returns error",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ shouldErr: true,
+ },
+ {
+ name: "burn creation fees returns error - send coins to module failed",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, false)
+
+ suite.bk.EXPECT().
+ SendCoinsFromAccountToModule(
+ gomock.Any(),
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100))),
+ ).
+ Return(fmt.Errorf("send coin to module error"))
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.NewParams(sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))))
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ shouldErr: true,
+ },
+ {
+ name: "burn creation fees returns error - bank burn failed",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, false)
+
+ suite.bk.EXPECT().
+ SendCoinsFromAccountToModule(
+ gomock.Any(),
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100))),
+ ).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ BurnCoins(gomock.Any(), types.ModuleName, sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))).
+ Return(fmt.Errorf("bank burn coins error"))
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.NewParams(sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))))
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ shouldErr: true,
+ },
+ {
+ name: "create denom properly",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, false).Times(2)
+
+ suite.bk.EXPECT().
+ SendCoinsFromAccountToModule(
+ gomock.Any(),
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100))),
+ ).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ BurnCoins(gomock.Any(), types.ModuleName, sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ SetDenomMetaData(gomock.Any(), banktypes.Metadata{
+ DenomUnits: []*banktypes.DenomUnit{{
+ Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ Exponent: 0,
+ }},
+ Base: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ })
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.NewParams(sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))))
+ },
+ check: func(ctx sdk.Context) {
+ suite.Require().Equal(
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"},
+ suite.k.GetAuthorityMetadata(ctx, "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"),
+ )
+ suite.Require().Equal(
+ []string{"factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"},
+ suite.k.GetDenomsFromCreator(ctx, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ )
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ expDenom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ },
+ {
+ name: "create denom properly - no creation fees",
+ setup: func() {
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, false).Times(2)
+
+ suite.bk.EXPECT().
+ SetDenomMetaData(gomock.Any(), banktypes.Metadata{
+ DenomUnits: []*banktypes.DenomUnit{{
+ Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ Exponent: 0,
+ }},
+ Base: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ })
+ },
+ check: func(ctx sdk.Context) {
+ suite.Require().Equal(
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"},
+ suite.k.GetAuthorityMetadata(ctx, "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"),
+ )
+ suite.Require().Equal(
+ []string{"factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"},
+ suite.k.GetDenomsFromCreator(ctx, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ )
+
+ },
+ subdenom: "uminttoken",
+ creator: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ expDenom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ denom, err := suite.k.CreateDenom(ctx, tc.creator, tc.subdenom)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expDenom, denom)
+ if tc.check != nil {
+ tc.check(ctx)
+ }
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/keeper/creators.go b/x/tokenfactory/keeper/creators.go
new file mode 100644
index 0000000000..ae78a52199
--- /dev/null
+++ b/x/tokenfactory/keeper/creators.go
@@ -0,0 +1,27 @@
+package keeper
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+func (k Keeper) AddDenomFromCreator(ctx sdk.Context, creator, denom string) {
+ store := k.GetCreatorPrefixStore(ctx, creator)
+ store.Set([]byte(denom), []byte(denom))
+}
+
+func (k Keeper) GetDenomsFromCreator(ctx sdk.Context, creator string) []string {
+ store := k.GetCreatorPrefixStore(ctx, creator)
+
+ iterator := store.Iterator(nil, nil)
+ defer iterator.Close()
+
+ denoms := []string{}
+ for ; iterator.Valid(); iterator.Next() {
+ denoms = append(denoms, string(iterator.Key()))
+ }
+ return denoms
+}
+
+func (k Keeper) GetAllDenomsIterator(ctx sdk.Context) sdk.Iterator {
+ return k.GetCreatorsPrefixStore(ctx).Iterator(nil, nil)
+}
diff --git a/x/tokenfactory/keeper/creators_test.go b/x/tokenfactory/keeper/creators_test.go
new file mode 100644
index 0000000000..7689435d3f
--- /dev/null
+++ b/x/tokenfactory/keeper/creators_test.go
@@ -0,0 +1,73 @@
+package keeper_test
+
+import sdk "github.com/cosmos/cosmos-sdk/types"
+
+func (suite *KeeperTestSuite) TestKeeper_AddDenomFromCreator() {
+ testCases := []struct {
+ name string
+ creator string
+ denom string
+ check func(ctx sdk.Context)
+ }{
+ {
+ name: "add denom creator properly",
+ creator: "creator",
+ denom: "denom",
+ check: func(ctx sdk.Context) {
+ store := suite.k.GetCreatorPrefixStore(ctx, "creator")
+ suite.Require().Equal(
+ []byte("denom"),
+ store.Get([]byte("denom")),
+ )
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+
+ suite.k.AddDenomFromCreator(ctx, tc.creator, tc.denom)
+
+ if tc.check != nil {
+ tc.check(ctx)
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestKeeper_GetDenomsFromCreator() {
+ testCases := []struct {
+ name string
+ store func(ctx sdk.Context)
+ creator string
+ denom string
+ expResult []string
+ }{
+ {
+ name: "get denoms from creator properly",
+ store: func(ctx sdk.Context) {
+ suite.k.AddDenomFromCreator(ctx, "creator", "bitcoin")
+ suite.k.AddDenomFromCreator(ctx, "creator", "litecoin")
+ },
+ creator: "creator",
+ expResult: []string{"bitcoin", "litecoin"},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ suite.Require().Equal(
+ tc.expResult,
+ suite.k.GetDenomsFromCreator(ctx, tc.creator),
+ )
+ })
+ }
+}
diff --git a/x/tokenfactory/keeper/genesis.go b/x/tokenfactory/keeper/genesis.go
new file mode 100644
index 0000000000..9325cfdc28
--- /dev/null
+++ b/x/tokenfactory/keeper/genesis.go
@@ -0,0 +1,52 @@
+package keeper
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// InitGenesis initializes the tokenfactory module's state from a provided genesis
+// state.
+func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) {
+ k.createModuleAccount(ctx)
+
+ k.SetParams(ctx, genState.Params)
+
+ for _, genDenom := range genState.GetFactoryDenoms() {
+ creator, _, err := types.DeconstructDenom(genDenom.GetDenom())
+ if err != nil {
+ panic(err)
+ }
+ err = k.createDenomAfterValidation(ctx, creator, genDenom.GetDenom())
+ if err != nil {
+ panic(err)
+ }
+ err = k.SetAuthorityMetadata(ctx, genDenom.GetDenom(), genDenom.GetAuthorityMetadata())
+ if err != nil {
+ panic(err)
+ }
+ }
+}
+
+// ExportGenesis returns the tokenfactory module's exported genesis.
+func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
+ genDenoms := []types.GenesisDenom{}
+ iterator := k.GetAllDenomsIterator(ctx)
+ defer iterator.Close()
+
+ for ; iterator.Valid(); iterator.Next() {
+ denom := string(iterator.Value())
+
+ authorityMetadata := k.GetAuthorityMetadata(ctx, denom)
+ genDenoms = append(genDenoms, types.GenesisDenom{
+ Denom: denom,
+ AuthorityMetadata: authorityMetadata,
+ })
+ }
+
+ return &types.GenesisState{
+ FactoryDenoms: genDenoms,
+ Params: k.GetParams(ctx),
+ }
+}
diff --git a/x/tokenfactory/keeper/genesis_test.go b/x/tokenfactory/keeper/genesis_test.go
new file mode 100644
index 0000000000..9113b704ba
--- /dev/null
+++ b/x/tokenfactory/keeper/genesis_test.go
@@ -0,0 +1,137 @@
+package keeper_test
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+ "github.com/golang/mock/gomock"
+)
+
+func (suite *KeeperTestSuite) TestKeeper_ExportGenesis() {
+ testCases := []struct {
+ name string
+ store func(ctx sdk.Context)
+ expGenesis *types.GenesisState
+ }{
+ {
+ name: "genesis denoms are exported properly",
+ store: func(ctx sdk.Context) {
+ suite.k.AddDenomFromCreator(ctx,
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin")
+
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+
+ suite.k.AddDenomFromCreator(ctx, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin")
+
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ expGenesis: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin", AuthorityMetadata: types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"}},
+ {Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin", AuthorityMetadata: types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"}},
+ },
+ },
+ },
+ {
+ name: "params are exported properly",
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.DefaultParams())
+ },
+ expGenesis: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{},
+ Params: types.DefaultParams(),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ genesis := suite.k.ExportGenesis(ctx)
+ suite.Require().Equal(tc.expGenesis, genesis)
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestKeeper_InitGenesis() {
+ testCases := []struct {
+ name string
+ setup func()
+ data types.GenesisState
+ check func(ctx sdk.Context)
+ }{
+ {
+ name: "denoms are imported properly",
+ setup: func() {
+ suite.ak.EXPECT().GetModuleAccount(gomock.Any(), types.ModuleName)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin").
+ Return(banktypes.Metadata{}, true)
+ },
+ data: types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin", AuthorityMetadata: types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"}},
+ {Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin", AuthorityMetadata: types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"}},
+ },
+ },
+ check: func(ctx sdk.Context) {
+ suite.Require().Equal(
+ []string{"factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin", "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin"},
+ suite.k.GetDenomsFromCreator(ctx, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ )
+
+ suite.Require().Equal(
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"},
+ suite.k.GetAuthorityMetadata(ctx, "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/bitcoin"),
+ )
+
+ suite.Require().Equal(
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"},
+ suite.k.GetAuthorityMetadata(ctx, "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/litecoin"),
+ )
+ },
+ },
+ {
+ name: "params are imported properly",
+ setup: func() {
+ suite.ak.EXPECT().GetModuleAccount(gomock.Any(), types.ModuleName)
+ },
+ data: types.GenesisState{
+ Params: types.DefaultParams(),
+ },
+ check: func(ctx sdk.Context) {
+ suite.Require().Equal(types.DefaultParams(), suite.k.GetParams(ctx))
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+
+ suite.k.InitGenesis(ctx, tc.data)
+ if tc.check != nil {
+ tc.check(ctx)
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/keeper/gprc_query.go b/x/tokenfactory/keeper/gprc_query.go
new file mode 100644
index 0000000000..ad69a1d2b8
--- /dev/null
+++ b/x/tokenfactory/keeper/gprc_query.go
@@ -0,0 +1,30 @@
+package keeper
+
+import (
+ "context"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+var _ types.QueryServer = Keeper{}
+
+// Params implements the Query/Params gRPC method
+func (k Keeper) Params(ctx context.Context, request *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
+ sdkCtx := sdk.UnwrapSDKContext(ctx)
+ params := k.GetParams(sdkCtx)
+
+ return &types.QueryParamsResponse{Params: params}, nil
+}
+
+// SubspaceDenoms implements the Query/SubspaceDenoms gRPC method
+func (k Keeper) SubspaceDenoms(ctx context.Context, request *types.QuerySubspaceDenomsRequest) (*types.QuerySubspaceDenomsResponse, error) {
+ sdkCtx := sdk.UnwrapSDKContext(ctx)
+
+ treasury := subspacestypes.GetTreasuryAddress(request.SubspaceId)
+ denoms := k.GetDenomsFromCreator(sdkCtx, treasury.String())
+
+ return &types.QuerySubspaceDenomsResponse{Denoms: denoms}, nil
+}
diff --git a/x/tokenfactory/keeper/grpc_query_test.go b/x/tokenfactory/keeper/grpc_query_test.go
new file mode 100644
index 0000000000..3b659e4c22
--- /dev/null
+++ b/x/tokenfactory/keeper/grpc_query_test.go
@@ -0,0 +1,85 @@
+package keeper_test
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func (suite *KeeperTestSuite) TestQueryServer_Params() {
+ testCases := []struct {
+ name string
+ store func(ctx sdk.Context)
+ request *types.QueryParamsRequest
+ shouldErr bool
+ expParams types.Params
+ }{
+ {
+ name: "params are returned properly",
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.DefaultParams())
+ },
+ request: types.NewQueryParamsRequest(),
+ shouldErr: false,
+ expParams: types.DefaultParams(),
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ res, err := suite.k.Params(sdk.WrapSDKContext(ctx), tc.request)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expParams, res.Params)
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestQueryServer_SubspaceDenoms() {
+ testCases := []struct {
+ name string
+ store func(ctx sdk.Context)
+ request *types.QuerySubspaceDenomsRequest
+ shouldErr bool
+ expDenoms []string
+ }{
+ {
+ name: "denoms are returned properly",
+ store: func(ctx sdk.Context) {
+ suite.k.AddDenomFromCreator(ctx, subspacestypes.GetTreasuryAddress(1).String(), "bitcoin")
+ suite.k.AddDenomFromCreator(ctx, subspacestypes.GetTreasuryAddress(1).String(), "minttoken")
+ },
+ request: types.NewQuerySubspaceDenomsRequest(1),
+ shouldErr: false,
+ expDenoms: []string{"bitcoin", "minttoken"},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ res, err := suite.k.SubspaceDenoms(sdk.WrapSDKContext(ctx), tc.request)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expDenoms, res.Denoms)
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/keeper/keeper.go b/x/tokenfactory/keeper/keeper.go
new file mode 100644
index 0000000000..2c6baa4633
--- /dev/null
+++ b/x/tokenfactory/keeper/keeper.go
@@ -0,0 +1,76 @@
+package keeper
+
+import (
+ "github.com/cometbft/cometbft/libs/log"
+ "github.com/cosmos/cosmos-sdk/codec"
+ "github.com/cosmos/cosmos-sdk/store/prefix"
+ storetypes "github.com/cosmos/cosmos-sdk/store/types"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+type Keeper struct {
+ storeKey storetypes.StoreKey
+ cdc codec.BinaryCodec
+
+ sk types.SubspacesKeeper
+ ak types.AccountKeeper
+ bk types.BankKeeper
+
+ // the address capable of executing a MsgUpdateParams message. Typically, this
+ // should be the x/gov module account.
+ authority string
+}
+
+// NewKeeper returns a new instance of the x/tokenfactory keeper
+func NewKeeper(
+ storeKey storetypes.StoreKey,
+ cdc codec.BinaryCodec,
+ sk types.SubspacesKeeper,
+ ak types.AccountKeeper,
+ bk types.BankKeeper,
+ authority string,
+) Keeper {
+ return Keeper{
+ storeKey: storeKey,
+ cdc: cdc,
+
+ sk: sk,
+ ak: ak,
+ bk: bk,
+
+ authority: authority,
+ }
+}
+
+// Logger returns a logger for the x/tokenfactory module
+func (k Keeper) Logger(ctx sdk.Context) log.Logger {
+ return ctx.Logger().With("module", "x/"+types.ModuleName)
+}
+
+// createModuleAccount creates a module account with minting and burning capabilities
+// This account isn't intended to store any coins,
+// it purely mints and burns them on behalf of the admin of respective denoms,
+// and sends to the relevant address.
+func (k Keeper) createModuleAccount(ctx sdk.Context) {
+ k.ak.GetModuleAccount(ctx, types.ModuleName)
+}
+
+// GetDenomPrefixStore returns the substore for a specific denom
+func (k Keeper) GetDenomPrefixStore(ctx sdk.Context, denom string) sdk.KVStore {
+ store := ctx.KVStore(k.storeKey)
+ return prefix.NewStore(store, types.GetDenomPrefixStore(denom))
+}
+
+// GetCreatorPrefixStore returns the substore for a specific creator address
+func (k Keeper) GetCreatorPrefixStore(ctx sdk.Context, creator string) sdk.KVStore {
+ store := ctx.KVStore(k.storeKey)
+ return prefix.NewStore(store, types.GetCreatorPrefix(creator))
+}
+
+// GetCreatorsPrefixStore returns the substore that contains a list of creators
+func (k Keeper) GetCreatorsPrefixStore(ctx sdk.Context) sdk.KVStore {
+ store := ctx.KVStore(k.storeKey)
+ return prefix.NewStore(store, types.GetCreatorsPrefix())
+}
diff --git a/x/tokenfactory/keeper/msg_server.go b/x/tokenfactory/keeper/msg_server.go
new file mode 100644
index 0000000000..4eddbea576
--- /dev/null
+++ b/x/tokenfactory/keeper/msg_server.go
@@ -0,0 +1,195 @@
+package keeper
+
+import (
+ "context"
+ "fmt"
+
+ "cosmossdk.io/errors"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+type msgServer struct {
+ Keeper
+}
+
+// NewMsgServerImpl returns an implementation of the stored MsgServer interface for the provided keeper
+func NewMsgServerImpl(keeper Keeper) types.MsgServer {
+ return &msgServer{Keeper: keeper}
+}
+
+// CreateDenom defines a rpc method for MsgCreateDenom
+func (k msgServer) CreateDenom(goCtx context.Context, msg *types.MsgCreateDenom) (*types.MsgCreateDenomResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ // Check if the subspace exists
+ subspace, exists := k.sk.GetSubspace(ctx, msg.SubspaceID)
+ if !exists {
+ return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID)
+ }
+
+ // Check the permission to manage the subspace tokens
+ if !k.sk.HasPermission(ctx, msg.SubspaceID, subspacestypes.RootSectionID, msg.Sender, types.PermissionManageSubspaceTokens) {
+ return nil, errors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot manage the subspace tokens inside this subspace")
+ }
+
+ denom, err := k.Keeper.CreateDenom(ctx, subspace.Treasury, msg.Subdenom)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)),
+ sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+ ),
+ sdk.NewEvent(
+ types.EventTypeCreateDenom,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)),
+ sdk.NewAttribute(types.AttributeCreator, msg.Sender),
+ sdk.NewAttribute(types.AttributeNewTokenDenom, denom),
+ ),
+ })
+
+ return &types.MsgCreateDenomResponse{
+ NewTokenDenom: denom,
+ }, nil
+}
+
+// Mint defines a rpc method for MsgMint
+func (k msgServer) Mint(goCtx context.Context, msg *types.MsgMint) (*types.MsgMintResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ // Check if the subspace exists
+ subspace, exists := k.sk.GetSubspace(ctx, msg.SubspaceID)
+ if !exists {
+ return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID)
+ }
+
+ err := k.ValidateManageTokenPermission(ctx, subspace, msg.Sender, msg.Amount.Denom)
+ if err != nil {
+ return nil, err
+ }
+
+ err = k.mintTo(ctx, msg.Amount, subspace.Treasury)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)),
+ sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+ ),
+ sdk.NewEvent(
+ types.EventTypeMint,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)),
+ sdk.NewAttribute(types.AttributeMintToAddress, subspace.Treasury),
+ sdk.NewAttribute(types.AttributeAmount, msg.Amount.String()),
+ ),
+ })
+
+ return &types.MsgMintResponse{}, nil
+}
+
+// Burn defines a rpc method for MsgBurn
+func (k msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.MsgBurnResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ // Check if the subspace exists
+ subspace, exists := k.sk.GetSubspace(ctx, msg.SubspaceID)
+ if !exists {
+ return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID)
+ }
+
+ err := k.ValidateManageTokenPermission(ctx, subspace, msg.Sender, msg.Amount.Denom)
+ if err != nil {
+ return nil, err
+ }
+
+ err = k.burnFrom(ctx, msg.Amount, subspace.Treasury)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)),
+ sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+ ),
+ sdk.NewEvent(
+ types.EventTypeBurn,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)),
+ sdk.NewAttribute(types.AttributeBurnFromAddress, subspace.Treasury),
+ sdk.NewAttribute(types.AttributeAmount, msg.Amount.String()),
+ ),
+ })
+
+ return &types.MsgBurnResponse{}, nil
+}
+
+// SetDenomMetadata defines a rpc method for MsgSetDenomMetadata
+func (k msgServer) SetDenomMetadata(goCtx context.Context, msg *types.MsgSetDenomMetadata) (*types.MsgSetDenomMetadataResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ // Check if the subspace exists
+ subspace, exists := k.sk.GetSubspace(ctx, msg.SubspaceID)
+ if !exists {
+ return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID)
+ }
+
+ err := k.ValidateManageTokenPermission(ctx, subspace, msg.Sender, msg.Metadata.Base)
+ if err != nil {
+ return nil, err
+ }
+
+ k.bk.SetDenomMetaData(ctx, msg.Metadata)
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)),
+ sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+ ),
+ sdk.NewEvent(
+ types.ActionSetDenomMetadata,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)),
+ sdk.NewAttribute(types.AttributeDenom, msg.Metadata.Base),
+ sdk.NewAttribute(types.AttributeDenomMetadata, msg.Metadata.String()),
+ ),
+ })
+
+ return &types.MsgSetDenomMetadataResponse{}, nil
+}
+
+// UpdateParams defines a rpc method for MsgUpdateParams
+func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
+ if k.authority != msg.Authority {
+ return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.authority, msg.Authority)
+ }
+
+ ctx := sdk.UnwrapSDKContext(goCtx)
+ k.SetParams(ctx, msg.Params)
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)),
+ sdk.NewAttribute(sdk.AttributeKeySender, msg.Authority),
+ ),
+ })
+
+ return &types.MsgUpdateParamsResponse{}, nil
+}
diff --git a/x/tokenfactory/keeper/msg_server_test.go b/x/tokenfactory/keeper/msg_server_test.go
new file mode 100644
index 0000000000..54cdf7971f
--- /dev/null
+++ b/x/tokenfactory/keeper/msg_server_test.go
@@ -0,0 +1,966 @@
+package keeper_test
+
+import (
+ "fmt"
+ "time"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/keeper"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+ "github.com/golang/mock/gomock"
+)
+
+func (suite *KeeperTestSuite) TestMsgServer_CreateDenom() {
+ testCases := []struct {
+ name string
+ setup func()
+ msg *types.MsgCreateDenom
+ shouldErr bool
+ expResponse *types.MsgCreateDenomResponse
+ expEvents sdk.Events
+ check func(ctx sdk.Context)
+ }{
+ {
+ name: "subspace does not exist returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.Subspace{}, false)
+ },
+ msg: types.NewMsgCreateDenom(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ "uminttoken",
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "no permissions returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(false)
+ },
+ msg: types.NewMsgCreateDenom(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ "uminttoken",
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "create denom failed returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(true)
+ },
+ msg: types.NewMsgCreateDenom(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ "uminttoken",
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ HasSupply(gomock.Any(), "uminttoken").
+ Return(false)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, false).
+ Times(2)
+
+ suite.bk.EXPECT().
+ SetDenomMetaData(gomock.Any(), banktypes.Metadata{
+ DenomUnits: []*banktypes.DenomUnit{{
+ Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ Exponent: 0,
+ }},
+ Base: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ })
+ },
+ msg: types.NewMsgCreateDenom(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ "uminttoken",
+ ),
+ expResponse: &types.MsgCreateDenomResponse{
+ NewTokenDenom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ },
+ expEvents: sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgCreateDenom{})),
+ sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69"),
+ ),
+ sdk.NewEvent(
+ types.EventTypeCreateDenom,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, "1"),
+ sdk.NewAttribute(types.AttributeCreator, "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69"),
+ sdk.NewAttribute(types.AttributeNewTokenDenom, "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"),
+ ),
+ },
+ check: func(ctx sdk.Context) {
+ suite.Require().Equal(
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"},
+ suite.k.GetAuthorityMetadata(ctx, "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"),
+ )
+ suite.Require().Equal(
+ []string{"factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"},
+ suite.k.GetDenomsFromCreator(ctx, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ )
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+
+ msgServer := keeper.NewMsgServerImpl(suite.k)
+ res, err := msgServer.CreateDenom(sdk.WrapSDKContext(ctx), tc.msg)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expResponse, res)
+ suite.Require().Equal(tc.expEvents, ctx.EventManager().Events())
+ if tc.check != nil {
+ tc.check(ctx)
+ }
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestMsgServer_Mint() {
+ testCases := []struct {
+ name string
+ setup func()
+ store func(ctx sdk.Context)
+ msg *types.MsgMint
+ shouldErr bool
+ expResponse *types.MsgMintResponse
+ expEvents sdk.Events
+ }{
+ {
+ name: "subspace does not exist returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.Subspace{}, false)
+ },
+ msg: types.NewMsgMint(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "no permissions returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(false)
+ },
+ msg: types.NewMsgMint(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "mint failed returns error - bank mint coins failed",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ MintCoins(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(fmt.Errorf("error"))
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgMint(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "mint failed returns error - module send coins to treasury failed",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ MintCoins(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ SendCoinsFromModuleToAccount(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(fmt.Errorf("error"))
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgMint(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ MintCoins(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ SendCoinsFromModuleToAccount(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(nil)
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgMint(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ expResponse: &types.MsgMintResponse{},
+ expEvents: sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgMint{})),
+ sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69"),
+ ),
+ sdk.NewEvent(
+ types.EventTypeMint,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, "1"),
+ sdk.NewAttribute(types.AttributeMintToAddress, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ sdk.NewAttribute(types.AttributeAmount, sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)).String()),
+ ),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ msgServer := keeper.NewMsgServerImpl(suite.k)
+ res, err := msgServer.Mint(sdk.WrapSDKContext(ctx), tc.msg)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expResponse, res)
+ suite.Require().Equal(tc.expEvents, ctx.EventManager().Events())
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestMsgServer_Burn() {
+ testCases := []struct {
+ name string
+ setup func()
+ store func(ctx sdk.Context)
+ msg *types.MsgBurn
+ shouldErr bool
+ expResponse *types.MsgBurnResponse
+ expEvents sdk.Events
+ }{
+ {
+ name: "subspace does not exist returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.Subspace{}, false)
+ },
+ msg: types.NewMsgBurn(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "no permissions returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(false)
+ },
+ msg: types.NewMsgBurn(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "burn failed returns error - send coins to module failed",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ SendCoinsFromAccountToModule(
+ gomock.Any(),
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(fmt.Errorf("error"))
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgBurn(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "burn failed returns error - bank burn failed",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ SendCoinsFromAccountToModule(
+ gomock.Any(),
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ BurnCoins(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(fmt.Errorf("error"))
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgBurn(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ SendCoinsFromAccountToModule(
+ gomock.Any(),
+ sdk.MustAccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(nil)
+
+ suite.bk.EXPECT().
+ BurnCoins(
+ gomock.Any(),
+ types.ModuleName,
+ sdk.NewCoins(sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100))),
+ ).
+ Return(nil)
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgBurn(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)),
+ ),
+ expResponse: &types.MsgBurnResponse{},
+ expEvents: sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgBurn{})),
+ sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69"),
+ ),
+ sdk.NewEvent(
+ types.EventTypeBurn,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, "1"),
+ sdk.NewAttribute(types.AttributeBurnFromAddress, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
+ sdk.NewAttribute(types.AttributeAmount, sdk.NewCoin("factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", sdk.NewInt(100)).String()),
+ ),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ msgServer := keeper.NewMsgServerImpl(suite.k)
+ res, err := msgServer.Burn(sdk.WrapSDKContext(ctx), tc.msg)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expResponse, res)
+ suite.Require().Equal(tc.expEvents, ctx.EventManager().Events())
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestMsgServer_SetDenomMetadata() {
+ metadata := banktypes.Metadata{
+ Name: "Mint Token",
+ Symbol: "MTK",
+ Description: "The custom token of the test subspace.",
+ DenomUnits: []*banktypes.DenomUnit{
+ {Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", Exponent: uint32(0), Aliases: nil},
+ {Denom: "minttoken", Exponent: uint32(6), Aliases: []string{"minttoken"}},
+ },
+ Base: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ Display: "minttoken",
+ }
+
+ testCases := []struct {
+ name string
+ setup func()
+ store func(ctx sdk.Context)
+ msg *types.MsgSetDenomMetadata
+ shouldErr bool
+ expResponse *types.MsgSetDenomMetadataResponse
+ expEvents sdk.Events
+ }{
+ {
+ name: "subspace does not exist returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.Subspace{}, false)
+ },
+ msg: types.NewMsgSetDenomMetadata(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ metadata,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "no permissions returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(false)
+ },
+ msg: types.NewMsgSetDenomMetadata(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ metadata,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ setup: func() {
+ suite.sk.EXPECT().
+ GetSubspace(gomock.Any(), uint64(1)).
+ Return(subspacestypes.NewSubspace(
+ 1,
+ "Test subspace",
+ "This is a test subspace",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5",
+ "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC),
+ nil,
+ ), true)
+
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+
+ suite.bk.EXPECT().
+ SetDenomMetaData(gomock.Any(), metadata)
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ msg: types.NewMsgSetDenomMetadata(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ metadata,
+ ),
+ expResponse: &types.MsgSetDenomMetadataResponse{},
+ expEvents: sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgSetDenomMetadata{})),
+ sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69"),
+ ),
+ sdk.NewEvent(
+ types.EventTypeSetDenomMetadata,
+ sdk.NewAttribute(types.AttributeKeySubspaceID, "1"),
+ sdk.NewAttribute(types.AttributeDenom, metadata.Base),
+ sdk.NewAttribute(types.AttributeDenomMetadata, metadata.String()),
+ ),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ msgServer := keeper.NewMsgServerImpl(suite.k)
+ res, err := msgServer.SetDenomMetadata(sdk.WrapSDKContext(ctx), tc.msg)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expResponse, res)
+ suite.Require().Equal(tc.expEvents, ctx.EventManager().Events())
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestMsgServer_UpdateParams() {
+ testCases := []struct {
+ name string
+ setup func()
+ msg *types.MsgUpdateParams
+ shouldErr bool
+ check func(ctx sdk.Context)
+ expEvents sdk.Events
+ }{
+ {
+ name: "invalid authority return error",
+ msg: types.NewMsgUpdateParams(
+ types.DefaultParams(),
+ "invalid",
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "set params properly",
+ msg: types.NewMsgUpdateParams(
+ types.NewParams(sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))),
+ authtypes.NewModuleAddress("gov").String(),
+ ),
+ shouldErr: false,
+ check: func(ctx sdk.Context) {
+ suite.Require().Equal(
+ types.NewParams(sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(100)))),
+ suite.k.GetParams(ctx),
+ )
+ },
+ expEvents: sdk.Events{
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
+ sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgUpdateParams{})),
+ sdk.NewAttribute(sdk.AttributeKeySender, "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn"),
+ ),
+ },
+ },
+ }
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+
+ // Reset any event that might have been emitted during the setup
+ ctx = ctx.WithEventManager(sdk.NewEventManager())
+
+ // Run the message
+ service := keeper.NewMsgServerImpl(suite.k)
+ _, err := service.UpdateParams(sdk.WrapSDKContext(ctx), tc.msg)
+
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ suite.Require().Equal(tc.expEvents, ctx.EventManager().Events())
+ if tc.check != nil {
+ tc.check(ctx)
+ }
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/keeper/params.go b/x/tokenfactory/keeper/params.go
new file mode 100644
index 0000000000..cac1080f7e
--- /dev/null
+++ b/x/tokenfactory/keeper/params.go
@@ -0,0 +1,25 @@
+package keeper
+
+import (
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+// SetParams sets the params on the store
+func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
+ store := ctx.KVStore(k.storeKey)
+ bz := k.cdc.MustMarshal(¶ms)
+ store.Set([]byte(types.ParamsPrefixKey), bz)
+}
+
+// GetParams returns the params from the store
+func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) {
+ store := ctx.KVStore(k.storeKey)
+ bz := store.Get([]byte(types.ParamsPrefixKey))
+ if bz == nil {
+ return p
+ }
+ k.cdc.MustUnmarshal(bz, &p)
+ return p
+}
diff --git a/x/tokenfactory/keeper/params_test.go b/x/tokenfactory/keeper/params_test.go
new file mode 100644
index 0000000000..036b829187
--- /dev/null
+++ b/x/tokenfactory/keeper/params_test.go
@@ -0,0 +1,80 @@
+package keeper_test
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func (suite *KeeperTestSuite) TestKeeper_SetParams() {
+ testCases := []struct {
+ name string
+ store func(ctx sdk.Context)
+ params types.Params
+ check func(ctx sdk.Context)
+ }{
+ {
+ name: "default params are saved correctly",
+ params: types.DefaultParams(),
+ check: func(ctx sdk.Context) {
+ stored := suite.k.GetParams(ctx)
+ suite.Require().Equal(stored, types.DefaultParams())
+ },
+ },
+ {
+ name: "params are overridden properly",
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.DefaultParams())
+ },
+ params: types.NewParams(sdk.NewCoins(sdk.NewCoin("udsm", sdk.NewInt(100)))),
+ check: func(ctx sdk.Context) {
+ stored := suite.k.GetParams(ctx)
+ suite.Require().Equal(types.NewParams(sdk.NewCoins(sdk.NewCoin("udsm", sdk.NewInt(100)))), stored)
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ suite.k.SetParams(ctx, tc.params)
+ if tc.check != nil {
+ tc.check(ctx)
+ }
+ })
+ }
+}
+
+func (suite *KeeperTestSuite) TestKeeper_GetParams() {
+ testCases := []struct {
+ name string
+ store func(ctx sdk.Context)
+ expParams types.Params
+ }{
+ {
+ name: "params are returned properly",
+ store: func(ctx sdk.Context) {
+ suite.k.SetParams(ctx, types.NewParams(sdk.NewCoins(sdk.NewCoin("udsm", sdk.NewInt(100)))))
+ },
+ expParams: types.NewParams(sdk.NewCoins(sdk.NewCoin("udsm", sdk.NewInt(100)))),
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ params := suite.k.GetParams(ctx)
+ suite.Require().Equal(tc.expParams, params)
+ })
+ }
+}
diff --git a/x/tokenfactory/keeper/permissions.go b/x/tokenfactory/keeper/permissions.go
new file mode 100644
index 0000000000..b781d3735a
--- /dev/null
+++ b/x/tokenfactory/keeper/permissions.go
@@ -0,0 +1,33 @@
+package keeper
+
+import (
+ "cosmossdk.io/errors"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// ValidateManageTokenPermission validates the sender has the manage denom permission to the subspace tokens inside the given subspace
+func (k Keeper) ValidateManageTokenPermission(ctx sdk.Context, subspace subspacestypes.Subspace, sender string, denom string) error {
+
+ // Check the permission to manage the subspace tokens
+ if !k.sk.HasPermission(ctx, subspace.ID, subspacestypes.RootSectionID, sender, types.PermissionManageSubspaceTokens) {
+ return errors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot manage the subspace tokens inside this subspace")
+ }
+
+ // Check if the denom exists
+ _, denomExists := k.bk.GetDenomMetaData(ctx, denom)
+ if !denomExists {
+ return types.ErrDenomDoesNotExist.Wrapf("denom: %s", denom)
+ }
+
+ authorityMetadata := k.GetAuthorityMetadata(ctx, denom)
+
+ // Check if the subspace treasury is the admin of the denom
+ if subspace.Treasury != authorityMetadata.GetAdmin() {
+ return types.ErrUnauthorized
+ }
+
+ return nil
+}
diff --git a/x/tokenfactory/keeper/permissions_test.go b/x/tokenfactory/keeper/permissions_test.go
new file mode 100644
index 0000000000..045a5c2c7f
--- /dev/null
+++ b/x/tokenfactory/keeper/permissions_test.go
@@ -0,0 +1,175 @@
+package keeper_test
+
+import (
+ "github.com/golang/mock/gomock"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func (suite *KeeperTestSuite) TestKeeper_ValidateSubspaceTokenPermission() {
+
+ testCases := []struct {
+ name string
+ setup func()
+ store func(ctx sdk.Context)
+ subspace subspacestypes.Subspace
+ sender string
+ denom string
+ shouldErr bool
+ }{
+ {
+ name: "no permissions returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(false)
+ },
+ subspace: subspacestypes.Subspace{
+ ID: 1,
+ Treasury: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ },
+ sender: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ shouldErr: true,
+ },
+ {
+ name: "denom does not exist returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, false)
+ },
+ subspace: subspacestypes.Subspace{
+ ID: 1,
+ Treasury: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ },
+ sender: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ shouldErr: true,
+ },
+ {
+ name: "denom admin does not match subspace treasury returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+ },
+ subspace: subspacestypes.Subspace{
+ ID: 1,
+ Treasury: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ },
+ sender: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ shouldErr: true,
+ },
+ {
+ name: "subspace treasury does not match admin returns error",
+ setup: func() {
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: ""})
+ },
+ subspace: subspacestypes.Subspace{
+ ID: 1,
+ Treasury: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ },
+ sender: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ shouldErr: true,
+ },
+ {
+ name: "valid request returns no error",
+ setup: func() {
+ suite.sk.EXPECT().
+ HasPermission(
+ gomock.Any(),
+ uint64(1),
+ uint32(subspacestypes.RootSectionID),
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ types.PermissionManageSubspaceTokens,
+ ).
+ Return(true)
+
+ suite.bk.EXPECT().
+ GetDenomMetaData(gomock.Any(), "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken").
+ Return(banktypes.Metadata{}, true)
+ },
+ store: func(ctx sdk.Context) {
+ suite.k.SetAuthorityMetadata(ctx,
+ "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ types.DenomAuthorityMetadata{Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"})
+ },
+ subspace: subspacestypes.Subspace{
+ ID: 1,
+ Treasury: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ },
+ sender: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ suite.Run(tc.name, func() {
+ ctx, _ := suite.ctx.CacheContext()
+ if tc.setup != nil {
+ tc.setup()
+ }
+ if tc.store != nil {
+ tc.store(ctx)
+ }
+
+ err := suite.k.ValidateManageTokenPermission(ctx, tc.subspace, tc.sender, tc.denom)
+ if tc.shouldErr {
+ suite.Require().Error(err)
+ } else {
+ suite.Require().NoError(err)
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/module.go b/x/tokenfactory/module.go
new file mode 100644
index 0000000000..46d5d4a57e
--- /dev/null
+++ b/x/tokenfactory/module.go
@@ -0,0 +1,272 @@
+package tokenfactory
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "cosmossdk.io/core/appmodule"
+ "cosmossdk.io/depinject"
+
+ abci "github.com/cometbft/cometbft/abci/types"
+ "github.com/cosmos/cosmos-sdk/client"
+ "github.com/cosmos/cosmos-sdk/codec"
+ codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+ storetypes "github.com/cosmos/cosmos-sdk/store/types"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/types/module"
+ simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+ authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+ distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
+ govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+ paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "github.com/spf13/cobra"
+
+ modulev1 "github.com/desmos-labs/desmos/v5/api/desmos/tokenfactory/module/v1"
+
+ subspaceskeeper "github.com/desmos-labs/desmos/v5/x/subspaces/keeper"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/client/cli"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/keeper"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/simulation"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+const (
+ consensusVersion = 1
+)
+
+// type check to ensure the interface is properly implemented
+var (
+ _ module.AppModule = AppModule{}
+ _ module.AppModuleBasic = AppModuleBasic{}
+ _ module.AppModuleSimulation = AppModule{}
+ _ appmodule.AppModule = AppModule{}
+ _ depinject.OnePerModuleType = AppModule{}
+)
+
+// AppModuleBasic defines the basic application module used by the tokenfactory module.
+type AppModuleBasic struct {
+ cdc codec.Codec
+}
+
+// Name returns the tokenfactory module's name.
+func (AppModuleBasic) Name() string {
+ return types.ModuleName
+}
+
+// RegisterLegacyAminoCodec registers the tokenfactory module's types for the given codec.
+func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+ types.RegisterLegacyAminoCodec(cdc)
+}
+
+// DefaultGenesis returns default genesis state as raw bytes for the tokenfactory module.
+func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
+ return cdc.MustMarshalJSON(types.DefaultGenesis())
+}
+
+// ValidateGenesis performs genesis state validation for the tokenfactory module.
+func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
+ var data types.GenesisState
+ if err := cdc.UnmarshalJSON(bz, &data); err != nil {
+ return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
+ }
+ return data.Validate()
+}
+
+// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the tokenfactory module.
+func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
+ types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
+}
+
+// GetTxCmd returns the root tx command for the tokenfactory module.
+func (ab AppModuleBasic) GetTxCmd() *cobra.Command {
+ return cli.GetTxCmd()
+}
+
+// GetQueryCmd returns the root query command for the tokenfactory module.
+func (AppModuleBasic) GetQueryCmd() *cobra.Command {
+ return cli.GetQueryCmd()
+}
+
+// RegisterInterfaces registers interfaces and implementations of the tokenfactory module.
+func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
+ types.RegisterInterfaces(registry)
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// AppModule implements an application module for the tokenfactory module.
+type AppModule struct {
+ AppModuleBasic
+
+ keeper keeper.Keeper
+
+ sk types.SubspacesKeeper
+ ak authkeeper.AccountKeeper
+ bk bankkeeper.Keeper
+}
+
+// RegisterServices registers module services.
+func (am AppModule) RegisterServices(cfg module.Configurator) {
+ types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
+ types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
+}
+
+// NewAppModule creates a new AppModule Object
+func NewAppModule(
+ cdc codec.Codec, keeper keeper.Keeper, sk types.SubspacesKeeper,
+ ak authkeeper.AccountKeeper, bk bankkeeper.Keeper,
+) AppModule {
+ return AppModule{
+ AppModuleBasic: AppModuleBasic{cdc: cdc},
+ keeper: keeper,
+ sk: sk,
+ ak: ak,
+ bk: bk,
+ }
+}
+
+// Name returns the tokenfactory module's name.
+func (AppModule) Name() string {
+ return types.ModuleName
+}
+
+// RegisterInvariants performs a no-op.
+func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {}
+
+// QuerierRoute returns the tokenfactory module's querier route name.
+func (am AppModule) QuerierRoute() string {
+ return types.QuerierRoute
+}
+
+// InitGenesis performs genesis initialization for the tokenfactory module.
+// It returns no validator updates.
+func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate {
+ var genesisState types.GenesisState
+ cdc.MustUnmarshalJSON(data, &genesisState)
+ am.keeper.InitGenesis(ctx, genesisState)
+ return []abci.ValidatorUpdate{}
+}
+
+// ExportGenesis returns the exported genesis state as raw bytes for the
+// tokenfactory module.
+func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
+ gs := am.keeper.ExportGenesis(ctx)
+ return cdc.MustMarshalJSON(gs)
+}
+
+// ConsensusVersion implements AppModule.
+func (AppModule) ConsensusVersion() uint64 {
+ return consensusVersion
+}
+
+// BeginBlock returns the begin blocker for the tokenfactory module.
+func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {
+}
+
+// EndBlock returns the end blocker for the tokenfactory module. It returns no validator
+// updates.
+func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
+ return []abci.ValidatorUpdate{}
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// AppModuleSimulation defines the module simulation functions used by the tokenfactory module.
+type AppModuleSimulation struct{}
+
+// GenerateGenesisState creates a randomized GenState of the bank module.
+func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
+ simulation.RandomizeGenState(simState)
+}
+
+// RegisterStoreDecoder performs a no-op.
+func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
+ sdr[types.ModuleName] = simulation.NewDecodeStore(am.cdc)
+}
+
+// ProposalMsgs returns msgs used for governance proposals for simulations.
+func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
+ return simulation.ProposalMsgs()
+}
+
+// WeightedOperations returns the all the tokenfactory module operations with their respective weights.
+func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
+ return simulation.WeightedOperations(simState.AppParams, simState.Cdc, am.keeper, am.sk, am.ak, am.bk)
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// App Wiring Setup
+
+// IsOnePerModuleType implements the depinject.OnePerModuleType interface.
+func (am AppModule) IsOnePerModuleType() {}
+
+// IsAppModule implements the appmodule.AppModule interface.
+func (am AppModule) IsAppModule() {}
+
+func init() {
+ appmodule.Register(
+ &modulev1.Module{},
+ appmodule.Provide(
+ ProvideModule,
+ ),
+ )
+}
+
+type ModuleInputs struct {
+ depinject.In
+
+ Cdc codec.Codec
+ Config *modulev1.Module
+ Key *storetypes.KVStoreKey
+
+ SubspacesKeeper *subspaceskeeper.Keeper
+ AccountKeeper authkeeper.AccountKeeper
+ BankKeeper bankkeeper.Keeper
+ CommunityPoolKeeper distrkeeper.Keeper
+
+ LegacySubspace paramtypes.Subspace `optional:"true"`
+}
+
+type ModuleOutputs struct {
+ depinject.Out
+
+ TokenFactoryKeeper keeper.Keeper
+ Module appmodule.AppModule
+}
+
+func ProvideModule(in ModuleInputs) ModuleOutputs {
+
+ // default to governance authority if not provided
+ authority := authtypes.NewModuleAddress(govtypes.ModuleName)
+ if in.Config.Authority != "" {
+ authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
+ }
+
+ bk := in.BankKeeper.WithMintCoinsRestriction(types.NewTokenFactoryDenomMintCoinsRestriction())
+
+ k := keeper.NewKeeper(
+ in.Key,
+ in.Cdc,
+ in.SubspacesKeeper,
+ in.AccountKeeper,
+ bk,
+ authority.String(),
+ )
+
+ m := NewAppModule(
+ in.Cdc,
+ k,
+ in.SubspacesKeeper,
+ in.AccountKeeper,
+ bk,
+ )
+
+ return ModuleOutputs{TokenFactoryKeeper: k, Module: m}
+}
diff --git a/x/tokenfactory/simulation/decoder.go b/x/tokenfactory/simulation/decoder.go
new file mode 100644
index 0000000000..b6f3bcf629
--- /dev/null
+++ b/x/tokenfactory/simulation/decoder.go
@@ -0,0 +1,37 @@
+package simulation
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/cosmos/cosmos-sdk/codec"
+ "github.com/cosmos/cosmos-sdk/types/kv"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// NewDecodeStore returns a new decoder that unmarshals the KVPair's Value
+// to the corresponding tokenfactory type
+func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string {
+ return func(kvA, kvB kv.Pair) string {
+ switch {
+ case bytes.HasPrefix(kvA.Key, []byte(types.DenomsPrefixKey)):
+ var metadataA, metadataB types.DenomAuthorityMetadata
+ cdc.MustUnmarshal(kvA.Value, &metadataA)
+ cdc.MustUnmarshal(kvB.Value, &metadataB)
+ return fmt.Sprintf("Authority MetadataA: %s\nAuthority MetadataB: %s\n", metadataA, metadataB)
+
+ case bytes.HasPrefix(kvA.Key, []byte(types.CreatorPrefixKey)):
+ return fmt.Sprintf("DenomA: %s\nDenomB: %s\n", kvA.Value, kvB.Value)
+
+ case bytes.HasPrefix(kvA.Key, []byte(types.ParamsPrefixKey)):
+ var paramA, paramB types.Params
+ cdc.MustUnmarshal(kvA.Value, ¶mA)
+ cdc.MustUnmarshal(kvA.Value, ¶mB)
+ return fmt.Sprintf("ParamsA: %s\nParamsB: %s\n", paramA, paramB)
+
+ default:
+ panic(fmt.Sprintf("unexpected %s key %X (%s)", types.ModuleName, kvA.Key, kvA.Key))
+ }
+ }
+}
diff --git a/x/tokenfactory/simulation/decoder_test.go b/x/tokenfactory/simulation/decoder_test.go
new file mode 100644
index 0000000000..134087db5f
--- /dev/null
+++ b/x/tokenfactory/simulation/decoder_test.go
@@ -0,0 +1,69 @@
+package simulation_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/cosmos/cosmos-sdk/types/kv"
+ "github.com/desmos-labs/desmos/v5/app"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/simulation"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+ "github.com/stretchr/testify/require"
+)
+
+func TestDecodeStore(t *testing.T) {
+ cdc, _ := app.MakeCodecs()
+ decoder := simulation.NewDecodeStore(cdc)
+
+ metadata := types.DenomAuthorityMetadata{
+ Admin: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
+ }
+
+ denom := "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"
+
+ params := types.DefaultParams()
+
+ kvPairs := kv.Pairs{Pairs: []kv.Pair{
+ {
+ Key: append(types.GetDenomPrefixStore("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"), []byte(types.DenomAuthorityMetadataKey)...),
+ Value: cdc.MustMarshal(&metadata),
+ },
+ {
+ Key: append(types.GetCreatorPrefix("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"), []byte(denom)...),
+ Value: []byte(denom),
+ },
+ {
+ Key: []byte(types.ParamsPrefixKey),
+ Value: cdc.MustMarshal(¶ms),
+ },
+ {
+ Key: []byte("Unknown key"),
+ Value: nil,
+ },
+ }}
+
+ testCases := []struct {
+ name string
+ expectedLog string
+ }{
+ {"Authority Metadata", fmt.Sprintf("Authority MetadataA: %s\nAuthority MetadataB: %s\n",
+ metadata, metadata)},
+ {"Denom", fmt.Sprintf("DenomA: %s\nDenomB: %s\n",
+ denom, denom)},
+ {"Params", fmt.Sprintf("ParamsA: %s\nParamsB: %s\n",
+ params, params)},
+ {"other", ""},
+ }
+
+ for i, tc := range testCases {
+ i, tc := i, tc
+ t.Run(tc.name, func(t *testing.T) {
+ switch i {
+ case len(testCases) - 1:
+ require.Panics(t, func() { decoder(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tc.name)
+ default:
+ require.Equal(t, tc.expectedLog, decoder(kvPairs.Pairs[i], kvPairs.Pairs[i]), tc.name)
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/simulation/genesis.go b/x/tokenfactory/simulation/genesis.go
new file mode 100644
index 0000000000..c400940dfb
--- /dev/null
+++ b/x/tokenfactory/simulation/genesis.go
@@ -0,0 +1,55 @@
+package simulation
+
+import (
+ "math/rand"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/types/module"
+ simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+ subspacessim "github.com/desmos-labs/desmos/v5/x/subspaces/simulation"
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// RandomizeGenState generates a random GenesisState for x/tokenfactory
+func RandomizeGenState(simState *module.SimulationState) {
+ // Read the subspaces data
+ subspacesGenesisBz := simState.GenState[subspacestypes.ModuleName]
+ var subspacesGenesis subspacestypes.GenesisState
+ simState.Cdc.MustUnmarshalJSON(subspacesGenesisBz, &subspacesGenesis)
+
+ genesis := &types.GenesisState{
+ Params: types.NewParams(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10))),
+ FactoryDenoms: randomFactoryDenoms(simState.Rand, subspacesGenesis.Subspaces),
+ }
+
+ simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesis)
+}
+
+// randomFactoryDenoms generates a list of random factory denoms
+func randomFactoryDenoms(r *rand.Rand, subspaces []subspacestypes.Subspace) []types.GenesisDenom {
+ if len(subspaces) == 0 {
+ return nil
+ }
+
+ denomsNumber := r.Intn(20)
+ denoms := make([]types.GenesisDenom, denomsNumber)
+ for i := 0; i < denomsNumber; i++ {
+ subspace := subspacessim.RandomSubspace(r, subspaces)
+
+ denom, err := types.GetTokenDenom(subspace.Treasury, simtypes.RandStringOfLength(r, 6))
+ if err != nil {
+ panic(err)
+ }
+
+ denoms[i] = types.GenesisDenom{
+ Denom: denom,
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: subspace.Treasury,
+ },
+ }
+ }
+
+ return denoms
+}
diff --git a/x/tokenfactory/simulation/operations.go b/x/tokenfactory/simulation/operations.go
new file mode 100644
index 0000000000..0d446e87fb
--- /dev/null
+++ b/x/tokenfactory/simulation/operations.go
@@ -0,0 +1,374 @@
+package simulation
+
+// DONTCOVER
+
+import (
+ "math/rand"
+
+ "cosmossdk.io/math"
+ "github.com/cosmos/cosmos-sdk/baseapp"
+ "github.com/cosmos/cosmos-sdk/codec"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+ authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
+ bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ sim "github.com/cosmos/cosmos-sdk/x/simulation"
+
+ "github.com/desmos-labs/desmos/v5/testutil/simtesting"
+ subspacessim "github.com/desmos-labs/desmos/v5/x/subspaces/simulation"
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/keeper"
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// DONTCOVER
+
+// Simulation operation weights constants
+// #nosec G101 -- This is a false positive
+const (
+ OpWeightMsgCreateDenom = "op_weight_msg_create_denom"
+ OpWeightMsgMint = "op_weight_msg_mint"
+ OpWeightMsgBurn = "op_weight_msg_burn"
+ OpWeightMsgSetDenomMetadata = "op_weight_msg_set_denom_metadata"
+
+ DefaultWeightMsgCreateDenom = 30
+ DefaultWeightMsgMint = 70
+ DefaultWeightMsgBurn = 40
+ DefaultWeightMsgSetDenomMetadata = 20
+)
+
+// WeightedOperations returns all the operations from the module with their respective weights
+func WeightedOperations(
+ appParams simtypes.AppParams, cdc codec.JSONCodec,
+ k keeper.Keeper, sk types.SubspacesKeeper, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper,
+) sim.WeightedOperations {
+
+ var weightMsgCreateDenom int
+ appParams.GetOrGenerate(cdc, OpWeightMsgCreateDenom, &weightMsgCreateDenom, nil,
+ func(_ *rand.Rand) {
+ weightMsgCreateDenom = DefaultWeightMsgCreateDenom
+ },
+ )
+
+ var weightMsgMint int
+ appParams.GetOrGenerate(cdc, OpWeightMsgMint, &weightMsgMint, nil,
+ func(_ *rand.Rand) {
+ weightMsgMint = DefaultWeightMsgMint
+ },
+ )
+
+ var weightMsgBurn int
+ appParams.GetOrGenerate(cdc, OpWeightMsgBurn, &weightMsgBurn, nil,
+ func(_ *rand.Rand) {
+ weightMsgBurn = DefaultWeightMsgBurn
+ },
+ )
+
+ var weightMsgSetDenomMetadata int
+ appParams.GetOrGenerate(cdc, OpWeightMsgSetDenomMetadata, &weightMsgSetDenomMetadata, nil,
+ func(_ *rand.Rand) {
+ weightMsgSetDenomMetadata = DefaultWeightMsgSetDenomMetadata
+ },
+ )
+
+ return sim.WeightedOperations{
+ sim.NewWeightedOperation(
+ weightMsgCreateDenom,
+ SimulateMsgCreateDenom(k, sk, ak, bk),
+ ),
+ sim.NewWeightedOperation(
+ weightMsgMint,
+ SimulateMsgMint(k, sk, ak, bk),
+ ),
+ sim.NewWeightedOperation(
+ weightMsgBurn,
+ SimulateMsgBurn(k, sk, ak, bk),
+ ),
+ sim.NewWeightedOperation(
+ weightMsgSetDenomMetadata,
+ SimulateMsgSetDenomMetadata(k, sk, ak, bk),
+ ),
+ }
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// SimulateMsgCreateDenom tests and runs a single MsgCreateDenom
+func SimulateMsgCreateDenom(
+ k keeper.Keeper, sk types.SubspacesKeeper, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper,
+) simtypes.Operation {
+ return func(
+ r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
+ accs []simtypes.Account, _ string,
+ ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+
+ // Get the data
+ subspaceID, subdenom, signer, skip := randomCreateDenomFields(r, ctx, accs, k, sk, bk)
+ if skip {
+ return simtypes.NoOpMsg(types.RouterKey, "MsgCreateDenom", "skip"), nil, nil
+ }
+
+ msg := types.NewMsgCreateDenom(subspaceID, signer.Address.String(), subdenom)
+
+ // Send the message
+ return simtesting.SendMsg(r, app, ak, bk, msg, ctx, signer)
+ }
+}
+
+// randomCreateDenomFields returns the data used to build a random MsgCreateDenom
+func randomCreateDenomFields(
+ r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, sk types.SubspacesKeeper, bk bankkeeper.Keeper,
+) (subspaceID uint64, subdenom string, signer simtypes.Account, skip bool) {
+
+ // Get a subspace id
+ subspaces := sk.GetAllSubspaces(ctx)
+ if len(subspaces) == 0 {
+ // Skip because there are no subspaces
+ skip = true
+ return
+ }
+ subspace := subspacessim.RandomSubspace(r, subspaces)
+ subspaceID = subspace.ID
+
+ // Check treasury balances
+ balances := bk.SpendableCoins(ctx, sdk.MustAccAddressFromBech32(subspace.Treasury))
+ creationFees := k.GetParams(ctx).DenomCreationFee
+ if !balances.IsAllGT(creationFees) {
+ // Skip because treasury does not have enough coins
+ skip = true
+ return
+ }
+
+ // Get a denom
+ subdenom = simtypes.RandStringOfLength(r, 6)
+ denom, _ := types.GetTokenDenom(subspace.Treasury, subdenom)
+ _, exists := bk.GetDenomMetaData(ctx, denom)
+ if exists {
+ // Skip because denom has already existed
+ skip = true
+ return
+ }
+
+ // Get a signer
+ admins := sk.GetUsersWithRootPermissions(ctx, subspace.ID, subspacestypes.NewPermissions(types.PermissionManageSubspaceTokens))
+ acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, admins), accs)
+ if acc == nil {
+ // Skip because the account is not valid
+ skip = true
+ return
+ }
+ signer = *acc
+
+ return subspaceID, subdenom, signer, false
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// SimulateMsgMint tests and runs a single MsgMint
+func SimulateMsgMint(
+ k keeper.Keeper, sk types.SubspacesKeeper, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper,
+) simtypes.Operation {
+ return func(
+ r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
+ accs []simtypes.Account, _ string,
+ ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+
+ // Get the data
+ subspaceID, amount, signer, skip := randomMintFields(r, ctx, accs, k, sk)
+ if skip {
+ return simtypes.NoOpMsg(types.RouterKey, "MsgMint", "skip"), nil, nil
+ }
+
+ msg := types.NewMsgMint(subspaceID, signer.Address.String(), amount)
+
+ // Send the message
+ return simtesting.SendMsg(r, app, ak, bk, msg, ctx, signer)
+ }
+}
+
+// randomMintFields returns the data used to build a random MsgMint
+func randomMintFields(
+ r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, sk types.SubspacesKeeper,
+) (subspaceID uint64, amount sdk.Coin, signer simtypes.Account, skip bool) {
+
+ // Get a subspace id
+ subspaces := sk.GetAllSubspaces(ctx)
+ if len(subspaces) == 0 {
+ // Skip because there are no subspaces
+ skip = true
+ return
+ }
+ subspace := subspacessim.RandomSubspace(r, subspaces)
+ subspaceID = subspace.ID
+
+ // Get an amount
+ denoms := k.GetDenomsFromCreator(ctx, subspace.Treasury)
+ if len(denoms) == 0 {
+ // Skip because there are no denoms
+ skip = true
+ return
+ }
+ denom := RandomDenom(r, denoms)
+ amount = sdk.NewCoin(denom, simtypes.RandomAmount(r, math.NewInt(1000)))
+ if amount.Amount.Equal(sdk.NewInt(0)) {
+ // Skip because amount with zero is invalid
+ skip = true
+ return
+ }
+
+ // Get a signer
+ admins := sk.GetUsersWithRootPermissions(ctx, subspace.ID, subspacestypes.NewPermissions(types.PermissionManageSubspaceTokens))
+ acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, admins), accs)
+ if acc == nil {
+ // Skip because the account is not valid
+ skip = true
+ return
+ }
+ signer = *acc
+
+ return subspaceID, amount, signer, false
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// SimulateMsgBurn tests and runs a single MsgBurn
+func SimulateMsgBurn(
+ k keeper.Keeper, sk types.SubspacesKeeper, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper,
+) simtypes.Operation {
+ return func(
+ r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
+ accs []simtypes.Account, _ string,
+ ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+
+ // Get the data
+ subspaceID, amount, signer, skip := randomBurnFields(r, ctx, accs, k, sk, bk)
+ if skip {
+ return simtypes.NoOpMsg(types.RouterKey, "MsgBurn", "skip"), nil, nil
+ }
+
+ msg := types.NewMsgBurn(subspaceID, signer.Address.String(), amount)
+
+ // Send the message
+ return simtesting.SendMsg(r, app, ak, bk, msg, ctx, signer)
+ }
+}
+
+// randomBurnFields returns the data used to build a random MsgBurn
+func randomBurnFields(
+ r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, sk types.SubspacesKeeper, bk bankkeeper.ViewKeeper,
+) (subspaceID uint64, amount sdk.Coin, signer simtypes.Account, skip bool) {
+
+ // Get a subspace id
+ subspaces := sk.GetAllSubspaces(ctx)
+ if len(subspaces) == 0 {
+ // Skip because there are no subspaces
+ skip = true
+ return
+ }
+ subspace := subspacessim.RandomSubspace(r, subspaces)
+ subspaceID = subspace.ID
+
+ // Get a denom
+ denoms := k.GetDenomsFromCreator(ctx, subspace.Treasury)
+ if len(denoms) == 0 {
+ // Skip because there are no denoms
+ skip = true
+ return
+ }
+ denom := RandomDenom(r, denoms)
+
+ // Get a amount to burn
+ balance := bk.SpendableCoin(ctx, sdk.MustAccAddressFromBech32(subspace.Treasury), denom)
+ amount = sdk.NewCoin(denom, simtypes.RandomAmount(r, balance.Amount))
+ if amount.Amount.Equal(sdk.NewInt(0)) {
+ // Skip because amount with zero is invalid
+ skip = true
+ return
+ }
+
+ // Get a signer
+ admins := sk.GetUsersWithRootPermissions(ctx, subspace.ID, subspacestypes.NewPermissions(types.PermissionManageSubspaceTokens))
+ acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, admins), accs)
+ if acc == nil {
+ // Skip because the account is invalid
+ skip = true
+ return
+ }
+ signer = *acc
+
+ return subspaceID, amount, signer, false
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// SimulateMsgSetDenomMetadata tests and runs a single MsgSetDenomMetadata
+func SimulateMsgSetDenomMetadata(
+ k keeper.Keeper, sk types.SubspacesKeeper, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper,
+) simtypes.Operation {
+ return func(
+ r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
+ accs []simtypes.Account, _ string,
+ ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+
+ // Get the data
+ subspaceID, metadata, signer, skip := randomSetDenomMetadataFields(r, ctx, accs, k, sk)
+ if skip {
+ return simtypes.NoOpMsg(types.RouterKey, "MsgSetDenomMetadata", "skip"), nil, nil
+ }
+
+ msg := types.NewMsgSetDenomMetadata(subspaceID, signer.Address.String(), metadata)
+
+ // Send the message
+ return simtesting.SendMsg(r, app, ak, bk, msg, ctx, signer)
+ }
+}
+
+// randomSetDenomMetadataFields returns the data used to build a random MsgSetDenomMetadata
+func randomSetDenomMetadataFields(
+ r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, sk types.SubspacesKeeper,
+) (subspaceID uint64, metadata banktypes.Metadata, signer simtypes.Account, skip bool) {
+
+ // Get a subspace id
+ subspaces := sk.GetAllSubspaces(ctx)
+ if len(subspaces) == 0 {
+ // Skip because there are no subspaces
+ skip = true
+ return
+ }
+ subspace := subspacessim.RandomSubspace(r, subspaces)
+ subspaceID = subspace.ID
+
+ // Get an metadata
+ denoms := k.GetDenomsFromCreator(ctx, subspace.Treasury)
+ if len(denoms) == 0 {
+ // Skip because there are no denoms
+ skip = true
+ return
+ }
+ denom := RandomDenom(r, denoms)
+ display := simtypes.RandStringOfLength(r, 3)
+ metadata = banktypes.Metadata{
+ Description: simtypes.RandStringOfLength(r, 15),
+ DenomUnits: []*banktypes.DenomUnit{
+ {Denom: denom, Exponent: 0, Aliases: nil},
+ {Denom: display, Exponent: 3, Aliases: nil},
+ },
+ Base: denom,
+ Display: display,
+ Name: simtypes.RandStringOfLength(r, 5),
+ Symbol: simtypes.RandStringOfLength(r, 5),
+ }
+
+ // Get a signer
+ admins := sk.GetUsersWithRootPermissions(ctx, subspace.ID, subspacestypes.NewPermissions(types.PermissionManageSubspaceTokens))
+ acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, admins), accs)
+ if acc == nil {
+ // Skip because the account is not valid
+ skip = true
+ return
+ }
+ signer = *acc
+
+ return subspaceID, metadata, signer, false
+}
diff --git a/x/tokenfactory/simulation/proposal.go b/x/tokenfactory/simulation/proposal.go
new file mode 100644
index 0000000000..2473afe33d
--- /dev/null
+++ b/x/tokenfactory/simulation/proposal.go
@@ -0,0 +1,47 @@
+package simulation
+
+import (
+ "math/rand"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/types/address"
+ simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+ "github.com/cosmos/cosmos-sdk/x/simulation"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+// DONTCOVER
+
+// Simulation operation weights constants
+const (
+ DefaultWeightMsgUpdateParams int = 50
+
+ OpWeightMsgUpdateParams = "op_weight_msg_update_params" //nolint:gosec
+)
+
+// ProposalMsgs defines the module weighted proposals' contents
+func ProposalMsgs() []simtypes.WeightedProposalMsg {
+ return []simtypes.WeightedProposalMsg{
+ simulation.NewWeightedProposalMsg(
+ OpWeightMsgUpdateParams,
+ DefaultWeightMsgUpdateParams,
+ SimulateMsgUpdateParams,
+ ),
+ }
+}
+
+// SimulateMsgUpdateParams returns a random MsgUpdateParams
+func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
+ // use the default gov module account address as authority
+ var authority sdk.AccAddress = address.Module("gov")
+
+ params := types.NewParams(sdk.NewCoins(
+ sdk.NewCoin(sdk.DefaultBondDenom, simtypes.RandomAmount(r, sdk.NewInt(100)))),
+ )
+
+ return &types.MsgUpdateParams{
+ Authority: authority.String(),
+ Params: params,
+ }
+}
diff --git a/x/tokenfactory/simulation/utils.go b/x/tokenfactory/simulation/utils.go
new file mode 100644
index 0000000000..427bbb7c97
--- /dev/null
+++ b/x/tokenfactory/simulation/utils.go
@@ -0,0 +1,10 @@
+package simulation
+
+import (
+ "math/rand"
+)
+
+// RandomDenom returns a random denom from the given slice
+func RandomDenom(r *rand.Rand, denoms []string) string {
+ return denoms[r.Intn(len(denoms))]
+}
diff --git a/x/tokenfactory/testutil/expected_keepers_mocks.go b/x/tokenfactory/testutil/expected_keepers_mocks.go
new file mode 100644
index 0000000000..af3c843af9
--- /dev/null
+++ b/x/tokenfactory/testutil/expected_keepers_mocks.go
@@ -0,0 +1,294 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: ./x/tokenfactory/types/expected_keepers.go
+
+// Package testutil is a generated GoMock package.
+package testutil
+
+import (
+ reflect "reflect"
+
+ types "github.com/cosmos/cosmos-sdk/types"
+ types0 "github.com/cosmos/cosmos-sdk/x/auth/types"
+ types1 "github.com/cosmos/cosmos-sdk/x/bank/types"
+ types2 "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+ gomock "github.com/golang/mock/gomock"
+)
+
+// MockBankKeeper is a mock of BankKeeper interface.
+type MockBankKeeper struct {
+ ctrl *gomock.Controller
+ recorder *MockBankKeeperMockRecorder
+}
+
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
+type MockBankKeeperMockRecorder struct {
+ mock *MockBankKeeper
+}
+
+// NewMockBankKeeper creates a new mock instance.
+func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
+ mock := &MockBankKeeper{ctrl: ctrl}
+ mock.recorder = &MockBankKeeperMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
+ return m.recorder
+}
+
+// BurnCoins mocks base method.
+func (m *MockBankKeeper) BurnCoins(ctx types.Context, moduleName string, amt types.Coins) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "BurnCoins", ctx, moduleName, amt)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// BurnCoins indicates an expected call of BurnCoins.
+func (mr *MockBankKeeperMockRecorder) BurnCoins(ctx, moduleName, amt interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BurnCoins", reflect.TypeOf((*MockBankKeeper)(nil).BurnCoins), ctx, moduleName, amt)
+}
+
+// GetDenomMetaData mocks base method.
+func (m *MockBankKeeper) GetDenomMetaData(ctx types.Context, denom string) (types1.Metadata, bool) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetDenomMetaData", ctx, denom)
+ ret0, _ := ret[0].(types1.Metadata)
+ ret1, _ := ret[1].(bool)
+ return ret0, ret1
+}
+
+// GetDenomMetaData indicates an expected call of GetDenomMetaData.
+func (mr *MockBankKeeperMockRecorder) GetDenomMetaData(ctx, denom interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDenomMetaData", reflect.TypeOf((*MockBankKeeper)(nil).GetDenomMetaData), ctx, denom)
+}
+
+// HasBalance mocks base method.
+func (m *MockBankKeeper) HasBalance(ctx types.Context, addr types.AccAddress, amt types.Coin) bool {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HasBalance", ctx, addr, amt)
+ ret0, _ := ret[0].(bool)
+ return ret0
+}
+
+// HasBalance indicates an expected call of HasBalance.
+func (mr *MockBankKeeperMockRecorder) HasBalance(ctx, addr, amt interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasBalance", reflect.TypeOf((*MockBankKeeper)(nil).HasBalance), ctx, addr, amt)
+}
+
+// HasSupply mocks base method.
+func (m *MockBankKeeper) HasSupply(ctx types.Context, denom string) bool {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HasSupply", ctx, denom)
+ ret0, _ := ret[0].(bool)
+ return ret0
+}
+
+// HasSupply indicates an expected call of HasSupply.
+func (mr *MockBankKeeperMockRecorder) HasSupply(ctx, denom interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasSupply", reflect.TypeOf((*MockBankKeeper)(nil).HasSupply), ctx, denom)
+}
+
+// MintCoins mocks base method.
+func (m *MockBankKeeper) MintCoins(ctx types.Context, moduleName string, amt types.Coins) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "MintCoins", ctx, moduleName, amt)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// MintCoins indicates an expected call of MintCoins.
+func (mr *MockBankKeeperMockRecorder) MintCoins(ctx, moduleName, amt interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MintCoins", reflect.TypeOf((*MockBankKeeper)(nil).MintCoins), ctx, moduleName, amt)
+}
+
+// SendCoins mocks base method.
+func (m *MockBankKeeper) SendCoins(ctx types.Context, fromAddr, toAddr types.AccAddress, amt types.Coins) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendCoins indicates an expected call of SendCoins.
+func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt)
+}
+
+// SendCoinsFromAccountToModule mocks base method.
+func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt)
+}
+
+// SendCoinsFromModuleToAccount mocks base method.
+func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt)
+}
+
+// SetDenomMetaData mocks base method.
+func (m *MockBankKeeper) SetDenomMetaData(ctx types.Context, denomMetaData types1.Metadata) {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "SetDenomMetaData", ctx, denomMetaData)
+}
+
+// SetDenomMetaData indicates an expected call of SetDenomMetaData.
+func (mr *MockBankKeeperMockRecorder) SetDenomMetaData(ctx, denomMetaData interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDenomMetaData", reflect.TypeOf((*MockBankKeeper)(nil).SetDenomMetaData), ctx, denomMetaData)
+}
+
+// MockAccountKeeper is a mock of AccountKeeper interface.
+type MockAccountKeeper struct {
+ ctrl *gomock.Controller
+ recorder *MockAccountKeeperMockRecorder
+}
+
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
+type MockAccountKeeperMockRecorder struct {
+ mock *MockAccountKeeper
+}
+
+// NewMockAccountKeeper creates a new mock instance.
+func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
+ mock := &MockAccountKeeper{ctrl: ctrl}
+ mock.recorder = &MockAccountKeeperMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
+ return m.recorder
+}
+
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(arg0 types.Context, arg1 types.AccAddress) types0.AccountI {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetAccount", arg0, arg1)
+ ret0, _ := ret[0].(types0.AccountI)
+ return ret0
+}
+
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1)
+}
+
+// GetModuleAccount mocks base method.
+func (m *MockAccountKeeper) GetModuleAccount(ctx types.Context, moduleName string) types0.ModuleAccountI {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName)
+ ret0, _ := ret[0].(types0.ModuleAccountI)
+ return ret0
+}
+
+// GetModuleAccount indicates an expected call of GetModuleAccount.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, moduleName)
+}
+
+// MockSubspacesKeeper is a mock of SubspacesKeeper interface.
+type MockSubspacesKeeper struct {
+ ctrl *gomock.Controller
+ recorder *MockSubspacesKeeperMockRecorder
+}
+
+// MockSubspacesKeeperMockRecorder is the mock recorder for MockSubspacesKeeper.
+type MockSubspacesKeeperMockRecorder struct {
+ mock *MockSubspacesKeeper
+}
+
+// NewMockSubspacesKeeper creates a new mock instance.
+func NewMockSubspacesKeeper(ctrl *gomock.Controller) *MockSubspacesKeeper {
+ mock := &MockSubspacesKeeper{ctrl: ctrl}
+ mock.recorder = &MockSubspacesKeeperMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockSubspacesKeeper) EXPECT() *MockSubspacesKeeperMockRecorder {
+ return m.recorder
+}
+
+// GetAllSubspaces mocks base method.
+func (m *MockSubspacesKeeper) GetAllSubspaces(ctx types.Context) []types2.Subspace {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetAllSubspaces", ctx)
+ ret0, _ := ret[0].([]types2.Subspace)
+ return ret0
+}
+
+// GetAllSubspaces indicates an expected call of GetAllSubspaces.
+func (mr *MockSubspacesKeeperMockRecorder) GetAllSubspaces(ctx interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllSubspaces", reflect.TypeOf((*MockSubspacesKeeper)(nil).GetAllSubspaces), ctx)
+}
+
+// GetSubspace mocks base method.
+func (m *MockSubspacesKeeper) GetSubspace(ctx types.Context, subspaceID uint64) (types2.Subspace, bool) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetSubspace", ctx, subspaceID)
+ ret0, _ := ret[0].(types2.Subspace)
+ ret1, _ := ret[1].(bool)
+ return ret0, ret1
+}
+
+// GetSubspace indicates an expected call of GetSubspace.
+func (mr *MockSubspacesKeeperMockRecorder) GetSubspace(ctx, subspaceID interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubspace", reflect.TypeOf((*MockSubspacesKeeper)(nil).GetSubspace), ctx, subspaceID)
+}
+
+// GetUsersWithRootPermissions mocks base method.
+func (m *MockSubspacesKeeper) GetUsersWithRootPermissions(ctx types.Context, subspaceID uint64, permission types2.Permissions) []string {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetUsersWithRootPermissions", ctx, subspaceID, permission)
+ ret0, _ := ret[0].([]string)
+ return ret0
+}
+
+// GetUsersWithRootPermissions indicates an expected call of GetUsersWithRootPermissions.
+func (mr *MockSubspacesKeeperMockRecorder) GetUsersWithRootPermissions(ctx, subspaceID, permission interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersWithRootPermissions", reflect.TypeOf((*MockSubspacesKeeper)(nil).GetUsersWithRootPermissions), ctx, subspaceID, permission)
+}
+
+// HasPermission mocks base method.
+func (m *MockSubspacesKeeper) HasPermission(ctx types.Context, subspaceID uint64, sectionID uint32, user string, permission types2.Permission) bool {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HasPermission", ctx, subspaceID, sectionID, user, permission)
+ ret0, _ := ret[0].(bool)
+ return ret0
+}
+
+// HasPermission indicates an expected call of HasPermission.
+func (mr *MockSubspacesKeeperMockRecorder) HasPermission(ctx, subspaceID, sectionID, user, permission interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPermission", reflect.TypeOf((*MockSubspacesKeeper)(nil).HasPermission), ctx, subspaceID, sectionID, user, permission)
+}
diff --git a/x/tokenfactory/types/authority_metadata.go b/x/tokenfactory/types/authority_metadata.go
new file mode 100644
index 0000000000..bc388c3b3c
--- /dev/null
+++ b/x/tokenfactory/types/authority_metadata.go
@@ -0,0 +1,16 @@
+package types
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+// Validate implements fmt.Validator
+func (metadata DenomAuthorityMetadata) Validate() error {
+ if metadata.Admin != "" {
+ _, err := sdk.AccAddressFromBech32(metadata.Admin)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/x/tokenfactory/types/codec.go b/x/tokenfactory/types/codec.go
new file mode 100644
index 0000000000..6143e5b122
--- /dev/null
+++ b/x/tokenfactory/types/codec.go
@@ -0,0 +1,61 @@
+package types
+
+import (
+ "github.com/cosmos/cosmos-sdk/codec"
+ "github.com/cosmos/cosmos-sdk/codec/legacy"
+ codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+ cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/types/msgservice"
+ authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec"
+ govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec"
+)
+
+func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+ legacy.RegisterAminoMsg(cdc, &MsgCreateDenom{}, "desmos/MsgCreateDenom")
+ legacy.RegisterAminoMsg(cdc, &MsgMint{}, "desmos/MsgMint")
+ legacy.RegisterAminoMsg(cdc, &MsgBurn{}, "desmos/MsgBurn")
+ legacy.RegisterAminoMsg(cdc, &MsgSetDenomMetadata{}, "desmos/MsgSetDenomMetadata")
+ legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "desmos/x/tokenfactory/MsgUpdateParams")
+
+ cdc.RegisterConcrete(&Params{}, "desmos/x/tokenfactory/Params", nil)
+}
+
+func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
+ registry.RegisterImplementations(
+ (*sdk.Msg)(nil),
+ &MsgCreateDenom{},
+ &MsgMint{},
+ &MsgBurn{},
+ &MsgSetDenomMetadata{},
+ &MsgUpdateParams{},
+ )
+
+ msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
+}
+
+var (
+ amino = codec.NewLegacyAmino()
+
+ // AminoCdc references the global x/tokenfactory module codec. Note, the codec should
+ // ONLY be used in certain instances of tests and for JSON encoding as Amino is
+ // still used for that purpose.
+ //
+ // The actual codec used for serialization should be provided to x/tokenfactory and
+ // defined at the application level.
+ AminoCdc = codec.NewAminoCodec(amino)
+)
+
+func init() {
+ RegisterLegacyAminoCodec(amino)
+ cryptocodec.RegisterCrypto(amino)
+ sdk.RegisterLegacyAminoCodec(amino)
+
+ // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be
+ // used to properly serialize MsgGrant and MsgExec instances
+ RegisterLegacyAminoCodec(authzcodec.Amino)
+
+ // Register all Amino interfaces and concrete types on the gov Amino codec so that this can later be
+ // used to properly serialize MsgSubmitProposal instances
+ RegisterLegacyAminoCodec(govcodec.Amino)
+}
diff --git a/x/tokenfactory/types/denoms.go b/x/tokenfactory/types/denoms.go
new file mode 100644
index 0000000000..97929529d3
--- /dev/null
+++ b/x/tokenfactory/types/denoms.go
@@ -0,0 +1,84 @@
+package types
+
+import (
+ fmt "fmt"
+ "strings"
+
+ "cosmossdk.io/errors"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+)
+
+const (
+ ModuleDenomPrefix = "factory"
+ // See the TokenFactory readme for a derivation of these.
+ // TL;DR, MaxSubdenomLength + MaxHrpLength = 60 comes from SDK max denom length = 128
+ // and the structure of tokenfactory denoms.
+ MaxSubdenomLength = 44
+ MaxHrpLength = 16
+ // MaxCreatorLength = 59 + MaxHrpLength
+ MaxCreatorLength = 59 + MaxHrpLength
+)
+
+// GetTokenDenom constructs a denom string for tokens created by tokenfactory
+// based on an input creator address and a subdenom
+// The denom constructed is factory/{creator}/{subdenom}
+func GetTokenDenom(creator, subdenom string) (string, error) {
+ if len(subdenom) > MaxSubdenomLength {
+ return "", ErrSubdenomTooLong
+ }
+ if len(creator) > MaxCreatorLength {
+ return "", ErrCreatorTooLong
+ }
+ if strings.Contains(creator, "/") {
+ return "", ErrInvalidCreator
+ }
+ denom := strings.Join([]string{ModuleDenomPrefix, creator, subdenom}, "/")
+ return denom, sdk.ValidateDenom(denom)
+}
+
+// DeconstructDenom takes a token denom string and verifies that it is a valid
+// denom of the tokenfactory module, and is of the form `factory/{creator}/{subdenom}`
+// If valid, it returns the creator address and subdenom
+func DeconstructDenom(denom string) (creator string, subdenom string, err error) {
+ err = sdk.ValidateDenom(denom)
+ if err != nil {
+ return "", "", err
+ }
+
+ strParts := strings.Split(denom, "/")
+ if len(strParts) < 3 {
+ return "", "", errors.Wrapf(ErrInvalidDenom, "not enough parts of denom %s", denom)
+ }
+
+ if strParts[0] != ModuleDenomPrefix {
+ return "", "", errors.Wrapf(ErrInvalidDenom, "denom prefix is incorrect. Is: %s. Should be: %s", strParts[0], ModuleDenomPrefix)
+ }
+
+ creator = strParts[1]
+ creatorAddr, err := sdk.AccAddressFromBech32(creator)
+ if err != nil {
+ return "", "", errors.Wrapf(ErrInvalidDenom, "Invalid creator address (%s)", err)
+ }
+
+ // Handle the case where a denom has a slash in its subdenom. For example,
+ // when we did the split, we'd turn factory/accaddr/atomderivative/sikka into ["factory", "accaddr", "atomderivative", "sikka"]
+ // So we have to join [2:] with a "/" as the delimiter to get back the correct subdenom which should be "atomderivative/sikka"
+ subdenom = strings.Join(strParts[2:], "/")
+
+ return creatorAddr.String(), subdenom, nil
+}
+
+// NewTokenFactoryDenomMintCoinsRestriction creates and returns a MintingRestrictionFn that only allows minting of
+// valid tokenfactory denoms
+func NewTokenFactoryDenomMintCoinsRestriction() bankkeeper.MintingRestrictionFn {
+ return func(ctx sdk.Context, coinsToMint sdk.Coins) error {
+ for _, coin := range coinsToMint {
+ _, _, err := DeconstructDenom(coin.Denom)
+ if err != nil {
+ return fmt.Errorf("does not have permission to mint %s", coin.Denom)
+ }
+ }
+ return nil
+ }
+}
diff --git a/x/tokenfactory/types/denoms_test.go b/x/tokenfactory/types/denoms_test.go
new file mode 100644
index 0000000000..0b693acab7
--- /dev/null
+++ b/x/tokenfactory/types/denoms_test.go
@@ -0,0 +1,122 @@
+package types_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func TestDeconstructDenom(t *testing.T) {
+
+ for _, tc := range []struct {
+ name string
+ denom string
+ expectedSubdenom string
+ err error
+ }{
+ {
+ name: "empty is invalid",
+ denom: "",
+ err: types.ErrInvalidDenom,
+ },
+ {
+ name: "normal",
+ denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ expectedSubdenom: "bitcoin",
+ },
+ {
+ name: "multiple slashes in subdenom",
+ denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin/1",
+ expectedSubdenom: "bitcoin/1",
+ },
+ {
+ name: "no subdenom",
+ denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/",
+ expectedSubdenom: "",
+ },
+ {
+ name: "incorrect prefix",
+ denom: "ibc/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ err: types.ErrInvalidDenom,
+ },
+ {
+ name: "subdenom of only slashes",
+ denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/////",
+ expectedSubdenom: "////",
+ },
+ {
+ name: "too long name",
+ denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf",
+ err: types.ErrInvalidDenom,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ expectedCreator := "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69"
+ creator, subdenom, err := types.DeconstructDenom(tc.denom)
+ if tc.err != nil {
+ require.ErrorContains(t, err, tc.err.Error())
+ } else {
+ require.NoError(t, err)
+ require.Equal(t, expectedCreator, creator)
+ require.Equal(t, tc.expectedSubdenom, subdenom)
+ }
+ })
+ }
+}
+
+func TestGetTokenDenom(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ creator string
+ subdenom string
+ shouldErr bool
+ }{
+ {
+ name: "normal returns no error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ subdenom: "bitcoin",
+ },
+ {
+ name: "multiple slashes in subdenom returns no error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ subdenom: "bitcoin/1",
+ },
+ {
+ name: "no subdenom returns no error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ subdenom: "",
+ },
+ {
+ name: "subdenom of only slashes returns no error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ subdenom: "/////",
+ },
+ {
+ name: "too long name returns error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ subdenom: "adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf",
+ shouldErr: true,
+ },
+ {
+ name: "subdenom is exactly max length returns no error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ subdenom: "bitcoinfsadfsdfeadfsafwefsefsefsdfsdafasefsf",
+ },
+ {
+ name: "creator is exactly max length return no error",
+ creator: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69jhgjhgkhjklhkjhkjhgjhgjgjghelu",
+ subdenom: "bitcoin",
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ _, err := types.GetTokenDenom(tc.creator, tc.subdenom)
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/types/errors.go b/x/tokenfactory/types/errors.go
new file mode 100644
index 0000000000..74cc8b14ae
--- /dev/null
+++ b/x/tokenfactory/types/errors.go
@@ -0,0 +1,22 @@
+package types
+
+// DONTCOVER
+
+import (
+ fmt "fmt"
+
+ "cosmossdk.io/errors"
+)
+
+// x/tokenfactory module sentinel errors
+var (
+ ErrDenomExists = errors.Register(ModuleName, 2, "attempting to create a denom that already exists (has bank metadata)")
+ ErrUnauthorized = errors.Register(ModuleName, 3, "unauthorized account")
+ ErrInvalidDenom = errors.Register(ModuleName, 4, "invalid denom")
+ ErrInvalidCreator = errors.Register(ModuleName, 5, "invalid creator")
+ ErrInvalidAuthorityMetadata = errors.Register(ModuleName, 6, "invalid authority metadata")
+ ErrInvalidGenesis = errors.Register(ModuleName, 7, "invalid genesis")
+ ErrSubdenomTooLong = errors.Register(ModuleName, 8, fmt.Sprintf("subdenom too long, max length is %d bytes", MaxSubdenomLength))
+ ErrCreatorTooLong = errors.Register(ModuleName, 9, fmt.Sprintf("creator too long, max length is %d bytes", MaxCreatorLength))
+ ErrDenomDoesNotExist = errors.Register(ModuleName, 10, "denom does not exist")
+)
diff --git a/x/tokenfactory/types/events.go b/x/tokenfactory/types/events.go
new file mode 100644
index 0000000000..73ffb16896
--- /dev/null
+++ b/x/tokenfactory/types/events.go
@@ -0,0 +1,22 @@
+package types
+
+// DONTCOVER
+
+// #nosec G101 -- This is a false positive
+const (
+ EventTypeCreateDenom = "create_denom"
+ EventTypeMint = "tf_mint"
+ EventTypeBurn = "tf_burn"
+ EventTypeSetDenomMetadata = "set_denom_metadata"
+
+ AttributeValueCategory = ModuleName
+ AttributeKeySubspaceID = "subspace_id"
+ AttributeAmount = "amount"
+ AttributeCreator = "creator"
+ AttributeSubdenom = "subdenom"
+ AttributeNewTokenDenom = "new_token_denom"
+ AttributeMintToAddress = "mint_to_address"
+ AttributeBurnFromAddress = "burn_from_address"
+ AttributeDenom = "denom"
+ AttributeDenomMetadata = "denom_metadata"
+)
diff --git a/x/tokenfactory/types/expected_keepers.go b/x/tokenfactory/types/expected_keepers.go
new file mode 100644
index 0000000000..d615315fe8
--- /dev/null
+++ b/x/tokenfactory/types/expected_keepers.go
@@ -0,0 +1,40 @@
+package types
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+)
+
+// BankKeeper represents a keeper that deals with x/bank
+type BankKeeper interface {
+ // Methods imported from bank should be defined here
+ GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
+ SetDenomMetaData(ctx sdk.Context, denomMetaData banktypes.Metadata)
+
+ HasSupply(ctx sdk.Context, denom string) bool
+
+ SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
+ SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
+ MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
+ BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
+
+ SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
+ HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool
+}
+
+// AccountKeeper represents a keeper that deals with x/auth
+type AccountKeeper interface {
+ GetAccount(sdk.Context, sdk.AccAddress) authtypes.AccountI
+ GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI
+}
+
+// SubspacesKeeper represents a keeper that deals with x/subspaces
+type SubspacesKeeper interface {
+ GetAllSubspaces(ctx sdk.Context) []subspacestypes.Subspace
+ GetSubspace(ctx sdk.Context, subspaceID uint64) (subspace subspacestypes.Subspace, found bool)
+ HasPermission(ctx sdk.Context, subspaceID uint64, sectionID uint32, user string, permission subspacestypes.Permission) bool
+ GetUsersWithRootPermissions(ctx sdk.Context, subspaceID uint64, permission subspacestypes.Permissions) []string
+}
diff --git a/x/tokenfactory/types/genesis.go b/x/tokenfactory/types/genesis.go
new file mode 100644
index 0000000000..1445389fd1
--- /dev/null
+++ b/x/tokenfactory/types/genesis.go
@@ -0,0 +1,49 @@
+package types
+
+import (
+ "cosmossdk.io/errors"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+// DefaultIndex is the default capability global index
+const DefaultIndex uint64 = 1
+
+// DefaultGenesis returns the default Capability genesis state
+func DefaultGenesis() *GenesisState {
+ return &GenesisState{
+ Params: DefaultParams(),
+ FactoryDenoms: []GenesisDenom{},
+ }
+}
+
+// Validate performs basic genesis state validation returning an error upon any
+// failure.
+func (gs GenesisState) Validate() error {
+ err := gs.Params.Validate()
+ if err != nil {
+ return err
+ }
+
+ seenDenoms := map[string]bool{}
+
+ for _, denom := range gs.GetFactoryDenoms() {
+ if seenDenoms[denom.GetDenom()] {
+ return errors.Wrapf(ErrInvalidGenesis, "duplicate denom: %s", denom.GetDenom())
+ }
+ seenDenoms[denom.GetDenom()] = true
+
+ _, _, err := DeconstructDenom(denom.GetDenom())
+ if err != nil {
+ return err
+ }
+
+ if denom.AuthorityMetadata.Admin != "" {
+ _, err = sdk.AccAddressFromBech32(denom.AuthorityMetadata.Admin)
+ if err != nil {
+ return errors.Wrapf(ErrInvalidAuthorityMetadata, "Invalid admin address (%s)", err)
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/x/tokenfactory/types/genesis.pb.go b/x/tokenfactory/types/genesis.pb.go
new file mode 100644
index 0000000000..c1f2489fd4
--- /dev/null
+++ b/x/tokenfactory/types/genesis.pb.go
@@ -0,0 +1,651 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: desmos/tokenfactory/v1beta1/genesis.proto
+
+package types
+
+import (
+ fmt "fmt"
+ _ "github.com/cosmos/cosmos-sdk/types/tx/amino"
+ _ "github.com/cosmos/gogoproto/gogoproto"
+ proto "github.com/cosmos/gogoproto/proto"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// GenesisState defines the tokenfactory module's genesis state.
+type GenesisState struct {
+ // params defines the paramaters of the module.
+ Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+ FactoryDenoms []GenesisDenom `protobuf:"bytes,2,rep,name=factory_denoms,json=factoryDenoms,proto3" json:"factory_denoms" yaml:"factory_denoms"`
+}
+
+func (m *GenesisState) Reset() { *m = GenesisState{} }
+func (m *GenesisState) String() string { return proto.CompactTextString(m) }
+func (*GenesisState) ProtoMessage() {}
+func (*GenesisState) Descriptor() ([]byte, []int) {
+ return fileDescriptor_b001fa8c2a4a126e, []int{0}
+}
+func (m *GenesisState) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *GenesisState) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_GenesisState.Merge(m, src)
+}
+func (m *GenesisState) XXX_Size() int {
+ return m.Size()
+}
+func (m *GenesisState) XXX_DiscardUnknown() {
+ xxx_messageInfo_GenesisState.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GenesisState proto.InternalMessageInfo
+
+func (m *GenesisState) GetParams() Params {
+ if m != nil {
+ return m.Params
+ }
+ return Params{}
+}
+
+func (m *GenesisState) GetFactoryDenoms() []GenesisDenom {
+ if m != nil {
+ return m.FactoryDenoms
+ }
+ return nil
+}
+
+// GenesisDenom defines a tokenfactory denom that is defined within genesis
+// state. The structure contains DenomAuthorityMetadata which defines the
+// denom's admin.
+type GenesisDenom struct {
+ Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty" yaml:"denom"`
+ AuthorityMetadata DenomAuthorityMetadata `protobuf:"bytes,2,opt,name=authority_metadata,json=authorityMetadata,proto3" json:"authority_metadata" yaml:"authority_metadata"`
+}
+
+func (m *GenesisDenom) Reset() { *m = GenesisDenom{} }
+func (m *GenesisDenom) String() string { return proto.CompactTextString(m) }
+func (*GenesisDenom) ProtoMessage() {}
+func (*GenesisDenom) Descriptor() ([]byte, []int) {
+ return fileDescriptor_b001fa8c2a4a126e, []int{1}
+}
+func (m *GenesisDenom) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *GenesisDenom) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_GenesisDenom.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *GenesisDenom) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_GenesisDenom.Merge(m, src)
+}
+func (m *GenesisDenom) XXX_Size() int {
+ return m.Size()
+}
+func (m *GenesisDenom) XXX_DiscardUnknown() {
+ xxx_messageInfo_GenesisDenom.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GenesisDenom proto.InternalMessageInfo
+
+func (m *GenesisDenom) GetDenom() string {
+ if m != nil {
+ return m.Denom
+ }
+ return ""
+}
+
+func (m *GenesisDenom) GetAuthorityMetadata() DenomAuthorityMetadata {
+ if m != nil {
+ return m.AuthorityMetadata
+ }
+ return DenomAuthorityMetadata{}
+}
+
+func init() {
+ proto.RegisterType((*GenesisState)(nil), "desmos.tokenfactory.v1beta1.GenesisState")
+ proto.RegisterType((*GenesisDenom)(nil), "desmos.tokenfactory.v1beta1.GenesisDenom")
+}
+
+func init() {
+ proto.RegisterFile("desmos/tokenfactory/v1beta1/genesis.proto", fileDescriptor_b001fa8c2a4a126e)
+}
+
+var fileDescriptor_b001fa8c2a4a126e = []byte{
+ // 381 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4c, 0x49, 0x2d, 0xce,
+ 0xcd, 0x2f, 0xd6, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x4b, 0x4b, 0x4c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4,
+ 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6,
+ 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0x28, 0xd5, 0x43, 0x56, 0xaa, 0x07, 0x55, 0x2a,
+ 0x25, 0x98, 0x98, 0x9b, 0x99, 0x97, 0xaf, 0x0f, 0x26, 0x21, 0xea, 0xa5, 0x44, 0xd2, 0xf3, 0xd3,
+ 0xf3, 0xc1, 0x4c, 0x7d, 0x10, 0x0b, 0x2a, 0xaa, 0x81, 0xcf, 0xc2, 0x82, 0xc4, 0xa2, 0xc4, 0xdc,
+ 0x62, 0x62, 0x54, 0xe6, 0xe6, 0xa7, 0xa4, 0xe6, 0x40, 0x55, 0x2a, 0x9d, 0x61, 0xe4, 0xe2, 0x71,
+ 0x87, 0xb8, 0x35, 0xb8, 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x8d, 0x8b, 0x0d, 0x62, 0x94, 0x04, 0xa3,
+ 0x02, 0xa3, 0x06, 0xb7, 0x91, 0xb2, 0x1e, 0x1e, 0xb7, 0xeb, 0x05, 0x80, 0x95, 0x3a, 0x71, 0x9e,
+ 0xb8, 0x27, 0xcf, 0xb0, 0xe2, 0xf9, 0x06, 0x2d, 0xc6, 0x20, 0xa8, 0x6e, 0xa1, 0x12, 0x2e, 0x3e,
+ 0xa8, 0xe2, 0xf8, 0x94, 0xd4, 0xbc, 0xfc, 0xdc, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23,
+ 0x4d, 0xbc, 0xe6, 0x41, 0x9d, 0xe2, 0x02, 0xd2, 0xe1, 0xa4, 0x04, 0x32, 0xf5, 0xd3, 0x3d, 0x79,
+ 0xd1, 0xca, 0xc4, 0xdc, 0x1c, 0x2b, 0x25, 0x54, 0xe3, 0x94, 0x20, 0xd6, 0xf1, 0x42, 0x45, 0x5d,
+ 0x20, 0x82, 0x27, 0x11, 0xde, 0x01, 0x8b, 0x08, 0xa9, 0x71, 0xb1, 0x82, 0xd5, 0x83, 0x7d, 0xc3,
+ 0xe9, 0x24, 0xf0, 0xe9, 0x9e, 0x3c, 0x0f, 0xc4, 0x38, 0xb0, 0xb0, 0x52, 0x10, 0x44, 0x5a, 0xa8,
+ 0x8b, 0x91, 0x4b, 0x28, 0xb1, 0xb4, 0x24, 0x23, 0xbf, 0x28, 0xb3, 0xa4, 0x32, 0x3e, 0x37, 0xb5,
+ 0x24, 0x31, 0x25, 0xb1, 0x24, 0x51, 0x82, 0x09, 0x1c, 0x06, 0xc6, 0x78, 0xdd, 0x0c, 0xb6, 0xc8,
+ 0x11, 0xa6, 0xd7, 0x17, 0xaa, 0xd5, 0x49, 0x0d, 0xea, 0x7a, 0x49, 0x88, 0x75, 0x98, 0x86, 0x43,
+ 0x7d, 0x20, 0x98, 0x88, 0xae, 0xd5, 0x8a, 0xe5, 0xc5, 0x02, 0x79, 0x46, 0x27, 0xff, 0x13, 0x8f,
+ 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b,
+ 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2,
+ 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0xb8, 0x4c, 0x37, 0x27, 0x31, 0xa9, 0x18, 0xca, 0xd6, 0x2f, 0x33,
+ 0xd5, 0xaf, 0x40, 0x8d, 0xfa, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0x70, 0x94, 0x1b, 0x03,
+ 0x02, 0x00, 0x00, 0xff, 0xff, 0x5e, 0x5d, 0x33, 0x24, 0xb9, 0x02, 0x00, 0x00,
+}
+
+func (this *GenesisDenom) Equal(that interface{}) bool {
+ if that == nil {
+ return this == nil
+ }
+
+ that1, ok := that.(*GenesisDenom)
+ if !ok {
+ that2, ok := that.(GenesisDenom)
+ if ok {
+ that1 = &that2
+ } else {
+ return false
+ }
+ }
+ if that1 == nil {
+ return this == nil
+ } else if this == nil {
+ return false
+ }
+ if this.Denom != that1.Denom {
+ return false
+ }
+ if !this.AuthorityMetadata.Equal(&that1.AuthorityMetadata) {
+ return false
+ }
+ return true
+}
+func (m *GenesisState) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.FactoryDenoms) > 0 {
+ for iNdEx := len(m.FactoryDenoms) - 1; iNdEx >= 0; iNdEx-- {
+ {
+ size, err := m.FactoryDenoms[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenesis(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x12
+ }
+ }
+ {
+ size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenesis(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ return len(dAtA) - i, nil
+}
+
+func (m *GenesisDenom) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *GenesisDenom) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GenesisDenom) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.AuthorityMetadata.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenesis(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x12
+ if len(m.Denom) > 0 {
+ i -= len(m.Denom)
+ copy(dAtA[i:], m.Denom)
+ i = encodeVarintGenesis(dAtA, i, uint64(len(m.Denom)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
+ offset -= sovGenesis(v)
+ base := offset
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *GenesisState) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = m.Params.Size()
+ n += 1 + l + sovGenesis(uint64(l))
+ if len(m.FactoryDenoms) > 0 {
+ for _, e := range m.FactoryDenoms {
+ l = e.Size()
+ n += 1 + l + sovGenesis(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *GenesisDenom) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.Denom)
+ if l > 0 {
+ n += 1 + l + sovGenesis(uint64(l))
+ }
+ l = m.AuthorityMetadata.Size()
+ n += 1 + l + sovGenesis(uint64(l))
+ return n
+}
+
+func sovGenesis(x uint64) (n int) {
+ return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozGenesis(x uint64) (n int) {
+ return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *GenesisState) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: GenesisState: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field FactoryDenoms", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.FactoryDenoms = append(m.FactoryDenoms, GenesisDenom{})
+ if err := m.FactoryDenoms[len(m.FactoryDenoms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenesis(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *GenesisDenom) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: GenesisDenom: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: GenesisDenom: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Denom = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field AuthorityMetadata", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.AuthorityMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenesis(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenesis
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipGenesis(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenesis
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthGenesis
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupGenesis
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthGenesis
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/tokenfactory/types/genesis_test.go b/x/tokenfactory/types/genesis_test.go
new file mode 100644
index 0000000000..8263ede195
--- /dev/null
+++ b/x/tokenfactory/types/genesis_test.go
@@ -0,0 +1,133 @@
+package types_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func TestGenesisState_Validate(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ genState *types.GenesisState
+ shouldErr bool
+ }{
+ {
+ name: "default is valid returns no error",
+ genState: types.DefaultGenesis(),
+ },
+ {
+ name: "valid genesis state returns no error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "different admin from creator returns no error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "empty admin returns no error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "no admin returns no error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ },
+ },
+ },
+ },
+ {
+ name: "invalid admin returns error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "moose",
+ },
+ },
+ },
+ },
+ shouldErr: true,
+ },
+ {
+ name: "multiple denoms returns no error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "",
+ },
+ },
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/litecoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "duplicate denoms returns error",
+ genState: &types.GenesisState{
+ FactoryDenoms: []types.GenesisDenom{
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "",
+ },
+ },
+ {
+ Denom: "factory/cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69/bitcoin",
+ AuthorityMetadata: types.DenomAuthorityMetadata{
+ Admin: "",
+ },
+ },
+ },
+ },
+ shouldErr: true,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.genState.Validate()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/x/tokenfactory/types/keys.go b/x/tokenfactory/types/keys.go
new file mode 100644
index 0000000000..9334055cf3
--- /dev/null
+++ b/x/tokenfactory/types/keys.go
@@ -0,0 +1,43 @@
+package types
+
+import "strings"
+
+const (
+ ModuleName = "tokenfactory"
+ StoreKey = ModuleName
+ RouterKey = ModuleName
+ QuerierRoute = ModuleName
+
+ ActionCreateDenom = "create_denom"
+ ActionMint = "tf_mint"
+ ActionBurn = "tf_burn"
+ ActionSetDenomMetadata = "set_denom_metadata"
+ ActionUpdateParams = "update_params"
+)
+
+// KeySeparator is used to combine parts of the keys in the store
+const KeySeparator = "|"
+
+var (
+ DenomAuthorityMetadataKey = "authoritymetadata"
+ DenomsPrefixKey = "denoms"
+ CreatorPrefixKey = "creator"
+ ParamsPrefixKey = "params"
+)
+
+// GetDenomPrefixStore returns the store prefix where all the data associated with a specific denom
+// is stored
+func GetDenomPrefixStore(denom string) []byte {
+ return []byte(strings.Join([]string{DenomsPrefixKey, denom, ""}, KeySeparator))
+}
+
+// GetCreatorsPrefix returns the store prefix where the list of the denoms created by a specific
+// creator are stored
+func GetCreatorPrefix(creator string) []byte {
+ return []byte(strings.Join([]string{CreatorPrefixKey, creator, ""}, KeySeparator))
+}
+
+// GetCreatorsPrefix returns the store prefix where a list of all creator addresses are stored
+func GetCreatorsPrefix() []byte {
+ return []byte(strings.Join([]string{CreatorPrefixKey, ""}, KeySeparator))
+}
diff --git a/x/tokenfactory/types/models.pb.go b/x/tokenfactory/types/models.pb.go
new file mode 100644
index 0000000000..081cea091d
--- /dev/null
+++ b/x/tokenfactory/types/models.pb.go
@@ -0,0 +1,352 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: desmos/tokenfactory/v1beta1/models.proto
+
+package types
+
+import (
+ fmt "fmt"
+ _ "github.com/cosmos/cosmos-proto"
+ _ "github.com/cosmos/gogoproto/gogoproto"
+ proto "github.com/cosmos/gogoproto/proto"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// DenomAuthorityMetadata specifies metadata for addresses that have specific
+// capabilities over a token factory denom. Right now there is only one Admin
+// permission, but is planned to be extended to the future.
+type DenomAuthorityMetadata struct {
+ // Can be empty for no admin, or a valid osmosis address
+ Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty" yaml:"admin"`
+}
+
+func (m *DenomAuthorityMetadata) Reset() { *m = DenomAuthorityMetadata{} }
+func (m *DenomAuthorityMetadata) String() string { return proto.CompactTextString(m) }
+func (*DenomAuthorityMetadata) ProtoMessage() {}
+func (*DenomAuthorityMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_7ae908e7df623a71, []int{0}
+}
+func (m *DenomAuthorityMetadata) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *DenomAuthorityMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_DenomAuthorityMetadata.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *DenomAuthorityMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DenomAuthorityMetadata.Merge(m, src)
+}
+func (m *DenomAuthorityMetadata) XXX_Size() int {
+ return m.Size()
+}
+func (m *DenomAuthorityMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_DenomAuthorityMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DenomAuthorityMetadata proto.InternalMessageInfo
+
+func (m *DenomAuthorityMetadata) GetAdmin() string {
+ if m != nil {
+ return m.Admin
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*DenomAuthorityMetadata)(nil), "desmos.tokenfactory.v1beta1.DenomAuthorityMetadata")
+}
+
+func init() {
+ proto.RegisterFile("desmos/tokenfactory/v1beta1/models.proto", fileDescriptor_7ae908e7df623a71)
+}
+
+var fileDescriptor_7ae908e7df623a71 = []byte{
+ // 253 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x48, 0x49, 0x2d, 0xce,
+ 0xcd, 0x2f, 0xd6, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x4b, 0x4b, 0x4c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4,
+ 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0xcf, 0xcd, 0x4f, 0x49, 0xcd, 0x29, 0xd6, 0x2b,
+ 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xa8, 0xd4, 0x43, 0x56, 0xa9, 0x07, 0x55, 0x29, 0x25,
+ 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa7, 0x0f, 0x62, 0x41, 0xb4, 0x48, 0x49, 0x26, 0xe7, 0x83,
+ 0xb4, 0xc4, 0x43, 0x24, 0x20, 0x1c, 0x88, 0x94, 0x52, 0x1c, 0x97, 0x98, 0x4b, 0x6a, 0x5e, 0x7e,
+ 0xae, 0x63, 0x69, 0x49, 0x46, 0x7e, 0x51, 0x66, 0x49, 0xa5, 0x6f, 0x6a, 0x49, 0x62, 0x4a, 0x62,
+ 0x49, 0xa2, 0x90, 0x1d, 0x17, 0x6b, 0x62, 0x4a, 0x6e, 0x66, 0x9e, 0x04, 0xa3, 0x02, 0xa3, 0x06,
+ 0xa7, 0x93, 0xc6, 0xa7, 0x7b, 0xf2, 0x3c, 0x95, 0x89, 0xb9, 0x39, 0x56, 0x4a, 0x60, 0x61, 0xa5,
+ 0x4b, 0x5b, 0x74, 0x45, 0xa0, 0x46, 0x39, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x07, 0x97, 0x14,
+ 0x65, 0xe6, 0xa5, 0x07, 0x41, 0xb4, 0x59, 0xb1, 0xbc, 0x58, 0x20, 0xcf, 0xe8, 0xe4, 0x7f, 0xe2,
+ 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70,
+ 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xa6, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49,
+ 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x10, 0x2f, 0xe9, 0xe6, 0x24, 0x26, 0x15, 0x43, 0xd9, 0xfa, 0x65,
+ 0xa6, 0xfa, 0x15, 0xa8, 0xa1, 0x51, 0x52, 0x59, 0x90, 0x5a, 0x9c, 0xc4, 0x06, 0x76, 0xb7, 0x31,
+ 0x20, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x7d, 0xe6, 0x6f, 0x31, 0x01, 0x00, 0x00,
+}
+
+func (this *DenomAuthorityMetadata) Equal(that interface{}) bool {
+ if that == nil {
+ return this == nil
+ }
+
+ that1, ok := that.(*DenomAuthorityMetadata)
+ if !ok {
+ that2, ok := that.(DenomAuthorityMetadata)
+ if ok {
+ that1 = &that2
+ } else {
+ return false
+ }
+ }
+ if that1 == nil {
+ return this == nil
+ } else if this == nil {
+ return false
+ }
+ if this.Admin != that1.Admin {
+ return false
+ }
+ return true
+}
+func (m *DenomAuthorityMetadata) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *DenomAuthorityMetadata) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *DenomAuthorityMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.Admin) > 0 {
+ i -= len(m.Admin)
+ copy(dAtA[i:], m.Admin)
+ i = encodeVarintModels(dAtA, i, uint64(len(m.Admin)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func encodeVarintModels(dAtA []byte, offset int, v uint64) int {
+ offset -= sovModels(v)
+ base := offset
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *DenomAuthorityMetadata) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.Admin)
+ if l > 0 {
+ n += 1 + l + sovModels(uint64(l))
+ }
+ return n
+}
+
+func sovModels(x uint64) (n int) {
+ return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozModels(x uint64) (n int) {
+ return sovModels(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *DenomAuthorityMetadata) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowModels
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: DenomAuthorityMetadata: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: DenomAuthorityMetadata: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowModels
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthModels
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthModels
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Admin = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipModels(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthModels
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipModels(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowModels
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowModels
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowModels
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthModels
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupModels
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthModels
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthModels = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowModels = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupModels = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/tokenfactory/types/msgs.go b/x/tokenfactory/types/msgs.go
new file mode 100644
index 0000000000..32cc348b4f
--- /dev/null
+++ b/x/tokenfactory/types/msgs.go
@@ -0,0 +1,256 @@
+package types
+
+import (
+ "cosmossdk.io/errors"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+var (
+ _ sdk.Msg = &MsgCreateDenom{}
+ _ legacytx.LegacyMsg = &MsgCreateDenom{}
+)
+
+// NewMsgCreateDenom creates a new MsgCreateDenom instance
+func NewMsgCreateDenom(subspaceID uint64, sender, subdenom string) *MsgCreateDenom {
+ return &MsgCreateDenom{
+ SubspaceID: subspaceID,
+ Sender: sender,
+ Subdenom: subdenom,
+ }
+}
+
+// ValidateBasic implements sdk.Msg
+func (msg MsgCreateDenom) ValidateBasic() error {
+ if msg.SubspaceID == 0 {
+ return errors.Wrapf(sdkerrors.ErrInvalidRequest, "Invalid subspace id: %d", msg.SubspaceID)
+ }
+
+ _, err := sdk.AccAddressFromBech32(msg.Sender)
+ if err != nil {
+ return errors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address: %s", err)
+ }
+
+ _, err = GetTokenDenom(msg.Sender, msg.Subdenom)
+ if err != nil {
+ return errors.Wrap(ErrInvalidDenom, err.Error())
+ }
+
+ return nil
+}
+
+// GetSigners implements sdk.Msg
+func (msg MsgCreateDenom) GetSigners() []sdk.AccAddress {
+ sender, _ := sdk.AccAddressFromBech32(msg.Sender)
+ return []sdk.AccAddress{sender}
+}
+
+// Route implements legacytx.LegacyMsg
+func (msg MsgCreateDenom) Route() string { return RouterKey }
+
+// Type implements legacytx.LegacyMsg
+func (msg MsgCreateDenom) Type() string { return ActionCreateDenom }
+
+// GetSignBytes implements legacytx.LegacyMsg
+func (msg MsgCreateDenom) GetSignBytes() []byte {
+ return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var (
+ _ sdk.Msg = &MsgMint{}
+ _ legacytx.LegacyMsg = &MsgMint{}
+)
+
+// NewMsgMint creates a new MsgMint instance
+func NewMsgMint(subspaceID uint64, sender string, amount sdk.Coin) *MsgMint {
+ return &MsgMint{
+ SubspaceID: subspaceID,
+ Sender: sender,
+ Amount: amount,
+ }
+}
+
+// ValidateBasic implements sdk.Msg
+func (msg MsgMint) ValidateBasic() error {
+ if msg.SubspaceID == 0 {
+ return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid subspace id: %d", msg.SubspaceID)
+ }
+
+ _, err := sdk.AccAddressFromBech32(msg.Sender)
+ if err != nil {
+ return errors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address: %s", err)
+ }
+
+ if !msg.Amount.IsValid() || msg.Amount.Amount.Equal(sdk.ZeroInt()) {
+ return errors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
+ }
+
+ return nil
+}
+
+// GetSigners implements sdk.Msg
+func (msg MsgMint) GetSigners() []sdk.AccAddress {
+ sender, _ := sdk.AccAddressFromBech32(msg.Sender)
+ return []sdk.AccAddress{sender}
+}
+
+// Route implements legacytx.LegacyMsg
+func (msg MsgMint) Route() string { return RouterKey }
+
+// Type implements legacytx.LegacyMsg
+func (msg MsgMint) Type() string { return ActionMint }
+
+// MsgMint implements legacytx.LegacyMsg
+func (msg MsgMint) GetSignBytes() []byte {
+ return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var (
+ _ sdk.Msg = &MsgBurn{}
+ _ legacytx.LegacyMsg = &MsgBurn{}
+)
+
+// NewMsgCreateDenom creates a new MsgBurn instance
+func NewMsgBurn(subspaceID uint64, sender string, amount sdk.Coin) *MsgBurn {
+ return &MsgBurn{
+ SubspaceID: subspaceID,
+ Sender: sender,
+ Amount: amount,
+ }
+}
+
+// ValidateBasic implements sdk.Msg
+func (msg MsgBurn) ValidateBasic() error {
+ if msg.SubspaceID == 0 {
+ return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid subspace id: %d", msg.SubspaceID)
+ }
+
+ _, err := sdk.AccAddressFromBech32(msg.Sender)
+ if err != nil {
+ return errors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address: %s", err)
+ }
+
+ if !msg.Amount.IsValid() || msg.Amount.Amount.Equal(sdk.ZeroInt()) {
+ return errors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
+ }
+
+ return nil
+}
+
+// GetSigners implements sdk.Msg
+func (msg MsgBurn) GetSigners() []sdk.AccAddress {
+ sender, _ := sdk.AccAddressFromBech32(msg.Sender)
+ return []sdk.AccAddress{sender}
+}
+
+// Route implements legacytx.LegacyMsg
+func (msg MsgBurn) Route() string { return RouterKey }
+
+// Type implements legacytx.LegacyMsg
+func (msg MsgBurn) Type() string { return ActionBurn }
+
+// GetSignBytes implements legacytx.LegacyMsg
+func (msg MsgBurn) GetSignBytes() []byte {
+ return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var (
+ _ sdk.Msg = &MsgSetDenomMetadata{}
+ _ legacytx.LegacyMsg = &MsgSetDenomMetadata{}
+)
+
+// NewMsgCreateDenom creates a new MsgSetDenomMetadata instance
+func NewMsgSetDenomMetadata(subspaceID uint64, sender string, metadata banktypes.Metadata) *MsgSetDenomMetadata {
+ return &MsgSetDenomMetadata{
+ SubspaceID: subspaceID,
+ Sender: sender,
+ Metadata: metadata,
+ }
+}
+
+// ValidateBasic implements sdk.Msg
+func (msg MsgSetDenomMetadata) ValidateBasic() error {
+ if msg.SubspaceID == 0 {
+ return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid subspace id: %d", msg.SubspaceID)
+ }
+
+ _, err := sdk.AccAddressFromBech32(msg.Sender)
+ if err != nil {
+ return errors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address: %s", err)
+ }
+
+ err = msg.Metadata.Validate()
+ if err != nil {
+ return err
+ }
+
+ _, _, err = DeconstructDenom(msg.Metadata.Base)
+ return err
+}
+
+// GetSigners implements sdk.Msg
+func (msg MsgSetDenomMetadata) GetSigners() []sdk.AccAddress {
+ sender, _ := sdk.AccAddressFromBech32(msg.Sender)
+ return []sdk.AccAddress{sender}
+}
+
+// Route implements legacytx.LegacyMsg
+func (msg MsgSetDenomMetadata) Route() string { return RouterKey }
+
+// Type implements legacytx.LegacyMsg
+func (msg MsgSetDenomMetadata) Type() string { return ActionSetDenomMetadata }
+
+// GetSignBytes implements legacytx.LegacyMsg
+func (msg MsgSetDenomMetadata) GetSignBytes() []byte {
+ return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var (
+ _ sdk.Msg = &MsgUpdateParams{}
+ _ legacytx.LegacyMsg = &MsgUpdateParams{}
+)
+
+// NewMsgUpdateParams creates a new MsgUpdateParams instance
+func NewMsgUpdateParams(params Params, authority string) *MsgUpdateParams {
+ return &MsgUpdateParams{
+ Params: params,
+ Authority: authority,
+ }
+}
+
+// ValidateBasic implements sdk.Msg
+func (msg MsgUpdateParams) ValidateBasic() error {
+ _, err := sdk.AccAddressFromBech32(msg.Authority)
+ if err != nil {
+ return errors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid authority address: %s", err)
+ }
+
+ return msg.Params.Validate()
+}
+
+// GetSigners implements sdk.Msg
+func (msg MsgUpdateParams) GetSigners() []sdk.AccAddress {
+ sender, _ := sdk.AccAddressFromBech32(msg.Authority)
+ return []sdk.AccAddress{sender}
+}
+
+// Route implements legacytx.LegacyMsg
+func (msg MsgUpdateParams) Route() string { return RouterKey }
+
+// Type implements legacytx.LegacyMsg
+func (msg MsgUpdateParams) Type() string { return ActionUpdateParams }
+
+// GetSignBytes implements legacytx.LegacyMsg
+func (msg MsgUpdateParams) GetSignBytes() []byte {
+ return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
diff --git a/x/tokenfactory/types/msgs.pb.go b/x/tokenfactory/types/msgs.pb.go
new file mode 100644
index 0000000000..d3bdcac853
--- /dev/null
+++ b/x/tokenfactory/types/msgs.pb.go
@@ -0,0 +1,2422 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: desmos/tokenfactory/v1beta1/msgs.proto
+
+package types
+
+import (
+ context "context"
+ fmt "fmt"
+ _ "github.com/cosmos/cosmos-proto"
+ types "github.com/cosmos/cosmos-sdk/types"
+ _ "github.com/cosmos/cosmos-sdk/types/msgservice"
+ _ "github.com/cosmos/cosmos-sdk/types/tx/amino"
+ types1 "github.com/cosmos/cosmos-sdk/x/bank/types"
+ _ "github.com/cosmos/gogoproto/gogoproto"
+ grpc1 "github.com/cosmos/gogoproto/grpc"
+ proto "github.com/cosmos/gogoproto/proto"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// MsgCreateDenom represents the message to be used to create a denom for
+// subspace
+//
+// Since: Desmos 6.0.0
+type MsgCreateDenom struct {
+ // Id of the subspace which manages the denom
+ SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"`
+ // Address of user having the permission to manage subspace denoms
+ Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+ // Subdenom name of the creating denom
+ // It can be up to 44 "alphanumeric" characters long
+ Subdenom string `protobuf:"bytes,3,opt,name=subdenom,proto3" json:"subdenom,omitempty" yaml:"subdenom"`
+}
+
+func (m *MsgCreateDenom) Reset() { *m = MsgCreateDenom{} }
+func (m *MsgCreateDenom) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateDenom) ProtoMessage() {}
+func (*MsgCreateDenom) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{0}
+}
+func (m *MsgCreateDenom) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgCreateDenom) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgCreateDenom.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgCreateDenom) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgCreateDenom.Merge(m, src)
+}
+func (m *MsgCreateDenom) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgCreateDenom) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgCreateDenom.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateDenom proto.InternalMessageInfo
+
+func (m *MsgCreateDenom) GetSubspaceID() uint64 {
+ if m != nil {
+ return m.SubspaceID
+ }
+ return 0
+}
+
+func (m *MsgCreateDenom) GetSender() string {
+ if m != nil {
+ return m.Sender
+ }
+ return ""
+}
+
+func (m *MsgCreateDenom) GetSubdenom() string {
+ if m != nil {
+ return m.Subdenom
+ }
+ return ""
+}
+
+// MsgCreateDenomResponse represents the Msg/CreateDenom response type
+// It returns the full string of the newly created denom
+type MsgCreateDenomResponse struct {
+ // Name of the newly created denom
+ NewTokenDenom string `protobuf:"bytes,1,opt,name=new_token_denom,json=newTokenDenom,proto3" json:"new_token_denom,omitempty" yaml:"new_token_denom"`
+}
+
+func (m *MsgCreateDenomResponse) Reset() { *m = MsgCreateDenomResponse{} }
+func (m *MsgCreateDenomResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateDenomResponse) ProtoMessage() {}
+func (*MsgCreateDenomResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{1}
+}
+func (m *MsgCreateDenomResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgCreateDenomResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgCreateDenomResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgCreateDenomResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgCreateDenomResponse.Merge(m, src)
+}
+func (m *MsgCreateDenomResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgCreateDenomResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgCreateDenomResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateDenomResponse proto.InternalMessageInfo
+
+func (m *MsgCreateDenomResponse) GetNewTokenDenom() string {
+ if m != nil {
+ return m.NewTokenDenom
+ }
+ return ""
+}
+
+// MsgMint represents the message to be used to mint subspace tokens to treasury
+// account
+//
+// Since: Desmos 6.0.0
+type MsgMint struct {
+ // Id of the subspace which manages the denom
+ SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"`
+ // Address of user having the permission to manage subspace denoms
+ Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+ // Amount of the minting subspace tokens
+ Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount" yaml:"amount"`
+}
+
+func (m *MsgMint) Reset() { *m = MsgMint{} }
+func (m *MsgMint) String() string { return proto.CompactTextString(m) }
+func (*MsgMint) ProtoMessage() {}
+func (*MsgMint) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{2}
+}
+func (m *MsgMint) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgMint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgMint.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgMint) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgMint.Merge(m, src)
+}
+func (m *MsgMint) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgMint) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgMint.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMint proto.InternalMessageInfo
+
+func (m *MsgMint) GetSubspaceID() uint64 {
+ if m != nil {
+ return m.SubspaceID
+ }
+ return 0
+}
+
+func (m *MsgMint) GetSender() string {
+ if m != nil {
+ return m.Sender
+ }
+ return ""
+}
+
+func (m *MsgMint) GetAmount() types.Coin {
+ if m != nil {
+ return m.Amount
+ }
+ return types.Coin{}
+}
+
+// MsgMintResponse represents the Msg/Mint response type
+//
+// Since: Desmos 6.0.0
+type MsgMintResponse struct {
+}
+
+func (m *MsgMintResponse) Reset() { *m = MsgMintResponse{} }
+func (m *MsgMintResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgMintResponse) ProtoMessage() {}
+func (*MsgMintResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{3}
+}
+func (m *MsgMintResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgMintResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgMintResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgMintResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgMintResponse.Merge(m, src)
+}
+func (m *MsgMintResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgMintResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgMintResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMintResponse proto.InternalMessageInfo
+
+// MsgBurn represents the message to be used to burn subspace tokens from
+// treasury account
+//
+// Since: Desmos 6.0.0
+type MsgBurn struct {
+ // Id of the subspace which manages the denom
+ SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"`
+ // Address of user having the permission to manage subspace denoms
+ Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+ // Amount of the burning subspace tokens
+ Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount" yaml:"amount"`
+}
+
+func (m *MsgBurn) Reset() { *m = MsgBurn{} }
+func (m *MsgBurn) String() string { return proto.CompactTextString(m) }
+func (*MsgBurn) ProtoMessage() {}
+func (*MsgBurn) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{4}
+}
+func (m *MsgBurn) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgBurn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgBurn.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgBurn) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgBurn.Merge(m, src)
+}
+func (m *MsgBurn) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgBurn) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgBurn.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgBurn proto.InternalMessageInfo
+
+func (m *MsgBurn) GetSubspaceID() uint64 {
+ if m != nil {
+ return m.SubspaceID
+ }
+ return 0
+}
+
+func (m *MsgBurn) GetSender() string {
+ if m != nil {
+ return m.Sender
+ }
+ return ""
+}
+
+func (m *MsgBurn) GetAmount() types.Coin {
+ if m != nil {
+ return m.Amount
+ }
+ return types.Coin{}
+}
+
+// MsgBurnResponse represents the Msg/Burn response type
+//
+// Since: Desmos 6.0.0
+type MsgBurnResponse struct {
+}
+
+func (m *MsgBurnResponse) Reset() { *m = MsgBurnResponse{} }
+func (m *MsgBurnResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgBurnResponse) ProtoMessage() {}
+func (*MsgBurnResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{5}
+}
+func (m *MsgBurnResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgBurnResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgBurnResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgBurnResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgBurnResponse.Merge(m, src)
+}
+func (m *MsgBurnResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgBurnResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgBurnResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgBurnResponse proto.InternalMessageInfo
+
+// MsgSetDenomMetadata represents the message to be used to set the subspace
+// token's bank metadata
+//
+// Since: Desmos 6.0.0
+type MsgSetDenomMetadata struct {
+ // Id of the subspace which manages the denom
+ SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"`
+ // Address of user having the permission to manage subspace denoms
+ Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+ // Metadata of the denom
+ Metadata types1.Metadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata" yaml:"metadata"`
+}
+
+func (m *MsgSetDenomMetadata) Reset() { *m = MsgSetDenomMetadata{} }
+func (m *MsgSetDenomMetadata) String() string { return proto.CompactTextString(m) }
+func (*MsgSetDenomMetadata) ProtoMessage() {}
+func (*MsgSetDenomMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{6}
+}
+func (m *MsgSetDenomMetadata) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgSetDenomMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgSetDenomMetadata.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgSetDenomMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgSetDenomMetadata.Merge(m, src)
+}
+func (m *MsgSetDenomMetadata) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgSetDenomMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgSetDenomMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSetDenomMetadata proto.InternalMessageInfo
+
+func (m *MsgSetDenomMetadata) GetSubspaceID() uint64 {
+ if m != nil {
+ return m.SubspaceID
+ }
+ return 0
+}
+
+func (m *MsgSetDenomMetadata) GetSender() string {
+ if m != nil {
+ return m.Sender
+ }
+ return ""
+}
+
+func (m *MsgSetDenomMetadata) GetMetadata() types1.Metadata {
+ if m != nil {
+ return m.Metadata
+ }
+ return types1.Metadata{}
+}
+
+// MsgSetDenomMetadataResponse represents the Msg/SetDenomMetadata response type
+//
+// Since: Desmos 6.0.0
+type MsgSetDenomMetadataResponse struct {
+}
+
+func (m *MsgSetDenomMetadataResponse) Reset() { *m = MsgSetDenomMetadataResponse{} }
+func (m *MsgSetDenomMetadataResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgSetDenomMetadataResponse) ProtoMessage() {}
+func (*MsgSetDenomMetadataResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{7}
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgSetDenomMetadataResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgSetDenomMetadataResponse.Merge(m, src)
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgSetDenomMetadataResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgSetDenomMetadataResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSetDenomMetadataResponse proto.InternalMessageInfo
+
+// MsgUpdateParams is the Msg/UpdateParams request type.
+//
+// Since: Desmos 6.0.0
+type MsgUpdateParams struct {
+ // authority is the address that controls the module (defaults to x/gov unless
+ // overwritten).
+ Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty" yaml:"authority"`
+ // params defines the parameters to update.
+ //
+ // NOTE: All parameters must be supplied.
+ Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params" yaml:"params"`
+}
+
+func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} }
+func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) }
+func (*MsgUpdateParams) ProtoMessage() {}
+func (*MsgUpdateParams) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{8}
+}
+func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgUpdateParams) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgUpdateParams.Merge(m, src)
+}
+func (m *MsgUpdateParams) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgUpdateParams) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo
+
+func (m *MsgUpdateParams) GetAuthority() string {
+ if m != nil {
+ return m.Authority
+ }
+ return ""
+}
+
+func (m *MsgUpdateParams) GetParams() Params {
+ if m != nil {
+ return m.Params
+ }
+ return Params{}
+}
+
+// MsgUpdateParamsResponse represents the Msg/UpdateParams response type
+//
+// Since: Desmos 6.0.0
+type MsgUpdateParamsResponse struct {
+}
+
+func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} }
+func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgUpdateParamsResponse) ProtoMessage() {}
+func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_096696d6347c943d, []int{9}
+}
+func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src)
+}
+func (m *MsgUpdateParamsResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo
+
+func init() {
+ proto.RegisterType((*MsgCreateDenom)(nil), "desmos.tokenfactory.v1beta1.MsgCreateDenom")
+ proto.RegisterType((*MsgCreateDenomResponse)(nil), "desmos.tokenfactory.v1beta1.MsgCreateDenomResponse")
+ proto.RegisterType((*MsgMint)(nil), "desmos.tokenfactory.v1beta1.MsgMint")
+ proto.RegisterType((*MsgMintResponse)(nil), "desmos.tokenfactory.v1beta1.MsgMintResponse")
+ proto.RegisterType((*MsgBurn)(nil), "desmos.tokenfactory.v1beta1.MsgBurn")
+ proto.RegisterType((*MsgBurnResponse)(nil), "desmos.tokenfactory.v1beta1.MsgBurnResponse")
+ proto.RegisterType((*MsgSetDenomMetadata)(nil), "desmos.tokenfactory.v1beta1.MsgSetDenomMetadata")
+ proto.RegisterType((*MsgSetDenomMetadataResponse)(nil), "desmos.tokenfactory.v1beta1.MsgSetDenomMetadataResponse")
+ proto.RegisterType((*MsgUpdateParams)(nil), "desmos.tokenfactory.v1beta1.MsgUpdateParams")
+ proto.RegisterType((*MsgUpdateParamsResponse)(nil), "desmos.tokenfactory.v1beta1.MsgUpdateParamsResponse")
+}
+
+func init() {
+ proto.RegisterFile("desmos/tokenfactory/v1beta1/msgs.proto", fileDescriptor_096696d6347c943d)
+}
+
+var fileDescriptor_096696d6347c943d = []byte{
+ // 822 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x96, 0x4d, 0x4f, 0xdb, 0x48,
+ 0x18, 0xc7, 0x63, 0x60, 0xb3, 0x30, 0xe1, 0xd5, 0xb0, 0xbc, 0x98, 0xc5, 0x46, 0x5e, 0x76, 0x95,
+ 0xcd, 0x82, 0xbd, 0xc0, 0xa2, 0x5d, 0xe5, 0x86, 0x61, 0x0f, 0xac, 0x14, 0xed, 0x2a, 0x6c, 0x85,
+ 0x84, 0x2a, 0x45, 0x93, 0x78, 0x6a, 0xac, 0x60, 0x4f, 0xe4, 0x99, 0x40, 0x73, 0xa9, 0xaa, 0x1e,
+ 0x39, 0xf5, 0xdc, 0x73, 0x0f, 0x6d, 0x4f, 0x39, 0xf4, 0x03, 0xf4, 0xc8, 0x11, 0xf5, 0xd4, 0x53,
+ 0x5a, 0x85, 0x43, 0x8e, 0x95, 0xf2, 0x09, 0x2a, 0x8f, 0xc7, 0x8e, 0x13, 0x5a, 0xd2, 0xdc, 0x38,
+ 0xf4, 0x02, 0xe3, 0x79, 0xfe, 0xcf, 0xcb, 0xfc, 0xe6, 0xf1, 0xe3, 0x80, 0x5f, 0x4c, 0x44, 0x1c,
+ 0x4c, 0x74, 0x8a, 0xcb, 0xc8, 0x7d, 0x00, 0x4b, 0x14, 0x7b, 0x35, 0xfd, 0x6c, 0xb3, 0x88, 0x28,
+ 0xdc, 0xd4, 0x1d, 0x62, 0x11, 0xad, 0xe2, 0x61, 0x8a, 0xc5, 0xe5, 0x40, 0xa7, 0xc5, 0x75, 0x1a,
+ 0xd7, 0x49, 0x33, 0xd0, 0xb1, 0x5d, 0xac, 0xb3, 0xbf, 0x81, 0x5e, 0x9a, 0xb3, 0xb0, 0x85, 0xd9,
+ 0x52, 0xf7, 0x57, 0x7c, 0x57, 0x2e, 0x61, 0x96, 0xad, 0x08, 0x09, 0x8a, 0xb2, 0x94, 0xb0, 0xed,
+ 0xde, 0xb0, 0xbb, 0xe5, 0xc8, 0xee, 0x3f, 0x70, 0xfb, 0x52, 0x60, 0x2f, 0x04, 0x81, 0x83, 0x07,
+ 0x6e, 0x5a, 0xe0, 0xae, 0x0e, 0xb1, 0xf4, 0x33, 0x56, 0x3a, 0x37, 0xa4, 0x6f, 0x3b, 0x61, 0x05,
+ 0x7a, 0xd0, 0xe1, 0x21, 0xd4, 0xb6, 0x00, 0x26, 0x73, 0xc4, 0xda, 0xf3, 0x10, 0xa4, 0x68, 0x1f,
+ 0xb9, 0xd8, 0x11, 0xff, 0x06, 0x29, 0x52, 0x2d, 0x92, 0x0a, 0x2c, 0xa1, 0x82, 0x6d, 0x2e, 0x0a,
+ 0xab, 0x42, 0x7a, 0xc4, 0x58, 0x6b, 0x36, 0x14, 0x70, 0xc8, 0xb7, 0x0f, 0xf6, 0xdb, 0x0d, 0x45,
+ 0xac, 0x41, 0xe7, 0x34, 0xab, 0xc6, 0xa4, 0x6a, 0x1e, 0x84, 0x4f, 0x07, 0xa6, 0xb8, 0x0b, 0x92,
+ 0x04, 0xb9, 0x26, 0xf2, 0x16, 0x87, 0x56, 0x85, 0xf4, 0x98, 0xf1, 0x6b, 0xbb, 0xa1, 0x4c, 0x70,
+ 0x1f, 0xb6, 0xaf, 0xbe, 0x7d, 0xbd, 0x31, 0xc7, 0xcf, 0xb3, 0x6b, 0x9a, 0x1e, 0x22, 0xe4, 0x90,
+ 0x7a, 0xb6, 0x6b, 0xe5, 0xb9, 0xa3, 0xa8, 0x83, 0x51, 0x52, 0x2d, 0x9a, 0x7e, 0x55, 0x8b, 0xc3,
+ 0x2c, 0xc8, 0x6c, 0xbb, 0xa1, 0x4c, 0x45, 0x89, 0x99, 0x45, 0xcd, 0x47, 0xa2, 0xec, 0xcf, 0x4f,
+ 0x5a, 0xf5, 0x0c, 0xf7, 0xbe, 0x68, 0xd5, 0x33, 0x3f, 0x70, 0x0e, 0xdd, 0x27, 0x54, 0xef, 0x83,
+ 0xf9, 0xee, 0x9d, 0x3c, 0x22, 0x15, 0xec, 0x12, 0x24, 0x1a, 0x60, 0xca, 0x45, 0xe7, 0x05, 0xc6,
+ 0xad, 0x10, 0x24, 0x16, 0x58, 0x62, 0xa9, 0xdd, 0x50, 0xe6, 0x83, 0xc4, 0x3d, 0x02, 0x35, 0x3f,
+ 0xe1, 0xa2, 0xf3, 0xff, 0xfd, 0x8d, 0x20, 0xfa, 0x9b, 0x21, 0xf0, 0x7d, 0x8e, 0x58, 0x39, 0xdb,
+ 0xa5, 0x77, 0x88, 0xe5, 0x85, 0x00, 0x92, 0xd0, 0xc1, 0x55, 0x97, 0x32, 0x94, 0xa9, 0xad, 0x25,
+ 0x8d, 0xeb, 0xfd, 0xc6, 0x0c, 0xdb, 0x5a, 0xdb, 0xc3, 0xb6, 0x6b, 0x1c, 0x5d, 0x36, 0x94, 0x44,
+ 0x27, 0x45, 0xe0, 0xa6, 0xbe, 0x7a, 0xaf, 0xa4, 0x2d, 0x9b, 0x9e, 0x54, 0x8b, 0x5a, 0x09, 0x3b,
+ 0xbc, 0x13, 0xf9, 0xbf, 0x0d, 0x62, 0x96, 0x75, 0x5a, 0xab, 0x20, 0xc2, 0x22, 0x90, 0x67, 0xad,
+ 0x7a, 0x66, 0xfc, 0x14, 0x59, 0xb0, 0x54, 0x2b, 0xf8, 0xbd, 0x4e, 0x5e, 0xb4, 0xea, 0x19, 0x21,
+ 0xcf, 0x2b, 0xc8, 0xca, 0x3d, 0xf7, 0x34, 0xd9, 0xb9, 0x27, 0x1f, 0x9b, 0x3a, 0x03, 0xa6, 0xf8,
+ 0x32, 0xbc, 0x99, 0x90, 0xaa, 0x51, 0xf5, 0xdc, 0x6f, 0x54, 0x07, 0xa6, 0xea, 0x63, 0xe3, 0x54,
+ 0xfd, 0x65, 0x44, 0xf5, 0xf9, 0x10, 0x98, 0xcd, 0x11, 0xeb, 0x10, 0x51, 0xd6, 0xbb, 0x39, 0x44,
+ 0xa1, 0x09, 0x29, 0xbc, 0x43, 0x84, 0x8f, 0xc0, 0xa8, 0xc3, 0xab, 0xe2, 0x88, 0x57, 0x3a, 0x88,
+ 0xdd, 0x72, 0x84, 0x38, 0x2c, 0xdd, 0xf8, 0x91, 0x63, 0xe6, 0x63, 0x22, 0x74, 0x56, 0x03, 0x56,
+ 0x51, 0xb0, 0x6c, 0xa6, 0x87, 0x96, 0xd4, 0xa1, 0xd5, 0x8b, 0x43, 0x5d, 0x01, 0xcb, 0x9f, 0xd9,
+ 0x8e, 0x28, 0x7e, 0x14, 0x18, 0xd9, 0x7b, 0x15, 0x13, 0x52, 0xf4, 0x1f, 0x1b, 0xaf, 0xe2, 0x3f,
+ 0x60, 0x0c, 0x56, 0xe9, 0x09, 0xf6, 0x6c, 0x5a, 0xe3, 0x33, 0x64, 0xbd, 0xdd, 0x50, 0xa6, 0xf9,
+ 0xe5, 0x87, 0xa6, 0x2f, 0x03, 0xe8, 0xb8, 0x8b, 0x47, 0x20, 0x19, 0x0c, 0x6d, 0x86, 0x31, 0xb5,
+ 0xf5, 0x93, 0x76, 0xcb, 0x97, 0x49, 0x0b, 0x0a, 0x30, 0xa4, 0xee, 0x76, 0x0b, 0x02, 0x70, 0x0a,
+ 0x3c, 0x5c, 0xf6, 0x4f, 0x9f, 0x41, 0x27, 0x91, 0x8f, 0x61, 0x8d, 0x63, 0x78, 0x18, 0xfb, 0x78,
+ 0xd4, 0xf4, 0x9e, 0xd3, 0xa9, 0x4b, 0x60, 0xa1, 0x67, 0x2b, 0x84, 0xb1, 0xf5, 0x72, 0x04, 0x0c,
+ 0xe7, 0x88, 0x25, 0x62, 0x90, 0x8a, 0x7f, 0x55, 0x7e, 0xbb, 0xb5, 0xe6, 0xee, 0x71, 0x2c, 0x6d,
+ 0x0f, 0x20, 0x8e, 0x66, 0xf7, 0x31, 0x18, 0x61, 0x33, 0x77, 0xad, 0x9f, 0xb3, 0xaf, 0x92, 0xd6,
+ 0xbf, 0x46, 0x15, 0x8f, 0xcd, 0x26, 0x4f, 0xdf, 0xd8, 0xbe, 0xaa, 0x7f, 0xec, 0xf8, 0x3b, 0x28,
+ 0x3e, 0x02, 0xd3, 0x37, 0xde, 0xbf, 0xdf, 0xfb, 0x45, 0xe8, 0xf5, 0x90, 0xfe, 0x1a, 0xd4, 0x23,
+ 0xca, 0xef, 0x81, 0xf1, 0xae, 0xce, 0xed, 0x5b, 0x7d, 0x5c, 0x2d, 0xfd, 0x31, 0x88, 0x3a, 0xcc,
+ 0x29, 0x7d, 0xf7, 0xd8, 0xef, 0x43, 0xe3, 0xdf, 0xcb, 0xa6, 0x2c, 0x5c, 0x35, 0x65, 0xe1, 0x43,
+ 0x53, 0x16, 0x9e, 0x5e, 0xcb, 0x89, 0xab, 0x6b, 0x39, 0xf1, 0xee, 0x5a, 0x4e, 0x1c, 0xef, 0xc4,
+ 0x66, 0x62, 0x90, 0x60, 0xe3, 0x14, 0x16, 0x09, 0x5f, 0xeb, 0x67, 0x3b, 0x5d, 0x0d, 0xea, 0xd5,
+ 0x82, 0x31, 0x59, 0x4c, 0xb2, 0x5f, 0x35, 0xdb, 0x9f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x0f,
+ 0xfe, 0x10, 0xe3, 0x09, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// MsgClient is the client API for Msg service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type MsgClient interface {
+ // CreateDenom allows an account to create a new denom for subspace. It
+ // requires a subspace and a sub denomination. The (subspace_treasury_address,
+ // sub_denomination) tuple must be unique and cannot be re-used.
+ //
+ // The resulting denom created is defined as
+ // . The resulting denom's admin is
+ // originally set to be the subspace treasury account, and this can not be
+ // changed later.
+ //
+ // Since: Desmos 6.0.0
+ CreateDenom(ctx context.Context, in *MsgCreateDenom, opts ...grpc.CallOption) (*MsgCreateDenomResponse, error)
+ // Mint allows subspace admins to mint more of a token.
+ //
+ // Since: Desmos 6.0.0
+ Mint(ctx context.Context, in *MsgMint, opts ...grpc.CallOption) (*MsgMintResponse, error)
+ // Burn allows subspace admins to burn a token.
+ // For now, we only support burning from the treasury account.
+ //
+ // Since: Desmos 6.0.0
+ Burn(ctx context.Context, in *MsgBurn, opts ...grpc.CallOption) (*MsgBurnResponse, error)
+ // SetDenomMetadata allows subspace admins to set the denom's bank metadata.
+ //
+ // Since: Desmos 6.0.0
+ SetDenomMetadata(ctx context.Context, in *MsgSetDenomMetadata, opts ...grpc.CallOption) (*MsgSetDenomMetadataResponse, error)
+ // UpdateParams defines a (governance) operation for updating the module
+ // parameters. The authority defaults to the x/gov module account.
+ //
+ // Since: Desmos 6.0.0
+ UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error)
+}
+
+type msgClient struct {
+ cc grpc1.ClientConn
+}
+
+func NewMsgClient(cc grpc1.ClientConn) MsgClient {
+ return &msgClient{cc}
+}
+
+func (c *msgClient) CreateDenom(ctx context.Context, in *MsgCreateDenom, opts ...grpc.CallOption) (*MsgCreateDenomResponse, error) {
+ out := new(MsgCreateDenomResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Msg/CreateDenom", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) Mint(ctx context.Context, in *MsgMint, opts ...grpc.CallOption) (*MsgMintResponse, error) {
+ out := new(MsgMintResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Msg/Mint", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) Burn(ctx context.Context, in *MsgBurn, opts ...grpc.CallOption) (*MsgBurnResponse, error) {
+ out := new(MsgBurnResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Msg/Burn", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) SetDenomMetadata(ctx context.Context, in *MsgSetDenomMetadata, opts ...grpc.CallOption) (*MsgSetDenomMetadataResponse, error) {
+ out := new(MsgSetDenomMetadataResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Msg/SetDenomMetadata", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) {
+ out := new(MsgUpdateParamsResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Msg/UpdateParams", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// MsgServer is the server API for Msg service.
+type MsgServer interface {
+ // CreateDenom allows an account to create a new denom for subspace. It
+ // requires a subspace and a sub denomination. The (subspace_treasury_address,
+ // sub_denomination) tuple must be unique and cannot be re-used.
+ //
+ // The resulting denom created is defined as
+ // . The resulting denom's admin is
+ // originally set to be the subspace treasury account, and this can not be
+ // changed later.
+ //
+ // Since: Desmos 6.0.0
+ CreateDenom(context.Context, *MsgCreateDenom) (*MsgCreateDenomResponse, error)
+ // Mint allows subspace admins to mint more of a token.
+ //
+ // Since: Desmos 6.0.0
+ Mint(context.Context, *MsgMint) (*MsgMintResponse, error)
+ // Burn allows subspace admins to burn a token.
+ // For now, we only support burning from the treasury account.
+ //
+ // Since: Desmos 6.0.0
+ Burn(context.Context, *MsgBurn) (*MsgBurnResponse, error)
+ // SetDenomMetadata allows subspace admins to set the denom's bank metadata.
+ //
+ // Since: Desmos 6.0.0
+ SetDenomMetadata(context.Context, *MsgSetDenomMetadata) (*MsgSetDenomMetadataResponse, error)
+ // UpdateParams defines a (governance) operation for updating the module
+ // parameters. The authority defaults to the x/gov module account.
+ //
+ // Since: Desmos 6.0.0
+ UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error)
+}
+
+// UnimplementedMsgServer can be embedded to have forward compatible implementations.
+type UnimplementedMsgServer struct {
+}
+
+func (*UnimplementedMsgServer) CreateDenom(ctx context.Context, req *MsgCreateDenom) (*MsgCreateDenomResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method CreateDenom not implemented")
+}
+func (*UnimplementedMsgServer) Mint(ctx context.Context, req *MsgMint) (*MsgMintResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method Mint not implemented")
+}
+func (*UnimplementedMsgServer) Burn(ctx context.Context, req *MsgBurn) (*MsgBurnResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method Burn not implemented")
+}
+func (*UnimplementedMsgServer) SetDenomMetadata(ctx context.Context, req *MsgSetDenomMetadata) (*MsgSetDenomMetadataResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method SetDenomMetadata not implemented")
+}
+func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented")
+}
+
+func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
+ s.RegisterService(&_Msg_serviceDesc, srv)
+}
+
+func _Msg_CreateDenom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgCreateDenom)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).CreateDenom(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Msg/CreateDenom",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).CreateDenom(ctx, req.(*MsgCreateDenom))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Mint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgMint)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).Mint(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Msg/Mint",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).Mint(ctx, req.(*MsgMint))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Burn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgBurn)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).Burn(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Msg/Burn",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).Burn(ctx, req.(*MsgBurn))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_SetDenomMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgSetDenomMetadata)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).SetDenomMetadata(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Msg/SetDenomMetadata",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).SetDenomMetadata(ctx, req.(*MsgSetDenomMetadata))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgUpdateParams)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).UpdateParams(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Msg/UpdateParams",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _Msg_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "desmos.tokenfactory.v1beta1.Msg",
+ HandlerType: (*MsgServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "CreateDenom",
+ Handler: _Msg_CreateDenom_Handler,
+ },
+ {
+ MethodName: "Mint",
+ Handler: _Msg_Mint_Handler,
+ },
+ {
+ MethodName: "Burn",
+ Handler: _Msg_Burn_Handler,
+ },
+ {
+ MethodName: "SetDenomMetadata",
+ Handler: _Msg_SetDenomMetadata_Handler,
+ },
+ {
+ MethodName: "UpdateParams",
+ Handler: _Msg_UpdateParams_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "desmos/tokenfactory/v1beta1/msgs.proto",
+}
+
+func (m *MsgCreateDenom) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgCreateDenom) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateDenom) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.Subdenom) > 0 {
+ i -= len(m.Subdenom)
+ copy(dAtA[i:], m.Subdenom)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.Subdenom)))
+ i--
+ dAtA[i] = 0x1a
+ }
+ if len(m.Sender) > 0 {
+ i -= len(m.Sender)
+ copy(dAtA[i:], m.Sender)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.Sender)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if m.SubspaceID != 0 {
+ i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgCreateDenomResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgCreateDenomResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateDenomResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.NewTokenDenom) > 0 {
+ i -= len(m.NewTokenDenom)
+ copy(dAtA[i:], m.NewTokenDenom)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.NewTokenDenom)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgMint) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgMint) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMint) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintMsgs(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Sender) > 0 {
+ i -= len(m.Sender)
+ copy(dAtA[i:], m.Sender)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.Sender)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if m.SubspaceID != 0 {
+ i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgMintResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgMintResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMintResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgBurn) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgBurn) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgBurn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintMsgs(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Sender) > 0 {
+ i -= len(m.Sender)
+ copy(dAtA[i:], m.Sender)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.Sender)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if m.SubspaceID != 0 {
+ i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgBurnResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgBurnResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgBurnResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgSetDenomMetadata) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgSetDenomMetadata) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSetDenomMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintMsgs(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Sender) > 0 {
+ i -= len(m.Sender)
+ copy(dAtA[i:], m.Sender)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.Sender)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if m.SubspaceID != 0 {
+ i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgSetDenomMetadataResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgSetDenomMetadataResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSetDenomMetadataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintMsgs(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x12
+ if len(m.Authority) > 0 {
+ i -= len(m.Authority)
+ copy(dAtA[i:], m.Authority)
+ i = encodeVarintMsgs(dAtA, i, uint64(len(m.Authority)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func encodeVarintMsgs(dAtA []byte, offset int, v uint64) int {
+ offset -= sovMsgs(v)
+ base := offset
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *MsgCreateDenom) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.SubspaceID != 0 {
+ n += 1 + sovMsgs(uint64(m.SubspaceID))
+ }
+ l = len(m.Sender)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ l = len(m.Subdenom)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ return n
+}
+
+func (m *MsgCreateDenomResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.NewTokenDenom)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ return n
+}
+
+func (m *MsgMint) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.SubspaceID != 0 {
+ n += 1 + sovMsgs(uint64(m.SubspaceID))
+ }
+ l = len(m.Sender)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ l = m.Amount.Size()
+ n += 1 + l + sovMsgs(uint64(l))
+ return n
+}
+
+func (m *MsgMintResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *MsgBurn) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.SubspaceID != 0 {
+ n += 1 + sovMsgs(uint64(m.SubspaceID))
+ }
+ l = len(m.Sender)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ l = m.Amount.Size()
+ n += 1 + l + sovMsgs(uint64(l))
+ return n
+}
+
+func (m *MsgBurnResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *MsgSetDenomMetadata) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.SubspaceID != 0 {
+ n += 1 + sovMsgs(uint64(m.SubspaceID))
+ }
+ l = len(m.Sender)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ l = m.Metadata.Size()
+ n += 1 + l + sovMsgs(uint64(l))
+ return n
+}
+
+func (m *MsgSetDenomMetadataResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *MsgUpdateParams) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.Authority)
+ if l > 0 {
+ n += 1 + l + sovMsgs(uint64(l))
+ }
+ l = m.Params.Size()
+ n += 1 + l + sovMsgs(uint64(l))
+ return n
+}
+
+func (m *MsgUpdateParamsResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func sovMsgs(x uint64) (n int) {
+ return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozMsgs(x uint64) (n int) {
+ return sovMsgs(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *MsgCreateDenom) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgCreateDenom: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgCreateDenom: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType)
+ }
+ m.SubspaceID = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.SubspaceID |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Sender = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Subdenom", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Subdenom = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgCreateDenomResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgCreateDenomResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgCreateDenomResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NewTokenDenom", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.NewTokenDenom = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgMint) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgMint: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgMint: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType)
+ }
+ m.SubspaceID = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.SubspaceID |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Sender = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgMintResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgMintResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgMintResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgBurn) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgBurn: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgBurn: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType)
+ }
+ m.SubspaceID = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.SubspaceID |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Sender = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgBurnResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgBurnResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgBurnResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgSetDenomMetadata) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgSetDenomMetadata: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgSetDenomMetadata: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType)
+ }
+ m.SubspaceID = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.SubspaceID |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Sender = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgSetDenomMetadataResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgSetDenomMetadataResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgSetDenomMetadataResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Authority = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ default:
+ iNdEx = preIndex
+ skippy, err := skipMsgs(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthMsgs
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipMsgs(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowMsgs
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthMsgs
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupMsgs
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthMsgs
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthMsgs = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowMsgs = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupMsgs = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/tokenfactory/types/msgs_test.go b/x/tokenfactory/types/msgs_test.go
new file mode 100644
index 0000000000..e7f24c4aed
--- /dev/null
+++ b/x/tokenfactory/types/msgs_test.go
@@ -0,0 +1,407 @@
+package types_test
+
+import (
+ "testing"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/stretchr/testify/require"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+var msgCreateDenom = types.NewMsgCreateDenom(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ "minttoken",
+)
+
+func TestMsgCreateDenom_Route(t *testing.T) {
+ require.Equal(t, types.RouterKey, msgCreateDenom.Route())
+}
+
+func TestMsgCreateDenom_Type(t *testing.T) {
+ require.Equal(t, types.ActionCreateDenom, msgCreateDenom.Type())
+}
+
+func TestMsgCreateDenom_ValidateBasic(t *testing.T) {
+ testCases := []struct {
+ name string
+ msg *types.MsgCreateDenom
+ shouldErr bool
+ }{
+ {
+ name: "invalid subspace id returns error",
+ msg: types.NewMsgCreateDenom(
+ 0,
+ msgCreateDenom.Sender,
+ msgCreateDenom.Subdenom,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid sender returns error",
+ msg: types.NewMsgCreateDenom(
+ msgCreateDenom.SubspaceID,
+ "",
+ msgCreateDenom.Subdenom,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid subdenom returns error",
+ msg: types.NewMsgCreateDenom(
+ msgCreateDenom.SubspaceID,
+ msgCreateDenom.Sender,
+ "%invalid%",
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid message returns no error",
+ msg: msgCreateDenom,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.msg.ValidateBasic()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestMsgCreateDenom_GetSignBytes(t *testing.T) {
+ expected := `{"type":"desmos/MsgCreateDenom","value":{"sender":"cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69","subdenom":"minttoken","subspace_id":"1"}}`
+ require.Equal(t, expected, string(msgCreateDenom.GetSignBytes()))
+}
+
+func TestMsgCreateDenom_GetSigners(t *testing.T) {
+ addr, _ := sdk.AccAddressFromBech32(msgCreateDenom.Sender)
+ require.Equal(t, []sdk.AccAddress{addr}, msgCreateDenom.GetSigners())
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var msgMint = types.NewMsgMint(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("uminttoken", sdk.NewInt(100)),
+)
+
+func TestMsgMint_Route(t *testing.T) {
+ require.Equal(t, types.RouterKey, msgMint.Route())
+}
+
+func TestMsgMint_Type(t *testing.T) {
+ require.Equal(t, types.ActionMint, msgMint.Type())
+}
+
+func TestMsgMint_ValidateBasic(t *testing.T) {
+ testCases := []struct {
+ name string
+ msg *types.MsgMint
+ shouldErr bool
+ }{
+ {
+ name: "invalid subspace id returns error",
+ msg: types.NewMsgMint(
+ 0,
+ msgMint.Sender,
+ msgMint.Amount,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid sender returns error",
+ msg: types.NewMsgMint(
+ msgMint.SubspaceID,
+ "",
+ msgMint.Amount,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid amount returns error",
+ msg: types.NewMsgMint(
+ msgMint.SubspaceID,
+ msgMint.Sender,
+ sdk.Coin{Denom: "%invalid%", Amount: sdk.NewInt(100)},
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid message returns no error",
+ msg: msgMint,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.msg.ValidateBasic()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestMsgMint_GetSignBytes(t *testing.T) {
+ expected := `{"type":"desmos/MsgMint","value":{"amount":{"amount":"100","denom":"uminttoken"},"sender":"cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69","subspace_id":"1"}}`
+ require.Equal(t, expected, string(msgMint.GetSignBytes()))
+}
+
+func TestMsgMint_GetSigners(t *testing.T) {
+ addr, _ := sdk.AccAddressFromBech32(msgMint.Sender)
+ require.Equal(t, []sdk.AccAddress{addr}, msgMint.GetSigners())
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var msgBurn = types.NewMsgBurn(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ sdk.NewCoin("uminttoken", sdk.NewInt(100)),
+)
+
+func TestMsgBurn_Route(t *testing.T) {
+ require.Equal(t, types.RouterKey, msgBurn.Route())
+}
+
+func TestMsgBurn_Type(t *testing.T) {
+ require.Equal(t, types.ActionBurn, msgBurn.Type())
+}
+
+func TestMsgBurn_ValidateBasic(t *testing.T) {
+ testCases := []struct {
+ name string
+ msg *types.MsgBurn
+ shouldErr bool
+ }{
+ {
+ name: "invalid subspace id returns error",
+ msg: types.NewMsgBurn(
+ 0,
+ msgBurn.Sender,
+ msgBurn.Amount,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid sender returns error",
+ msg: types.NewMsgBurn(
+ msgBurn.SubspaceID,
+ "",
+ msgBurn.Amount,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid amount returns error",
+ msg: types.NewMsgBurn(
+ msgBurn.SubspaceID,
+ msgBurn.Sender,
+ sdk.Coin{Denom: "%invalid%", Amount: sdk.NewInt(100)},
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid message returns no error",
+ msg: msgBurn,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.msg.ValidateBasic()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestMsgBurn_GetSignBytes(t *testing.T) {
+ expected := `{"type":"desmos/MsgBurn","value":{"amount":{"amount":"100","denom":"uminttoken"},"sender":"cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69","subspace_id":"1"}}`
+ require.Equal(t, expected, string(msgBurn.GetSignBytes()))
+}
+
+func TestMsgBurn_GetSigners(t *testing.T) {
+ addr, _ := sdk.AccAddressFromBech32(msgBurn.Sender)
+ require.Equal(t, []sdk.AccAddress{addr}, msgBurn.GetSigners())
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var msgSetDenomMetadata = types.NewMsgSetDenomMetadata(
+ 1,
+ "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69",
+ banktypes.Metadata{
+ Name: "Mint Token",
+ Symbol: "MTK",
+ Description: "The custom token of the test subspace.",
+ DenomUnits: []*banktypes.DenomUnit{
+ {Denom: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken", Exponent: uint32(0), Aliases: nil},
+ {Denom: "minttoken", Exponent: uint32(6), Aliases: []string{"minttoken"}},
+ },
+ Base: "factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken",
+ Display: "minttoken",
+ },
+)
+
+func TestMsgSetDenomMetadata_Route(t *testing.T) {
+ require.Equal(t, types.RouterKey, msgSetDenomMetadata.Route())
+}
+
+func TestMsgSetDenomMetadata_Type(t *testing.T) {
+ require.Equal(t, types.ActionSetDenomMetadata, msgSetDenomMetadata.Type())
+}
+
+func TestMsgSetDenomMetadata_ValidateBasic(t *testing.T) {
+ testCases := []struct {
+ name string
+ msg *types.MsgSetDenomMetadata
+ shouldErr bool
+ }{
+ {
+ name: "invalid subspace id returns error",
+ msg: types.NewMsgSetDenomMetadata(
+ 0,
+ msgSetDenomMetadata.Sender,
+ msgSetDenomMetadata.Metadata,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid sender returns error",
+ msg: types.NewMsgSetDenomMetadata(
+ msgSetDenomMetadata.SubspaceID,
+ "",
+ msgSetDenomMetadata.Metadata,
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid metadata returns error",
+ msg: types.NewMsgSetDenomMetadata(
+ msgSetDenomMetadata.SubspaceID,
+ msgSetDenomMetadata.Sender,
+ banktypes.Metadata{},
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "invalid metadata base returns error",
+ msg: types.NewMsgSetDenomMetadata(
+ msgSetDenomMetadata.SubspaceID,
+ msgSetDenomMetadata.Sender,
+ banktypes.Metadata{
+ Name: "Mint Token",
+ Symbol: "MINTTOKEN",
+ Description: "The custom token of the test subspace.",
+ DenomUnits: []*banktypes.DenomUnit{
+ {Denom: "uminttoken", Exponent: uint32(0), Aliases: nil},
+ },
+ Base: "uminttoken",
+ Display: "uminttoken",
+ },
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid message returns no error",
+ msg: msgSetDenomMetadata,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.msg.ValidateBasic()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestMsgSetDenomMetadata_GetSignBytes(t *testing.T) {
+ expected := `{"type":"desmos/MsgSetDenomMetadata","value":{"metadata":{"base":"factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken","denom_units":[{"denom":"factory/cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47/uminttoken"},{"aliases":["minttoken"],"denom":"minttoken","exponent":6}],"description":"The custom token of the test subspace.","display":"minttoken","name":"Mint Token","symbol":"MTK"},"sender":"cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69","subspace_id":"1"}}`
+ require.Equal(t, expected, string(msgSetDenomMetadata.GetSignBytes()))
+}
+
+func TestMsgSetDenomMetadata_GetSigners(t *testing.T) {
+ addr, _ := sdk.AccAddressFromBech32(msgSetDenomMetadata.Sender)
+ require.Equal(t, []sdk.AccAddress{addr}, msgSetDenomMetadata.GetSigners())
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+var msgUpdateParams = types.NewMsgUpdateParams(
+ types.DefaultParams(),
+ "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
+)
+
+func TestMsgUpdateParams_Route(t *testing.T) {
+ require.Equal(t, types.RouterKey, msgUpdateParams.Route())
+}
+
+func TestMsgUpdateParams_Type(t *testing.T) {
+ require.Equal(t, types.ActionUpdateParams, msgUpdateParams.Type())
+}
+
+func TestMsgUpdateParams_ValidateBasic(t *testing.T) {
+ testCases := []struct {
+ name string
+ msg *types.MsgUpdateParams
+ shouldErr bool
+ }{
+ {
+ name: "invalid authority returns error",
+ msg: types.NewMsgUpdateParams(
+ types.DefaultParams(),
+ "invalid",
+ ),
+ shouldErr: true,
+ },
+ {
+ name: "valid message returns no error",
+ msg: msgUpdateParams,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.msg.ValidateBasic()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestMsgUpdateParams_GetSignBytes(t *testing.T) {
+ expected := `{"type":"desmos/x/tokenfactory/MsgUpdateParams","value":{"authority":"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd","params":{"denom_creation_fee":[{"amount":"10000000000","denom":"stake"}]}}}`
+ require.Equal(t, expected, string(msgUpdateParams.GetSignBytes()))
+}
+
+func TestMsgUpdateParams_GetSigners(t *testing.T) {
+ addr, _ := sdk.AccAddressFromBech32(msgUpdateParams.Authority)
+ require.Equal(t, []sdk.AccAddress{addr}, msgUpdateParams.GetSigners())
+}
diff --git a/x/tokenfactory/types/params.go b/x/tokenfactory/types/params.go
new file mode 100644
index 0000000000..263915e771
--- /dev/null
+++ b/x/tokenfactory/types/params.go
@@ -0,0 +1,28 @@
+package types
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+// NewParams creates a new params instance
+func NewParams(denomCreationFee sdk.Coins) Params {
+ return Params{
+ DenomCreationFee: denomCreationFee,
+ }
+}
+
+// DefaultParams creates a default params instance
+func DefaultParams() Params {
+ return Params{
+ DenomCreationFee: sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10_000_000_000)), // 10,000 DSM
+ }
+}
+
+// Validate implements fmt.Validator
+func (p Params) Validate() error {
+ if err := p.DenomCreationFee.Validate(); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/x/tokenfactory/types/params.pb.go b/x/tokenfactory/types/params.pb.go
new file mode 100644
index 0000000000..521549b7fb
--- /dev/null
+++ b/x/tokenfactory/types/params.pb.go
@@ -0,0 +1,348 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: desmos/tokenfactory/v1beta1/params.proto
+
+package types
+
+import (
+ fmt "fmt"
+ github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
+ types "github.com/cosmos/cosmos-sdk/types"
+ _ "github.com/cosmos/cosmos-sdk/types/tx/amino"
+ _ "github.com/cosmos/gogoproto/gogoproto"
+ proto "github.com/cosmos/gogoproto/proto"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// Params defines the parameters for the tokenfactory module.
+//
+// Since: Desmos 6.0.0
+type Params struct {
+ // DenomCreationFee defines the fee to be charged on the creation of a new
+ // denom. The fee is drawn from the subspace treasruy account, and
+ // burned.
+ DenomCreationFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=denom_creation_fee,json=denomCreationFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"denom_creation_fee" yaml:"denom_creation_fee"`
+}
+
+func (m *Params) Reset() { *m = Params{} }
+func (m *Params) String() string { return proto.CompactTextString(m) }
+func (*Params) ProtoMessage() {}
+func (*Params) Descriptor() ([]byte, []int) {
+ return fileDescriptor_bc2a96561a4f8a18, []int{0}
+}
+func (m *Params) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_Params.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *Params) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Params.Merge(m, src)
+}
+func (m *Params) XXX_Size() int {
+ return m.Size()
+}
+func (m *Params) XXX_DiscardUnknown() {
+ xxx_messageInfo_Params.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Params proto.InternalMessageInfo
+
+func (m *Params) GetDenomCreationFee() github_com_cosmos_cosmos_sdk_types.Coins {
+ if m != nil {
+ return m.DenomCreationFee
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterType((*Params)(nil), "desmos.tokenfactory.v1beta1.Params")
+}
+
+func init() {
+ proto.RegisterFile("desmos/tokenfactory/v1beta1/params.proto", fileDescriptor_bc2a96561a4f8a18)
+}
+
+var fileDescriptor_bc2a96561a4f8a18 = []byte{
+ // 322 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x31, 0x4f, 0x02, 0x31,
+ 0x1c, 0xc5, 0xaf, 0x31, 0x61, 0x40, 0x07, 0x25, 0x0e, 0x82, 0xa6, 0x28, 0x13, 0x21, 0xa1, 0x0d,
+ 0x1a, 0x16, 0x46, 0x48, 0x5c, 0x35, 0x8e, 0x2e, 0xa4, 0x57, 0xfe, 0x9c, 0x17, 0xb8, 0xfe, 0xc9,
+ 0xb5, 0x12, 0xef, 0x2b, 0x38, 0x39, 0xfb, 0x09, 0x8c, 0x71, 0xe0, 0x63, 0x30, 0x32, 0x1a, 0x07,
+ 0x34, 0x30, 0xb0, 0xfb, 0x09, 0x0c, 0x6d, 0x35, 0xa8, 0xcb, 0xdd, 0x4b, 0xfb, 0xf2, 0x7b, 0xaf,
+ 0x2f, 0x5f, 0xed, 0x81, 0x4e, 0x50, 0x73, 0x83, 0x03, 0x50, 0x7d, 0x21, 0x0d, 0xa6, 0x19, 0x1f,
+ 0x37, 0x42, 0x30, 0xa2, 0xc1, 0x47, 0x22, 0x15, 0x89, 0x66, 0xa3, 0x14, 0x0d, 0x16, 0x0e, 0x9d,
+ 0x93, 0x6d, 0x3a, 0x99, 0x77, 0x96, 0xf6, 0x44, 0x12, 0x2b, 0xe4, 0xf6, 0xeb, 0xfc, 0xa5, 0xfd,
+ 0x08, 0x23, 0xb4, 0x92, 0xaf, 0x95, 0x3f, 0xa5, 0x12, 0x6d, 0x5e, 0x28, 0x34, 0xfc, 0xe4, 0x48,
+ 0x8c, 0x95, 0xbb, 0xaf, 0xbc, 0x91, 0x7c, 0xee, 0xd2, 0xc6, 0x16, 0x5e, 0x48, 0xbe, 0xd0, 0x03,
+ 0x85, 0x49, 0x57, 0xa6, 0x20, 0x4c, 0x8c, 0xaa, 0xdb, 0x07, 0x38, 0x20, 0xc7, 0x5b, 0xd5, 0xed,
+ 0xd3, 0x22, 0x73, 0x20, 0xb6, 0x06, 0x7d, 0xd7, 0x60, 0x1d, 0x8c, 0x55, 0x5b, 0x4e, 0xe7, 0xe5,
+ 0xe0, 0x73, 0x5e, 0x2e, 0x66, 0x22, 0x19, 0xb6, 0x2a, 0xff, 0x11, 0x95, 0xe7, 0xf7, 0x72, 0x35,
+ 0x8a, 0xcd, 0xcd, 0x6d, 0xc8, 0x24, 0x26, 0xdc, 0x57, 0x72, 0xbf, 0xba, 0xee, 0x0d, 0xb8, 0xc9,
+ 0x46, 0xa0, 0x2d, 0x4d, 0x3f, 0xae, 0x26, 0xb5, 0x9d, 0x21, 0x44, 0x42, 0x66, 0xdd, 0x75, 0x4f,
+ 0xfd, 0xb4, 0x9a, 0xd4, 0xc8, 0xd5, 0xae, 0xc5, 0x76, 0x3c, 0xf5, 0x1c, 0xa0, 0x75, 0x72, 0xbf,
+ 0x9a, 0xd4, 0x8e, 0xfc, 0x9c, 0x77, 0xbf, 0x07, 0x75, 0x2f, 0x6a, 0x5f, 0x4c, 0x17, 0x94, 0xcc,
+ 0x16, 0x94, 0x7c, 0x2c, 0x28, 0x79, 0x58, 0xd2, 0x60, 0xb6, 0xa4, 0xc1, 0xeb, 0x92, 0x06, 0xd7,
+ 0xcd, 0x8d, 0x3a, 0x0e, 0x51, 0x1f, 0x8a, 0x50, 0x7b, 0xcd, 0xc7, 0xcd, 0xbf, 0x44, 0xdb, 0x30,
+ 0xcc, 0xd9, 0xd1, 0xce, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xbc, 0xa1, 0x04, 0xc6, 0x01,
+ 0x00, 0x00,
+}
+
+func (m *Params) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *Params) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.DenomCreationFee) > 0 {
+ for iNdEx := len(m.DenomCreationFee) - 1; iNdEx >= 0; iNdEx-- {
+ {
+ size, err := m.DenomCreationFee[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintParams(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ }
+ }
+ return len(dAtA) - i, nil
+}
+
+func encodeVarintParams(dAtA []byte, offset int, v uint64) int {
+ offset -= sovParams(v)
+ base := offset
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *Params) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if len(m.DenomCreationFee) > 0 {
+ for _, e := range m.DenomCreationFee {
+ l = e.Size()
+ n += 1 + l + sovParams(uint64(l))
+ }
+ }
+ return n
+}
+
+func sovParams(x uint64) (n int) {
+ return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozParams(x uint64) (n int) {
+ return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Params) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowParams
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: Params: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field DenomCreationFee", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowParams
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthParams
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthParams
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.DenomCreationFee = append(m.DenomCreationFee, types.Coin{})
+ if err := m.DenomCreationFee[len(m.DenomCreationFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipParams(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthParams
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipParams(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowParams
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowParams
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowParams
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthParams
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupParams
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthParams
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowParams = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/tokenfactory/types/params_test.go b/x/tokenfactory/types/params_test.go
new file mode 100644
index 0000000000..90bc0436b0
--- /dev/null
+++ b/x/tokenfactory/types/params_test.go
@@ -0,0 +1,47 @@
+package types_test
+
+import (
+ "testing"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/stretchr/testify/require"
+
+ "github.com/desmos-labs/desmos/v5/x/tokenfactory/types"
+)
+
+func TestParams_Validate(t *testing.T) {
+ testCases := []struct {
+ name string
+ params types.Params
+ shouldErr bool
+ }{
+ {
+ name: "invalid coins return error",
+ params: types.NewParams(sdk.Coins{sdk.Coin{Denom: "%invalid%", Amount: sdk.NewInt(100)}}),
+ shouldErr: true,
+ },
+ {
+ name: "valid params return no error",
+ params: types.NewParams(sdk.NewCoins(sdk.Coin{Denom: "udsm", Amount: sdk.NewInt(100)})),
+ shouldErr: false,
+ },
+ {
+ name: "default params return no error",
+ params: types.DefaultParams(),
+ shouldErr: false,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.params.Validate()
+ if tc.shouldErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+
+}
diff --git a/x/tokenfactory/types/permissions.go b/x/tokenfactory/types/permissions.go
new file mode 100644
index 0000000000..ad0f43e05f
--- /dev/null
+++ b/x/tokenfactory/types/permissions.go
@@ -0,0 +1,12 @@
+package types
+
+// DONTCOVER
+
+import (
+ subspacestypes "github.com/desmos-labs/desmos/v5/x/subspaces/types"
+)
+
+var (
+ // PermissionManageSubspaceTokens allows users to manage subspace tokens
+ PermissionManageSubspaceTokens = subspacestypes.RegisterPermission("manage custom subspace tokens")
+)
diff --git a/x/tokenfactory/types/query.go b/x/tokenfactory/types/query.go
new file mode 100644
index 0000000000..f054f970ee
--- /dev/null
+++ b/x/tokenfactory/types/query.go
@@ -0,0 +1,13 @@
+package types
+
+// NewQuerySubspaceDenomsRequest returns a new QuerySubspaceDenomsRequest instance
+func NewQuerySubspaceDenomsRequest(subspaceID uint64) *QuerySubspaceDenomsRequest {
+ return &QuerySubspaceDenomsRequest{
+ SubspaceId: subspaceID,
+ }
+}
+
+// NewQueryParamsRequest returns a new QueryParamsRequest instance
+func NewQueryParamsRequest() *QueryParamsRequest {
+ return &QueryParamsRequest{}
+}
diff --git a/x/tokenfactory/types/query.pb.go b/x/tokenfactory/types/query.pb.go
new file mode 100644
index 0000000000..0e32435fac
--- /dev/null
+++ b/x/tokenfactory/types/query.pb.go
@@ -0,0 +1,922 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: desmos/tokenfactory/v1beta1/query.proto
+
+package types
+
+import (
+ context "context"
+ fmt "fmt"
+ _ "github.com/cosmos/cosmos-sdk/types/tx/amino"
+ _ "github.com/cosmos/gogoproto/gogoproto"
+ grpc1 "github.com/cosmos/gogoproto/grpc"
+ proto "github.com/cosmos/gogoproto/proto"
+ _ "google.golang.org/genproto/googleapis/api/annotations"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// QueryParamsRequest is the request type for the Query/Params RPC method.
+type QueryParamsRequest struct {
+}
+
+func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} }
+func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsRequest) ProtoMessage() {}
+func (*QueryParamsRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_026c0f6cafecaba8, []int{0}
+}
+func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *QueryParamsRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_QueryParamsRequest.Merge(m, src)
+}
+func (m *QueryParamsRequest) XXX_Size() int {
+ return m.Size()
+}
+func (m *QueryParamsRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo
+
+// QueryParamsResponse is the response type for the Query/Params RPC method.
+type QueryParamsResponse struct {
+ // params defines the parameters of the module.
+ Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+}
+
+func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} }
+func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsResponse) ProtoMessage() {}
+func (*QueryParamsResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_026c0f6cafecaba8, []int{1}
+}
+func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *QueryParamsResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_QueryParamsResponse.Merge(m, src)
+}
+func (m *QueryParamsResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *QueryParamsResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo
+
+func (m *QueryParamsResponse) GetParams() Params {
+ if m != nil {
+ return m.Params
+ }
+ return Params{}
+}
+
+// QuerySubspaceDenomsRequest defines the request structure for the
+// SubspaceDenoms gRPC query.
+type QuerySubspaceDenomsRequest struct {
+ SubspaceId uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"creator"`
+}
+
+func (m *QuerySubspaceDenomsRequest) Reset() { *m = QuerySubspaceDenomsRequest{} }
+func (m *QuerySubspaceDenomsRequest) String() string { return proto.CompactTextString(m) }
+func (*QuerySubspaceDenomsRequest) ProtoMessage() {}
+func (*QuerySubspaceDenomsRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_026c0f6cafecaba8, []int{2}
+}
+func (m *QuerySubspaceDenomsRequest) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *QuerySubspaceDenomsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_QuerySubspaceDenomsRequest.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *QuerySubspaceDenomsRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_QuerySubspaceDenomsRequest.Merge(m, src)
+}
+func (m *QuerySubspaceDenomsRequest) XXX_Size() int {
+ return m.Size()
+}
+func (m *QuerySubspaceDenomsRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_QuerySubspaceDenomsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QuerySubspaceDenomsRequest proto.InternalMessageInfo
+
+func (m *QuerySubspaceDenomsRequest) GetSubspaceId() uint64 {
+ if m != nil {
+ return m.SubspaceId
+ }
+ return 0
+}
+
+// QuerySubspaceDenomsResponse defines the response structure for the
+// SubspaceDenoms gRPC query.
+type QuerySubspaceDenomsResponse struct {
+ Denoms []string `protobuf:"bytes,1,rep,name=denoms,proto3" json:"denoms,omitempty" yaml:"denoms"`
+}
+
+func (m *QuerySubspaceDenomsResponse) Reset() { *m = QuerySubspaceDenomsResponse{} }
+func (m *QuerySubspaceDenomsResponse) String() string { return proto.CompactTextString(m) }
+func (*QuerySubspaceDenomsResponse) ProtoMessage() {}
+func (*QuerySubspaceDenomsResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_026c0f6cafecaba8, []int{3}
+}
+func (m *QuerySubspaceDenomsResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *QuerySubspaceDenomsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_QuerySubspaceDenomsResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *QuerySubspaceDenomsResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_QuerySubspaceDenomsResponse.Merge(m, src)
+}
+func (m *QuerySubspaceDenomsResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *QuerySubspaceDenomsResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_QuerySubspaceDenomsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QuerySubspaceDenomsResponse proto.InternalMessageInfo
+
+func (m *QuerySubspaceDenomsResponse) GetDenoms() []string {
+ if m != nil {
+ return m.Denoms
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterType((*QueryParamsRequest)(nil), "desmos.tokenfactory.v1beta1.QueryParamsRequest")
+ proto.RegisterType((*QueryParamsResponse)(nil), "desmos.tokenfactory.v1beta1.QueryParamsResponse")
+ proto.RegisterType((*QuerySubspaceDenomsRequest)(nil), "desmos.tokenfactory.v1beta1.QuerySubspaceDenomsRequest")
+ proto.RegisterType((*QuerySubspaceDenomsResponse)(nil), "desmos.tokenfactory.v1beta1.QuerySubspaceDenomsResponse")
+}
+
+func init() {
+ proto.RegisterFile("desmos/tokenfactory/v1beta1/query.proto", fileDescriptor_026c0f6cafecaba8)
+}
+
+var fileDescriptor_026c0f6cafecaba8 = []byte{
+ // 443 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4f, 0x49, 0x2d, 0xce,
+ 0xcd, 0x2f, 0xd6, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x4b, 0x4b, 0x4c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4,
+ 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28,
+ 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0x28, 0xd4, 0x43, 0x56, 0xa8, 0x07, 0x55, 0x28, 0x25, 0x98,
+ 0x98, 0x9b, 0x99, 0x97, 0xaf, 0x0f, 0x26, 0x21, 0xea, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1,
+ 0x4c, 0x7d, 0x10, 0x0b, 0x2a, 0x2a, 0x93, 0x9e, 0x9f, 0x9f, 0x9e, 0x93, 0xaa, 0x9f, 0x58, 0x90,
+ 0xa9, 0x9f, 0x98, 0x97, 0x97, 0x5f, 0x92, 0x58, 0x92, 0x99, 0x9f, 0x57, 0x0c, 0x95, 0xd5, 0xc0,
+ 0xe7, 0x98, 0x82, 0xc4, 0xa2, 0xc4, 0x5c, 0xa8, 0x4a, 0x25, 0x11, 0x2e, 0xa1, 0x40, 0x90, 0xe3,
+ 0x02, 0xc0, 0x82, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x4a, 0xb1, 0x5c, 0xc2, 0x28, 0xa2,
+ 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x6e, 0x5c, 0x6c, 0x10, 0xcd, 0x12, 0x8c, 0x0a, 0x8c,
+ 0x1a, 0xdc, 0x46, 0xca, 0x7a, 0x78, 0xfc, 0xa2, 0x07, 0xd1, 0xec, 0xc4, 0x79, 0xe2, 0x9e, 0x3c,
+ 0xc3, 0x8a, 0xe7, 0x1b, 0xb4, 0x18, 0x83, 0xa0, 0xba, 0x95, 0x02, 0xb9, 0xa4, 0xc0, 0xc6, 0x07,
+ 0x97, 0x26, 0x15, 0x17, 0x24, 0x26, 0xa7, 0xba, 0xa4, 0xe6, 0xe5, 0xc3, 0x2d, 0x17, 0x32, 0xe6,
+ 0xe2, 0x2e, 0x86, 0x4a, 0xc4, 0x67, 0xa6, 0x80, 0xad, 0x62, 0x71, 0x12, 0xfa, 0x74, 0x4f, 0x9e,
+ 0xaf, 0x32, 0x31, 0x37, 0xc7, 0x4a, 0x29, 0xb9, 0x28, 0x35, 0xb1, 0x24, 0xbf, 0x48, 0x29, 0x88,
+ 0x0b, 0xa6, 0xcc, 0x33, 0x45, 0xc9, 0x83, 0x4b, 0x1a, 0xab, 0x91, 0x50, 0x97, 0x6b, 0x72, 0xb1,
+ 0xa5, 0x80, 0x45, 0x24, 0x18, 0x15, 0x98, 0x35, 0x38, 0x9d, 0x04, 0x3f, 0xdd, 0x93, 0xe7, 0x85,
+ 0x18, 0x07, 0x11, 0x57, 0x0a, 0x82, 0x2a, 0x30, 0x7a, 0xcb, 0xc4, 0xc5, 0x0a, 0x36, 0x4a, 0x68,
+ 0x06, 0x23, 0x17, 0x1b, 0xc4, 0x13, 0x42, 0xfa, 0x78, 0x7d, 0x8a, 0x19, 0x82, 0x52, 0x06, 0xc4,
+ 0x6b, 0x80, 0x38, 0x51, 0x49, 0xbb, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, 0xaa, 0x42, 0xca, 0xfa, 0x84,
+ 0x23, 0x4f, 0xe8, 0x04, 0x23, 0x17, 0x1f, 0xaa, 0x57, 0x85, 0xcc, 0x09, 0xdb, 0x88, 0x35, 0xbc,
+ 0xa5, 0x2c, 0x48, 0xd7, 0x08, 0x75, 0xb2, 0x33, 0xd8, 0xc9, 0xb6, 0x42, 0xd6, 0x78, 0x9d, 0x0c,
+ 0x8b, 0xa5, 0x62, 0xfd, 0x6a, 0xa4, 0x78, 0xad, 0xd5, 0x87, 0x84, 0xb7, 0x93, 0xff, 0x89, 0x47,
+ 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85,
+ 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9,
+ 0x25, 0xe7, 0xe7, 0x42, 0x2d, 0xd0, 0xcd, 0x49, 0x4c, 0x2a, 0x86, 0x59, 0x56, 0x66, 0xaa, 0x5f,
+ 0x81, 0x6a, 0x63, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0x65, 0x1b, 0x03, 0x02, 0x00,
+ 0x00, 0xff, 0xff, 0x56, 0x7a, 0x98, 0x6f, 0x92, 0x03, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// QueryClient is the client API for Query service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type QueryClient interface {
+ // Params defines a gRPC query method that returns the tokenfactory module's
+ // parameters.
+ Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
+ // SubspaceDenoms defines a gRPC query method for fetching all
+ // denominations created by a specific subspace.
+ SubspaceDenoms(ctx context.Context, in *QuerySubspaceDenomsRequest, opts ...grpc.CallOption) (*QuerySubspaceDenomsResponse, error)
+}
+
+type queryClient struct {
+ cc grpc1.ClientConn
+}
+
+func NewQueryClient(cc grpc1.ClientConn) QueryClient {
+ return &queryClient{cc}
+}
+
+func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
+ out := new(QueryParamsResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Query/Params", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *queryClient) SubspaceDenoms(ctx context.Context, in *QuerySubspaceDenomsRequest, opts ...grpc.CallOption) (*QuerySubspaceDenomsResponse, error) {
+ out := new(QuerySubspaceDenomsResponse)
+ err := c.cc.Invoke(ctx, "/desmos.tokenfactory.v1beta1.Query/SubspaceDenoms", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// QueryServer is the server API for Query service.
+type QueryServer interface {
+ // Params defines a gRPC query method that returns the tokenfactory module's
+ // parameters.
+ Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
+ // SubspaceDenoms defines a gRPC query method for fetching all
+ // denominations created by a specific subspace.
+ SubspaceDenoms(context.Context, *QuerySubspaceDenomsRequest) (*QuerySubspaceDenomsResponse, error)
+}
+
+// UnimplementedQueryServer can be embedded to have forward compatible implementations.
+type UnimplementedQueryServer struct {
+}
+
+func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
+}
+func (*UnimplementedQueryServer) SubspaceDenoms(ctx context.Context, req *QuerySubspaceDenomsRequest) (*QuerySubspaceDenomsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method SubspaceDenoms not implemented")
+}
+
+func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
+ s.RegisterService(&_Query_serviceDesc, srv)
+}
+
+func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(QueryParamsRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(QueryServer).Params(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Query/Params",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Query_SubspaceDenoms_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(QuerySubspaceDenomsRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(QueryServer).SubspaceDenoms(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/desmos.tokenfactory.v1beta1.Query/SubspaceDenoms",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(QueryServer).SubspaceDenoms(ctx, req.(*QuerySubspaceDenomsRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _Query_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "desmos.tokenfactory.v1beta1.Query",
+ HandlerType: (*QueryServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Params",
+ Handler: _Query_Params_Handler,
+ },
+ {
+ MethodName: "SubspaceDenoms",
+ Handler: _Query_SubspaceDenoms_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "desmos/tokenfactory/v1beta1/query.proto",
+}
+
+func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintQuery(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ return len(dAtA) - i, nil
+}
+
+func (m *QuerySubspaceDenomsRequest) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *QuerySubspaceDenomsRequest) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QuerySubspaceDenomsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.SubspaceId != 0 {
+ i = encodeVarintQuery(dAtA, i, uint64(m.SubspaceId))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *QuerySubspaceDenomsResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *QuerySubspaceDenomsResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QuerySubspaceDenomsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.Denoms) > 0 {
+ for iNdEx := len(m.Denoms) - 1; iNdEx >= 0; iNdEx-- {
+ i -= len(m.Denoms[iNdEx])
+ copy(dAtA[i:], m.Denoms[iNdEx])
+ i = encodeVarintQuery(dAtA, i, uint64(len(m.Denoms[iNdEx])))
+ i--
+ dAtA[i] = 0xa
+ }
+ }
+ return len(dAtA) - i, nil
+}
+
+func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
+ offset -= sovQuery(v)
+ base := offset
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *QueryParamsRequest) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *QueryParamsResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = m.Params.Size()
+ n += 1 + l + sovQuery(uint64(l))
+ return n
+}
+
+func (m *QuerySubspaceDenomsRequest) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.SubspaceId != 0 {
+ n += 1 + sovQuery(uint64(m.SubspaceId))
+ }
+ return n
+}
+
+func (m *QuerySubspaceDenomsResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if len(m.Denoms) > 0 {
+ for _, s := range m.Denoms {
+ l = len(s)
+ n += 1 + l + sovQuery(uint64(l))
+ }
+ }
+ return n
+}
+
+func sovQuery(x uint64) (n int) {
+ return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozQuery(x uint64) (n int) {
+ return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ default:
+ iNdEx = preIndex
+ skippy, err := skipQuery(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthQuery
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipQuery(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *QuerySubspaceDenomsRequest) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: QuerySubspaceDenomsRequest: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: QuerySubspaceDenomsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SubspaceId", wireType)
+ }
+ m.SubspaceId = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.SubspaceId |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ iNdEx = preIndex
+ skippy, err := skipQuery(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *QuerySubspaceDenomsResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: QuerySubspaceDenomsResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: QuerySubspaceDenomsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Denoms", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthQuery
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Denoms = append(m.Denoms, string(dAtA[iNdEx:postIndex]))
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipQuery(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipQuery(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthQuery
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupQuery
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthQuery
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/tokenfactory/types/query.pb.gw.go b/x/tokenfactory/types/query.pb.gw.go
new file mode 100644
index 0000000000..b6a4c1eb37
--- /dev/null
+++ b/x/tokenfactory/types/query.pb.gw.go
@@ -0,0 +1,254 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: desmos/tokenfactory/v1beta1/query.proto
+
+/*
+Package types is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package types
+
+import (
+ "context"
+ "io"
+ "net/http"
+
+ "github.com/golang/protobuf/descriptor"
+ "github.com/golang/protobuf/proto"
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "github.com/grpc-ecosystem/grpc-gateway/utilities"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = descriptor.ForMessage
+var _ = metadata.Join
+
+func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq QueryParamsRequest
+ var metadata runtime.ServerMetadata
+
+ msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq QueryParamsRequest
+ var metadata runtime.ServerMetadata
+
+ msg, err := server.Params(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+func request_Query_SubspaceDenoms_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq QuerySubspaceDenomsRequest
+ var metadata runtime.ServerMetadata
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["subspace_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "subspace_id")
+ }
+
+ protoReq.SubspaceId, err = runtime.Uint64(val)
+
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "subspace_id", err)
+ }
+
+ msg, err := client.SubspaceDenoms(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_Query_SubspaceDenoms_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq QuerySubspaceDenomsRequest
+ var metadata runtime.ServerMetadata
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["subspace_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "subspace_id")
+ }
+
+ protoReq.SubspaceId, err = runtime.Uint64(val)
+
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "subspace_id", err)
+ }
+
+ msg, err := server.SubspaceDenoms(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+// RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
+// UnaryRPC :call QueryServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead.
+func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error {
+
+ mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_Query_SubspaceDenoms_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_Query_SubspaceDenoms_0(rctx, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Query_SubspaceDenoms_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+ conn, err := grpc.Dial(endpoint, opts...)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ return
+ }
+ go func() {
+ <-ctx.Done()
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ }()
+ }()
+
+ return RegisterQueryHandler(ctx, mux, conn)
+}
+
+// RegisterQueryHandler registers the http handlers for service Query to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn))
+}
+
+// RegisterQueryHandlerClient registers the http handlers for service Query
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "QueryClient" to call the correct interceptors.
+func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error {
+
+ mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ rctx, err := runtime.AnnotateContext(ctx, mux, req)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams)
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_Query_SubspaceDenoms_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ rctx, err := runtime.AnnotateContext(ctx, mux, req)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_Query_SubspaceDenoms_0(rctx, inboundMarshaler, client, req, pathParams)
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Query_SubspaceDenoms_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+var (
+ pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"desmos", "tokenfactory", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false)))
+
+ pattern_Query_SubspaceDenoms_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"desmos", "tokenfactory", "v1beta1", "subspaces", "subspace_id", "denoms"}, "", runtime.AssumeColonVerbOpt(false)))
+)
+
+var (
+ forward_Query_Params_0 = runtime.ForwardResponseMessage
+
+ forward_Query_SubspaceDenoms_0 = runtime.ForwardResponseMessage
+)