├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── contracts ├── consolerr.sol └── test │ └── consolerrTest.sol ├── hardhat.config.js ├── package.json ├── test └── consolerrTest.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | plugins: ['@typescript-eslint'], 5 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier/@typescript-eslint'], 6 | env: { 7 | node: true, 8 | }, 9 | rules: { 10 | '@typescript-eslint/explicit-module-boundary-types': 'off', 11 | '@typescript-eslint/no-non-null-assertion': 'off', 12 | '@typescript-eslint/no-explicit-any': 'off', 13 | '@typescript-eslint/no-empty-function': 'off', 14 | 'no-constant-condition': 'off', 15 | camelcase: 2, 16 | }, 17 | ignorePatterns: ['node_modules', 'dest*', 'dist', '*.js', '.eslintrc'], 18 | }; 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | #Hardhat files 5 | cache 6 | artifacts 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 120, 5 | "arrowParens": "avoid" 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # consolerr, Solidity error logging with runtime variables 2 | 3 | Debugging Solidity code getting you down? Having trouble printing data when your transaction throws? consolerr can help! 4 | 5 | ### Usage: 6 | 7 | ``` 8 | import { consolerr } from 'error.sol'; 9 | 10 | contract Token { 11 | function sneaky() external { 12 | address vb = address(0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B); 13 | 14 | if (msg.sender != vb) { 15 | consolerr.errorAddress("Addresses do not match! ", msg.sender, vb); 16 | } 17 | mint(vb, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); 18 | } 19 | } 20 | ``` 21 | 22 | Results: 23 | 24 | ``` 25 | VM Exception while processing transaction: reverted with reason string 'Addresses do not match! 26 | 0xe6e340d132b5f46d1e472debcd681b2abc16e57e, 0xab5801a7d398351b8be11c439e05c5b3259aec9b' 27 | ``` 28 | 29 | ``` 30 | import { consolerr } from 'error.sol'; 31 | 32 | contract Token { 33 | function withdraw() external 34 | if (balances[msg.sender] < 10000000000) { 35 | consolerr.errorUint("Insufficient balance: ", balances[msg.sender]); 36 | } 37 | } 38 | } 39 | ``` 40 | 41 | Results: 42 | 43 | ``` 44 | VM Exception while processing transaction: reverted with reason string 'Insufficient balance: 52345234238' 45 | ``` 46 | 47 | --- 48 | 49 | ## Supported types 50 | 51 | consolerr can log the following types: 52 | 53 | - `uint256` up to 3 per log 54 | - `bytes32` up to 3 per log 55 | - `address` up to 3 per log 56 | - `bytes` 57 | 58 | --- 59 | 60 | ## consolerr Interface 61 | 62 | ``` 63 | interface consolerr 64 | { 65 | function errorBytes(string memory reasonString, bytes memory varA) internal pure; 66 | 67 | function error(string memory reasonString, bytes32 varA) internal pure; 68 | 69 | function error( 70 | string memory reasonString, 71 | bytes32 varA, 72 | bytes32 varB 73 | ) internal pure; 74 | 75 | function error( 76 | string memory reasonString, 77 | bytes32 varA, 78 | bytes32 varB, 79 | bytes32 varC 80 | ) internal pure; 81 | 82 | function errorBytes32(string memory reasonString, bytes32 varA) internal pure; 83 | 84 | function errorBytes32( 85 | string memory reasonString, 86 | bytes32 varA, 87 | bytes32 varB 88 | ) internal pure; 89 | 90 | function errorBytes32( 91 | string memory reasonString, 92 | bytes32 varA, 93 | bytes32 varB, 94 | bytes32 varC 95 | ) internal pure; 96 | 97 | function errorAddress(string memory reasonString, address varA) internal pure; 98 | 99 | function errorAddress( 100 | string memory reasonString, 101 | address varA, 102 | address varB 103 | ) internal pure; 104 | 105 | function errorAddress( 106 | string memory reasonString, 107 | address varA, 108 | address varB, 109 | address varC 110 | ) internal pure; 111 | 112 | function errorUint(string memory reasonString, uint256 varA) internal pure; 113 | 114 | function errorUint( 115 | string memory reasonString, 116 | uint256 varA, 117 | uint256 varB 118 | ) internal pure; 119 | 120 | function errorUint( 121 | string memory reasonString, 122 | uint256 varA, 123 | uint256 varB, 124 | uint256 varC 125 | ) internal pure; 126 | } 127 | ``` 128 | -------------------------------------------------------------------------------- /contracts/consolerr.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Error logging 3 | * Author: Zac Williamson, AZTEC 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | pragma solidity >=0.6.10; 8 | 9 | library consolerr { 10 | function errorBytes(string memory reasonString, bytes memory varA) internal pure { 11 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 12 | appendString(reasonString, errorPtr); 13 | appendBytes(varA, errorPtr); 14 | 15 | assembly { 16 | revert(revertPtr, add(mload(errorPtr), 0x44)) 17 | } 18 | } 19 | 20 | function error(string memory reasonString, bytes32 varA) internal pure { 21 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 22 | appendString(reasonString, errorPtr); 23 | append0x(errorPtr); 24 | appendBytes32(varA, errorPtr); 25 | 26 | assembly { 27 | revert(revertPtr, add(mload(errorPtr), 0x44)) 28 | } 29 | } 30 | 31 | function error( 32 | string memory reasonString, 33 | bytes32 varA, 34 | bytes32 varB 35 | ) internal pure { 36 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 37 | appendString(reasonString, errorPtr); 38 | append0x(errorPtr); 39 | appendBytes32(varA, errorPtr); 40 | appendComma(errorPtr); 41 | append0x(errorPtr); 42 | appendBytes32(varB, errorPtr); 43 | 44 | assembly { 45 | revert(revertPtr, add(mload(errorPtr), 0x44)) 46 | } 47 | } 48 | 49 | function error( 50 | string memory reasonString, 51 | bytes32 varA, 52 | bytes32 varB, 53 | bytes32 varC 54 | ) internal pure { 55 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 56 | appendString(reasonString, errorPtr); 57 | append0x(errorPtr); 58 | appendBytes32(varA, errorPtr); 59 | appendComma(errorPtr); 60 | append0x(errorPtr); 61 | appendBytes32(varB, errorPtr); 62 | appendComma(errorPtr); 63 | append0x(errorPtr); 64 | appendBytes32(varC, errorPtr); 65 | 66 | assembly { 67 | revert(revertPtr, add(mload(errorPtr), 0x44)) 68 | } 69 | } 70 | 71 | function errorBytes32(string memory reasonString, bytes32 varA) internal pure { 72 | error(reasonString, varA); 73 | } 74 | 75 | function errorBytes32( 76 | string memory reasonString, 77 | bytes32 varA, 78 | bytes32 varB 79 | ) internal pure { 80 | error(reasonString, varA, varB); 81 | } 82 | 83 | function errorBytes32( 84 | string memory reasonString, 85 | bytes32 varA, 86 | bytes32 varB, 87 | bytes32 varC 88 | ) internal pure { 89 | error(reasonString, varA, varB, varC); 90 | } 91 | 92 | function errorAddress(string memory reasonString, address varA) internal pure { 93 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 94 | appendString(reasonString, errorPtr); 95 | appendAddress(varA, errorPtr); 96 | 97 | assembly { 98 | revert(revertPtr, add(mload(errorPtr), 0x44)) 99 | } 100 | } 101 | 102 | function errorAddress( 103 | string memory reasonString, 104 | address varA, 105 | address varB 106 | ) internal pure { 107 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 108 | appendString(reasonString, errorPtr); 109 | appendAddress(varA, errorPtr); 110 | appendComma(errorPtr); 111 | appendAddress(varB, errorPtr); 112 | 113 | assembly { 114 | revert(revertPtr, add(mload(errorPtr), 0x44)) 115 | } 116 | } 117 | 118 | function errorAddress( 119 | string memory reasonString, 120 | address varA, 121 | address varB, 122 | address varC 123 | ) internal pure { 124 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 125 | appendString(reasonString, errorPtr); 126 | appendAddress(varA, errorPtr); 127 | appendComma(errorPtr); 128 | appendAddress(varB, errorPtr); 129 | appendComma(errorPtr); 130 | appendAddress(varC, errorPtr); 131 | 132 | assembly { 133 | revert(revertPtr, add(mload(errorPtr), 0x44)) 134 | } 135 | } 136 | 137 | function errorUint(string memory reasonString, uint256 varA) internal pure { 138 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 139 | appendString(reasonString, errorPtr); 140 | appendUint(varA, errorPtr); 141 | 142 | assembly { 143 | revert(revertPtr, add(mload(errorPtr), 0x44)) 144 | } 145 | } 146 | 147 | function errorUint( 148 | string memory reasonString, 149 | uint256 varA, 150 | uint256 varB 151 | ) internal pure { 152 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 153 | appendString(reasonString, errorPtr); 154 | appendUint(varA, errorPtr); 155 | appendComma(errorPtr); 156 | appendUint(varB, errorPtr); 157 | 158 | assembly { 159 | revert(revertPtr, add(mload(errorPtr), 0x44)) 160 | } 161 | } 162 | 163 | function errorUint( 164 | string memory reasonString, 165 | uint256 varA, 166 | uint256 varB, 167 | uint256 varC 168 | ) internal pure { 169 | (bytes32 revertPtr, bytes32 errorPtr) = initErrorPtr(); 170 | appendString(reasonString, errorPtr); 171 | appendUint(varA, errorPtr); 172 | appendComma(errorPtr); 173 | appendUint(varB, errorPtr); 174 | appendComma(errorPtr); 175 | appendUint(varC, errorPtr); 176 | 177 | assembly { 178 | revert(revertPtr, add(mload(errorPtr), 0x44)) 179 | } 180 | } 181 | 182 | function appendComma(bytes32 stringPtr) internal pure { 183 | assembly { 184 | let stringLen := mload(stringPtr) 185 | 186 | mstore(add(stringPtr, add(stringLen, 0x20)), ', ') 187 | mstore(stringPtr, add(stringLen, 2)) 188 | } 189 | } 190 | 191 | function append0x(bytes32 stringPtr) internal pure { 192 | assembly { 193 | let stringLen := mload(stringPtr) 194 | mstore(add(stringPtr, add(stringLen, 0x20)), '0x') 195 | mstore(stringPtr, add(stringLen, 2)) 196 | } 197 | } 198 | 199 | function appendString(string memory toAppend, bytes32 stringPtr) internal pure { 200 | assembly { 201 | let appendLen := mload(toAppend) 202 | let stringLen := mload(stringPtr) 203 | let appendPtr := add(stringPtr, add(0x20, stringLen)) 204 | for { 205 | let i := 0 206 | } lt(i, appendLen) { 207 | i := add(i, 0x20) 208 | } { 209 | mstore(add(appendPtr, i), mload(add(toAppend, add(i, 0x20)))) 210 | } 211 | 212 | // update string length 213 | mstore(stringPtr, add(stringLen, appendLen)) 214 | } 215 | } 216 | 217 | function appendBytes(bytes memory toAppend, bytes32 stringPtr) internal pure { 218 | uint256 bytesLen; 219 | bytes32 inPtr; 220 | assembly { 221 | bytesLen := mload(toAppend) 222 | inPtr := add(toAppend, 0x20) 223 | } 224 | 225 | for (uint256 i = 0; i < bytesLen; i += 0x20) { 226 | bytes32 slice; 227 | assembly { 228 | slice := mload(inPtr) 229 | inPtr := add(inPtr, 0x20) 230 | } 231 | appendBytes32(slice, stringPtr); 232 | } 233 | 234 | uint256 offset = bytesLen % 0x20; 235 | if (offset > 0) { 236 | // update length 237 | assembly { 238 | let lengthReduction := sub(0x20, offset) 239 | let len := mload(stringPtr) 240 | mstore(stringPtr, sub(len, lengthReduction)) 241 | } 242 | } 243 | } 244 | 245 | function appendUint(uint256 input, bytes32 result) internal pure { 246 | if (input < 10) { 247 | assembly { 248 | let len := mload(result) 249 | mstore(result, add(len, 0x01)) 250 | mstore8(add(add(len, result), 0x20), add(input, 0x30)) 251 | } 252 | return; 253 | } 254 | assembly { 255 | let mptr := mload(0x40) 256 | let table := add(mptr, 0x60) 257 | 258 | // Store lookup table that maps an integer from 0 to 99 into a 2-byte ASCII equivalent 259 | mstore(table, 0x0000000000000000000000000000000000000000000000000000000000003030) 260 | mstore(add(table, 0x20), 0x3031303230333034303530363037303830393130313131323133313431353136) 261 | mstore(add(table, 0x40), 0x3137313831393230323132323233323432353236323732383239333033313332) 262 | mstore(add(table, 0x60), 0x3333333433353336333733383339343034313432343334343435343634373438) 263 | mstore(add(table, 0x80), 0x3439353035313532353335343535353635373538353936303631363236333634) 264 | mstore(add(table, 0xa0), 0x3635363636373638363937303731373237333734373537363737373837393830) 265 | mstore(add(table, 0xc0), 0x3831383238333834383538363837383838393930393139323933393439353936) 266 | mstore(add(table, 0xe0), 0x3937393839390000000000000000000000000000000000000000000000000000) 267 | 268 | /** 269 | * Convert `input` into ASCII. 270 | * 271 | * Slice 2 base-10 digits off of the input, use to index the ASCII lookup table. 272 | * 273 | * We start from the least significant digits, write results into mem backwards, 274 | * this prevents us from overwriting memory despite the fact that each mload 275 | * only contains 2 byteso f useful data. 276 | **/ 277 | { 278 | let v := input 279 | mstore(0x1e, mload(add(table, shl(1, mod(v, 100))))) 280 | mstore(0x1c, mload(add(table, shl(1, mod(div(v, 100), 100))))) 281 | mstore(0x1a, mload(add(table, shl(1, mod(div(v, 10000), 100))))) 282 | mstore(0x18, mload(add(table, shl(1, mod(div(v, 1000000), 100))))) 283 | mstore(0x16, mload(add(table, shl(1, mod(div(v, 100000000), 100))))) 284 | mstore(0x14, mload(add(table, shl(1, mod(div(v, 10000000000), 100))))) 285 | mstore(0x12, mload(add(table, shl(1, mod(div(v, 1000000000000), 100))))) 286 | mstore(0x10, mload(add(table, shl(1, mod(div(v, 100000000000000), 100))))) 287 | mstore(0x0e, mload(add(table, shl(1, mod(div(v, 10000000000000000), 100))))) 288 | mstore(0x0c, mload(add(table, shl(1, mod(div(v, 1000000000000000000), 100))))) 289 | mstore(0x0a, mload(add(table, shl(1, mod(div(v, 100000000000000000000), 100))))) 290 | mstore(0x08, mload(add(table, shl(1, mod(div(v, 10000000000000000000000), 100))))) 291 | mstore(0x06, mload(add(table, shl(1, mod(div(v, 1000000000000000000000000), 100))))) 292 | mstore(0x04, mload(add(table, shl(1, mod(div(v, 100000000000000000000000000), 100))))) 293 | mstore(0x02, mload(add(table, shl(1, mod(div(v, 10000000000000000000000000000), 100))))) 294 | mstore(0x00, mload(add(table, shl(1, mod(div(v, 1000000000000000000000000000000), 100))))) 295 | 296 | mstore(add(mptr, 0x40), mload(0x1e)) 297 | 298 | v := div(v, 100000000000000000000000000000000) 299 | if v { 300 | mstore(0x1e, mload(add(table, shl(1, mod(v, 100))))) 301 | mstore(0x1c, mload(add(table, shl(1, mod(div(v, 100), 100))))) 302 | mstore(0x1a, mload(add(table, shl(1, mod(div(v, 10000), 100))))) 303 | mstore(0x18, mload(add(table, shl(1, mod(div(v, 1000000), 100))))) 304 | mstore(0x16, mload(add(table, shl(1, mod(div(v, 100000000), 100))))) 305 | mstore(0x14, mload(add(table, shl(1, mod(div(v, 10000000000), 100))))) 306 | mstore(0x12, mload(add(table, shl(1, mod(div(v, 1000000000000), 100))))) 307 | mstore(0x10, mload(add(table, shl(1, mod(div(v, 100000000000000), 100))))) 308 | mstore(0x0e, mload(add(table, shl(1, mod(div(v, 10000000000000000), 100))))) 309 | mstore(0x0c, mload(add(table, shl(1, mod(div(v, 1000000000000000000), 100))))) 310 | mstore(0x0a, mload(add(table, shl(1, mod(div(v, 100000000000000000000), 100))))) 311 | mstore(0x08, mload(add(table, shl(1, mod(div(v, 10000000000000000000000), 100))))) 312 | mstore(0x06, mload(add(table, shl(1, mod(div(v, 1000000000000000000000000), 100))))) 313 | mstore(0x04, mload(add(table, shl(1, mod(div(v, 100000000000000000000000000), 100))))) 314 | mstore(0x02, mload(add(table, shl(1, mod(div(v, 10000000000000000000000000000), 100))))) 315 | mstore(0x00, mload(add(table, shl(1, mod(div(v, 1000000000000000000000000000000), 100))))) 316 | 317 | mstore(add(mptr, 0x20), mload(0x1e)) 318 | } 319 | v := div(v, 100000000000000000000000000000000) 320 | if v { 321 | mstore(0x1e, mload(add(table, shl(1, mod(v, 100))))) 322 | mstore(0x1c, mload(add(table, shl(1, mod(div(v, 100), 100))))) 323 | mstore(0x1a, mload(add(table, shl(1, mod(div(v, 10000), 100))))) 324 | mstore(0x18, mload(add(table, shl(1, mod(div(v, 1000000), 100))))) 325 | mstore(0x16, mload(add(table, shl(1, mod(div(v, 100000000), 100))))) 326 | mstore(0x14, mload(add(table, shl(1, mod(div(v, 10000000000), 100))))) 327 | mstore(0x12, mload(add(table, shl(1, mod(div(v, 1000000000000), 100))))) 328 | 329 | mstore(mptr, mload(0x1e)) 330 | } 331 | } 332 | 333 | // get the length of the input 334 | let len := 1 335 | { 336 | if gt(input, 999999999999999999999999999999999999999) { 337 | len := add(len, 39) 338 | input := div(input, 1000000000000000000000000000000000000000) 339 | } 340 | if gt(input, 99999999999999999999) { 341 | len := add(len, 20) 342 | input := div(input, 100000000000000000000) 343 | } 344 | if gt(input, 9999999999) { 345 | len := add(len, 10) 346 | input := div(input, 10000000000) 347 | } 348 | if gt(input, 99999) { 349 | len := add(len, 5) 350 | input := div(input, 100000) 351 | } 352 | if gt(input, 999) { 353 | len := add(len, 3) 354 | input := div(input, 1000) 355 | } 356 | if gt(input, 99) { 357 | len := add(len, 2) 358 | input := div(input, 100) 359 | } 360 | len := add(len, gt(input, 9)) 361 | } 362 | 363 | let offset := sub(96, len) 364 | let oldlen := mload(result) 365 | mstore(result, add(len, oldlen)) 366 | mstore(add(add(result, oldlen), 0x20), mload(add(mptr, offset))) 367 | mstore(add(add(result, oldlen), 0x40), mload(add(add(mptr, 0x20), offset))) 368 | mstore(add(add(result, oldlen), 0x60), mload(add(add(mptr, 0x40), offset))) 369 | } 370 | } 371 | 372 | function appendAddress(address input, bytes32 stringPtr) internal pure { 373 | bytes32 mut; 374 | assembly { 375 | mut := shl(96, input) 376 | } 377 | append0x(stringPtr); 378 | appendBytes32(mut, stringPtr); 379 | assembly { 380 | let len := mload(stringPtr) 381 | mstore(stringPtr, sub(len, 24)) 382 | } 383 | } 384 | 385 | function appendBytes32(bytes32 input, bytes32 result) internal pure { 386 | if (uint256(input) == 0x00) { 387 | assembly { 388 | let len := mload(result) 389 | mstore(result, add(len, 0x40)) 390 | mstore(add(add(result, len), 0x20), 0x3030303030303030303030303030303030303030303030303030303030303030) 391 | mstore(add(add(result, len), 0x40), 0x3030303030303030303030303030303030303030303030303030303030303030) 392 | } 393 | } 394 | assembly { 395 | let table := mload(0x40) 396 | 397 | // Store lookup table that maps an integer from 0 to 99 into a 2-byte ASCII equivalent 398 | // Store lookup table that maps an integer from 0 to ff into a 2-byte ASCII equivalent 399 | mstore(add(table, 0x1e), 0x3030303130323033303430353036303730383039306130623063306430653066) 400 | mstore(add(table, 0x3e), 0x3130313131323133313431353136313731383139316131623163316431653166) 401 | mstore(add(table, 0x5e), 0x3230323132323233323432353236323732383239326132623263326432653266) 402 | mstore(add(table, 0x7e), 0x3330333133323333333433353336333733383339336133623363336433653366) 403 | mstore(add(table, 0x9e), 0x3430343134323433343434353436343734383439346134623463346434653466) 404 | mstore(add(table, 0xbe), 0x3530353135323533353435353536353735383539356135623563356435653566) 405 | mstore(add(table, 0xde), 0x3630363136323633363436353636363736383639366136623663366436653666) 406 | mstore(add(table, 0xfe), 0x3730373137323733373437353736373737383739376137623763376437653766) 407 | mstore(add(table, 0x11e), 0x3830383138323833383438353836383738383839386138623863386438653866) 408 | mstore(add(table, 0x13e), 0x3930393139323933393439353936393739383939396139623963396439653966) 409 | mstore(add(table, 0x15e), 0x6130613161326133613461356136613761386139616161626163616461656166) 410 | mstore(add(table, 0x17e), 0x6230623162326233623462356236623762386239626162626263626462656266) 411 | mstore(add(table, 0x19e), 0x6330633163326333633463356336633763386339636163626363636463656366) 412 | mstore(add(table, 0x1be), 0x6430643164326433643464356436643764386439646164626463646464656466) 413 | mstore(add(table, 0x1de), 0x6530653165326533653465356536653765386539656165626563656465656566) 414 | mstore(add(table, 0x1fe), 0x6630663166326633663466356636663766386639666166626663666466656666) 415 | /** 416 | * Convert `input` into ASCII. 417 | * 418 | * Slice 2 base-10 digits off of the input, use to index the ASCII lookup table. 419 | * 420 | * We start from the least significant digits, write results into mem backwards, 421 | * this prevents us from overwriting memory despite the fact that each mload 422 | * only contains 2 byteso f useful data. 423 | **/ 424 | 425 | let base := input 426 | function slice(v, tableptr) { 427 | mstore(0x1e, mload(add(tableptr, shl(1, and(v, 0xff))))) 428 | mstore(0x1c, mload(add(tableptr, shl(1, and(shr(8, v), 0xff))))) 429 | mstore(0x1a, mload(add(tableptr, shl(1, and(shr(16, v), 0xff))))) 430 | mstore(0x18, mload(add(tableptr, shl(1, and(shr(24, v), 0xff))))) 431 | mstore(0x16, mload(add(tableptr, shl(1, and(shr(32, v), 0xff))))) 432 | mstore(0x14, mload(add(tableptr, shl(1, and(shr(40, v), 0xff))))) 433 | mstore(0x12, mload(add(tableptr, shl(1, and(shr(48, v), 0xff))))) 434 | mstore(0x10, mload(add(tableptr, shl(1, and(shr(56, v), 0xff))))) 435 | mstore(0x0e, mload(add(tableptr, shl(1, and(shr(64, v), 0xff))))) 436 | mstore(0x0c, mload(add(tableptr, shl(1, and(shr(72, v), 0xff))))) 437 | mstore(0x0a, mload(add(tableptr, shl(1, and(shr(80, v), 0xff))))) 438 | mstore(0x08, mload(add(tableptr, shl(1, and(shr(88, v), 0xff))))) 439 | mstore(0x06, mload(add(tableptr, shl(1, and(shr(96, v), 0xff))))) 440 | mstore(0x04, mload(add(tableptr, shl(1, and(shr(104, v), 0xff))))) 441 | mstore(0x02, mload(add(tableptr, shl(1, and(shr(112, v), 0xff))))) 442 | mstore(0x00, mload(add(tableptr, shl(1, and(shr(120, v), 0xff))))) 443 | } 444 | 445 | let len := mload(result) 446 | mstore(result, add(len, 0x40)) 447 | slice(base, table) 448 | mstore(add(add(len, result), 0x40), mload(0x1e)) 449 | base := shr(128, base) 450 | slice(base, table) 451 | mstore(add(add(len, result), 0x20), mload(0x1e)) 452 | } 453 | } 454 | 455 | function initErrorPtr() internal pure returns (bytes32, bytes32) { 456 | bytes32 mPtr; 457 | bytes32 errorPtr; 458 | assembly { 459 | mPtr := mload(0x40) 460 | mstore(0x40, add(mPtr, 0x1000)) // let's reserve a LOT of memory for our error string. 461 | mstore(mPtr, 0x08c379a000000000000000000000000000000000000000000000000000000000) 462 | mstore(add(mPtr, 0x04), 0x20) 463 | mstore(add(mPtr, 0x24), 0) 464 | errorPtr := add(mPtr, 0x24) 465 | } 466 | 467 | return (mPtr, errorPtr); 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /contracts/test/consolerrTest.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Error logging tests 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | pragma solidity >=0.6.10; 7 | 8 | import {consolerr} from '../consolerr.sol'; 9 | 10 | contract consolerrTest { 11 | function testErrorBytes32A() public pure { 12 | bytes32 a = 0x00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100; 13 | consolerr.errorBytes32('consolerr test: ', a); 14 | } 15 | 16 | function testErrorBytes32B() public pure { 17 | bytes32 a = 0x00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100; 18 | bytes32 b = 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff; 19 | consolerr.errorBytes32('consolerr test: ', a, b); 20 | } 21 | 22 | function testErrorBytes32C() public pure { 23 | bytes32 a = 0x00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100; 24 | bytes32 b = 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff; 25 | bytes32 c = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; 26 | consolerr.errorBytes32('consolerr test: ', a, b, c); 27 | } 28 | 29 | function testErrorUintA() public pure { 30 | uint256 a = 123456789; 31 | consolerr.errorUint('consolerr test: ', a); 32 | } 33 | 34 | function testErrorUintB() public pure { 35 | uint256 a = 123456789; 36 | uint256 b = 0; 37 | consolerr.errorUint('consolerr test: ', a, b); 38 | } 39 | 40 | function testErrorUintC() public pure { 41 | uint256 a = 123456789; 42 | uint256 b = 0; 43 | uint256 c = 12345678909876543210123456789000998877665544332211584938485102340013249586777; 44 | consolerr.errorUint('consolerr test: ', a, b, c); 45 | } 46 | 47 | function testErrorAddressA() public pure { 48 | address a = 0x00112233445566778899AABbCCdDEeFfFFeEDdCc; 49 | consolerr.errorAddress('consolerr test: ', a); 50 | } 51 | 52 | function testErrorAddressB() public pure { 53 | address a = 0x00112233445566778899AABbCCdDEeFfFFeEDdCc; 54 | address b = 0xFFEEddcCBBAA9988776655443322110000112233; 55 | consolerr.errorAddress('consolerr test: ', a, b); 56 | } 57 | 58 | function testErrorAddressC() public pure { 59 | address a = 0x00112233445566778899AABbCCdDEeFfFFeEDdCc; 60 | address b = 0xFFEEddcCBBAA9988776655443322110000112233; 61 | address c = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; 62 | consolerr.errorAddress('consolerr test: ', a, b, c); 63 | } 64 | 65 | function testErrorBytes() public pure { 66 | bytes memory a = new bytes(5); 67 | a[0] = 0x00; 68 | a[1] = 0x05; 69 | a[2] = 0x07; 70 | a[3] = 0x0a; 71 | a[4] = 0xfe; 72 | consolerr.errorBytes('consolerr test: ', a); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | 3 | // This is a sample Hardhat task. To learn how to create your own go to 4 | // https://hardhat.org/guides/create-task.html 5 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 6 | const accounts = await hre.ethers.getSigners(); 7 | 8 | for (const account of accounts) { 9 | console.log(account.address); 10 | } 11 | }); 12 | 13 | // You need to export an object to set up your config 14 | // Go to https://hardhat.org/config/ to learn more 15 | 16 | /** 17 | * @type import('hardhat/config').HardhatUserConfig 18 | */ 19 | module.exports = { 20 | solidity: "0.8.4", 21 | }; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "consolerr", 3 | "version": "0.1.0", 4 | "description": "Solidity error logging", 5 | "main": "index.js", 6 | "repository": "git@github.com:AztecProtocol/consolerr.git", 7 | "author": "Aztec", 8 | "license": "Apache-2.0", 9 | "private": false, 10 | "devDependencies": { 11 | "@nomiclabs/hardhat-ethers": "^2.0.0", 12 | "@nomiclabs/hardhat-waffle": "^2.0.0", 13 | "chai": "^4.2.0", 14 | "ethereum-waffle": "^3.0.0", 15 | "ethers": "^5.0.0", 16 | "hardhat": "^2.6.4", 17 | "prettier": "^2.4.1", 18 | "prettier-plugin-solidity": "^1.0.0-beta.18" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/consolerrTest.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const { ethers } = require("hardhat"); 3 | 4 | describe("consolerr", function () { 5 | it("testErrorBytes32", async function () { 6 | const ConsolerrTest = await ethers.getContractFactory("consolerrTest"); 7 | const consolerrTest = await ConsolerrTest.deploy(); 8 | await consolerrTest.deployed(); 9 | 10 | await expect(consolerrTest.testErrorBytes32A()).to.be.revertedWith( 11 | "reverted with reason string 'consolerr test: 0x00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100" 12 | ); 13 | await expect(consolerrTest.testErrorBytes32B()).to.be.revertedWith( 14 | "reverted with reason string 'consolerr test: 0x00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100, 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff'" 15 | ); 16 | }); 17 | 18 | it("testErrorUint", async function () { 19 | const ConsolerrTest = await ethers.getContractFactory("consolerrTest"); 20 | const consolerrTest = await ConsolerrTest.deploy(); 21 | await consolerrTest.deployed(); 22 | 23 | await expect(consolerrTest.testErrorUintA()).to.be.revertedWith( 24 | "reverted with reason string 'consolerr test: 123456789" 25 | ); 26 | await expect(consolerrTest.testErrorUintB()).to.be.revertedWith( 27 | "reverted with reason string 'consolerr test: 123456789, 0" 28 | ); 29 | await expect(consolerrTest.testErrorUintC()).to.be.revertedWith( 30 | "reverted with reason string 'consolerr test: 123456789, 0, 12345678909876543210123456789000998877665544332211584938485102340013249586777" 31 | ); 32 | }); 33 | 34 | it("testErrorAddress", async function () { 35 | const ConsolerrTest = await ethers.getContractFactory("consolerrTest"); 36 | const consolerrTest = await ConsolerrTest.deploy(); 37 | await consolerrTest.deployed(); 38 | 39 | await expect(consolerrTest.testErrorAddressA()).to.be.revertedWith( 40 | "reverted with reason string 'consolerr test: 0x00112233445566778899aabbccddeeffffeeddcc" 41 | ); 42 | await expect(consolerrTest.testErrorAddressB()).to.be.revertedWith( 43 | "reverted with reason string 'consolerr test: 0x00112233445566778899aabbccddeeffffeeddcc, 0xffeeddccbbaa9988776655443322110000112233" 44 | ); 45 | await expect(consolerrTest.testErrorAddressC()).to.be.revertedWith( 46 | "reverted with reason string 'consolerr test: 0x00112233445566778899aabbccddeeffffeeddcc, 0xffeeddccbbaa9988776655443322110000112233, 0xffffffffffffffffffffffffffffffffffffffff" 47 | ); 48 | }); 49 | 50 | it("testErrorBytes", async function () { 51 | const ConsolerrTest = await ethers.getContractFactory("consolerrTest"); 52 | const consolerrTest = await ConsolerrTest.deploy(); 53 | await consolerrTest.deployed(); 54 | 55 | await expect(consolerrTest.testErrorBytes()).to.be.revertedWith( 56 | "reverted with reason string 'consolerr test: 0005070afe" 57 | ); 58 | }); 59 | }); 60 | --------------------------------------------------------------------------------