diff --git a/Pipfile.lock b/Pipfile.lock index f84f885..541c8a3 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -159,11 +159,11 @@ }, "py-algorand-sdk": { "hashes": [ - "sha256:0278be14e0c20851433a363b7c571e4b9409c0dff36b7b65341c166e77f68840", - "sha256:09e536dcca01fbf4442f960e8496c89aa26618de28dd9a97a1d496c941d3d7f0" + "sha256:37079777a2eb3883f0001e0daa3eacdb7dcc0dbca5ae470ec11bbdf6acd4872f", + "sha256:ee5c999c5209fe5a5be8fd8b5fff16009187fc61cfd4714782e676c985aa163c" ], "index": "pypi", - "version": "==1.18.0" + "version": "==1.20.2" }, "pycparser": { "hashes": [ @@ -174,39 +174,35 @@ }, "pycryptodomex": { "hashes": [ - "sha256:04cc393045a8f19dd110c975e30f38ed7ab3faf21ede415ea67afebd95a22380", - "sha256:0776bfaf2c48154ab54ea45392847c1283d2fcf64e232e85565f858baedfc1fa", - "sha256:0fadb9f7fa3150577800eef35f62a8a24b9ddf1563ff060d9bd3af22d3952c8c", - "sha256:18e2ab4813883ae63396c0ffe50b13554b32bb69ec56f0afaf052e7a7ae0d55b", - "sha256:191e73bc84a8064ad1874dba0ebadedd7cce4dedee998549518f2c74a003b2e1", - "sha256:35a8f7afe1867118330e2e0e0bf759c409e28557fb1fc2fbb1c6c937297dbe9a", - "sha256:3709f13ca3852b0b07fc04a2c03b379189232b24007c466be0f605dd4723e9d4", - "sha256:4540904c09704b6f831059c0dfb38584acb82cb97b0125cd52688c1f1e3fffa6", - "sha256:463119d7d22d0fc04a0f9122e9d3e6121c6648bcb12a052b51bd1eed1b996aa2", - "sha256:46b3f05f2f7ac7841053da4e0f69616929ca3c42f238c405f6c3df7759ad2780", - "sha256:48697790203909fab02a33226fda546604f4e2653f9d47bc5d3eb40879fa7c64", - "sha256:5676a132169a1c1a3712edf25250722ebc8c9102aa9abd814df063ca8362454f", - "sha256:65204412d0c6a8e3c41e21e93a5e6054a74fea501afa03046a388cf042e3377a", - "sha256:67e1e6a92151023ccdfcfbc0afb3314ad30080793b4c27956ea06ab1fb9bcd8a", - "sha256:6f5b6ba8aefd624834bc177a2ac292734996bb030f9d1b388e7504103b6fcddf", - "sha256:7341f1bb2dadb0d1a0047f34c3a58208a92423cdbd3244d998e4b28df5eac0ed", - "sha256:78d9621cf0ea35abf2d38fa2ca6d0634eab6c991a78373498ab149953787e5e5", - "sha256:8eecdf9cdc7343001d047f951b9cc805cd68cb6cd77b20ea46af5bffc5bd3dfb", - "sha256:94c7b60e1f52e1a87715571327baea0733708ab4723346598beca4a3b6879794", - "sha256:996e1ba717077ce1e6d4849af7a1426f38b07b3d173b879e27d5e26d2e958beb", - "sha256:a07a64709e366c2041cd5cfbca592b43998bf4df88f7b0ca73dca37071ccf1bd", - "sha256:b6306403228edde6e289f626a3908a2f7f67c344e712cf7c0a508bab3ad9e381", - "sha256:b9279adc16e4b0f590ceff581f53a80179b02cba9056010d733eb4196134a870", - "sha256:c4cb9cb492ea7dcdf222a8d19a1d09002798ea516aeae8877245206d27326d86", - "sha256:dd452a5af7014e866206d41751886c9b4bf379a339fdf2dbfc7dd16c0fb4f8e0", - "sha256:e2b12968522a0358b8917fc7b28865acac002f02f4c4c6020fcb264d76bfd06d", - "sha256:e3164a18348bd53c69b4435ebfb4ac8a4076291ffa2a70b54f0c4b80c7834b1d", - "sha256:e47bf8776a7e15576887f04314f5228c6527b99946e6638cf2f16da56d260cab", - "sha256:f8be976cec59b11f011f790b88aca67b4ea2bd286578d0bd3e31bcd19afcd3e4", - "sha256:fc9bc7a9b79fe5c750fc81a307052f8daabb709bdaabb0fb18fb136b66b653b5" + "sha256:04610536921c1ec7adba158ef570348550c9f3a40bc24be9f8da2ef7ab387981", + "sha256:0ba28aa97cdd3ff5ed1a4f2b7f5cd04e721166bd75bd2b929e2734433882b583", + "sha256:0da835af786fdd1c9930994c78b23e88d816dc3f99aa977284a21bbc26d19735", + "sha256:1619087fb5b31510b0b0b058a54f001a5ffd91e6ffee220d9913064519c6a69d", + "sha256:1cda60207be8c1cf0b84b9138f9e3ca29335013d2b690774a5e94678ff29659a", + "sha256:22aed0868622d95179217c298e37ed7410025c7b29dac236d3230617d1e4ed56", + "sha256:231dc8008cbdd1ae0e34645d4523da2dbc7a88c325f0d4a59635a86ee25b41dd", + "sha256:2ad9bb86b355b6104796567dd44c215b3dc953ef2fae5e0bdfb8516731df92cf", + "sha256:4dbbe18cc232b5980c7633972ae5417d0df76fe89e7db246eefd17ef4d8e6d7a", + "sha256:6a465e4f856d2a4f2a311807030c89166529ccf7ccc65bef398de045d49144b6", + "sha256:70288d9bfe16b2fd0d20b6c365db614428f1bcde7b20d56e74cf88ade905d9eb", + "sha256:7993d26dae4d83b8f4ce605bb0aecb8bee330bb3c95475ef06f3694403621e71", + "sha256:8851585ff19871e5d69e1790f4ca5f6fd1699d6b8b14413b472a4c0dbc7ea780", + "sha256:893f8a97d533c66cc3a56e60dd3ed40a3494ddb4aafa7e026429a08772f8a849", + "sha256:8dd2d9e3c617d0712ed781a77efd84ea579e76c5f9b2a4bc0b684ebeddf868b2", + "sha256:a1c0ae7123448ecb034c75c713189cb00ebe2d415b11682865b6c54d200d9c93", + "sha256:b0789a8490114a2936ed77c87792cfe77582c829cb43a6d86ede0f9624ba8aa3", + "sha256:b3d04c00d777c36972b539fb79958790126847d84ec0129fce1efef250bfe3ce", + "sha256:ba57ac7861fd2c837cdb33daf822f2a052ff57dd769a2107807f52a36d0e8d38", + "sha256:ce338a9703f54b2305a408fc9890eb966b727ce72b69f225898bb4e9d9ed3f1f", + "sha256:daa67f5ebb6fbf1ee9c90decaa06ca7fc88a548864e5e484d52b0920a57fe8a5", + "sha256:e2453162f473c1eae4826eb10cd7bce19b5facac86d17fb5f29a570fde145abd", + "sha256:e25a2f5667d91795f9417cb856f6df724ccdb0cdd5cbadb212ee9bf43946e9f8", + "sha256:e5a670919076b71522c7d567a9043f66f14b202414a63c3a078b5831ae342c03", + "sha256:e9ba9d8ed638733c9e95664470b71d624a6def149e2db6cc52c1aca5a6a2df1d", + "sha256:f2b971a7b877348a27dcfd0e772a0343fb818df00b74078e91c008632284137d" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==3.15.0" + "version": "==3.16.0" }, "pynacl": { "hashes": [ @@ -226,11 +222,11 @@ }, "pyteal": { "hashes": [ - "sha256:3ae0dd32a3464728b6cce789974fd7a86ae60fa8fa862c58f59b1535472b52b5", - "sha256:52324f03eb9f48ea4cb9165207069fde8fa61d875cac410d60882cfea477b6a3" + "sha256:75e64c820944f5602b0e16cfef36d4edf77d4c6543de3bc8d368412c5a8d00d3", + "sha256:d1c2125b5af07e3b5d845e8df27084e628b59f1deacda751a8f1ee281a58d7f8" ], "index": "pypi", - "version": "==0.18.1" + "version": "==0.20.1" }, "semantic-version": { "hashes": [ @@ -244,48 +240,37 @@ "develop": { "attrs": { "hashes": [ - "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", - "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" + "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", + "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" ], - "markers": "python_version >= '3.5'", - "version": "==22.1.0" + "markers": "python_version >= '3.6'", + "version": "==22.2.0" }, "autoflake": { "hashes": [ - "sha256:6d313038abf4ad829cb88c9b01cd16387369ac529842bcd7f25a967ab4e99b8f", - "sha256:d5de7da3786809bbdedbdbdeecbb410d55277b3492a4a3ede882998f1e87f156" + "sha256:7185b596e70d8970c6d4106c112ef41921e472bd26abf3613db99eca88cc8c2a", + "sha256:d58ed4187c6b4f623a942b9a90c43ff84bf6a266f3682f407b42ca52073c9678" ], "index": "pypi", - "version": "==1.6.0" + "version": "==2.0.0" }, "black": { "hashes": [ - "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411", - "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c", - "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497", - "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e", - "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342", - "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27", - "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41", - "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab", - "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5", - "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16", - "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e", - "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c", - "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe", - "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3", - "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec", - "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3", - "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd", - "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c", - "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4", - "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90", - "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869", - "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747", - "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875" + "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320", + "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351", + "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350", + "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f", + "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf", + "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148", + "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4", + "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d", + "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc", + "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d", + "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2", + "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f" ], "index": "pypi", - "version": "==22.8.0" + "version": "==22.12.0" }, "click": { "hashes": [ @@ -295,6 +280,14 @@ "markers": "python_version >= '3.7'", "version": "==8.1.3" }, + "exceptiongroup": { + "hashes": [ + "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e", + "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23" + ], + "markers": "python_version < '3.11'", + "version": "==1.1.0" + }, "iniconfig": { "hashes": [ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", @@ -311,27 +304,27 @@ }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], - "markers": "python_version >= '3.6'", - "version": "==21.3" + "markers": "python_version >= '3.7'", + "version": "==22.0" }, "pathspec": { "hashes": [ - "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93", - "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d" + "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6", + "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6" ], "markers": "python_version >= '3.7'", - "version": "==0.10.1" + "version": "==0.10.3" }, "platformdirs": { "hashes": [ - "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788", - "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19" + "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490", + "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2" ], "markers": "python_version >= '3.7'", - "version": "==2.5.2" + "version": "==2.6.2" }, "pluggy": { "hashes": [ @@ -341,44 +334,28 @@ "markers": "python_version >= '3.6'", "version": "==1.0.0" }, - "py": { - "hashes": [ - "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", - "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.11.0" - }, "pyflakes": { "hashes": [ - "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2", - "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3" + "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf", + "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd" ], "markers": "python_version >= '3.6'", - "version": "==2.5.0" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" + "version": "==3.0.1" }, "pytest": { "hashes": [ - "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7", - "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39" + "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71", + "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59" ], "index": "pypi", - "version": "==7.1.3" + "version": "==7.2.0" }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_version >= '3.7'", + "markers": "python_version < '3.11'", "version": "==2.0.1" } } diff --git a/smart_asa_approval.teal b/smart_asa_approval.teal index 5b98a59..0f269cf 100644 --- a/smart_asa_approval.teal +++ b/smart_asa_approval.teal @@ -637,6 +637,7 @@ isvalidaddressbyteslength_8: len pushint 32 // 32 == +// Invalid Address length (must be 32 bytes) assert retsub @@ -658,11 +659,13 @@ getterpreconditions_10: store 115 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 115 == +// Invalid Smart ASA ID assert retsub @@ -671,18 +674,22 @@ assetappcreate_11: txn GlobalNumUint pushint 5 // 5 == +// Wrong State Schema - Expexted Global Ints: 5 assert txn GlobalNumByteSlice intc_2 // 8 == +// Wrong State Schema - Expexted Global Bytes: 8 assert txn LocalNumUint pushint 2 // 2 == +// Wrong State Schema - Expexted Local Ints: 2 assert txn LocalNumByteSlice intc_0 // 0 == +// Wrong State Schema - Expexted Local Bytes: 0 assert callsub initglobalstate_0 intc_1 // 1 @@ -694,43 +701,51 @@ store 74 store 73 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 73 txnas Assets == +// Invalid Smart ASA ID assert load 74 gtxns TypeEnum intc_3 // axfer == +// Reference Opt-In Txn: Wrong Txn Type (Expected: Axfer) assert load 74 gtxns XferAsset bytec_0 // "smart_asa_id" app_global_get == +// Reference Opt-In Txn: Wrong Asset ID (Expected: Smart ASA ID) assert load 74 gtxns Sender txn Sender == +// Reference Opt-In Txn: Wrong Sender (Expected: App Caller) assert load 74 gtxns AssetReceiver txn Sender == +// Reference Opt-In Txn: Wrong Asset Receiver (Expected: App Caller) assert load 74 gtxns AssetAmount intc_0 // 0 == +// Reference Opt-In Txn: Wrong Asset Amount (Expected: 0) assert load 74 gtxns AssetCloseTo global ZeroAddress == +// Reference Opt-In Txn: Wrong Asset CloseTo (Expected: Zero Address) assert txn Sender load 73 @@ -739,6 +754,7 @@ asset_holding_get AssetBalance store 76 store 75 load 76 +// Missing Opt-In to Underlying ASA assert callsub initlocalstate_1 bytec 8 // "default_frozen" @@ -772,10 +788,12 @@ store 14 txn Sender global CreatorAddress == +// Caller not authorized (must be: App Creator Address) assert bytec_0 // "smart_asa_id" app_global_get ! +// Smart ASA ID already exists assert load 21 callsub isvalidaddressbyteslength_8 @@ -845,12 +863,14 @@ store 80 store 79 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 79 txnas Assets == +// Invalid Smart ASA ID assert load 87 callsub isvalidaddressbyteslength_8 @@ -864,6 +884,7 @@ txn Sender bytec 6 // "manager_addr" app_global_get == +// Caller not authorized (must be: Manager Address) assert bytec_2 // "reserve_addr" app_global_get @@ -886,6 +907,7 @@ bytec 4 // "clawback_addr" app_global_get global ZeroAddress != +// Clawback Address has been deleted assert b assetconfig_14_l6 assetconfig_14_l4: @@ -893,6 +915,7 @@ bytec_3 // "freeze_addr" app_global_get global ZeroAddress != +// Freeze Address has been deleted assert b assetconfig_14_l2 assetconfig_14_l5: @@ -900,6 +923,7 @@ bytec_2 // "reserve_addr" app_global_get global ZeroAddress != +// Reserve Address has been deleted assert b assetconfig_14_l1 assetconfig_14_l6: @@ -908,6 +932,7 @@ bytec_0 // "smart_asa_id" app_global_get callsub circulatingsupply_9 >= +// Invalid Total (must be >= Circulating Supply) assert bytec 7 // "total" load 80 @@ -956,12 +981,14 @@ store 95 store 94 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 94 txnas Assets == +// Invalid Smart ASA ID assert load 96 txnas Accounts @@ -1009,6 +1036,7 @@ txn Sender bytec 4 // "clawback_addr" app_global_get == +// Caller not authorized (must be: Clawback Address) assert bytec_0 // "smart_asa_id" app_global_get @@ -1025,18 +1053,21 @@ bytec_0 // "smart_asa_id" app_local_get == && +// Invalid Smart ASA ID assert b assettransfer_15_l7 assettransfer_15_l4: bytec_1 // "frozen" app_global_get ! +// Smart ASA is frozen assert load 96 txnas Accounts bytec_1 // "frozen" app_local_get ! +// Sender is frozen assert bytec_0 // "smart_asa_id" app_global_get @@ -1045,18 +1076,21 @@ txnas Accounts bytec_0 // "smart_asa_id" app_local_get == +// Invalid Smart ASA ID assert b assettransfer_15_l7 assettransfer_15_l5: bytec_1 // "frozen" app_global_get ! +// Smart ASA is frozen assert load 97 txnas Accounts bytec_1 // "frozen" app_local_get ! +// Receiver is frozen assert bytec_0 // "smart_asa_id" app_global_get @@ -1065,6 +1099,7 @@ txnas Accounts bytec_0 // "smart_asa_id" app_local_get == +// Invalid Smart ASA ID assert bytec_0 // "smart_asa_id" app_global_get @@ -1074,24 +1109,28 @@ load 95 bytec 7 // "total" app_global_get <= +// Over-minting (can not mint more than Total) assert b assettransfer_15_l7 assettransfer_15_l6: bytec_1 // "frozen" app_global_get ! +// Smart ASA is frozen assert load 96 txnas Accounts bytec_1 // "frozen" app_local_get ! +// Sender is frozen assert load 97 txnas Accounts bytec_1 // "frozen" app_local_get ! +// Receiver is frozen assert bytec_0 // "smart_asa_id" app_global_get @@ -1108,6 +1147,7 @@ bytec_0 // "smart_asa_id" app_local_get == && +// Invalid Smart ASA ID assert assettransfer_15_l7: load 94 @@ -1126,17 +1166,20 @@ store 103 store 102 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 102 txnas Assets == +// Invalid Smart ASA ID assert txn Sender bytec_3 // "freeze_addr" app_global_get == +// Caller not authorized (must be: Freeze Address) assert bytec_1 // "frozen" load 103 @@ -1153,17 +1196,20 @@ txnas Accounts callsub isvalidaddressbyteslength_8 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 104 txnas Assets == +// Invalid Smart ASA ID assert txn Sender bytec_3 // "freeze_addr" app_global_get == +// Caller not authorized (must be: Freeze Address) assert load 105 txnas Accounts @@ -1185,12 +1231,14 @@ app_local_get load 107 txnas Assets == +// Invalid Smart ASA ID assert global GroupSize txn GroupIndex intc_1 // 1 + > +// Smart ASA CloseOut: Wrong group size (Expected: 2) assert txn GroupIndex intc_1 // 1 @@ -1198,6 +1246,7 @@ intc_1 // 1 gtxns TypeEnum intc_3 // axfer == +// Underlying ASA CloseOut Txn: Wrong Txn type (Expected: Axfer) assert txn GroupIndex intc_1 // 1 @@ -1206,6 +1255,7 @@ gtxns XferAsset load 107 txnas Assets == +// Underlying ASA CloseOut Txn: Wrong ASA ID (Expected: Smart ASA ID) assert txn GroupIndex intc_1 // 1 @@ -1213,6 +1263,7 @@ intc_1 // 1 gtxns Sender txn Sender == +// Underlying ASA CloseOut Txn: Wrong sender (Expected: Smart ASA CloseOut caller) assert txn GroupIndex intc_1 // 1 @@ -1220,6 +1271,7 @@ intc_1 // 1 gtxns AssetAmount intc_0 // 0 == +// Underlying ASA CloseOut Txn: Wrong amount (Expected: 0) assert txn GroupIndex intc_1 // 1 @@ -1227,6 +1279,7 @@ intc_1 // 1 gtxns AssetCloseTo global CurrentApplicationAddress == +// Underlying ASA CloseOut Txn: Wrong CloseTo address (Expected: Smart ASA App Account) assert load 107 txnas Assets @@ -1240,6 +1293,7 @@ app_global_get load 107 txnas Assets == +// Invalid Smart ASA ID assert bytec_1 // "frozen" app_global_get @@ -1277,6 +1331,7 @@ txnas Accounts bytec_0 // "smart_asa_id" app_local_get == +// Invalid Smart ASA ID assert b assetappcloseout_18_l3 assetappcloseout_18_l5: @@ -1284,6 +1339,7 @@ load 108 txnas Accounts global CurrentApplicationAddress == +// Wrong CloseTo address: Frozen Smart ASA must be closed-out to creator assert b assetappcloseout_18_l2 assetappcloseout_18_l6: @@ -1295,17 +1351,20 @@ assetdestroy_19: store 113 bytec_0 // "smart_asa_id" app_global_get +// Smart ASA ID dose not exist assert bytec_0 // "smart_asa_id" app_global_get load 113 txnas Assets == +// Invalid Smart ASA ID assert txn Sender bytec 6 // "manager_addr" app_global_get == +// Caller not authorized (must be: Manager Address) assert load 113 txnas Assets diff --git a/smart_asa_asc.py b/smart_asa_asc.py index 2a78fc3..5390c1d 100644 --- a/smart_asa_asc.py +++ b/smart_asa_asc.py @@ -67,6 +67,21 @@ def static_attrs(cls): # / --- SMART ASA ASC +# / --- --- ERRORS +class Error: + address_length = "Invalid Address length (must be 32 bytes)" + missing_smart_asa_id = "Smart ASA ID does not exist" + invalid_smart_asa_id = "Invalid Smart ASA ID" + not_creator_addr = "Caller not authorized (must be: App Creator Address)" + not_manager_addr = "Caller not authorized (must be: Manager Address)" + not_reserve_addr = "Caller not authorized (must be: Reserve Address)" + not_freeze_addr = "Caller not authorized (must be: Freeze Address)" + not_clawback_addr = "Caller not authorized (must be: Clawback Address)" + asset_frozen = "Smart ASA is frozen" + sender_frozen = "Sender is frozen" + receiver_frozen = "Receiver is frozen" + + # / --- --- GLOBAL STATE class GlobalInts: total = Bytes("total") @@ -224,8 +239,7 @@ def strip_len_prefix(abi_encoded: Expr) -> Expr: @Subroutine(TealType.uint64) def underlying_asa_create_inner_tx() -> Expr: return Seq( - InnerTxnBuilder.Begin(), - InnerTxnBuilder.SetFields( + InnerTxnBuilder.Execute( { TxnField.fee: Int(0), TxnField.type_enum: TxnType.AssetConfig, @@ -241,7 +255,6 @@ def underlying_asa_create_inner_tx() -> Expr: TxnField.config_asset_clawback: UNDERLYING_ASA_CLAWBACK_ADDR, } ), - InnerTxnBuilder.Submit(), Return(InnerTxn.created_asset_id()), ) @@ -253,34 +266,26 @@ def smart_asa_transfer_inner_txn( asset_sender: Expr, asset_receiver: Expr, ) -> Expr: - return Seq( - InnerTxnBuilder.Begin(), - InnerTxnBuilder.SetFields( - { - TxnField.fee: Int(0), - TxnField.type_enum: TxnType.AssetTransfer, - TxnField.xfer_asset: smart_asa_id, - TxnField.asset_amount: asset_amount, - TxnField.asset_sender: asset_sender, - TxnField.asset_receiver: asset_receiver, - } - ), - InnerTxnBuilder.Submit(), + return InnerTxnBuilder.Execute( + { + TxnField.fee: Int(0), + TxnField.type_enum: TxnType.AssetTransfer, + TxnField.xfer_asset: smart_asa_id, + TxnField.asset_amount: asset_amount, + TxnField.asset_sender: asset_sender, + TxnField.asset_receiver: asset_receiver, + } ) @Subroutine(TealType.none) def smart_asa_destroy_inner_txn(smart_asa_id: Expr) -> Expr: - return Seq( - InnerTxnBuilder.Begin(), - InnerTxnBuilder.SetFields( - { - TxnField.fee: Int(0), - TxnField.type_enum: TxnType.AssetConfig, - TxnField.config_asset: smart_asa_id, - } - ), - InnerTxnBuilder.Submit(), + return InnerTxnBuilder.Execute( + { + TxnField.fee: Int(0), + TxnField.type_enum: TxnType.AssetConfig, + TxnField.config_asset: smart_asa_id, + } ) @@ -288,7 +293,7 @@ def smart_asa_destroy_inner_txn(smart_asa_id: Expr) -> Expr: def is_valid_address_bytes_length(address: Expr) -> Expr: # WARNING: Note this check only ensures proper bytes' length on `address`, # but doesn't ensure that those 32 bytes are a _proper_ Algorand address. - return Assert(Len(address) == Int(key_len_bytes)) + return Assert(Len(address) == Int(key_len_bytes), comment=Error.address_length) @Subroutine(TealType.uint64) @@ -303,9 +308,9 @@ def circulating_supply(asset_id: Expr): def getter_preconditions(asset_id: Expr) -> Expr: smart_asa_id = App.globalGet(GlobalState.smart_asa_id) is_correct_smart_asa_id = smart_asa_id == asset_id - return Assert( - smart_asa_id, - is_correct_smart_asa_id, + return Seq( + Assert(smart_asa_id, comment=Error.missing_smart_asa_id), + Assert(is_correct_smart_asa_id, comment=Error.invalid_smart_asa_id), ) @@ -318,9 +323,23 @@ def asset_app_create() -> Expr: # Not mandatory - Smart ASA Application self validate its state. Assert( Txn.global_num_uints() == Int(GlobalState.num_uints()), + comment=f"Wrong State Schema - Expexted Global Ints: " + f"{GlobalState.num_uints()}", + ), + Assert( Txn.global_num_byte_slices() == Int(GlobalState.num_bytes()), + comment=f"Wrong State Schema - Expexted Global Bytes: " + f"{GlobalState.num_bytes()}", + ), + Assert( Txn.local_num_uints() == Int(LocalState.num_uints()), + comment=f"Wrong State Schema - Expexted Local Ints: " + f"{LocalState.num_uints()}", + ), + Assert( Txn.local_num_byte_slices() == Int(LocalState.num_bytes()), + comment=f"Wrong State Schema - Expexted Local Bytes: " + f"{LocalState.num_bytes()}", ), init_global_state(), Approve(), @@ -367,18 +386,34 @@ def asset_app_optin( optin_to_underlying_asa = account_balance.hasValue() return Seq( # Preconditions + Assert(smart_asa_id, comment=Error.missing_smart_asa_id), + Assert(is_correct_smart_asa_id, comment=Error.invalid_smart_asa_id), Assert( - smart_asa_id, - is_correct_smart_asa_id, underlying_asa_optin.get().type_enum() == TxnType.AssetTransfer, + comment="Underlying ASA Opt-In Txn: Wrong Txn Type (Expected: Axfer)", + ), + Assert( underlying_asa_optin.get().xfer_asset() == smart_asa_id, + comment="Underlying ASA Opt-In Txn: Wrong Asset ID (Expected: Smart ASA ID)", + ), + Assert( underlying_asa_optin.get().sender() == Txn.sender(), + comment="Underlying ASA Opt-In Txn: Wrong Sender (Expected: App Caller)", + ), + Assert( underlying_asa_optin.get().asset_receiver() == Txn.sender(), + comment="Underlying ASA Opt-In Txn: Wrong Asset Receiver (Expected: App Caller)", + ), + Assert( underlying_asa_optin.get().asset_amount() == Int(0), + comment="Underlying ASA Opt-In Txn: Wrong Asset Amount (Expected: 0)", + ), + Assert( underlying_asa_optin.get().asset_close_to() == Global.zero_address(), + comment="Underlying ASA Opt-In Txn: Wrong Asset CloseTo (Expected: Zero Address)", ), account_balance, - Assert(optin_to_underlying_asa), + Assert(optin_to_underlying_asa, comment="Missing Opt-In to Underlying ASA"), # Effects init_local_state(), If(Or(default_frozen, account_balance.value() > Int(0))).Then(freeze_account), @@ -428,7 +463,8 @@ def asset_create( return Seq( # Preconditions - Assert(is_creator, smart_asa_not_created), + Assert(is_creator, comment=Error.not_creator_addr), + Assert(smart_asa_not_created, comment="Smart ASA ID already exists"), is_valid_address_bytes_length(manager_addr.get()), is_valid_address_bytes_length(reserve_addr.get()), is_valid_address_bytes_length(freeze_addr.get()), @@ -506,25 +542,33 @@ def asset_config( return Seq( # Preconditions - Assert( - smart_asa_id, - is_correct_smart_asa_id, - ), # NOTE: usless in ref. impl since 1 ASA : 1 App + Assert(smart_asa_id, comment=Error.missing_smart_asa_id), + # NOTE: useless in ref. impl since 1 ASA : 1 App + Assert(is_correct_smart_asa_id, comment=Error.invalid_smart_asa_id), is_valid_address_bytes_length(manager_addr.get()), is_valid_address_bytes_length(reserve_addr.get()), is_valid_address_bytes_length(freeze_addr.get()), is_valid_address_bytes_length(clawback_addr.get()), - Assert(is_manager_addr), + Assert(is_manager_addr, comment=Error.not_manager_addr), If(update_reserve_addr).Then( - Assert(current_reserve_addr != Global.zero_address()) + Assert( + current_reserve_addr != Global.zero_address(), + comment="Reserve Address has been deleted", + ) ), If(update_freeze_addr).Then( - Assert(current_freeze_addr != Global.zero_address()) + Assert( + current_freeze_addr != Global.zero_address(), + comment="Freeze Address has been deleted", + ) ), If(update_clawback_addr).Then( - Assert(current_clawback_addr != Global.zero_address()) + Assert( + current_clawback_addr != Global.zero_address(), + comment="Clawback Address has been deleted", + ) ), - Assert(is_valid_total), + Assert(is_valid_total, comment="Invalid Total (must be >= Circulating Supply)"), # Effects App.globalPut(GlobalState.total, total.get()), App.globalPut(GlobalState.decimals, decimals.get()), @@ -599,54 +643,55 @@ def asset_transfer( asset_receiver_frozen = App.localGet(asset_receiver.address(), LocalState.frozen) return Seq( # Preconditions - Assert( - smart_asa_id, - is_correct_smart_asa_id, - ), + Assert(smart_asa_id, comment=Error.missing_smart_asa_id), + Assert(is_correct_smart_asa_id, comment=Error.invalid_smart_asa_id), is_valid_address_bytes_length(asset_sender.address()), is_valid_address_bytes_length(asset_receiver.address()), If(is_not_clawback) .Then( # Asset Regular Transfer Preconditions - Assert( - Not(asset_frozen), - Not(asset_sender_frozen), - Not(asset_receiver_frozen), - is_current_smart_asa_id, - ), + Assert(Not(asset_frozen), comment=Error.asset_frozen), + Assert(Not(asset_sender_frozen), comment=Error.sender_frozen), + Assert(Not(asset_receiver_frozen), comment=Error.receiver_frozen), + Assert(is_current_smart_asa_id, comment=Error.invalid_smart_asa_id), ) .ElseIf(is_minting) .Then( # Asset Minting Preconditions + Assert(Not(asset_frozen), comment=Error.asset_frozen), + Assert(Not(asset_receiver_frozen), comment=Error.receiver_frozen), Assert( - Not(asset_frozen), - Not(asset_receiver_frozen), smart_asa_id == App.localGet(asset_receiver.address(), LocalState.smart_asa_id), - # NOTE: Ref. implementation prevents minting more than `total`. + comment=Error.invalid_smart_asa_id, + ), + # NOTE: Ref. implementation prevents minting more than `total`. + Assert( circulating_supply(smart_asa_id) + asset_amount.get() <= App.globalGet(GlobalState.total), + comment="Over-minting (can not mint more than Total)", ), ) .ElseIf(is_burning) .Then( # Asset Burning Preconditions + Assert(Not(asset_frozen), comment=Error.asset_frozen), + Assert(Not(asset_sender_frozen), comment=Error.sender_frozen), Assert( - Not(asset_frozen), - Not(asset_sender_frozen), smart_asa_id == App.localGet(asset_sender.address(), LocalState.smart_asa_id), + comment=Error.invalid_smart_asa_id, ), ) .Else( # Asset Clawback Preconditions - Assert(is_clawback), + Assert(is_clawback, comment=Error.not_clawback_addr), # NOTE: `is_current_smart_asa_id` implicitly checks that both # `asset_sender` and `asset_receiver` opted-in the Smart ASA # App. This ensures that _mint_ and _burn_ can not be # executed as _clawback_, since the Smart ASA App can not # opt-in to itself. - Assert(is_current_smart_asa_id), + Assert(is_current_smart_asa_id, comment=Error.invalid_smart_asa_id), ), # Effects smart_asa_transfer_inner_txn( @@ -674,8 +719,15 @@ def asset_freeze(freeze_asset: abi.Asset, asset_frozen: abi.Bool) -> Expr: # Asset Freeze Preconditions Assert( smart_asa_id, + comment=Error.missing_smart_asa_id, + ), + Assert( is_correct_smart_asa_id, + comment=Error.invalid_smart_asa_id, + ), + Assert( is_freeze_addr, + comment=Error.not_freeze_addr, ), # Effects App.globalPut(GlobalState.frozen, asset_frozen.get()), @@ -702,7 +754,18 @@ def account_freeze( return Seq( # Account Freeze Preconditions is_valid_address_bytes_length(freeze_account.address()), - Assert(smart_asa_id, is_correct_smart_asa_id, is_freeze_addr), + Assert( + smart_asa_id, + comment=Error.missing_smart_asa_id, + ), + Assert( + is_correct_smart_asa_id, + comment=Error.invalid_smart_asa_id, + ), + Assert( + is_freeze_addr, + comment=Error.not_freeze_addr, + ), # Effects App.localPut(freeze_account.address(), LocalState.frozen, asset_frozen.get()), ) @@ -736,13 +799,32 @@ def asset_app_closeout( is_valid_address_bytes_length(close_to.address()), Assert( is_current_smart_asa_id, + comment=Error.invalid_smart_asa_id, + ), + Assert( Global.group_size() > asa_closeout_relative_idx, + comment="Smart ASA CloseOut: Wrong group size (Expected: 2)", + ), + Assert( Gtxn[asa_closeout_relative_idx].type_enum() == TxnType.AssetTransfer, + comment="Underlying ASA CloseOut Txn: Wrong Txn type (Expected: Axfer)", + ), + Assert( Gtxn[asa_closeout_relative_idx].xfer_asset() == close_asset.asset_id(), + comment="Underlying ASA CloseOut Txn: Wrong ASA ID (Expected: Smart ASA ID)", + ), + Assert( Gtxn[asa_closeout_relative_idx].sender() == Txn.sender(), + comment="Underlying ASA CloseOut Txn: Wrong sender (Expected: Smart ASA CloseOut caller)", + ), + Assert( Gtxn[asa_closeout_relative_idx].asset_amount() == Int(0), + comment="Underlying ASA CloseOut Txn: Wrong amount (Expected: 0)", + ), + Assert( Gtxn[asa_closeout_relative_idx].asset_close_to() == Global.current_application_address(), + comment="Underlying ASA CloseOut Txn: Wrong CloseTo address (Expected: Smart ASA App Account)", ), # Effects asset_creator, @@ -750,18 +832,22 @@ def asset_app_closeout( # users' lock-in. If(asset_creator.hasValue()).Then( # NOTE: Smart ASA has not been destroyed. - Assert(is_correct_smart_asa_id), + Assert(is_correct_smart_asa_id, comment=Error.invalid_smart_asa_id), If(Or(asset_frozen, asset_closer_frozen)).Then( # NOTE: If Smart ASA is frozen, users can only close-out to # Creator - Assert(close_to.address() == Global.current_application_address()) + Assert( + close_to.address() == Global.current_application_address(), + comment="Wrong CloseTo address: Frozen Smart ASA must be closed-out to creator", + ), ), If(close_to.address() != Global.current_application_address()).Then( # NOTE: If the target of close-out is not Creator, it MUST be # opted-in to the current Smart ASA. Assert( smart_asa_id - == App.localGet(close_to.address(), LocalState.smart_asa_id) + == App.localGet(close_to.address(), LocalState.smart_asa_id), + comment=Error.invalid_smart_asa_id, ) ), account_balance, @@ -794,8 +880,15 @@ def asset_destroy(destroy_asset: abi.Asset) -> Expr: # Asset Destroy Preconditions Assert( smart_asa_id, + comment=Error.missing_smart_asa_id, + ), + Assert( is_correct_smart_asa_id, + comment=Error.invalid_smart_asa_id, + ), + Assert( is_manager_addr, + comment=Error.not_manager_addr, ), # Effects smart_asa_destroy_inner_txn(destroy_asset.asset_id()),