├── .ci └── prepare_env.sh ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bivrost-abi-parser ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── pm │ │ └── gnosis │ │ ├── AbiParser.kt │ │ ├── EventParser.kt │ │ ├── TypeHolders.kt │ │ ├── model │ │ └── AbiJsonModel.kt │ │ └── utils │ │ └── ParserExtensions.kt │ └── test │ ├── kotlin │ └── pm │ │ └── gnosis │ │ ├── AbiGeneratorTest.kt │ │ └── AbiParserTest.kt │ └── resources │ └── automatic_tests │ ├── 00_empty_contract │ ├── abis │ │ └── empty_contract.json │ └── expected │ │ └── EmptyContract.kt │ ├── 01_simple_function │ ├── abis │ │ └── abi1.json │ └── expected │ │ └── Abi1.kt │ ├── 02_function_no_type │ ├── abis │ │ └── abi2.json │ └── expected │ │ └── Abi2.kt │ ├── 03_no_inputs_and_outputs │ ├── abis │ │ └── abi3.json │ └── expected │ │ └── Abi3.kt │ ├── 04_unnamed_function │ ├── abis │ │ └── abi4.json │ └── expected │ │ └── Abi4.kt │ ├── 05_only_name │ ├── abis │ │ └── abi5.json │ └── expected │ │ └── Abi5.kt │ ├── 06_function_input │ ├── abis │ │ └── abi6.json │ └── expected │ │ └── Abi6.kt │ ├── 07_function_input_output │ ├── abis │ │ └── abi7.json │ └── expected │ │ └── Abi7.kt │ ├── 08_unnamed_parameters │ ├── abis │ │ └── abi8.json │ └── expected │ │ └── Abi8.kt │ ├── 09_complex_arrays │ ├── abis │ │ └── abi9.json │ └── expected │ │ ├── Abi9.kt │ │ └── arrays │ │ ├── Array5.kt │ │ └── Array7.kt │ ├── 10_simple_event │ ├── abis │ │ └── abi10.json │ └── expected │ │ └── Abi10.kt │ ├── 11_event_static_indexed │ ├── abis │ │ └── abi11.json │ └── expected │ │ └── Abi11.kt │ ├── 12_event_static_not_indexed │ ├── abis │ │ └── abi12.json │ └── expected │ │ └── Abi12.kt │ ├── 13_simple_event_dynamic_indexed │ ├── abis │ │ └── abi13.json │ └── expected │ │ └── Abi13.kt │ ├── 14_simple_event_dynamic_not_indexed │ ├── abis │ │ └── abi14.json │ └── expected │ │ └── Abi14.kt │ ├── 15_anonymous_event │ ├── abis │ │ └── abi15.json │ └── expected │ │ └── Abi15.kt │ └── 16_malformed_bytes_encoding │ ├── abis │ └── abi16.json │ └── expected │ └── Abi16.kt ├── bivrost-gradle-plugin ├── build.gradle ├── gradle.properties ├── settings.gradle └── src │ └── main │ ├── kotlin │ └── pm │ │ └── gnosis │ │ └── plugin │ │ └── BivrostPlugin.kt │ └── resources │ └── META-INF │ └── gradle-plugins │ └── bivrost.properties ├── bivrost-solidity-types-generator ├── build.gradle └── src │ └── main │ └── kotlin │ └── pm │ └── gnosis │ └── SolidityTypeGenerator.kt ├── bivrost-solidity-types ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── pm │ │ └── gnosis │ │ ├── exceptions │ │ └── InvalidBitLengthException.kt │ │ ├── model │ │ ├── Solidity.kt │ │ └── SolidityBase.kt │ │ └── utils │ │ └── DataTypeExtensions.kt │ └── test │ └── kotlin │ └── pm │ └── gnosis │ └── model │ └── SolidityBaseTest.kt ├── bivrost-utils ├── build.gradle └── src │ └── main │ └── kotlin │ └── pm │ └── gnosis │ └── GeneratorUtils.kt ├── build.gradle ├── docs ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sample └── app │ ├── abi │ ├── EtherToken.json │ └── test.json │ ├── build.gradle │ └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── abiparser │ │ └── MainActivity.kt │ └── res │ └── values │ └── strings.xml └── settings.gradle /.ci/prepare_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # fail if any commands fails 3 | set -e 4 | 5 | git branch 6 | 7 | # strip the first char as that should always be "v" (as tags should be in the format "vX.X.X") 8 | description="$(git describe --tags)" 9 | export LIBRARY_VERSION=${description:1} 10 | export RELEASE_NOTES=$TRAVIS_COMMIT_MESSAGE -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/misc.xml 6 | /.idea/libraries 7 | .DS_Store 8 | build 9 | repo 10 | /captures 11 | .externalNativeBuild 12 | out/ 13 | .idea/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | android: 3 | components: 4 | - tools 5 | - platform-tools 6 | - tools 7 | 8 | cache: 9 | directories: 10 | - $HOME/.gradle/caches/ 11 | - $HOME/.gradle/wrapper/ 12 | - $HOME/.android/build-cache 13 | 14 | before_cache: 15 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 16 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 17 | 18 | script: 19 | - . .ci/prepare_env.sh 20 | - ./gradlew check jar --stacktrace 21 | 22 | deploy: 23 | provider: releases 24 | api_key: "${GITHUB_API_KEY}" 25 | file_glob: true 26 | file: ./*/build/libs/*.jar 27 | skip_cleanup: true 28 | on: 29 | tags: true -------------------------------------------------------------------------------- /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 | ### Bivrost for Kotlin 2 | 3 | 🔥 🌈 Bridge between Solidity Contracts and Kotlin 4 | 5 | [![](https://jitpack.io/v/gnosis/bivrost-kotlin.svg)](https://jitpack.io/#gnosis/bivrost-kotlin) 6 | [![Build Status](https://travis-ci.org/gnosis/bivrost-kotlin.svg?branch=master)](https://travis-ci.org/gnosis/bivrost-kotlin) 7 | 8 | #### Include via jitpack 9 | 10 | * Add jitpack repository (see https://www.jitpack.io/#gnosis/bivrost-kotlin) 11 | 12 | * Add classpath dependency: 13 | ``` 14 | classpath ('com.github.gnosis.bivrost-kotlin:bivrost-gradle-plugin:') 15 | ``` 16 | 17 | * Add runtime dependency: 18 | ``` 19 | implementation ('com.github.gnosis.abi-kotlin:bivrost-solidity-types:') 20 | ``` 21 | 22 | * Apply plugin: 23 | ``` 24 | apply plugin: 'bivrost' 25 | ``` 26 | 27 | * Add abi json to project in `app/abi` folder (see sample app) 28 | 29 | 30 | 31 | #### Setup of Sample App 32 | * Optional: Generate the Solidity types: 33 | - `./gradlew :bivrost-solidity-types-generator:runSolidityTypeGenerator` 34 | * Add library artifacts to local maven: 35 | - `./gradlew :bivrost-utils:uploadArchives` 36 | - `./gradlew :bivrost-solidity-types:uploadArchives` 37 | - `./gradlew :bivrost-abi-parser:uploadArchives` 38 | - `./gradlew :bivrost-gradle-plugin:uploadArchives` 39 | 40 | * Uncomment `include ':sample:app'` in the `settings.gradle` to include sample app module. 41 | 42 | * Build sample app. This should also generate the class `MultiSigWalletWithDailyLimit` 43 | -------------------------------------------------------------------------------- /bivrost-abi-parser/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'kotlin' 3 | apply plugin: 'maven' 4 | 5 | dependencies { 6 | implementation project(':bivrost-utils') 7 | implementation project(':bivrost-solidity-types') 8 | 9 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" 10 | 11 | implementation "com.squareup.moshi:moshi:$versions.moshi" 12 | implementation "com.squareup.moshi:moshi-kotlin:$versions.moshi" 13 | implementation "com.squareup:kotlinpoet:$versions.kotlinPoet" 14 | implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.61' 15 | 16 | testImplementation group: 'junit', name: 'junit', version: '4.12' 17 | } 18 | 19 | uploadArchives { 20 | repositories { 21 | mavenDeployer { 22 | repository(url: uri('../repo')) 23 | } 24 | } 25 | } 26 | 27 | task sourcesJar(type: Jar, dependsOn: classes) { 28 | from sourceSets.main.allSource 29 | classifier = 'sources' 30 | } 31 | 32 | artifacts { 33 | archives sourcesJar 34 | } 35 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis 2 | 3 | import com.squareup.kotlinpoet.* 4 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 5 | import com.squareup.moshi.Moshi 6 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 7 | import pm.gnosis.model.* 8 | import pm.gnosis.utils.BigIntegerUtils 9 | import pm.gnosis.utils.generateSolidityMethodId 10 | import java.io.File 11 | import java.math.BigInteger 12 | 13 | 14 | object AbiParser { 15 | internal const val DECODER_FUN_ARG_NAME = "data" 16 | internal const val DECODER_VAR_PARTITIONS_NAME = "source" 17 | internal const val DECODER_VAR_ARG_PREFIX = "arg" //arg0, arg1... 18 | internal const val DECODER_VAR_ARG_OFFSET_SUFFIX = "Offset" 19 | internal const val INDENTATION = " " 20 | internal lateinit var context: GeneratorContext 21 | 22 | fun generateWrapper(packageName: String, abi: String, output: File, arraysMap: ArraysMap) { 23 | val jsonAdapter = Moshi.Builder().add(KotlinJsonAdapterFactory()).build().adapter(AbiRoot::class.java) 24 | val abiRoot = jsonAdapter.fromJson(abi) ?: return 25 | context = GeneratorContext(abiRoot, arraysMap) 26 | 27 | val kotlinClass = TypeSpec.classBuilder(abiRoot.contractName) 28 | val kotlinFile = FileSpec.builder(packageName, abiRoot.contractName) 29 | 30 | kotlinClass.addTypes(generateFunctionObjects()) 31 | EventParser.generateEventObjects()?.let { kotlinClass.addType(it) } 32 | kotlinClass.addTypes(generateTupleObjects()) 33 | 34 | val build = kotlinFile.addType(kotlinClass.build()).indent(INDENTATION).build() 35 | output.mkdirs() 36 | build.writeTo(output) 37 | } 38 | 39 | class ArraysMap(basePackageName: String, private val map: MutableMap = HashMap()) { 40 | 41 | private val arraysPackageName = "$basePackageName.arrays" 42 | 43 | fun get(capacity: Int) = map.getOrPut(capacity) { ClassName(arraysPackageName, "Array$capacity") } 44 | 45 | fun generate(output: File) { 46 | map.forEach { 47 | val capacity = it.key 48 | val arrayType = it.value 49 | 50 | val kotlinFile = FileSpec.builder(arraysPackageName, arrayType.simpleName) 51 | 52 | val typeVariable = TypeVariableName.invoke("T", SolidityBase.Type::class) 53 | val itemsType = List::class.asClassName().parameterizedBy(typeVariable) 54 | 55 | val parameterizedClassType = arrayType.parameterizedBy(typeVariable) 56 | val parameterizedItemDecoderType = SolidityBase.TypeDecoder::class.asClassName().parameterizedBy(typeVariable) 57 | val isDynamicBlock = CodeBlock.of("return " + if (capacity > 0) "$VARIABLE_NAME_ITEM_DECODER.isDynamic()" else "false") 58 | val decodeBlock = 59 | CodeBlock.builder() 60 | .addStatement( 61 | "return·%1T(%2T.decodeList(%3L,·%4L,·%5L))", 62 | arrayType, 63 | SolidityBase::class, 64 | "source", 65 | capacity, 66 | VARIABLE_NAME_ITEM_DECODER 67 | ) 68 | .build() 69 | val decoderBuilder = GeneratorUtils.generateDecoderBuilder(parameterizedClassType, decodeBlock, isDynamicBlock) 70 | .addTypeVariable(typeVariable) 71 | .addProperty( 72 | PropertySpec.builder( 73 | VARIABLE_NAME_ITEM_DECODER, 74 | parameterizedItemDecoderType 75 | ).initializer(VARIABLE_NAME_ITEM_DECODER).build() 76 | ) 77 | .primaryConstructor(FunSpec.constructorBuilder().addParameter(VARIABLE_NAME_ITEM_DECODER, parameterizedItemDecoderType).build()) 78 | 79 | val kotlinClass = TypeSpec.classBuilder(arrayType) 80 | .superclass(SolidityBase.Array::class.asClassName().parameterizedBy(typeVariable)) 81 | .addSuperclassConstructorParameter("items, %L", capacity) 82 | .addTypeVariable(typeVariable) 83 | .primaryConstructor( 84 | FunSpec.constructorBuilder() 85 | .addParameter(ParameterSpec.builder("items", itemsType).build()) 86 | .build() 87 | ) 88 | .addType(decoderBuilder.build()) 89 | val build = kotlinFile.addType(kotlinClass.build()).indent(INDENTATION).build() 90 | output.mkdirs() 91 | build.writeTo(output) 92 | } 93 | } 94 | 95 | companion object { 96 | const val VARIABLE_NAME_ITEM_DECODER = "itemDecoder" 97 | } 98 | } 99 | 100 | internal class GeneratorContext(val root: AbiRoot, val arrays: ArraysMap, val tuples: MutableMap = HashMap()) 101 | 102 | private fun generateFunctionObjects() = 103 | context.root.abi 104 | .filter { it.type == "function" && it.name.isNotBlank() } 105 | .groupBy { it.name } 106 | .flatMap { (_, value) -> value.map { Pair(it, value.size > 1) } } 107 | .map { (functionJson, useMethodId) -> 108 | 109 | //Add method id 110 | val methodId = "${functionJson.name}${generateMethodSignature(functionJson.inputs)}".generateSolidityMethodId() 111 | val baseName = functionJson.name.capitalize() 112 | val name = if (useMethodId) "${baseName}_$methodId" else baseName 113 | val functionObject = TypeSpec.objectBuilder(name) 114 | 115 | functionObject.addProperty(PropertySpec.builder("METHOD_ID", String::class, KModifier.CONST).initializer("\"$methodId\"").build()) 116 | functionObject.addFunction(generateFunctionEncoder(functionJson)) 117 | if (functionJson.outputs.isNotEmpty()) { 118 | val returnHolder = generateParameterHolder("Return", functionJson.outputs) 119 | functionObject.addFunction(generateParameterDecoder("decode", functionJson.outputs, returnHolder.name!!)) 120 | functionObject.addType(returnHolder) 121 | } 122 | 123 | if (functionJson.inputs.isNotEmpty()) { 124 | val argumentsHolder = generateParameterHolder("Arguments", functionJson.inputs) 125 | functionObject.addFunction(generateParameterDecoder("decodeArguments", functionJson.inputs, argumentsHolder.name!!)) 126 | functionObject.addType(argumentsHolder) 127 | } 128 | 129 | functionObject.build() 130 | }.toList() 131 | 132 | private fun generateMethodSignature(parameters: List): String = 133 | "(${parameters.joinToString(",") { 134 | if (it.type.startsWith(TUPLE_TYPE_NAME)) { 135 | it.components ?: return@joinToString "" 136 | generateMethodSignature(it.components) + it.type.removePrefix(TUPLE_TYPE_NAME) 137 | } else { 138 | checkType(it.type) 139 | } 140 | }})" 141 | 142 | private fun generateTupleObjects() = 143 | context.tuples.map { 144 | val decoderTypeName = ClassName.bestGuess("Decoder") 145 | val typeHolder = it.value 146 | 147 | val builder = TypeSpec.classBuilder(typeHolder.name) 148 | val constructor = FunSpec.constructorBuilder() 149 | 150 | typeHolder.entries.forEach { (name, holder) -> 151 | val className = holder.toTypeName() 152 | constructor.addParameter(name, className) 153 | builder.addProperty(PropertySpec.builder(name, className).initializer(name).build()) 154 | } 155 | 156 | //Generate decodings 157 | val decodeBlockBuilder = CodeBlock.builder() 158 | generateStaticArgDecoding(typeHolder, decodeBlockBuilder) 159 | decodeBlockBuilder.addStatement( 160 | "return·%1N(${(0 until typeHolder.entries.size).joinToString(",·") { "arg$it" }})", 161 | typeHolder.name 162 | ) 163 | 164 | builder 165 | .addModifiers(KModifier.DATA) 166 | .addSuperinterface(if (typeHolder.isDynamic()) SolidityBase.DynamicType::class else SolidityBase.StaticType::class) 167 | .addFunction(generateTupleEncoder(typeHolder)) 168 | .addFunction(generateTuplePackedEncoder()) 169 | .addType( 170 | GeneratorUtils.generateDecoder( 171 | typeHolder.name, 172 | decodeBlockBuilder.build(), 173 | typeHolder.isDynamic(), 174 | DECODER_VAR_PARTITIONS_NAME 175 | ) 176 | ) 177 | .addType( 178 | GeneratorUtils.generateDecoderCompanion( 179 | decoderTypeName, 180 | CodeBlock.of("%1T()", decoderTypeName) 181 | ) 182 | ) 183 | builder.primaryConstructor(constructor.build()).build() 184 | }.toList() 185 | 186 | 187 | private fun generateStaticArgDecoding(typeHolder: TupleTypeHolder, function: CodeBlock.Builder) { 188 | typeHolder.entries.forEachIndexed { index, info -> 189 | addDecoderStatementForType(function, index, info.second) 190 | } 191 | } 192 | 193 | private fun generateTupleEncoder(typeHolder: TupleTypeHolder) = 194 | FunSpec.builder("encode") 195 | .returns(String::class) 196 | .addModifiers(KModifier.OVERRIDE) 197 | .addStatement( 198 | "return·%T.encodeFunctionArguments(${typeHolder.entries.map { it.first }.joinToString { it }})", 199 | SolidityBase::class 200 | ) 201 | .build() 202 | 203 | private fun generateTuplePackedEncoder() = 204 | FunSpec.builder("encodePacked") 205 | .returns(String::class) 206 | .addModifiers(KModifier.OVERRIDE) 207 | .addStatement( 208 | "throw·UnsupportedOperationException(\"Structs are not supported via encodePacked\")" 209 | ) 210 | .build() 211 | 212 | private fun generateFunctionEncoder(functionJson: AbiElementJson): FunSpec { 213 | val funSpec = FunSpec.builder("encode") 214 | functionJson.inputs.forEachIndexed { index, parameter -> 215 | val name = if (parameter.name.isEmpty()) "arg${index + 1}" else parameter.name 216 | val type = mapType(parameter, context) 217 | funSpec.addParameter(name, type.toTypeName()) 218 | } 219 | 220 | val funWithParams = funSpec.build() 221 | val finalFun = funWithParams.toBuilder().returns(String::class) 222 | finalFun.addStatement( 223 | "return·\"0x\"·+·METHOD_ID${if (funWithParams.parameters.isNotEmpty()) { 224 | "·+·pm.gnosis.model.SolidityBase.encodeFunctionArguments(${funWithParams.parameters.joinToString { it.name }})" 225 | } else ""}" 226 | ) 227 | 228 | return finalFun.build() 229 | } 230 | 231 | private fun generateParameterDecoder(functionName: String, parameters: List, dataClassName: String): FunSpec { 232 | val funSpecBuilder = FunSpec.builder(functionName).addParameter(DECODER_FUN_ARG_NAME, String::class) 233 | 234 | //Set function return 235 | val typeName = ClassName("", dataClassName) 236 | funSpecBuilder.returns(typeName) 237 | 238 | funSpecBuilder.addStatement("val $DECODER_VAR_PARTITIONS_NAME = %1T.of($DECODER_FUN_ARG_NAME)", SolidityBase.PartitionData::class) 239 | funSpecBuilder.addStatement("") 240 | funSpecBuilder.addComment("Add decoders") 241 | funSpecBuilder.addCode(generateParameterDecoderCode(parameters)) 242 | funSpecBuilder.addStatement("") 243 | funSpecBuilder.addStatement("return·$dataClassName(${(0 until parameters.size).joinToString(",·") { "arg$it" }})") 244 | 245 | return funSpecBuilder.build() 246 | } 247 | 248 | internal fun generateParameterDecoderCode(parameters: List): CodeBlock { 249 | val codeBlock = CodeBlock.builder() 250 | 251 | generateStaticArgDecoding(parameters, codeBlock) 252 | 253 | return codeBlock.build() 254 | } 255 | 256 | private fun generateParameterHolder(holderName: String, parameters: List): TypeSpec { 257 | val returnContainerBuilder = TypeSpec.classBuilder(holderName).addModifiers(KModifier.DATA) 258 | val returnContainerConstructor = FunSpec.constructorBuilder() 259 | 260 | parameters.forEachIndexed { index, parameterJson -> 261 | val name = if (parameterJson.name.isEmpty()) "param$index" else parameterJson.name.toLowerCase() 262 | val className = mapType(parameterJson, context).toTypeName() 263 | returnContainerConstructor.addParameter(name, className) 264 | returnContainerBuilder.addProperty(PropertySpec.builder(name, className).initializer(name).build()) 265 | } 266 | 267 | return returnContainerBuilder.primaryConstructor(returnContainerConstructor.build()).build() 268 | } 269 | 270 | private fun generateStaticArgDecoding(parameters: List, function: CodeBlock.Builder) { 271 | parameters.forEachIndexed { index, outputJson -> 272 | val className = mapType(outputJson, context) 273 | addDecoderStatementForType(function, index, className) 274 | } 275 | } 276 | 277 | private fun addDecoderStatementForType(function: CodeBlock.Builder, index: Int, className: TypeHolder) { 278 | val dynamicValName = "$DECODER_VAR_ARG_PREFIX$index" 279 | val source = if (isSolidityDynamicType(className)) { 280 | val dynamicValOffsetName = "$dynamicValName$DECODER_VAR_ARG_OFFSET_SUFFIX" 281 | function.addStatement( 282 | "val·$dynamicValOffsetName·=·%T.exact(%T(%L.consume(),·16))", 283 | BigIntegerUtils::class.asClassName(), 284 | BigInteger::class.asClassName(), 285 | DECODER_VAR_PARTITIONS_NAME 286 | ) 287 | "%L.subData(%L)" to mutableListOf(DECODER_VAR_PARTITIONS_NAME, dynamicValOffsetName) 288 | } else { 289 | DECODER_VAR_PARTITIONS_NAME to mutableListOf() 290 | } 291 | 292 | if (isSolidityArray(className)) { 293 | val format = buildArrayDecoder(className) 294 | function.addStatement( 295 | "val·$dynamicValName·=·${format.first}.decode(${source.first})", 296 | *format.second.toTypedArray(), 297 | *source.second.toTypedArray() 298 | ) 299 | } else { 300 | function.addStatement( 301 | "val·$dynamicValName·=·%T.DECODER.decode(${source.first})", 302 | className.toTypeName(), 303 | *source.second.toTypedArray() 304 | ) 305 | } 306 | } 307 | 308 | private fun buildArrayDecoder(className: TypeHolder, forceStatic: Boolean = false): Pair> { 309 | if (forceStatic && ( 310 | (className is VectorTypeHolder) || 311 | (className is ArrayTypeHolder && className.isDynamic()) || 312 | className.toTypeName() == Solidity.Bytes::class.asClassName() || className.toTypeName() == Solidity.String::class.asClassName()) 313 | ) { 314 | throw IllegalArgumentException("No dynamic types allowed!") 315 | } 316 | if (className is CollectionTypeHolder) { 317 | val childClass = className.itemType 318 | val codeInfo = buildArrayDecoder(childClass, forceStatic) 319 | codeInfo.second.add(0, className.listType) 320 | return Pair("%T.Decoder(${codeInfo.first})", codeInfo.second) 321 | } 322 | val types = ArrayList() 323 | types.add(className.toTypeName()) 324 | return Pair("%T.DECODER", types) 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/main/kotlin/pm/gnosis/EventParser.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis 2 | 3 | import com.squareup.kotlinpoet.* 4 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 5 | import pm.gnosis.AbiParser.context 6 | import pm.gnosis.model.AbiElementJson 7 | import pm.gnosis.model.ParameterJson 8 | import pm.gnosis.model.SolidityBase 9 | import pm.gnosis.utils.keccak256 10 | 11 | internal object EventParser { 12 | private const val ROOT_OBJECT_NAME = "Events" 13 | private const val ABI_SPEC_EVENT_TYPE = "event" 14 | private const val EVENT_ID_PROPERTY_NAME = "EVENT_ID" 15 | private const val EVENT_ARGUMENTS_CLASS_NAME = "Arguments" 16 | private const val DECODE_FUN_NAME = "decode" 17 | private const val TOPIC_ARG_NAME = "topics" 18 | 19 | internal fun generateEventObjects(): TypeSpec? { 20 | val eventsObject = TypeSpec.objectBuilder(ROOT_OBJECT_NAME) 21 | if (!context.root.abi.any { it.type == ABI_SPEC_EVENT_TYPE }) { 22 | return null 23 | } 24 | 25 | context.root.abi.filter { it.type == ABI_SPEC_EVENT_TYPE } 26 | .map { generateEventObject(it) } 27 | .forEach { eventsObject.addType(it) } 28 | 29 | return eventsObject.build() 30 | } 31 | 32 | private fun generateEventObject(abiElementJson: AbiElementJson): TypeSpec { 33 | val eventObject = TypeSpec.objectBuilder(abiElementJson.name.capitalize()) 34 | 35 | val eventId = "${abiElementJson.name}(${abiElementJson.inputs.joinToString(",") { it.type }})".keccak256() 36 | eventObject.addProperty(PropertySpec.builder(EVENT_ID_PROPERTY_NAME, String::class, KModifier.CONST).initializer("\"$eventId\"").build()) 37 | 38 | if (abiElementJson.inputs.isNotEmpty()) { 39 | val holder = generateEventParameterHolder(EVENT_ARGUMENTS_CLASS_NAME, abiElementJson.inputs) 40 | eventObject.addFunction(generateEventDecoder(abiElementJson, holder.name!!)) 41 | eventObject.addType(holder) 42 | } 43 | 44 | return eventObject.build() 45 | } 46 | 47 | private fun generateEventDecoder(abiElementJson: AbiElementJson, holderName: String): FunSpec { 48 | val funSpec = FunSpec.builder(DECODE_FUN_NAME) 49 | val codeBlock = CodeBlock.builder() 50 | 51 | val topicElements = abiElementJson.inputs.filter { it.indexed } 52 | val dataElements = abiElementJson.inputs.filter { !it.indexed } 53 | 54 | funSpec.addParameter(TOPIC_ARG_NAME, List::class.asClassName().parameterizedBy(String::class.asClassName())) 55 | funSpec.addCode(generateTopicsDecoderCodeBlock(topicElements, abiElementJson.anonymous)) 56 | 57 | if (dataElements.isNotEmpty()) { 58 | funSpec.addStatement("") 59 | funSpec.addParameter(AbiParser.DECODER_FUN_ARG_NAME, String::class) 60 | funSpec.addCode(generateDataDecoderCodeBlock(dataElements)) 61 | } 62 | 63 | val holderClassName = ClassName("", holderName) 64 | funSpec.returns(holderClassName) 65 | 66 | var dataIndex = 0 67 | var topicIndex = 1 68 | val args = abiElementJson.inputs.joinToString(", ") { parameterJson -> 69 | val param: String 70 | if (parameterJson.indexed) { 71 | param = "t$topicIndex" 72 | topicIndex++ 73 | } else { 74 | param = "${AbiParser.DECODER_VAR_ARG_PREFIX}$dataIndex" 75 | dataIndex++ 76 | } 77 | param 78 | } 79 | 80 | codeBlock.addStatement("return %1T($args)", holderClassName) 81 | 82 | return funSpec.addCode(codeBlock.build()).build() 83 | } 84 | 85 | private fun generateTopicsDecoderCodeBlock(indexedParameters: List, isAnonymous: Boolean): CodeBlock { 86 | val codeBlock = CodeBlock.builder() 87 | codeBlock.addStatement("// Decode topics") 88 | if (!isAnonymous) { 89 | codeBlock.addStatement( 90 | "if·($TOPIC_ARG_NAME.first().removePrefix(\"0x\")·!=·$EVENT_ID_PROPERTY_NAME)·throw·IllegalArgumentException(\"topics[0]·does·not·match·event·id\")" 91 | ) 92 | } 93 | 94 | indexedParameters.forEachIndexed { index, topic -> 95 | val typeHolder = mapType(topic, context) 96 | if (typeHolder.isHashTopic(topic)) { 97 | codeBlock.addStatement("val·t${index + 1}·=·$TOPIC_ARG_NAME[${index + 1}]") 98 | } else { 99 | val sourceName = "source${index + 1}" 100 | codeBlock 101 | .addStatement("val·$sourceName·=·%1T.of($TOPIC_ARG_NAME[${index + 1}])", SolidityBase.PartitionData::class) 102 | .addStatement("val·t${index + 1}·=·%1T.DECODER.decode(%2L)", typeHolder.toTypeName(), sourceName) 103 | } 104 | } 105 | 106 | return codeBlock.build() 107 | } 108 | 109 | private fun generateDataDecoderCodeBlock(nonIndexedParameters: List): CodeBlock { 110 | val codeBlock = CodeBlock.builder() 111 | 112 | codeBlock.apply { 113 | addStatement("// Decode data") 114 | addStatement( 115 | "val·${AbiParser.DECODER_VAR_PARTITIONS_NAME}·=·%1T.of(${AbiParser.DECODER_FUN_ARG_NAME})", 116 | SolidityBase.PartitionData::class 117 | ) 118 | add(AbiParser.generateParameterDecoderCode(nonIndexedParameters)) 119 | } 120 | 121 | return codeBlock.build() 122 | } 123 | 124 | private fun generateEventParameterHolder(holderName: String, parameters: List): TypeSpec { 125 | val returnContainerBuilder = TypeSpec.classBuilder(holderName).addModifiers(KModifier.DATA) 126 | val returnContainerConstructor = FunSpec.constructorBuilder() 127 | 128 | parameters.forEachIndexed { index, parameterJson -> 129 | var name = if (parameterJson.name.isEmpty()) "param$index" else parameterJson.name.toLowerCase() 130 | 131 | val typeHolder = mapType(parameterJson, context) 132 | val className = if (typeHolder.isHashTopic(parameterJson)) { 133 | name = if (parameterJson.name.isEmpty()) "param${index}Hash" else "${parameterJson.name.toLowerCase()}Hash" 134 | ClassName.bestGuess("kotlin.String") 135 | } else { 136 | typeHolder.toTypeName() 137 | } 138 | returnContainerConstructor.addParameter(name, className) 139 | returnContainerBuilder.addProperty(PropertySpec.builder(name, className).initializer(name).build()) 140 | } 141 | 142 | return returnContainerBuilder.primaryConstructor(returnContainerConstructor.build()).build() 143 | } 144 | 145 | // Indexed arrays, strings and bytes and tuples only have the keccak hash 146 | private fun TypeHolder.isHashTopic(parameterJson: ParameterJson) = 147 | parameterJson.indexed && (this is TupleTypeHolder || this is CollectionTypeHolder || isDynamic()) 148 | } 149 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/main/kotlin/pm/gnosis/TypeHolders.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis 2 | 3 | import com.squareup.kotlinpoet.ClassName 4 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 5 | import com.squareup.kotlinpoet.TypeName 6 | import com.squareup.kotlinpoet.asClassName 7 | import org.bouncycastle.crypto.digests.KeccakDigest 8 | import pm.gnosis.model.ParameterJson 9 | import pm.gnosis.model.Solidity 10 | import pm.gnosis.model.SolidityBase 11 | import pm.gnosis.utils.toHex 12 | import java.util.regex.Pattern 13 | 14 | private val TUPLE_NAME_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray() 15 | private val TYPE_PATTERN = Pattern.compile("^(\\w+)((?>\\[\\d*])*)$") 16 | private val ARRAY_DEF_PATTERN = Pattern.compile("^\\[[0-9]*]") 17 | internal const val TUPLE_TYPE_NAME = "tuple" 18 | 19 | internal interface TypeHolder { 20 | fun toTypeName(): TypeName 21 | 22 | fun isDynamic(): Boolean 23 | 24 | fun hash(): String 25 | } 26 | 27 | internal class SimpleTypeHolder private constructor(private val className: ClassName, private val dynamic: Boolean) : TypeHolder { 28 | override fun toTypeName() = className 29 | 30 | override fun isDynamic() = dynamic 31 | 32 | override fun hash() = generateHash(listOf(className.toString())) 33 | 34 | companion object { 35 | /** 36 | * Create a SimpleTypeHolder for a given type if possible. 37 | * If the given type is not a simple type (e.g. Tuple) null will be returned. 38 | */ 39 | fun forType(type: String): SimpleTypeHolder? { 40 | val baseType = Solidity.types[checkType(type)] ?: return null 41 | return SimpleTypeHolder(ClassName.bestGuess(baseType), SolidityBase.dynamicTypes.contains(type.toLowerCase())) 42 | } 43 | } 44 | } 45 | 46 | internal abstract class CollectionTypeHolder(val listType: ClassName, val itemType: TypeHolder) : TypeHolder { 47 | override fun hash() = generateHash(listOf(listType.toString(), itemType.hash())) 48 | } 49 | 50 | internal class ArrayTypeHolder(arraysMap: AbiParser.ArraysMap, itemType: TypeHolder, val capacity: Int) : 51 | CollectionTypeHolder(arraysMap.get(capacity), itemType) { 52 | 53 | override fun toTypeName() = listType.parameterizedBy(itemType.toTypeName()) 54 | 55 | override fun isDynamic() = capacity > 0 && itemType.isDynamic() 56 | } 57 | 58 | internal class VectorTypeHolder(itemType: TypeHolder) : CollectionTypeHolder(SolidityBase.Vector::class.asClassName(), itemType) { 59 | 60 | override fun toTypeName() = listType.parameterizedBy(itemType.toTypeName()) 61 | 62 | override fun isDynamic() = true 63 | } 64 | 65 | internal class TupleTypeHolder(index: Int, val entries: List>) : TypeHolder { 66 | 67 | val name = "Tuple" + numberToLetter(index) 68 | 69 | override fun toTypeName() = ClassName("", name) 70 | 71 | override fun hash() = generateHash(entries.map { "${it.first}:${it.second.hash()} " }) 72 | 73 | override fun isDynamic() = entries.any { it.second.isDynamic() } 74 | } 75 | 76 | internal fun checkType(type: String): String { 77 | return Solidity.aliases.getOrElse(type) { type } 78 | } 79 | 80 | internal fun mapType(parameter: ParameterJson, context: AbiParser.GeneratorContext): TypeHolder { 81 | val matcher = TYPE_PATTERN.matcher(parameter.type) 82 | if (!matcher.find()) { 83 | throw IllegalArgumentException("Invalid parameter definition: ${parameter.type}!") 84 | } 85 | val arrayType = matcher.group(1) 86 | val baseType = SimpleTypeHolder.forType(arrayType) ?: generateTuple(arrayType, parameter, context) 87 | ?: throw IllegalArgumentException("Unknown parameter ${parameter.type}!") 88 | val arrayDef = matcher.group(2) 89 | if (arrayType.length < parameter.type.length && arrayDef.isNullOrBlank()) { 90 | throw IllegalArgumentException("Invalid parameter definition: ${parameter.type}!") 91 | } 92 | return parseArrayDefinition(arrayDef, baseType, context) 93 | } 94 | 95 | private fun generateTuple(type: String, parameters: ParameterJson, context: AbiParser.GeneratorContext): TypeHolder? { 96 | if (type != TUPLE_TYPE_NAME || parameters.components == null) { 97 | return null 98 | } 99 | val entries = parameters.components.mapIndexed { index, param -> 100 | Pair(if (param.name.isEmpty()) "param$index" else param.name.toLowerCase(), mapType(param, context)) 101 | } 102 | val tupleTypeHolder = TupleTypeHolder(context.tuples.size, entries) 103 | return context.tuples.getOrPut(tupleTypeHolder.hash()) { tupleTypeHolder } 104 | } 105 | 106 | private fun parseArrayDefinition(arrayDef: String, innerType: TypeHolder, context: AbiParser.GeneratorContext): TypeHolder { 107 | if (arrayDef.isBlank()) { 108 | return innerType 109 | } 110 | val matcher = ARRAY_DEF_PATTERN.matcher(arrayDef) 111 | if (!matcher.find()) { 112 | throw IllegalArgumentException("Illegal array definition $arrayDef!") 113 | } 114 | val match = matcher.group() ?: throw IllegalArgumentException() 115 | val arraySizeDef = match.substring(1, match.length - 1) 116 | val arraySize = if (arraySizeDef.isBlank()) -1 else arraySizeDef.toInt() 117 | val collectionTypeHolder = if (arraySizeDef.isBlank()) { 118 | VectorTypeHolder(innerType) 119 | } else { 120 | ArrayTypeHolder(context.arrays, innerType, arraySize) 121 | } 122 | return parseArrayDefinition(arrayDef.removePrefix(match), collectionTypeHolder, context) 123 | } 124 | 125 | internal fun isSolidityDynamicType(type: TypeHolder) = type.isDynamic() 126 | 127 | internal fun isSolidityArray(type: TypeHolder) = type is CollectionTypeHolder 128 | 129 | private fun numberToLetter(index: Int): String { 130 | val builder = StringBuilder() 131 | var i = index 132 | do { 133 | builder.append(TUPLE_NAME_ALPHABET[i % TUPLE_NAME_ALPHABET.size]) 134 | i /= TUPLE_NAME_ALPHABET.size 135 | } while (i > 0) 136 | return builder.toString() 137 | } 138 | 139 | private fun generateHash(parts: List): String { 140 | val digest = KeccakDigest() 141 | parts.forEach { 142 | val bytes = it.toByteArray() 143 | digest.update(bytes, 0, bytes.size) 144 | } 145 | val hash = ByteArray(digest.digestSize) 146 | digest.doFinal(hash, 0) 147 | return hash.toHex() 148 | } 149 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/main/kotlin/pm/gnosis/model/AbiJsonModel.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.model 2 | 3 | import com.squareup.moshi.Json 4 | 5 | class AbiRoot(@Json(name = "abi") val abi: List, 6 | @Json(name = "contractName") val contractName: String) 7 | 8 | class AbiElementJson(@Json(name = "constant") val constant: Boolean = false, 9 | @Json(name = "inputs") val inputs: List = listOf(), 10 | @Json(name = "name") val name: String = "", 11 | @Json(name = "outputs") val outputs: List = listOf(), 12 | @Json(name = "payable") val payable: Boolean = false, 13 | @Json(name = "type") val type: String = "function", 14 | @Json(name = "anonymous") val anonymous: Boolean = false) 15 | 16 | class ParameterJson(@Json(name = "name") val name: String, 17 | @Json(name = "type") val type: String, 18 | @Json(name = "components") val components: List? = null, 19 | @Json(name = "indexed") val indexed: Boolean = false) 20 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/main/kotlin/pm/gnosis/utils/ParserExtensions.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.utils 2 | 3 | import org.bouncycastle.jcajce.provider.digest.Keccak 4 | import kotlin.experimental.and 5 | 6 | fun String.generateSolidityMethodId() = keccak256().substring(0..7) 7 | 8 | fun String.keccak256(): String { 9 | val sha3 = Keccak.Digest256() 10 | sha3.update(this.toByteArray()) 11 | val buff = StringBuffer() 12 | for (b in sha3.digest()) { 13 | buff.append(String.format("%02x", b and 0xFF.toByte())) 14 | } 15 | return buff.toString() 16 | } 17 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiGeneratorTest.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Assert.assertTrue 5 | import org.junit.Before 6 | import org.junit.Rule 7 | import org.junit.Test 8 | import org.junit.rules.TemporaryFolder 9 | import java.io.File 10 | 11 | class AbiGeneratorTest { 12 | @Rule 13 | @JvmField 14 | val folder = TemporaryFolder() 15 | 16 | private lateinit var generatedFolder: File 17 | 18 | companion object { 19 | const val PACKAGE_NAME = "expected" 20 | val PATH = PACKAGE_NAME.replace('.', File.separatorChar) 21 | } 22 | 23 | @Before 24 | fun setup() { 25 | generatedFolder = folder.newFolder("testGeneratedCode") 26 | } 27 | 28 | @Test 29 | fun testGeneratedCode() { 30 | val testsFolder = File(javaClass.classLoader.getResource("automatic_tests").toURI()) 31 | for (testFolder in testsFolder.listFiles().sortedBy { it.name }) { 32 | testAbi(testFolder) 33 | } 34 | } 35 | 36 | private fun testAbi(testFolder: File) { 37 | println("Testing ${testFolder.nameWithoutExtension}") 38 | 39 | generatedFolder.deleteRecursively() 40 | val abisFolder = File(testFolder, "abis") 41 | 42 | val arraysMap = AbiParser.ArraysMap(PACKAGE_NAME) 43 | abisFolder.listFiles().forEach { jsonAbi -> 44 | val jsonAbiContents = readAllFromFile(jsonAbi) 45 | AbiParser.generateWrapper(PACKAGE_NAME, jsonAbiContents, generatedFolder, arraysMap) 46 | } 47 | 48 | arraysMap.generate(generatedFolder) 49 | 50 | val generatedRootFolder = File(generatedFolder, PATH) 51 | assertTrue("Root folder was not generated", generatedRootFolder.exists() && generatedRootFolder.isDirectory) 52 | 53 | val expectedFolder = File(testFolder, PATH) 54 | checkGeneratedFolder(generatedRootFolder, expectedFolder) 55 | checkGeneratedFileList(generatedRootFolder, expectedFolder) 56 | } 57 | 58 | private fun checkGeneratedFileList(generatedFolder: File, expectedFolder: File) { 59 | expectedFolder.listFiles().forEach { 60 | val generated = File(generatedFolder, it.name) 61 | assertTrue("$it was not generated!", generated.exists() && 62 | generated.isDirectory == it.isDirectory && generated.isFile == it.isFile) 63 | if (it.isDirectory) { 64 | checkGeneratedFileList(generated, it) 65 | } 66 | } 67 | } 68 | 69 | private fun checkGeneratedFolder(generatedFolder: File, expectedFolder: File) { 70 | generatedFolder.listFiles().forEach { 71 | if (it.isDirectory) { 72 | val target = File(expectedFolder, it.name) 73 | assertTrue("$it was not expected as a generated folder!", target.exists() && target.isDirectory) 74 | checkGeneratedFolder(it, target) 75 | } else { 76 | checkGeneratedFile(it, expectedFolder) 77 | } 78 | } 79 | } 80 | 81 | private fun checkGeneratedFile(generatedFile: File, expectedFolder: File) { 82 | val expectedFile = File(expectedFolder, generatedFile.name) 83 | assertTrue("$generatedFile was not expected to be generated!", expectedFile.exists() && expectedFile.isFile) 84 | 85 | val generatedContent = readAllFromFile(generatedFile) 86 | val expectedContent = readAllFromFile(expectedFile) 87 | 88 | assertEquals("${generatedFile.name} does not match the expected file", 89 | expectedContent, generatedContent) 90 | } 91 | 92 | private fun readAllFromFile(file: File) = file.bufferedReader().use { it.readText() } 93 | } 94 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis 2 | 3 | 4 | import com.squareup.kotlinpoet.ClassName 5 | import com.squareup.kotlinpoet.asClassName 6 | import com.squareup.kotlinpoet.asTypeName 7 | import org.junit.Assert 8 | import org.junit.Assert.* 9 | import org.junit.Test 10 | import pm.gnosis.model.AbiRoot 11 | import pm.gnosis.model.ParameterJson 12 | import pm.gnosis.model.Solidity 13 | import pm.gnosis.model.SolidityBase 14 | import java.math.BigInteger 15 | import kotlin.reflect.KClass 16 | 17 | 18 | class AbiParserTest { 19 | 20 | private fun testContext() = AbiParser.GeneratorContext(AbiRoot(ArrayList(), "Test"), AbiParser.ArraysMap("com.example")) 21 | 22 | private fun testParameter(type: String, name: String = "test", components: List? = null) 23 | = ParameterJson(name, type, components) 24 | 25 | private fun assertType(instance: Any, type: KClass<*>) { 26 | assertTrue("$instance should be a $type", type.isInstance(instance)) 27 | } 28 | 29 | @Test(expected = IllegalArgumentException::class) 30 | fun testInvalidArrayDefOpeningBracketStart() { 31 | mapType(testParameter("uint[[5][]"), testContext()) 32 | } 33 | 34 | @Test(expected = IllegalArgumentException::class) 35 | fun testInvalidArrayDefOpeningBracketMiddle() { 36 | mapType(testParameter("uint[5][[]"), testContext()) 37 | } 38 | 39 | @Test(expected = IllegalArgumentException::class) 40 | fun testInvalidArrayDefOpeningBracketEnd() { 41 | mapType(testParameter("uint[5][]["), testContext()) 42 | } 43 | 44 | @Test(expected = IllegalArgumentException::class) 45 | fun testInvalidArrayDefLetterAsSize() { 46 | mapType(testParameter("uint[a][]"), testContext()) 47 | } 48 | 49 | @Test(expected = IllegalArgumentException::class) 50 | fun testInvalidArrayDefClosingBracket() { 51 | mapType(testParameter("uint[5][]]"), testContext()) 52 | } 53 | 54 | @Test(expected = IllegalArgumentException::class) 55 | fun testInvalidArrayDefUnknownType() { 56 | mapType(testParameter("gnosis[1][]"), testContext()) 57 | } 58 | 59 | @Test() 60 | fun testSimpleTypeHolder() { 61 | assertEquals("Unknown type should return null", SimpleTypeHolder.forType("unknown"), null) 62 | assertEquals("Tuple type should return null", SimpleTypeHolder.forType("tuple"), null) 63 | 64 | val bytes32 = SimpleTypeHolder.forType("bytes32")!! 65 | assertFalse("bytes32 should be static", bytes32.isDynamic()) 66 | 67 | val uint = SimpleTypeHolder.forType("uint")!! 68 | assertFalse("uint should be static", uint.isDynamic()) 69 | 70 | val int = SimpleTypeHolder.forType("int")!! 71 | assertFalse("int should be static", int.isDynamic()) 72 | 73 | val bytes = SimpleTypeHolder.forType("bytes")!! 74 | assertTrue("bytes should be dynamic", bytes.isDynamic()) 75 | 76 | val string = SimpleTypeHolder.forType("string")!! 77 | assertTrue("string should be dynamic", string.isDynamic()) 78 | } 79 | 80 | @Test() 81 | fun testParseAliasTypes() { 82 | val uintType = mapType(testParameter("uint"), testContext()) 83 | assertType(uintType, SimpleTypeHolder::class) 84 | assertEquals(Solidity.UInt256::class.asClassName(), uintType.toTypeName()) 85 | 86 | val intType = mapType(testParameter("int"), testContext()) 87 | assertType(intType, SimpleTypeHolder::class) 88 | assertEquals(Solidity.Int256::class.asClassName(), intType.toTypeName()) 89 | 90 | val byteType = mapType(testParameter("byte"), testContext()) 91 | assertType(byteType, SimpleTypeHolder::class) 92 | assertEquals(Solidity.Bytes1::class.asClassName(), byteType.toTypeName()) 93 | } 94 | 95 | @Test() 96 | fun testParseDynamicTupleType() { 97 | val components = listOf(testParameter("uint", "a"), testParameter("uint[]", "b")) 98 | val tupleType = mapType(testParameter("tuple", components = components), testContext()) 99 | assertType(tupleType, TupleTypeHolder::class) 100 | val pTuple = tupleType as TupleTypeHolder 101 | assertEquals(2, pTuple.entries.size) 102 | assertEquals("a", pTuple.entries[0].first) 103 | assertType(pTuple.entries[0].second, SimpleTypeHolder::class) 104 | assertEquals(Solidity.UInt256::class.asClassName(), pTuple.entries[0].second.toTypeName()) 105 | 106 | assertEquals("b", pTuple.entries[1].first) 107 | assertType(pTuple.entries[1].second, VectorTypeHolder::class) 108 | val pArray = pTuple.entries[1].second as VectorTypeHolder 109 | assertType(pArray.itemType, SimpleTypeHolder::class) 110 | assertEquals(Solidity.UInt256::class.asClassName(), pArray.itemType.toTypeName()) 111 | 112 | assertEquals(true, tupleType.isDynamic()) 113 | assertEquals("TupleA", tupleType.name) 114 | assertEquals(ClassName("", "TupleA"), tupleType.toTypeName()) 115 | } 116 | 117 | @Test() 118 | fun testStaticDynamicTupleType() { 119 | val components = listOf(testParameter("uint", "a"), testParameter("uint[5]", "b")) 120 | val tupleType = mapType(testParameter("tuple", components = components), testContext()) 121 | assertType(tupleType, TupleTypeHolder::class) 122 | val pTuple = tupleType as TupleTypeHolder 123 | assertEquals(2, pTuple.entries.size) 124 | assertEquals("a", pTuple.entries[0].first) 125 | assertType(pTuple.entries[0].second, SimpleTypeHolder::class) 126 | assertEquals(Solidity.UInt256::class.asClassName(), pTuple.entries[0].second.toTypeName()) 127 | 128 | assertEquals("b", pTuple.entries[1].first) 129 | assertType(pTuple.entries[1].second, ArrayTypeHolder::class) 130 | val pArray = pTuple.entries[1].second as ArrayTypeHolder 131 | assertType(pArray.itemType, SimpleTypeHolder::class) 132 | assertEquals(Solidity.UInt256::class.asClassName(), pArray.itemType.toTypeName()) 133 | assertEquals(5, pArray.capacity) 134 | 135 | assertEquals(false, tupleType.isDynamic()) 136 | assertEquals("TupleA", tupleType.name) 137 | assertEquals(ClassName("", "TupleA"), tupleType.toTypeName()) 138 | } 139 | 140 | @Test() 141 | fun testParseUIntNestedArray() { 142 | val type = mapType(testParameter("uint[5][]"), testContext()) 143 | assertType(type, VectorTypeHolder::class) 144 | val pType = type as VectorTypeHolder 145 | assertEquals(SolidityBase.Vector::class.asClassName(), pType.listType) 146 | 147 | // First generic type 148 | val g1Type = pType.itemType 149 | assertType(g1Type, ArrayTypeHolder::class) 150 | val g1pType = g1Type as ArrayTypeHolder 151 | assertEquals(ClassName("com.example.arrays", "Array5"), g1pType.listType) 152 | 153 | // Second generic type 154 | val g2Type = g1pType.itemType 155 | assertEquals(Solidity.UInt256::class.asTypeName(), g2Type.toTypeName()) 156 | } 157 | 158 | @Test() 159 | fun testParseStringDynamicArray() { 160 | val type = mapType(testParameter("string[]"), testContext()) 161 | assertType(type, VectorTypeHolder::class) 162 | val pType = type as VectorTypeHolder 163 | assertEquals(SolidityBase.Vector::class.asClassName(), pType.listType) 164 | 165 | // First generic type 166 | val g1Type = pType.itemType 167 | assertEquals(Solidity.String::class.asTypeName(), g1Type.toTypeName()) 168 | } 169 | 170 | @Test() 171 | fun testParseStringStaticArray() { 172 | val type = mapType(testParameter("string[5]"), testContext()) 173 | assertType(type, ArrayTypeHolder::class) 174 | val pType = type as ArrayTypeHolder 175 | assertEquals(5, pType.capacity) 176 | assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType) 177 | 178 | // First generic type 179 | val g1Type = pType.itemType 180 | assertEquals(Solidity.String::class.asTypeName(), g1Type.toTypeName()) 181 | } 182 | 183 | @Test() 184 | fun testParseBytesArray() { 185 | val type = mapType(testParameter("bytes[5]"), testContext()) 186 | assertType(type, ArrayTypeHolder::class) 187 | val pType = type as ArrayTypeHolder 188 | assertEquals(5, pType.capacity) 189 | assertTrue(pType.isDynamic()) 190 | assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType) 191 | 192 | // First generic type 193 | val g1Type = pType.itemType 194 | assertEquals(Solidity.Bytes::class.asTypeName(), g1Type.toTypeName()) 195 | assertTrue(g1Type.isDynamic()) 196 | } 197 | 198 | @Test() 199 | fun testParseBytesXArray() { 200 | val type = mapType(testParameter("bytes32[5]"), testContext()) 201 | assertType(type, ArrayTypeHolder::class) 202 | val pType = type as ArrayTypeHolder 203 | assertEquals(5, pType.capacity) 204 | assertFalse(pType.isDynamic()) 205 | assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType) 206 | 207 | // First generic type 208 | val g1Type = pType.itemType 209 | assertEquals(Solidity.Bytes32::class.asTypeName(), g1Type.toTypeName()) 210 | assertFalse(g1Type.isDynamic()) 211 | } 212 | 213 | @Test 214 | fun testDecodeFunctionArguments() { 215 | /* 216 | f(uint,uint32[],bytes10,bytes) 217 | with values 218 | (0x123, [0x456, 0x789], "1234567890", "Hello, world!") 219 | */ 220 | val testData = SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000123" + 221 | "0000000000000000000000000000000000000000000000000000000000000080" + 222 | "3132333435363738393000000000000000000000000000000000000000000000" + 223 | "00000000000000000000000000000000000000000000000000000000000000e0" + 224 | "0000000000000000000000000000000000000000000000000000000000000002" + 225 | "0000000000000000000000000000000000000000000000000000000000000456" + 226 | "0000000000000000000000000000000000000000000000000000000000000789" + 227 | "000000000000000000000000000000000000000000000000000000000000000d" + 228 | "48656c6c6f2c20776f726c642100000000000000000000000000000000000000") 229 | // Decode uint 230 | assertEquals( 231 | Solidity.UInt256.DECODER.decode(testData).value, 232 | BigInteger("123", 16)) 233 | 234 | // Decode uint32[] 235 | val uint32Offset = BigInteger(testData.consume(), 16).intValueExact() 236 | assertEquals( 237 | SolidityBase.Vector.Decoder(Solidity.UInt32.DECODER).decode(testData.subData(uint32Offset)).items, 238 | listOf(Solidity.UInt32(BigInteger("456", 16)), Solidity.UInt32(BigInteger("789", 16)))) 239 | 240 | // Decode bytes10 241 | Assert.assertArrayEquals( 242 | Solidity.Bytes10.DECODER.decode(testData).bytes, 243 | "1234567890".toByteArray()) 244 | 245 | // Consume location of bytes (we don't need it) 246 | val bytesOffset = BigInteger(testData.consume(), 16).intValueExact() 247 | Assert.assertArrayEquals( 248 | Solidity.Bytes.DECODER.decode(testData.subData(bytesOffset)).items, 249 | "Hello, world!".toByteArray()) 250 | } 251 | 252 | @Test 253 | fun testEncodeFunctionArguments() { 254 | /* 255 | f(uint,uint32[],bytes10,bytes) 256 | with values 257 | (0x123, [0x456, 0x789], "1234567890", "Hello, world!") 258 | */ 259 | 260 | val arg1 = Solidity.UInt256(BigInteger("123", 16)) 261 | val arg2 = SolidityBase.Vector( 262 | listOf(Solidity.UInt32(BigInteger("456", 16)), Solidity.UInt32(BigInteger("789", 16))) 263 | ) 264 | val arg3 = Solidity.Bytes10("1234567890".toByteArray()) 265 | val arg4 = Solidity.String("Hello, world!") 266 | val data = SolidityBase.encodeFunctionArguments(arg1, arg2, arg3, arg4) 267 | 268 | val expected = "" + 269 | "0000000000000000000000000000000000000000000000000000000000000123" + 270 | "0000000000000000000000000000000000000000000000000000000000000080" + 271 | "3132333435363738393000000000000000000000000000000000000000000000" + 272 | "00000000000000000000000000000000000000000000000000000000000000e0" + 273 | "0000000000000000000000000000000000000000000000000000000000000002" + 274 | "0000000000000000000000000000000000000000000000000000000000000456" + 275 | "0000000000000000000000000000000000000000000000000000000000000789" + 276 | "000000000000000000000000000000000000000000000000000000000000000d" + 277 | "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" 278 | assertEquals(data, expected) 279 | } 280 | 281 | @Test 282 | fun testEncodeFunctionArgumentsWithStaticArray() { 283 | /* 284 | f(uint32[2],bytes,uint32[]) 285 | with values 286 | ([0x456, 0x789], "Hello, world!", [0x123]) 287 | */ 288 | 289 | val arg1 = TestArray( 290 | listOf(Solidity.UInt32(BigInteger("456", 16)), Solidity.UInt32(BigInteger("789", 16))), 2 291 | ) 292 | val arg2 = Solidity.String("Hello, world!") 293 | val arg3 = SolidityBase.Vector( 294 | listOf(Solidity.UInt32(BigInteger("123", 16))) 295 | ) 296 | val data = SolidityBase.encodeFunctionArguments(arg1, arg2, arg3) 297 | 298 | val expected = "" + 299 | // uint32[2] 300 | "0000000000000000000000000000000000000000000000000000000000000456" + 301 | "0000000000000000000000000000000000000000000000000000000000000789" + 302 | // Pointer to bytes 303 | "0000000000000000000000000000000000000000000000000000000000000080" + 304 | // Pointer to uint32[] 305 | "00000000000000000000000000000000000000000000000000000000000000c0" + 306 | 307 | // Dynamic Part 308 | // bytes -> "Hello world!" 309 | "000000000000000000000000000000000000000000000000000000000000000d" + 310 | "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + 311 | // uint32[] -> [0x123] 312 | "0000000000000000000000000000000000000000000000000000000000000001" + 313 | "0000000000000000000000000000000000000000000000000000000000000123" 314 | assertEquals(data, expected) 315 | } 316 | 317 | private class TestArray(items: List, capacity: Int) : SolidityBase.Array(items, capacity) 318 | } 319 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/00_empty_contract/abis/empty_contract.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "EmptyContract", 3 | "abi": [] 4 | } 5 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/00_empty_contract/expected/EmptyContract.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | class EmptyContract 4 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/01_simple_function/abis/abi1.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi1", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [], 7 | "name": "function", 8 | "outputs": [], 9 | "payable": false, 10 | "type": "function" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/01_simple_function/expected/Abi1.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | 5 | class Abi1 { 6 | object Function { 7 | const val METHOD_ID: String = "9d96e2df" 8 | 9 | fun encode(): String { 10 | return "0x" + METHOD_ID 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/02_function_no_type/abis/abi2.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi2", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [], 7 | "name": "function", 8 | "outputs": [], 9 | "payable": false 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/02_function_no_type/expected/Abi2.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | 5 | class Abi2 { 6 | object Function { 7 | const val METHOD_ID: String = "9d96e2df" 8 | 9 | fun encode(): String { 10 | return "0x" + METHOD_ID 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/03_no_inputs_and_outputs/abis/abi3.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi3", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "name": "function", 7 | "payable": false 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/03_no_inputs_and_outputs/expected/Abi3.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | 5 | class Abi3 { 6 | object Function { 7 | const val METHOD_ID: String = "9d96e2df" 8 | 9 | fun encode(): String { 10 | return "0x" + METHOD_ID 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/04_unnamed_function/abis/abi4.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi4", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "payable": false 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/04_unnamed_function/expected/Abi4.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | class Abi4 4 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/05_only_name/abis/abi5.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi5", 3 | "abi": [ 4 | { 5 | "name": "function" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/05_only_name/expected/Abi5.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | 5 | class Abi5 { 6 | object Function { 7 | const val METHOD_ID: String = "9d96e2df" 8 | 9 | fun encode(): String { 10 | return "0x" + METHOD_ID 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/06_function_input/abis/abi6.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi6", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [ 7 | { 8 | "name": "owner", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "function", 13 | "payable": false 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/06_function_input/expected/Abi6.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | import pm.gnosis.model.Solidity 5 | import pm.gnosis.model.SolidityBase 6 | 7 | class Abi6 { 8 | object Function { 9 | const val METHOD_ID: String = "06da0736" 10 | 11 | fun encode(owner: Solidity.Address): String { 12 | return "0x" + METHOD_ID + pm.gnosis.model.SolidityBase.encodeFunctionArguments(owner) 13 | } 14 | 15 | fun decodeArguments(data: String): Arguments { 16 | val source = SolidityBase.PartitionData.of(data) 17 | 18 | // Add decoders 19 | val arg0 = Solidity.Address.DECODER.decode(source) 20 | 21 | return Arguments(arg0) 22 | } 23 | 24 | data class Arguments( 25 | val owner: Solidity.Address 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/abis/abi7.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi7", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [ 7 | { 8 | "name": "owner", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "function", 13 | "outputs": [ 14 | { 15 | "name": "data", 16 | "type": "bytes" 17 | } 18 | ], 19 | "payable": false 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/expected/Abi7.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import java.math.BigInteger 4 | import kotlin.String 5 | import pm.gnosis.model.Solidity 6 | import pm.gnosis.model.SolidityBase 7 | import pm.gnosis.utils.BigIntegerUtils 8 | 9 | class Abi7 { 10 | object Function { 11 | const val METHOD_ID: String = "06da0736" 12 | 13 | fun encode(owner: Solidity.Address): String { 14 | return "0x" + METHOD_ID + pm.gnosis.model.SolidityBase.encodeFunctionArguments(owner) 15 | } 16 | 17 | fun decode(data: String): Return { 18 | val source = SolidityBase.PartitionData.of(data) 19 | 20 | // Add decoders 21 | val arg0Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 22 | val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) 23 | 24 | return Return(arg0) 25 | } 26 | 27 | fun decodeArguments(data: String): Arguments { 28 | val source = SolidityBase.PartitionData.of(data) 29 | 30 | // Add decoders 31 | val arg0 = Solidity.Address.DECODER.decode(source) 32 | 33 | return Arguments(arg0) 34 | } 35 | 36 | data class Return( 37 | val data: Solidity.Bytes 38 | ) 39 | 40 | data class Arguments( 41 | val owner: Solidity.Address 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/abis/abi8.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi8", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [ 7 | { 8 | "name": "", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "function", 13 | "outputs": [ 14 | { 15 | "name": "", 16 | "type": "bytes" 17 | } 18 | ], 19 | "payable": false 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/expected/Abi8.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import java.math.BigInteger 4 | import kotlin.String 5 | import pm.gnosis.model.Solidity 6 | import pm.gnosis.model.SolidityBase 7 | import pm.gnosis.utils.BigIntegerUtils 8 | 9 | class Abi8 { 10 | object Function { 11 | const val METHOD_ID: String = "06da0736" 12 | 13 | fun encode(arg1: Solidity.Address): String { 14 | return "0x" + METHOD_ID + pm.gnosis.model.SolidityBase.encodeFunctionArguments(arg1) 15 | } 16 | 17 | fun decode(data: String): Return { 18 | val source = SolidityBase.PartitionData.of(data) 19 | 20 | // Add decoders 21 | val arg0Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 22 | val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) 23 | 24 | return Return(arg0) 25 | } 26 | 27 | fun decodeArguments(data: String): Arguments { 28 | val source = SolidityBase.PartitionData.of(data) 29 | 30 | // Add decoders 31 | val arg0 = Solidity.Address.DECODER.decode(source) 32 | 33 | return Arguments(arg0) 34 | } 35 | 36 | data class Return( 37 | val param0: Solidity.Bytes 38 | ) 39 | 40 | data class Arguments( 41 | val param0: Solidity.Address 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/abis/abi9.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi9", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [ 7 | { 8 | "name": "c", 9 | "type": "tuple[]", 10 | "components": [ 11 | { 12 | "name": "a", 13 | "type": "uint256" 14 | }, 15 | { 16 | "name": "b", 17 | "type": "uint256" 18 | }, 19 | { 20 | "name": "", 21 | "type": "uint256[5][7][][]" 22 | } 23 | ] 24 | }, 25 | { 26 | "name": "", 27 | "type": "uint256[5][7][][]" 28 | } 29 | ], 30 | "name": "owners", 31 | "outputs": [ 32 | { 33 | "name": "", 34 | "type": "tuple", 35 | "components": [ 36 | { 37 | "name": "x", 38 | "type": "uint256" 39 | }, 40 | { 41 | "name": "y", 42 | "type": "uint256" 43 | } 44 | ] 45 | }, 46 | { 47 | "name": "", 48 | "type": "tuple[]", 49 | "components": [ 50 | { 51 | "name": "x", 52 | "type": "uint256" 53 | }, 54 | { 55 | "name": "y", 56 | "type": "uint256" 57 | } 58 | ] 59 | } 60 | ], 61 | "payable": false, 62 | "type": "function" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/Abi9.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import expected.arrays.Array5 4 | import expected.arrays.Array7 5 | import java.math.BigInteger 6 | import kotlin.Boolean 7 | import kotlin.String 8 | import pm.gnosis.model.Solidity 9 | import pm.gnosis.model.SolidityBase 10 | import pm.gnosis.utils.BigIntegerUtils 11 | 12 | class Abi9 { 13 | object Owners { 14 | const val METHOD_ID: String = "9f767eb7" 15 | 16 | fun encode(c: SolidityBase.Vector, 17 | arg2: SolidityBase.Vector>>>): 18 | String { 19 | return "0x" + METHOD_ID + pm.gnosis.model.SolidityBase.encodeFunctionArguments(c, arg2) 20 | } 21 | 22 | fun decode(data: String): Return { 23 | val source = SolidityBase.PartitionData.of(data) 24 | 25 | // Add decoders 26 | val arg0 = TupleB.DECODER.decode(source) 27 | val arg1Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 28 | val arg1 = SolidityBase.Vector.Decoder(TupleB.DECODER).decode(source.subData(arg1Offset)) 29 | 30 | return Return(arg0, arg1) 31 | } 32 | 33 | fun decodeArguments(data: String): Arguments { 34 | val source = SolidityBase.PartitionData.of(data) 35 | 36 | // Add decoders 37 | val arg0Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 38 | val arg0 = SolidityBase.Vector.Decoder(TupleA.DECODER).decode(source.subData(arg0Offset)) 39 | val arg1Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 40 | val arg1 = SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Array7.Decoder(Array5.Decoder(Solidity.UInt256.DECODER)))).decode(source.subData(arg1Offset)) 41 | 42 | return Arguments(arg0, arg1) 43 | } 44 | 45 | data class Return( 46 | val param0: TupleB, 47 | val param1: SolidityBase.Vector 48 | ) 49 | 50 | data class Arguments( 51 | val c: SolidityBase.Vector, 52 | val param1: SolidityBase.Vector>>> 53 | ) 54 | } 55 | 56 | data class TupleA( 57 | val a: Solidity.UInt256, 58 | val b: Solidity.UInt256, 59 | val param2: SolidityBase.Vector>>> 60 | ) : SolidityBase.DynamicType { 61 | override fun encode(): String { 62 | return SolidityBase.encodeFunctionArguments(a, b, param2) 63 | } 64 | 65 | override fun encodePacked(): String { 66 | throw UnsupportedOperationException("Structs are not supported via encodePacked") 67 | } 68 | 69 | class Decoder : SolidityBase.TypeDecoder { 70 | override fun isDynamic(): Boolean = true 71 | override fun decode(source: SolidityBase.PartitionData): TupleA { 72 | val arg0 = Solidity.UInt256.DECODER.decode(source) 73 | val arg1 = Solidity.UInt256.DECODER.decode(source) 74 | val arg2Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 75 | val arg2 = SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Array7.Decoder(Array5.Decoder(Solidity.UInt256.DECODER)))).decode(source.subData(arg2Offset)) 76 | return TupleA(arg0, arg1, arg2) 77 | } 78 | } 79 | 80 | companion object { 81 | val DECODER: Decoder = Decoder() 82 | } 83 | } 84 | 85 | data class TupleB( 86 | val x: Solidity.UInt256, 87 | val y: Solidity.UInt256 88 | ) : SolidityBase.StaticType { 89 | override fun encode(): String { 90 | return SolidityBase.encodeFunctionArguments(x, y) 91 | } 92 | 93 | override fun encodePacked(): String { 94 | throw UnsupportedOperationException("Structs are not supported via encodePacked") 95 | } 96 | 97 | class Decoder : SolidityBase.TypeDecoder { 98 | override fun isDynamic(): Boolean = false 99 | override fun decode(source: SolidityBase.PartitionData): TupleB { 100 | val arg0 = Solidity.UInt256.DECODER.decode(source) 101 | val arg1 = Solidity.UInt256.DECODER.decode(source) 102 | return TupleB(arg0, arg1) 103 | } 104 | } 105 | 106 | companion object { 107 | val DECODER: Decoder = Decoder() 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/arrays/Array5.kt: -------------------------------------------------------------------------------- 1 | package expected.arrays 2 | 3 | import kotlin.Boolean 4 | import kotlin.collections.List 5 | import pm.gnosis.model.SolidityBase 6 | 7 | class Array5( 8 | items: List 9 | ) : SolidityBase.Array(items, 5) { 10 | class Decoder( 11 | val itemDecoder: SolidityBase.TypeDecoder 12 | ) : SolidityBase.TypeDecoder> { 13 | override fun isDynamic(): Boolean = itemDecoder.isDynamic() 14 | override fun decode(source: SolidityBase.PartitionData): Array5 { 15 | return Array5(SolidityBase.decodeList(source, 5, itemDecoder)) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/arrays/Array7.kt: -------------------------------------------------------------------------------- 1 | package expected.arrays 2 | 3 | import kotlin.Boolean 4 | import kotlin.collections.List 5 | import pm.gnosis.model.SolidityBase 6 | 7 | class Array7( 8 | items: List 9 | ) : SolidityBase.Array(items, 7) { 10 | class Decoder( 11 | val itemDecoder: SolidityBase.TypeDecoder 12 | ) : SolidityBase.TypeDecoder> { 13 | override fun isDynamic(): Boolean = itemDecoder.isDynamic() 14 | override fun decode(source: SolidityBase.PartitionData): Array7 { 15 | return Array7(SolidityBase.decodeList(source, 7, itemDecoder)) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/10_simple_event/abis/abi10.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi10", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [ 7 | ], 8 | "name": "executed", 9 | "outputs": [ 10 | ], 11 | "payable": false, 12 | "type": "event" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/10_simple_event/expected/Abi10.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | 5 | class Abi10 { 6 | object Events { 7 | object Executed { 8 | const val EVENT_ID: String = 9 | "31a38c898682b31869cf766157e0c918f2026132f81da53d2220a90addbdd887" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/11_event_static_indexed/abis/abi11.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi11", 3 | "abi": [ 4 | { 5 | "anonymous": false, 6 | "inputs": [ 7 | { 8 | "indexed": true, 9 | "name": "transactionId", 10 | "type": "uint256" 11 | } 12 | ], 13 | "name": "Submission", 14 | "type": "event" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/11_event_static_indexed/expected/Abi11.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | import kotlin.collections.List 5 | import pm.gnosis.model.Solidity 6 | import pm.gnosis.model.SolidityBase 7 | 8 | class Abi11 { 9 | object Events { 10 | object Submission { 11 | const val EVENT_ID: String = 12 | "c0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e51" 13 | 14 | fun decode(topics: List): Arguments { 15 | // Decode topics 16 | if (topics.first().removePrefix("0x") != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") 17 | val source1 = SolidityBase.PartitionData.of(topics[1]) 18 | val t1 = Solidity.UInt256.DECODER.decode(source1) 19 | return Arguments(t1) 20 | } 21 | 22 | data class Arguments( 23 | val transactionid: Solidity.UInt256 24 | ) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/12_event_static_not_indexed/abis/abi12.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi12", 3 | "abi": [ 4 | { 5 | "anonymous": false, 6 | "inputs": [ 7 | { 8 | "indexed": false, 9 | "name": "transactionId", 10 | "type": "uint256" 11 | } 12 | ], 13 | "name": "Submission", 14 | "type": "event" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/12_event_static_not_indexed/expected/Abi12.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | import kotlin.collections.List 5 | import pm.gnosis.model.Solidity 6 | import pm.gnosis.model.SolidityBase 7 | 8 | class Abi12 { 9 | object Events { 10 | object Submission { 11 | const val EVENT_ID: String = 12 | "c0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e51" 13 | 14 | fun decode(topics: List, data: String): Arguments { 15 | // Decode topics 16 | if (topics.first().removePrefix("0x") != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") 17 | 18 | // Decode data 19 | val source = SolidityBase.PartitionData.of(data) 20 | val arg0 = Solidity.UInt256.DECODER.decode(source) 21 | return Arguments(arg0) 22 | } 23 | 24 | data class Arguments( 25 | val transactionid: Solidity.UInt256 26 | ) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/13_simple_event_dynamic_indexed/abis/abi13.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi13", 3 | "abi": [ 4 | { 5 | "anonymous": false, 6 | "inputs": [ 7 | { 8 | "indexed": true, 9 | "name": "bytes", 10 | "type": "bytes" 11 | }, 12 | { 13 | "indexed": true, 14 | "name": "string", 15 | "type": "string" 16 | }, 17 | { 18 | "indexed": true, 19 | "type": "tuple", 20 | "name": "tuple", 21 | "components": [ 22 | { 23 | "name": "x", 24 | "type": "uint256" 25 | }, 26 | { 27 | "name": "y", 28 | "type": "uint256" 29 | } 30 | ] 31 | } 32 | ], 33 | "name": "Submission", 34 | "type": "event" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/13_simple_event_dynamic_indexed/expected/Abi13.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.Boolean 4 | import kotlin.String 5 | import kotlin.collections.List 6 | import pm.gnosis.model.Solidity 7 | import pm.gnosis.model.SolidityBase 8 | 9 | class Abi13 { 10 | object Events { 11 | object Submission { 12 | const val EVENT_ID: String = 13 | "0c5212e9d002fa3e0c9bd8c78b6d4df3e94f4e956761bd40f0859c979600a2e7" 14 | 15 | fun decode(topics: List): Arguments { 16 | // Decode topics 17 | if (topics.first().removePrefix("0x") != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") 18 | val t1 = topics[1] 19 | val t2 = topics[2] 20 | val t3 = topics[3] 21 | return Arguments(t1, t2, t3) 22 | } 23 | 24 | data class Arguments( 25 | val bytesHash: String, 26 | val stringHash: String, 27 | val tupleHash: String 28 | ) 29 | } 30 | } 31 | 32 | data class TupleA( 33 | val x: Solidity.UInt256, 34 | val y: Solidity.UInt256 35 | ) : SolidityBase.StaticType { 36 | override fun encode(): String { 37 | return SolidityBase.encodeFunctionArguments(x, y) 38 | } 39 | 40 | override fun encodePacked(): String { 41 | throw UnsupportedOperationException("Structs are not supported via encodePacked") 42 | } 43 | 44 | class Decoder : SolidityBase.TypeDecoder { 45 | override fun isDynamic(): Boolean = false 46 | override fun decode(source: SolidityBase.PartitionData): TupleA { 47 | val arg0 = Solidity.UInt256.DECODER.decode(source) 48 | val arg1 = Solidity.UInt256.DECODER.decode(source) 49 | return TupleA(arg0, arg1) 50 | } 51 | } 52 | 53 | companion object { 54 | val DECODER: Decoder = Decoder() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/14_simple_event_dynamic_not_indexed/abis/abi14.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi14", 3 | "abi": [ 4 | { 5 | "anonymous": false, 6 | "inputs": [ 7 | { 8 | "indexed": false, 9 | "name": "bytes", 10 | "type": "bytes" 11 | }, 12 | { 13 | "indexed": false, 14 | "name": "string", 15 | "type": "string" 16 | }, 17 | { 18 | "indexed": false, 19 | "type": "tuple", 20 | "name": "tuple", 21 | "components": [ 22 | { 23 | "name": "x", 24 | "type": "uint256" 25 | }, 26 | { 27 | "name": "y", 28 | "type": "uint256" 29 | } 30 | ] 31 | } 32 | ], 33 | "name": "Submission", 34 | "type": "event" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/14_simple_event_dynamic_not_indexed/expected/Abi14.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import java.math.BigInteger 4 | import kotlin.Boolean 5 | import kotlin.String 6 | import kotlin.collections.List 7 | import pm.gnosis.model.Solidity 8 | import pm.gnosis.model.SolidityBase 9 | import pm.gnosis.utils.BigIntegerUtils 10 | 11 | class Abi14 { 12 | object Events { 13 | object Submission { 14 | const val EVENT_ID: String = 15 | "0c5212e9d002fa3e0c9bd8c78b6d4df3e94f4e956761bd40f0859c979600a2e7" 16 | 17 | fun decode(topics: List, data: String): Arguments { 18 | // Decode topics 19 | if (topics.first().removePrefix("0x") != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") 20 | 21 | // Decode data 22 | val source = SolidityBase.PartitionData.of(data) 23 | val arg0Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 24 | val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) 25 | val arg1Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 26 | val arg1 = Solidity.String.DECODER.decode(source.subData(arg1Offset)) 27 | val arg2 = TupleA.DECODER.decode(source) 28 | return Arguments(arg0, arg1, arg2) 29 | } 30 | 31 | data class Arguments( 32 | val bytes: Solidity.Bytes, 33 | val string: Solidity.String, 34 | val tuple: TupleA 35 | ) 36 | } 37 | } 38 | 39 | data class TupleA( 40 | val x: Solidity.UInt256, 41 | val y: Solidity.UInt256 42 | ) : SolidityBase.StaticType { 43 | override fun encode(): String { 44 | return SolidityBase.encodeFunctionArguments(x, y) 45 | } 46 | 47 | override fun encodePacked(): String { 48 | throw UnsupportedOperationException("Structs are not supported via encodePacked") 49 | } 50 | 51 | class Decoder : SolidityBase.TypeDecoder { 52 | override fun isDynamic(): Boolean = false 53 | override fun decode(source: SolidityBase.PartitionData): TupleA { 54 | val arg0 = Solidity.UInt256.DECODER.decode(source) 55 | val arg1 = Solidity.UInt256.DECODER.decode(source) 56 | return TupleA(arg0, arg1) 57 | } 58 | } 59 | 60 | companion object { 61 | val DECODER: Decoder = Decoder() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/15_anonymous_event/abis/abi15.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi15", 3 | "abi": [ 4 | { 5 | "anonymous": true, 6 | "constant": true, 7 | "inputs": [ 8 | { 9 | "indexed": true, 10 | "name": "transactionId", 11 | "type": "uint256" 12 | } 13 | ], 14 | "name": "executed", 15 | "outputs": [ 16 | ], 17 | "payable": false, 18 | "type": "event" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/15_anonymous_event/expected/Abi15.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import kotlin.String 4 | import kotlin.collections.List 5 | import pm.gnosis.model.Solidity 6 | import pm.gnosis.model.SolidityBase 7 | 8 | class Abi15 { 9 | object Events { 10 | object Executed { 11 | const val EVENT_ID: String = 12 | "d3ecebd73eb8f84cd179cd91fe1e27c1a112b3c76ca5ceaca1661a808d6081aa" 13 | 14 | fun decode(topics: List): Arguments { 15 | // Decode topics 16 | val source1 = SolidityBase.PartitionData.of(topics[1]) 17 | val t1 = Solidity.UInt256.DECODER.decode(source1) 18 | return Arguments(t1) 19 | } 20 | 21 | data class Arguments( 22 | val transactionid: Solidity.UInt256 23 | ) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/abis/abi16.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Abi16", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [ 7 | { 8 | "name": "c", 9 | "type": "tuple", 10 | "components": [ 11 | { 12 | "name": "bytesVar", 13 | "type": "bytes" 14 | }, 15 | { 16 | "name": "stringVar", 17 | "type": "string" 18 | } 19 | ] 20 | } 21 | ], 22 | "name": "malformed", 23 | "outputs": [], 24 | "payable": false, 25 | "type": "function" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/expected/Abi16.kt: -------------------------------------------------------------------------------- 1 | package expected 2 | 3 | import java.math.BigInteger 4 | import kotlin.Boolean 5 | import kotlin.String 6 | import pm.gnosis.model.Solidity 7 | import pm.gnosis.model.SolidityBase 8 | import pm.gnosis.utils.BigIntegerUtils 9 | 10 | class Abi16 { 11 | object Malformed { 12 | const val METHOD_ID: String = "a76411a9" 13 | 14 | fun encode(c: TupleA): String { 15 | return "0x" + METHOD_ID + pm.gnosis.model.SolidityBase.encodeFunctionArguments(c) 16 | } 17 | 18 | fun decodeArguments(data: String): Arguments { 19 | val source = SolidityBase.PartitionData.of(data) 20 | 21 | // Add decoders 22 | val arg0Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 23 | val arg0 = TupleA.DECODER.decode(source.subData(arg0Offset)) 24 | 25 | return Arguments(arg0) 26 | } 27 | 28 | data class Arguments( 29 | val c: TupleA 30 | ) 31 | } 32 | 33 | data class TupleA( 34 | val bytesvar: Solidity.Bytes, 35 | val stringvar: Solidity.String 36 | ) : SolidityBase.DynamicType { 37 | override fun encode(): String { 38 | return SolidityBase.encodeFunctionArguments(bytesvar, stringvar) 39 | } 40 | 41 | override fun encodePacked(): String { 42 | throw UnsupportedOperationException("Structs are not supported via encodePacked") 43 | } 44 | 45 | class Decoder : SolidityBase.TypeDecoder { 46 | override fun isDynamic(): Boolean = true 47 | override fun decode(source: SolidityBase.PartitionData): TupleA { 48 | val arg0Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 49 | val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) 50 | val arg1Offset = BigIntegerUtils.exact(BigInteger(source.consume(), 16)) 51 | val arg1 = Solidity.String.DECODER.decode(source.subData(arg1Offset)) 52 | return TupleA(arg0, arg1) 53 | } 54 | } 55 | 56 | companion object { 57 | val DECODER: Decoder = Decoder() 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /bivrost-gradle-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'kotlin' 3 | apply plugin: 'maven' 4 | 5 | dependencies { 6 | compileOnly gradleApi() 7 | 8 | implementation project(':bivrost-abi-parser') 9 | implementation deps.android.gradlePlugin 10 | } 11 | 12 | sourceSets { 13 | main { 14 | kotlin { 15 | srcDir 'build/generated/source/abi' 16 | } 17 | } 18 | } 19 | 20 | uploadArchives { 21 | repositories { 22 | mavenDeployer { 23 | repository(url: uri('../repo')) 24 | } 25 | } 26 | } 27 | 28 | task sourcesJar(type: Jar, dependsOn:classes) { 29 | from sourceSets.main.allSource 30 | classifier = 'sources' 31 | } 32 | 33 | artifacts { 34 | archives sourcesJar 35 | } 36 | -------------------------------------------------------------------------------- /bivrost-gradle-plugin/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Bivrost Gradle Plugin 2 | POM_ARTIFACT_ID=bivrost-gradle-plugin 3 | POM_PACKAGING=jar -------------------------------------------------------------------------------- /bivrost-gradle-plugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'bivrost-gradle-plugin' -------------------------------------------------------------------------------- /bivrost-gradle-plugin/src/main/kotlin/pm/gnosis/plugin/BivrostPlugin.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.plugin 2 | 3 | import com.android.build.gradle.* 4 | import com.android.build.gradle.api.BaseVariant 5 | import org.gradle.api.DomainObjectSet 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | import org.gradle.api.plugins.ExtensionContainer 9 | import pm.gnosis.AbiParser 10 | import java.util.concurrent.atomic.AtomicBoolean 11 | import kotlin.reflect.KClass 12 | 13 | class BivrostPlugin : Plugin { 14 | override fun apply(project: Project) { 15 | println("Plugin Project: " + project.toString()) 16 | project.plugins.all { 17 | when (it) { 18 | is FeaturePlugin -> { 19 | project.extensions[FeatureExtension::class].run { 20 | configureCodeGeneration(project, featureVariants) 21 | configureCodeGeneration(project, libraryVariants) 22 | } 23 | } 24 | is LibraryPlugin -> { 25 | project.extensions[LibraryExtension::class].run { 26 | configureCodeGeneration(project, libraryVariants) 27 | } 28 | } 29 | is AppPlugin -> { 30 | project.extensions[AppExtension::class].run { 31 | configureCodeGeneration(project, applicationVariants) 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | private fun configureCodeGeneration(project: Project, variants: DomainObjectSet) { 39 | variants.all { variant -> 40 | val outputDir = project.buildDir.resolve( 41 | "generated/source/abi/${variant.dirName}") 42 | val task = project.tasks.create("pm.gnosis.generate${variant.name.capitalize()}AbiWrapper") 43 | task.outputs.dir(outputDir) 44 | variant.registerJavaGeneratingTask(task, outputDir) 45 | 46 | val once = AtomicBoolean() 47 | variant.outputs.all { output -> 48 | 49 | val processResources = output.processResourcesProvider.get() 50 | task.dependsOn(processResources) 51 | 52 | // Though there might be multiple outputs, their R files are all the same. Thus, we only 53 | // need to configure the task once with the R.java input and action. 54 | if (once.compareAndSet(false, true)) { 55 | val abiFolder = project.projectDir.resolve("abi") 56 | 57 | task.apply { 58 | inputs.files(abiFolder.listFiles()) 59 | 60 | doLast { 61 | val packageName = variant.applicationId.removeSuffix(variant.buildType.applicationIdSuffix ?: "") 62 | // We can reuse arrays for all ABIs 63 | val arraysMap = AbiParser.ArraysMap(packageName) 64 | 65 | abiFolder.listFiles().forEach { 66 | println("Generating wrapper for <$it>") 67 | AbiParser.generateWrapper(packageName, it.readText(), outputDir, arraysMap) 68 | } 69 | 70 | arraysMap.generate(outputDir) 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | private operator fun ExtensionContainer.get(type: KClass): T { 79 | return getByType(type.java)!! 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /bivrost-gradle-plugin/src/main/resources/META-INF/gradle-plugins/bivrost.properties: -------------------------------------------------------------------------------- 1 | implementation-class=pm.gnosis.plugin.BivrostPlugin -------------------------------------------------------------------------------- /bivrost-solidity-types-generator/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'kotlin' 3 | apply plugin: 'maven' 4 | 5 | task runSolidityTypeGenerator(type: JavaExec) { 6 | def targetProject = project.parent.childProjects.get("bivrost-solidity-types") ?: project 7 | def srcDirs = targetProject.sourceSets.main.kotlin.getSrcDirs() 8 | if (srcDirs.isEmpty()) { 9 | logger.error("Couldn't find kotlin main source sets") 10 | return 11 | } 12 | 13 | def path = "${srcDirs[0]}" 14 | main = 'pm.gnosis.SolidityTypeGenerator' 15 | classpath = sourceSets.main.runtimeClasspath 16 | args (path, project.group) 17 | } 18 | 19 | dependencies { 20 | implementation "org.jetbrains.kotlin:kotlin-stdlib" 21 | 22 | implementation project(':bivrost-utils') 23 | implementation project(':bivrost-solidity-types') 24 | implementation "com.squareup:kotlinpoet:${versions.kotlinPoet}" 25 | 26 | testImplementation group: 'junit', name: 'junit', version: '4.12' 27 | } 28 | 29 | task sourcesJar(type: Jar, dependsOn:classes) { 30 | from sourceSets.main.allSource 31 | classifier = 'sources' 32 | } 33 | 34 | artifacts { 35 | archives sourcesJar 36 | } 37 | -------------------------------------------------------------------------------- /bivrost-solidity-types-generator/src/main/kotlin/pm/gnosis/SolidityTypeGenerator.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("SolidityTypeGenerator") 2 | 3 | package pm.gnosis 4 | 5 | import com.squareup.kotlinpoet.* 6 | import pm.gnosis.model.SolidityBase 7 | import java.io.File 8 | import java.math.BigInteger 9 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 10 | 11 | fun main(vararg args: String) { 12 | args.forEach { println(it.split(File.separator).last()) } 13 | if (args.isNotEmpty()) { 14 | generate(args[0], args[1]) 15 | } 16 | } 17 | 18 | fun generate(path: String, packageName: String) { 19 | val fileName = "Solidity" 20 | val indentation = " " 21 | 22 | val modelPackageName = "$packageName.model" 23 | val kotlinFile = FileSpec.builder(modelPackageName, fileName) 24 | val solidityGeneratedObject = TypeSpec.objectBuilder(fileName) 25 | 26 | kotlinFile.addImport("pm.gnosis.utils", "padEndMultiple", "toHex") 27 | solidityGeneratedObject.addKdoc("Generated code. Do not modify\n") 28 | 29 | // Generate types 30 | val uInts = generateUInts() 31 | val ints = generateInts() 32 | val staticBytes = generateStaticBytes() 33 | val address = generateAddress() 34 | val bool = generateBool() 35 | val dynamicBytes = generateDynamicBytes() 36 | val string = generateString() 37 | 38 | // Add types to object 39 | solidityGeneratedObject.addTypes(uInts) 40 | solidityGeneratedObject.addTypes(ints) 41 | solidityGeneratedObject.addTypes(staticBytes) 42 | solidityGeneratedObject.addType(address) 43 | solidityGeneratedObject.addType(bool) 44 | solidityGeneratedObject.addType(dynamicBytes) 45 | solidityGeneratedObject.addType(string) 46 | 47 | val mapClassName = ClassName("kotlin.collections", "Map") 48 | val stringClassName = ClassName("kotlin", "String") 49 | val mapType = mapClassName.parameterizedBy(stringClassName, stringClassName) 50 | 51 | // Generate aliases map 52 | val aliasesMapContent = arrayOf( 53 | "int" to "int256", 54 | "uint" to "uint256", 55 | "byte" to "bytes1" 56 | ) 57 | .joinToString(",\n") { "\"${it.first}\" to \"${it.second}\"" } 58 | 59 | val aliasesMapBlock = CodeBlock.builder().add(CodeBlock.of("mapOf(\n$aliasesMapContent)")) 60 | 61 | // Add map to object 62 | solidityGeneratedObject.addProperty(PropertySpec.builder("aliases", mapType).initializer(aliasesMapBlock.build()).build()) 63 | 64 | // Generate type map 65 | val typeMapContent = (uInts + ints + staticBytes + address + bool + dynamicBytes + string) 66 | .map { it.name?.toLowerCase() to it.name } 67 | .joinToString(",\n") { "\"${it.first}\" to \"$modelPackageName.$fileName.${it.second}\"" } 68 | 69 | val typeMapBlock = CodeBlock.builder().add(CodeBlock.of("mapOf(\n$typeMapContent)")) 70 | 71 | // Add map to object 72 | solidityGeneratedObject.addProperty(PropertySpec.builder("types", mapType).initializer(typeMapBlock.build()).build()) 73 | 74 | // Write object file 75 | kotlinFile.indent(indentation).addType(solidityGeneratedObject.build()).build().writeTo(File(path.removeSuffix(modelPackageName))) 76 | } 77 | 78 | private fun generateUInts(): List = (8..256 step 8).map { generateUInt("UInt$it", it) }.toList() 79 | 80 | private fun generateUInt(className: String, nBits: Int): TypeSpec { 81 | val decoderTypeName = SolidityBase.UIntBase.Decoder::class.asClassName().parameterizedBy(ClassName("", className)) 82 | return TypeSpec.classBuilder(className) 83 | .addModifiers(KModifier.DATA) 84 | .superclass(SolidityBase.UIntBase::class) 85 | .addSuperclassConstructorParameter("%1L, %2L", "value", nBits) 86 | .addType(GeneratorUtils.generateDecoderCompanion( 87 | decoderTypeName, 88 | CodeBlock.of("%1T({ %2L(it) })", decoderTypeName, className))) 89 | .primaryConstructor(FunSpec.constructorBuilder().addParameter( 90 | ParameterSpec.builder("value", BigInteger::class).build()).build()) 91 | .addProperty(PropertySpec.builder("value", BigInteger::class) 92 | .initializer("value") 93 | .build()) 94 | .build() 95 | } 96 | 97 | private fun generateAddress(): TypeSpec { 98 | val name = "Address" 99 | val decoderTypeName = SolidityBase.UIntBase.Decoder::class.asClassName().parameterizedBy(ClassName("", name)) 100 | return TypeSpec.classBuilder(name) 101 | .addModifiers(KModifier.DATA) 102 | .superclass(SolidityBase.UIntBase::class) 103 | .addSuperclassConstructorParameter("%1L, %2L", "value", "160") 104 | .addType(GeneratorUtils.generateDecoderCompanion( 105 | decoderTypeName, 106 | CodeBlock.of("%1T({ %2L(it) })", decoderTypeName, name))) 107 | .primaryConstructor(FunSpec.constructorBuilder().addParameter( 108 | ParameterSpec.builder("value", BigInteger::class).build()).build()) 109 | .addProperty(PropertySpec.builder("value", BigInteger::class) 110 | .initializer("value") 111 | .build()) 112 | .build() 113 | } 114 | 115 | private fun generateBool(): TypeSpec { 116 | val name = "Bool" 117 | val decoderTypeName = ClassName.bestGuess("Decoder") 118 | return TypeSpec.classBuilder(name) 119 | .addModifiers(KModifier.DATA) 120 | .superclass(SolidityBase.UIntBase::class) 121 | .addSuperclassConstructorParameter("if (%1L) %2T.ONE else %2T.ZERO, %3L", "value", BigInteger::class, "8") 122 | .primaryConstructor(FunSpec.constructorBuilder().addParameter( 123 | ParameterSpec.builder("value", Boolean::class).build()).build()) 124 | .addProperty(PropertySpec.builder("value", Boolean::class) 125 | .initializer("value") 126 | .build()) 127 | .addType(GeneratorUtils.generateDecoder(name, 128 | CodeBlock.builder() 129 | .addStatement("return %1N(%2T.decodeBool(%3L.consume()))", name, SolidityBase::class, "source") 130 | .build(), 131 | false) 132 | ) 133 | .addType(GeneratorUtils.generateDecoderCompanion( 134 | decoderTypeName, 135 | CodeBlock.of("%1T()", decoderTypeName))) 136 | .build() 137 | } 138 | 139 | private fun generateInts() = (8..256 step 8).map { generateInt("Int$it", it) }.toList() 140 | 141 | private fun generateInt(className: String, nBits: Int): TypeSpec { 142 | val decoderTypeName = SolidityBase.IntBase.Decoder::class.asClassName().parameterizedBy(ClassName("", className)) 143 | return TypeSpec.classBuilder(className) 144 | .addModifiers(KModifier.DATA) 145 | .superclass(SolidityBase.IntBase::class) 146 | .addSuperclassConstructorParameter("%1L, %2L", "value", nBits) 147 | .addType(GeneratorUtils.generateDecoderCompanion( 148 | decoderTypeName, 149 | CodeBlock.of("%1T({ %2L(it) })", decoderTypeName, className))) 150 | .primaryConstructor(FunSpec.constructorBuilder().addParameter( 151 | ParameterSpec.builder("value", BigInteger::class).build()).build()) 152 | .addProperty(PropertySpec.builder("value", BigInteger::class) 153 | .initializer("value") 154 | .build()) 155 | .build() 156 | } 157 | 158 | private fun generateStaticBytes() = (1..32).map { 159 | val name = "Bytes$it" 160 | val decoderTypeName = SolidityBase.StaticBytes.Decoder::class.asClassName().parameterizedBy(ClassName("", name)) 161 | TypeSpec.classBuilder(name) 162 | .superclass(SolidityBase.StaticBytes::class) 163 | .addSuperclassConstructorParameter("%1L, %2L", "bytes", it) 164 | .addType(GeneratorUtils.generateDecoderCompanion( 165 | decoderTypeName, 166 | CodeBlock.of("%1T({ %2L(it) }, %3L)", decoderTypeName, name, it))) 167 | .primaryConstructor(FunSpec.constructorBuilder().addParameter( 168 | ParameterSpec.builder("bytes", ByteArray::class).build()).build()) 169 | .addProperty(PropertySpec.builder("bytes", ByteArray::class) 170 | .initializer("bytes") 171 | .build()) 172 | .build() 173 | }.toList() 174 | 175 | private fun generateDynamicBytes(): TypeSpec { 176 | val name = "Bytes" 177 | val decoderTypeName = ClassName.bestGuess("Decoder") 178 | return TypeSpec.classBuilder(name) 179 | .addModifiers(KModifier.OPEN) 180 | .addSuperinterface(SolidityBase.DynamicType::class) 181 | .primaryConstructor(FunSpec.constructorBuilder() 182 | .addParameter("items", ByteArray::class) 183 | .build()) 184 | .addProperty(PropertySpec.builder("items", ByteArray::class).initializer("items").build()) 185 | .addInitializerBlock(CodeBlock.builder() 186 | .addStatement("if (%1T(items.size.toString(10)) > %1T.valueOf(2).pow(256)) throw %2T()", BigInteger::class, Exception::class) 187 | .build()) 188 | .addFunction(FunSpec.builder("encode") 189 | .addModifiers(KModifier.OVERRIDE) 190 | .returns(String::class) 191 | .addCode(CodeBlock.builder() 192 | .addStatement("val parts = encodeParts()") 193 | .addStatement("return parts.static + parts.dynamic") 194 | .build()) 195 | .build()) 196 | .addFunction(FunSpec.builder("encodeParts") 197 | .addModifiers(KModifier.PRIVATE) 198 | .returns(SolidityBase.DynamicType.Parts::class) 199 | .addCode(CodeBlock.builder() 200 | .addStatement("val length = items.size.toString(16).padStart(64, '0')") 201 | .addStatement("val contents = items.toHex().padEndMultiple(64, '0')") 202 | .addStatement("return %1T(length, contents)", SolidityBase.DynamicType.Parts::class) 203 | .build()) 204 | .build()) 205 | .addFunction(FunSpec.builder("encodePacked") 206 | .addModifiers(KModifier.OVERRIDE) 207 | .returns(String::class) 208 | .addCode(CodeBlock.builder() 209 | .addStatement("return items.toHex()") 210 | .build()) 211 | .build()) 212 | .addType(GeneratorUtils.generateDecoder(name, 213 | CodeBlock.builder() 214 | .addStatement("return %1N(%2T.decodeBytes(source))", name, SolidityBase::class) 215 | .build())) 216 | .addType(GeneratorUtils.generateDecoderCompanion( 217 | decoderTypeName, 218 | CodeBlock.of("%1T()", decoderTypeName))) 219 | .build() 220 | } 221 | 222 | private fun generateString(): TypeSpec { 223 | val name = "String" 224 | val superClass = ClassName.bestGuess("Bytes") 225 | val decoderTypeName = ClassName.bestGuess("Decoder") 226 | return TypeSpec.classBuilder(name) 227 | .addModifiers(KModifier.DATA) 228 | .superclass(superClass) 229 | .addSuperclassConstructorParameter("%1L.toByteArray()", "value") 230 | .primaryConstructor(FunSpec.constructorBuilder().addParameter( 231 | ParameterSpec.builder("value", String::class).build()).build()) 232 | .addProperty(PropertySpec.builder("value", String::class) 233 | .initializer("value") 234 | .build()) 235 | .addType(GeneratorUtils.generateDecoder(name, 236 | CodeBlock.builder() 237 | .addStatement("return %1N(%2T.decodeString(%3L))", name, SolidityBase::class, "source") 238 | .build()) 239 | ) 240 | .addType(GeneratorUtils.generateDecoderCompanion( 241 | decoderTypeName, 242 | CodeBlock.of("%1T()", decoderTypeName))) 243 | .build() 244 | } 245 | -------------------------------------------------------------------------------- /bivrost-solidity-types/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'kotlin' 3 | apply plugin: 'maven' 4 | 5 | dependencies { 6 | implementation "org.jetbrains.kotlin:kotlin-stdlib" 7 | 8 | testImplementation group: 'junit', name: 'junit', version: '4.12' 9 | } 10 | 11 | uploadArchives { 12 | repositories { 13 | mavenDeployer { 14 | repository(url: uri('../repo')) 15 | } 16 | } 17 | } 18 | 19 | task sourcesJar(type: Jar, dependsOn:classes) { 20 | from sourceSets.main.allSource 21 | classifier = 'sources' 22 | } 23 | 24 | artifacts { 25 | archives sourcesJar 26 | } 27 | -------------------------------------------------------------------------------- /bivrost-solidity-types/src/main/kotlin/pm/gnosis/exceptions/InvalidBitLengthException.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.exceptions 2 | 3 | class InvalidBitLengthException(message: String?) : Exception(message) { 4 | companion object { 5 | val NOT_MULTIPLE_OF_EIGHT = InvalidBitLengthException("The bit length of the value is not a multiple of 8") 6 | val BIG_VALUE = InvalidBitLengthException("The bit length of the value is too big") 7 | } 8 | } -------------------------------------------------------------------------------- /bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/Solidity.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.model 2 | 3 | import java.lang.Exception 4 | import java.math.BigInteger 5 | import kotlin.Boolean 6 | import kotlin.ByteArray 7 | import kotlin.collections.Map 8 | import pm.gnosis.utils.padEndMultiple 9 | import pm.gnosis.utils.toHex 10 | 11 | /** 12 | * Generated code. Do not modify 13 | */ 14 | object Solidity { 15 | val aliases: Map = mapOf( 16 | "int" to "int256", 17 | "uint" to "uint256", 18 | "byte" to "bytes1") 19 | 20 | val types: Map = mapOf( 21 | "uint8" to "pm.gnosis.model.Solidity.UInt8", 22 | "uint16" to "pm.gnosis.model.Solidity.UInt16", 23 | "uint24" to "pm.gnosis.model.Solidity.UInt24", 24 | "uint32" to "pm.gnosis.model.Solidity.UInt32", 25 | "uint40" to "pm.gnosis.model.Solidity.UInt40", 26 | "uint48" to "pm.gnosis.model.Solidity.UInt48", 27 | "uint56" to "pm.gnosis.model.Solidity.UInt56", 28 | "uint64" to "pm.gnosis.model.Solidity.UInt64", 29 | "uint72" to "pm.gnosis.model.Solidity.UInt72", 30 | "uint80" to "pm.gnosis.model.Solidity.UInt80", 31 | "uint88" to "pm.gnosis.model.Solidity.UInt88", 32 | "uint96" to "pm.gnosis.model.Solidity.UInt96", 33 | "uint104" to "pm.gnosis.model.Solidity.UInt104", 34 | "uint112" to "pm.gnosis.model.Solidity.UInt112", 35 | "uint120" to "pm.gnosis.model.Solidity.UInt120", 36 | "uint128" to "pm.gnosis.model.Solidity.UInt128", 37 | "uint136" to "pm.gnosis.model.Solidity.UInt136", 38 | "uint144" to "pm.gnosis.model.Solidity.UInt144", 39 | "uint152" to "pm.gnosis.model.Solidity.UInt152", 40 | "uint160" to "pm.gnosis.model.Solidity.UInt160", 41 | "uint168" to "pm.gnosis.model.Solidity.UInt168", 42 | "uint176" to "pm.gnosis.model.Solidity.UInt176", 43 | "uint184" to "pm.gnosis.model.Solidity.UInt184", 44 | "uint192" to "pm.gnosis.model.Solidity.UInt192", 45 | "uint200" to "pm.gnosis.model.Solidity.UInt200", 46 | "uint208" to "pm.gnosis.model.Solidity.UInt208", 47 | "uint216" to "pm.gnosis.model.Solidity.UInt216", 48 | "uint224" to "pm.gnosis.model.Solidity.UInt224", 49 | "uint232" to "pm.gnosis.model.Solidity.UInt232", 50 | "uint240" to "pm.gnosis.model.Solidity.UInt240", 51 | "uint248" to "pm.gnosis.model.Solidity.UInt248", 52 | "uint256" to "pm.gnosis.model.Solidity.UInt256", 53 | "int8" to "pm.gnosis.model.Solidity.Int8", 54 | "int16" to "pm.gnosis.model.Solidity.Int16", 55 | "int24" to "pm.gnosis.model.Solidity.Int24", 56 | "int32" to "pm.gnosis.model.Solidity.Int32", 57 | "int40" to "pm.gnosis.model.Solidity.Int40", 58 | "int48" to "pm.gnosis.model.Solidity.Int48", 59 | "int56" to "pm.gnosis.model.Solidity.Int56", 60 | "int64" to "pm.gnosis.model.Solidity.Int64", 61 | "int72" to "pm.gnosis.model.Solidity.Int72", 62 | "int80" to "pm.gnosis.model.Solidity.Int80", 63 | "int88" to "pm.gnosis.model.Solidity.Int88", 64 | "int96" to "pm.gnosis.model.Solidity.Int96", 65 | "int104" to "pm.gnosis.model.Solidity.Int104", 66 | "int112" to "pm.gnosis.model.Solidity.Int112", 67 | "int120" to "pm.gnosis.model.Solidity.Int120", 68 | "int128" to "pm.gnosis.model.Solidity.Int128", 69 | "int136" to "pm.gnosis.model.Solidity.Int136", 70 | "int144" to "pm.gnosis.model.Solidity.Int144", 71 | "int152" to "pm.gnosis.model.Solidity.Int152", 72 | "int160" to "pm.gnosis.model.Solidity.Int160", 73 | "int168" to "pm.gnosis.model.Solidity.Int168", 74 | "int176" to "pm.gnosis.model.Solidity.Int176", 75 | "int184" to "pm.gnosis.model.Solidity.Int184", 76 | "int192" to "pm.gnosis.model.Solidity.Int192", 77 | "int200" to "pm.gnosis.model.Solidity.Int200", 78 | "int208" to "pm.gnosis.model.Solidity.Int208", 79 | "int216" to "pm.gnosis.model.Solidity.Int216", 80 | "int224" to "pm.gnosis.model.Solidity.Int224", 81 | "int232" to "pm.gnosis.model.Solidity.Int232", 82 | "int240" to "pm.gnosis.model.Solidity.Int240", 83 | "int248" to "pm.gnosis.model.Solidity.Int248", 84 | "int256" to "pm.gnosis.model.Solidity.Int256", 85 | "bytes1" to "pm.gnosis.model.Solidity.Bytes1", 86 | "bytes2" to "pm.gnosis.model.Solidity.Bytes2", 87 | "bytes3" to "pm.gnosis.model.Solidity.Bytes3", 88 | "bytes4" to "pm.gnosis.model.Solidity.Bytes4", 89 | "bytes5" to "pm.gnosis.model.Solidity.Bytes5", 90 | "bytes6" to "pm.gnosis.model.Solidity.Bytes6", 91 | "bytes7" to "pm.gnosis.model.Solidity.Bytes7", 92 | "bytes8" to "pm.gnosis.model.Solidity.Bytes8", 93 | "bytes9" to "pm.gnosis.model.Solidity.Bytes9", 94 | "bytes10" to "pm.gnosis.model.Solidity.Bytes10", 95 | "bytes11" to "pm.gnosis.model.Solidity.Bytes11", 96 | "bytes12" to "pm.gnosis.model.Solidity.Bytes12", 97 | "bytes13" to "pm.gnosis.model.Solidity.Bytes13", 98 | "bytes14" to "pm.gnosis.model.Solidity.Bytes14", 99 | "bytes15" to "pm.gnosis.model.Solidity.Bytes15", 100 | "bytes16" to "pm.gnosis.model.Solidity.Bytes16", 101 | "bytes17" to "pm.gnosis.model.Solidity.Bytes17", 102 | "bytes18" to "pm.gnosis.model.Solidity.Bytes18", 103 | "bytes19" to "pm.gnosis.model.Solidity.Bytes19", 104 | "bytes20" to "pm.gnosis.model.Solidity.Bytes20", 105 | "bytes21" to "pm.gnosis.model.Solidity.Bytes21", 106 | "bytes22" to "pm.gnosis.model.Solidity.Bytes22", 107 | "bytes23" to "pm.gnosis.model.Solidity.Bytes23", 108 | "bytes24" to "pm.gnosis.model.Solidity.Bytes24", 109 | "bytes25" to "pm.gnosis.model.Solidity.Bytes25", 110 | "bytes26" to "pm.gnosis.model.Solidity.Bytes26", 111 | "bytes27" to "pm.gnosis.model.Solidity.Bytes27", 112 | "bytes28" to "pm.gnosis.model.Solidity.Bytes28", 113 | "bytes29" to "pm.gnosis.model.Solidity.Bytes29", 114 | "bytes30" to "pm.gnosis.model.Solidity.Bytes30", 115 | "bytes31" to "pm.gnosis.model.Solidity.Bytes31", 116 | "bytes32" to "pm.gnosis.model.Solidity.Bytes32", 117 | "address" to "pm.gnosis.model.Solidity.Address", 118 | "bool" to "pm.gnosis.model.Solidity.Bool", 119 | "bytes" to "pm.gnosis.model.Solidity.Bytes", 120 | "string" to "pm.gnosis.model.Solidity.String") 121 | 122 | data class UInt8( 123 | val value: BigInteger 124 | ) : SolidityBase.UIntBase(value, 8) { 125 | companion object { 126 | val DECODER: SolidityBase.UIntBase.Decoder = 127 | SolidityBase.UIntBase.Decoder({ UInt8(it) }) 128 | } 129 | } 130 | 131 | data class UInt16( 132 | val value: BigInteger 133 | ) : SolidityBase.UIntBase(value, 16) { 134 | companion object { 135 | val DECODER: SolidityBase.UIntBase.Decoder = 136 | SolidityBase.UIntBase.Decoder({ UInt16(it) }) 137 | } 138 | } 139 | 140 | data class UInt24( 141 | val value: BigInteger 142 | ) : SolidityBase.UIntBase(value, 24) { 143 | companion object { 144 | val DECODER: SolidityBase.UIntBase.Decoder = 145 | SolidityBase.UIntBase.Decoder({ UInt24(it) }) 146 | } 147 | } 148 | 149 | data class UInt32( 150 | val value: BigInteger 151 | ) : SolidityBase.UIntBase(value, 32) { 152 | companion object { 153 | val DECODER: SolidityBase.UIntBase.Decoder = 154 | SolidityBase.UIntBase.Decoder({ UInt32(it) }) 155 | } 156 | } 157 | 158 | data class UInt40( 159 | val value: BigInteger 160 | ) : SolidityBase.UIntBase(value, 40) { 161 | companion object { 162 | val DECODER: SolidityBase.UIntBase.Decoder = 163 | SolidityBase.UIntBase.Decoder({ UInt40(it) }) 164 | } 165 | } 166 | 167 | data class UInt48( 168 | val value: BigInteger 169 | ) : SolidityBase.UIntBase(value, 48) { 170 | companion object { 171 | val DECODER: SolidityBase.UIntBase.Decoder = 172 | SolidityBase.UIntBase.Decoder({ UInt48(it) }) 173 | } 174 | } 175 | 176 | data class UInt56( 177 | val value: BigInteger 178 | ) : SolidityBase.UIntBase(value, 56) { 179 | companion object { 180 | val DECODER: SolidityBase.UIntBase.Decoder = 181 | SolidityBase.UIntBase.Decoder({ UInt56(it) }) 182 | } 183 | } 184 | 185 | data class UInt64( 186 | val value: BigInteger 187 | ) : SolidityBase.UIntBase(value, 64) { 188 | companion object { 189 | val DECODER: SolidityBase.UIntBase.Decoder = 190 | SolidityBase.UIntBase.Decoder({ UInt64(it) }) 191 | } 192 | } 193 | 194 | data class UInt72( 195 | val value: BigInteger 196 | ) : SolidityBase.UIntBase(value, 72) { 197 | companion object { 198 | val DECODER: SolidityBase.UIntBase.Decoder = 199 | SolidityBase.UIntBase.Decoder({ UInt72(it) }) 200 | } 201 | } 202 | 203 | data class UInt80( 204 | val value: BigInteger 205 | ) : SolidityBase.UIntBase(value, 80) { 206 | companion object { 207 | val DECODER: SolidityBase.UIntBase.Decoder = 208 | SolidityBase.UIntBase.Decoder({ UInt80(it) }) 209 | } 210 | } 211 | 212 | data class UInt88( 213 | val value: BigInteger 214 | ) : SolidityBase.UIntBase(value, 88) { 215 | companion object { 216 | val DECODER: SolidityBase.UIntBase.Decoder = 217 | SolidityBase.UIntBase.Decoder({ UInt88(it) }) 218 | } 219 | } 220 | 221 | data class UInt96( 222 | val value: BigInteger 223 | ) : SolidityBase.UIntBase(value, 96) { 224 | companion object { 225 | val DECODER: SolidityBase.UIntBase.Decoder = 226 | SolidityBase.UIntBase.Decoder({ UInt96(it) }) 227 | } 228 | } 229 | 230 | data class UInt104( 231 | val value: BigInteger 232 | ) : SolidityBase.UIntBase(value, 104) { 233 | companion object { 234 | val DECODER: SolidityBase.UIntBase.Decoder = 235 | SolidityBase.UIntBase.Decoder({ UInt104(it) }) 236 | } 237 | } 238 | 239 | data class UInt112( 240 | val value: BigInteger 241 | ) : SolidityBase.UIntBase(value, 112) { 242 | companion object { 243 | val DECODER: SolidityBase.UIntBase.Decoder = 244 | SolidityBase.UIntBase.Decoder({ UInt112(it) }) 245 | } 246 | } 247 | 248 | data class UInt120( 249 | val value: BigInteger 250 | ) : SolidityBase.UIntBase(value, 120) { 251 | companion object { 252 | val DECODER: SolidityBase.UIntBase.Decoder = 253 | SolidityBase.UIntBase.Decoder({ UInt120(it) }) 254 | } 255 | } 256 | 257 | data class UInt128( 258 | val value: BigInteger 259 | ) : SolidityBase.UIntBase(value, 128) { 260 | companion object { 261 | val DECODER: SolidityBase.UIntBase.Decoder = 262 | SolidityBase.UIntBase.Decoder({ UInt128(it) }) 263 | } 264 | } 265 | 266 | data class UInt136( 267 | val value: BigInteger 268 | ) : SolidityBase.UIntBase(value, 136) { 269 | companion object { 270 | val DECODER: SolidityBase.UIntBase.Decoder = 271 | SolidityBase.UIntBase.Decoder({ UInt136(it) }) 272 | } 273 | } 274 | 275 | data class UInt144( 276 | val value: BigInteger 277 | ) : SolidityBase.UIntBase(value, 144) { 278 | companion object { 279 | val DECODER: SolidityBase.UIntBase.Decoder = 280 | SolidityBase.UIntBase.Decoder({ UInt144(it) }) 281 | } 282 | } 283 | 284 | data class UInt152( 285 | val value: BigInteger 286 | ) : SolidityBase.UIntBase(value, 152) { 287 | companion object { 288 | val DECODER: SolidityBase.UIntBase.Decoder = 289 | SolidityBase.UIntBase.Decoder({ UInt152(it) }) 290 | } 291 | } 292 | 293 | data class UInt160( 294 | val value: BigInteger 295 | ) : SolidityBase.UIntBase(value, 160) { 296 | companion object { 297 | val DECODER: SolidityBase.UIntBase.Decoder = 298 | SolidityBase.UIntBase.Decoder({ UInt160(it) }) 299 | } 300 | } 301 | 302 | data class UInt168( 303 | val value: BigInteger 304 | ) : SolidityBase.UIntBase(value, 168) { 305 | companion object { 306 | val DECODER: SolidityBase.UIntBase.Decoder = 307 | SolidityBase.UIntBase.Decoder({ UInt168(it) }) 308 | } 309 | } 310 | 311 | data class UInt176( 312 | val value: BigInteger 313 | ) : SolidityBase.UIntBase(value, 176) { 314 | companion object { 315 | val DECODER: SolidityBase.UIntBase.Decoder = 316 | SolidityBase.UIntBase.Decoder({ UInt176(it) }) 317 | } 318 | } 319 | 320 | data class UInt184( 321 | val value: BigInteger 322 | ) : SolidityBase.UIntBase(value, 184) { 323 | companion object { 324 | val DECODER: SolidityBase.UIntBase.Decoder = 325 | SolidityBase.UIntBase.Decoder({ UInt184(it) }) 326 | } 327 | } 328 | 329 | data class UInt192( 330 | val value: BigInteger 331 | ) : SolidityBase.UIntBase(value, 192) { 332 | companion object { 333 | val DECODER: SolidityBase.UIntBase.Decoder = 334 | SolidityBase.UIntBase.Decoder({ UInt192(it) }) 335 | } 336 | } 337 | 338 | data class UInt200( 339 | val value: BigInteger 340 | ) : SolidityBase.UIntBase(value, 200) { 341 | companion object { 342 | val DECODER: SolidityBase.UIntBase.Decoder = 343 | SolidityBase.UIntBase.Decoder({ UInt200(it) }) 344 | } 345 | } 346 | 347 | data class UInt208( 348 | val value: BigInteger 349 | ) : SolidityBase.UIntBase(value, 208) { 350 | companion object { 351 | val DECODER: SolidityBase.UIntBase.Decoder = 352 | SolidityBase.UIntBase.Decoder({ UInt208(it) }) 353 | } 354 | } 355 | 356 | data class UInt216( 357 | val value: BigInteger 358 | ) : SolidityBase.UIntBase(value, 216) { 359 | companion object { 360 | val DECODER: SolidityBase.UIntBase.Decoder = 361 | SolidityBase.UIntBase.Decoder({ UInt216(it) }) 362 | } 363 | } 364 | 365 | data class UInt224( 366 | val value: BigInteger 367 | ) : SolidityBase.UIntBase(value, 224) { 368 | companion object { 369 | val DECODER: SolidityBase.UIntBase.Decoder = 370 | SolidityBase.UIntBase.Decoder({ UInt224(it) }) 371 | } 372 | } 373 | 374 | data class UInt232( 375 | val value: BigInteger 376 | ) : SolidityBase.UIntBase(value, 232) { 377 | companion object { 378 | val DECODER: SolidityBase.UIntBase.Decoder = 379 | SolidityBase.UIntBase.Decoder({ UInt232(it) }) 380 | } 381 | } 382 | 383 | data class UInt240( 384 | val value: BigInteger 385 | ) : SolidityBase.UIntBase(value, 240) { 386 | companion object { 387 | val DECODER: SolidityBase.UIntBase.Decoder = 388 | SolidityBase.UIntBase.Decoder({ UInt240(it) }) 389 | } 390 | } 391 | 392 | data class UInt248( 393 | val value: BigInteger 394 | ) : SolidityBase.UIntBase(value, 248) { 395 | companion object { 396 | val DECODER: SolidityBase.UIntBase.Decoder = 397 | SolidityBase.UIntBase.Decoder({ UInt248(it) }) 398 | } 399 | } 400 | 401 | data class UInt256( 402 | val value: BigInteger 403 | ) : SolidityBase.UIntBase(value, 256) { 404 | companion object { 405 | val DECODER: SolidityBase.UIntBase.Decoder = 406 | SolidityBase.UIntBase.Decoder({ UInt256(it) }) 407 | } 408 | } 409 | 410 | data class Int8( 411 | val value: BigInteger 412 | ) : SolidityBase.IntBase(value, 8) { 413 | companion object { 414 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 415 | Int8(it) }) 416 | } 417 | } 418 | 419 | data class Int16( 420 | val value: BigInteger 421 | ) : SolidityBase.IntBase(value, 16) { 422 | companion object { 423 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 424 | Int16(it) }) 425 | } 426 | } 427 | 428 | data class Int24( 429 | val value: BigInteger 430 | ) : SolidityBase.IntBase(value, 24) { 431 | companion object { 432 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 433 | Int24(it) }) 434 | } 435 | } 436 | 437 | data class Int32( 438 | val value: BigInteger 439 | ) : SolidityBase.IntBase(value, 32) { 440 | companion object { 441 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 442 | Int32(it) }) 443 | } 444 | } 445 | 446 | data class Int40( 447 | val value: BigInteger 448 | ) : SolidityBase.IntBase(value, 40) { 449 | companion object { 450 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 451 | Int40(it) }) 452 | } 453 | } 454 | 455 | data class Int48( 456 | val value: BigInteger 457 | ) : SolidityBase.IntBase(value, 48) { 458 | companion object { 459 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 460 | Int48(it) }) 461 | } 462 | } 463 | 464 | data class Int56( 465 | val value: BigInteger 466 | ) : SolidityBase.IntBase(value, 56) { 467 | companion object { 468 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 469 | Int56(it) }) 470 | } 471 | } 472 | 473 | data class Int64( 474 | val value: BigInteger 475 | ) : SolidityBase.IntBase(value, 64) { 476 | companion object { 477 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 478 | Int64(it) }) 479 | } 480 | } 481 | 482 | data class Int72( 483 | val value: BigInteger 484 | ) : SolidityBase.IntBase(value, 72) { 485 | companion object { 486 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 487 | Int72(it) }) 488 | } 489 | } 490 | 491 | data class Int80( 492 | val value: BigInteger 493 | ) : SolidityBase.IntBase(value, 80) { 494 | companion object { 495 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 496 | Int80(it) }) 497 | } 498 | } 499 | 500 | data class Int88( 501 | val value: BigInteger 502 | ) : SolidityBase.IntBase(value, 88) { 503 | companion object { 504 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 505 | Int88(it) }) 506 | } 507 | } 508 | 509 | data class Int96( 510 | val value: BigInteger 511 | ) : SolidityBase.IntBase(value, 96) { 512 | companion object { 513 | val DECODER: SolidityBase.IntBase.Decoder = SolidityBase.IntBase.Decoder({ 514 | Int96(it) }) 515 | } 516 | } 517 | 518 | data class Int104( 519 | val value: BigInteger 520 | ) : SolidityBase.IntBase(value, 104) { 521 | companion object { 522 | val DECODER: SolidityBase.IntBase.Decoder = 523 | SolidityBase.IntBase.Decoder({ Int104(it) }) 524 | } 525 | } 526 | 527 | data class Int112( 528 | val value: BigInteger 529 | ) : SolidityBase.IntBase(value, 112) { 530 | companion object { 531 | val DECODER: SolidityBase.IntBase.Decoder = 532 | SolidityBase.IntBase.Decoder({ Int112(it) }) 533 | } 534 | } 535 | 536 | data class Int120( 537 | val value: BigInteger 538 | ) : SolidityBase.IntBase(value, 120) { 539 | companion object { 540 | val DECODER: SolidityBase.IntBase.Decoder = 541 | SolidityBase.IntBase.Decoder({ Int120(it) }) 542 | } 543 | } 544 | 545 | data class Int128( 546 | val value: BigInteger 547 | ) : SolidityBase.IntBase(value, 128) { 548 | companion object { 549 | val DECODER: SolidityBase.IntBase.Decoder = 550 | SolidityBase.IntBase.Decoder({ Int128(it) }) 551 | } 552 | } 553 | 554 | data class Int136( 555 | val value: BigInteger 556 | ) : SolidityBase.IntBase(value, 136) { 557 | companion object { 558 | val DECODER: SolidityBase.IntBase.Decoder = 559 | SolidityBase.IntBase.Decoder({ Int136(it) }) 560 | } 561 | } 562 | 563 | data class Int144( 564 | val value: BigInteger 565 | ) : SolidityBase.IntBase(value, 144) { 566 | companion object { 567 | val DECODER: SolidityBase.IntBase.Decoder = 568 | SolidityBase.IntBase.Decoder({ Int144(it) }) 569 | } 570 | } 571 | 572 | data class Int152( 573 | val value: BigInteger 574 | ) : SolidityBase.IntBase(value, 152) { 575 | companion object { 576 | val DECODER: SolidityBase.IntBase.Decoder = 577 | SolidityBase.IntBase.Decoder({ Int152(it) }) 578 | } 579 | } 580 | 581 | data class Int160( 582 | val value: BigInteger 583 | ) : SolidityBase.IntBase(value, 160) { 584 | companion object { 585 | val DECODER: SolidityBase.IntBase.Decoder = 586 | SolidityBase.IntBase.Decoder({ Int160(it) }) 587 | } 588 | } 589 | 590 | data class Int168( 591 | val value: BigInteger 592 | ) : SolidityBase.IntBase(value, 168) { 593 | companion object { 594 | val DECODER: SolidityBase.IntBase.Decoder = 595 | SolidityBase.IntBase.Decoder({ Int168(it) }) 596 | } 597 | } 598 | 599 | data class Int176( 600 | val value: BigInteger 601 | ) : SolidityBase.IntBase(value, 176) { 602 | companion object { 603 | val DECODER: SolidityBase.IntBase.Decoder = 604 | SolidityBase.IntBase.Decoder({ Int176(it) }) 605 | } 606 | } 607 | 608 | data class Int184( 609 | val value: BigInteger 610 | ) : SolidityBase.IntBase(value, 184) { 611 | companion object { 612 | val DECODER: SolidityBase.IntBase.Decoder = 613 | SolidityBase.IntBase.Decoder({ Int184(it) }) 614 | } 615 | } 616 | 617 | data class Int192( 618 | val value: BigInteger 619 | ) : SolidityBase.IntBase(value, 192) { 620 | companion object { 621 | val DECODER: SolidityBase.IntBase.Decoder = 622 | SolidityBase.IntBase.Decoder({ Int192(it) }) 623 | } 624 | } 625 | 626 | data class Int200( 627 | val value: BigInteger 628 | ) : SolidityBase.IntBase(value, 200) { 629 | companion object { 630 | val DECODER: SolidityBase.IntBase.Decoder = 631 | SolidityBase.IntBase.Decoder({ Int200(it) }) 632 | } 633 | } 634 | 635 | data class Int208( 636 | val value: BigInteger 637 | ) : SolidityBase.IntBase(value, 208) { 638 | companion object { 639 | val DECODER: SolidityBase.IntBase.Decoder = 640 | SolidityBase.IntBase.Decoder({ Int208(it) }) 641 | } 642 | } 643 | 644 | data class Int216( 645 | val value: BigInteger 646 | ) : SolidityBase.IntBase(value, 216) { 647 | companion object { 648 | val DECODER: SolidityBase.IntBase.Decoder = 649 | SolidityBase.IntBase.Decoder({ Int216(it) }) 650 | } 651 | } 652 | 653 | data class Int224( 654 | val value: BigInteger 655 | ) : SolidityBase.IntBase(value, 224) { 656 | companion object { 657 | val DECODER: SolidityBase.IntBase.Decoder = 658 | SolidityBase.IntBase.Decoder({ Int224(it) }) 659 | } 660 | } 661 | 662 | data class Int232( 663 | val value: BigInteger 664 | ) : SolidityBase.IntBase(value, 232) { 665 | companion object { 666 | val DECODER: SolidityBase.IntBase.Decoder = 667 | SolidityBase.IntBase.Decoder({ Int232(it) }) 668 | } 669 | } 670 | 671 | data class Int240( 672 | val value: BigInteger 673 | ) : SolidityBase.IntBase(value, 240) { 674 | companion object { 675 | val DECODER: SolidityBase.IntBase.Decoder = 676 | SolidityBase.IntBase.Decoder({ Int240(it) }) 677 | } 678 | } 679 | 680 | data class Int248( 681 | val value: BigInteger 682 | ) : SolidityBase.IntBase(value, 248) { 683 | companion object { 684 | val DECODER: SolidityBase.IntBase.Decoder = 685 | SolidityBase.IntBase.Decoder({ Int248(it) }) 686 | } 687 | } 688 | 689 | data class Int256( 690 | val value: BigInteger 691 | ) : SolidityBase.IntBase(value, 256) { 692 | companion object { 693 | val DECODER: SolidityBase.IntBase.Decoder = 694 | SolidityBase.IntBase.Decoder({ Int256(it) }) 695 | } 696 | } 697 | 698 | class Bytes1( 699 | val bytes: ByteArray 700 | ) : SolidityBase.StaticBytes(bytes, 1) { 701 | companion object { 702 | val DECODER: SolidityBase.StaticBytes.Decoder = 703 | SolidityBase.StaticBytes.Decoder({ Bytes1(it) }, 1) 704 | } 705 | } 706 | 707 | class Bytes2( 708 | val bytes: ByteArray 709 | ) : SolidityBase.StaticBytes(bytes, 2) { 710 | companion object { 711 | val DECODER: SolidityBase.StaticBytes.Decoder = 712 | SolidityBase.StaticBytes.Decoder({ Bytes2(it) }, 2) 713 | } 714 | } 715 | 716 | class Bytes3( 717 | val bytes: ByteArray 718 | ) : SolidityBase.StaticBytes(bytes, 3) { 719 | companion object { 720 | val DECODER: SolidityBase.StaticBytes.Decoder = 721 | SolidityBase.StaticBytes.Decoder({ Bytes3(it) }, 3) 722 | } 723 | } 724 | 725 | class Bytes4( 726 | val bytes: ByteArray 727 | ) : SolidityBase.StaticBytes(bytes, 4) { 728 | companion object { 729 | val DECODER: SolidityBase.StaticBytes.Decoder = 730 | SolidityBase.StaticBytes.Decoder({ Bytes4(it) }, 4) 731 | } 732 | } 733 | 734 | class Bytes5( 735 | val bytes: ByteArray 736 | ) : SolidityBase.StaticBytes(bytes, 5) { 737 | companion object { 738 | val DECODER: SolidityBase.StaticBytes.Decoder = 739 | SolidityBase.StaticBytes.Decoder({ Bytes5(it) }, 5) 740 | } 741 | } 742 | 743 | class Bytes6( 744 | val bytes: ByteArray 745 | ) : SolidityBase.StaticBytes(bytes, 6) { 746 | companion object { 747 | val DECODER: SolidityBase.StaticBytes.Decoder = 748 | SolidityBase.StaticBytes.Decoder({ Bytes6(it) }, 6) 749 | } 750 | } 751 | 752 | class Bytes7( 753 | val bytes: ByteArray 754 | ) : SolidityBase.StaticBytes(bytes, 7) { 755 | companion object { 756 | val DECODER: SolidityBase.StaticBytes.Decoder = 757 | SolidityBase.StaticBytes.Decoder({ Bytes7(it) }, 7) 758 | } 759 | } 760 | 761 | class Bytes8( 762 | val bytes: ByteArray 763 | ) : SolidityBase.StaticBytes(bytes, 8) { 764 | companion object { 765 | val DECODER: SolidityBase.StaticBytes.Decoder = 766 | SolidityBase.StaticBytes.Decoder({ Bytes8(it) }, 8) 767 | } 768 | } 769 | 770 | class Bytes9( 771 | val bytes: ByteArray 772 | ) : SolidityBase.StaticBytes(bytes, 9) { 773 | companion object { 774 | val DECODER: SolidityBase.StaticBytes.Decoder = 775 | SolidityBase.StaticBytes.Decoder({ Bytes9(it) }, 9) 776 | } 777 | } 778 | 779 | class Bytes10( 780 | val bytes: ByteArray 781 | ) : SolidityBase.StaticBytes(bytes, 10) { 782 | companion object { 783 | val DECODER: SolidityBase.StaticBytes.Decoder = 784 | SolidityBase.StaticBytes.Decoder({ Bytes10(it) }, 10) 785 | } 786 | } 787 | 788 | class Bytes11( 789 | val bytes: ByteArray 790 | ) : SolidityBase.StaticBytes(bytes, 11) { 791 | companion object { 792 | val DECODER: SolidityBase.StaticBytes.Decoder = 793 | SolidityBase.StaticBytes.Decoder({ Bytes11(it) }, 11) 794 | } 795 | } 796 | 797 | class Bytes12( 798 | val bytes: ByteArray 799 | ) : SolidityBase.StaticBytes(bytes, 12) { 800 | companion object { 801 | val DECODER: SolidityBase.StaticBytes.Decoder = 802 | SolidityBase.StaticBytes.Decoder({ Bytes12(it) }, 12) 803 | } 804 | } 805 | 806 | class Bytes13( 807 | val bytes: ByteArray 808 | ) : SolidityBase.StaticBytes(bytes, 13) { 809 | companion object { 810 | val DECODER: SolidityBase.StaticBytes.Decoder = 811 | SolidityBase.StaticBytes.Decoder({ Bytes13(it) }, 13) 812 | } 813 | } 814 | 815 | class Bytes14( 816 | val bytes: ByteArray 817 | ) : SolidityBase.StaticBytes(bytes, 14) { 818 | companion object { 819 | val DECODER: SolidityBase.StaticBytes.Decoder = 820 | SolidityBase.StaticBytes.Decoder({ Bytes14(it) }, 14) 821 | } 822 | } 823 | 824 | class Bytes15( 825 | val bytes: ByteArray 826 | ) : SolidityBase.StaticBytes(bytes, 15) { 827 | companion object { 828 | val DECODER: SolidityBase.StaticBytes.Decoder = 829 | SolidityBase.StaticBytes.Decoder({ Bytes15(it) }, 15) 830 | } 831 | } 832 | 833 | class Bytes16( 834 | val bytes: ByteArray 835 | ) : SolidityBase.StaticBytes(bytes, 16) { 836 | companion object { 837 | val DECODER: SolidityBase.StaticBytes.Decoder = 838 | SolidityBase.StaticBytes.Decoder({ Bytes16(it) }, 16) 839 | } 840 | } 841 | 842 | class Bytes17( 843 | val bytes: ByteArray 844 | ) : SolidityBase.StaticBytes(bytes, 17) { 845 | companion object { 846 | val DECODER: SolidityBase.StaticBytes.Decoder = 847 | SolidityBase.StaticBytes.Decoder({ Bytes17(it) }, 17) 848 | } 849 | } 850 | 851 | class Bytes18( 852 | val bytes: ByteArray 853 | ) : SolidityBase.StaticBytes(bytes, 18) { 854 | companion object { 855 | val DECODER: SolidityBase.StaticBytes.Decoder = 856 | SolidityBase.StaticBytes.Decoder({ Bytes18(it) }, 18) 857 | } 858 | } 859 | 860 | class Bytes19( 861 | val bytes: ByteArray 862 | ) : SolidityBase.StaticBytes(bytes, 19) { 863 | companion object { 864 | val DECODER: SolidityBase.StaticBytes.Decoder = 865 | SolidityBase.StaticBytes.Decoder({ Bytes19(it) }, 19) 866 | } 867 | } 868 | 869 | class Bytes20( 870 | val bytes: ByteArray 871 | ) : SolidityBase.StaticBytes(bytes, 20) { 872 | companion object { 873 | val DECODER: SolidityBase.StaticBytes.Decoder = 874 | SolidityBase.StaticBytes.Decoder({ Bytes20(it) }, 20) 875 | } 876 | } 877 | 878 | class Bytes21( 879 | val bytes: ByteArray 880 | ) : SolidityBase.StaticBytes(bytes, 21) { 881 | companion object { 882 | val DECODER: SolidityBase.StaticBytes.Decoder = 883 | SolidityBase.StaticBytes.Decoder({ Bytes21(it) }, 21) 884 | } 885 | } 886 | 887 | class Bytes22( 888 | val bytes: ByteArray 889 | ) : SolidityBase.StaticBytes(bytes, 22) { 890 | companion object { 891 | val DECODER: SolidityBase.StaticBytes.Decoder = 892 | SolidityBase.StaticBytes.Decoder({ Bytes22(it) }, 22) 893 | } 894 | } 895 | 896 | class Bytes23( 897 | val bytes: ByteArray 898 | ) : SolidityBase.StaticBytes(bytes, 23) { 899 | companion object { 900 | val DECODER: SolidityBase.StaticBytes.Decoder = 901 | SolidityBase.StaticBytes.Decoder({ Bytes23(it) }, 23) 902 | } 903 | } 904 | 905 | class Bytes24( 906 | val bytes: ByteArray 907 | ) : SolidityBase.StaticBytes(bytes, 24) { 908 | companion object { 909 | val DECODER: SolidityBase.StaticBytes.Decoder = 910 | SolidityBase.StaticBytes.Decoder({ Bytes24(it) }, 24) 911 | } 912 | } 913 | 914 | class Bytes25( 915 | val bytes: ByteArray 916 | ) : SolidityBase.StaticBytes(bytes, 25) { 917 | companion object { 918 | val DECODER: SolidityBase.StaticBytes.Decoder = 919 | SolidityBase.StaticBytes.Decoder({ Bytes25(it) }, 25) 920 | } 921 | } 922 | 923 | class Bytes26( 924 | val bytes: ByteArray 925 | ) : SolidityBase.StaticBytes(bytes, 26) { 926 | companion object { 927 | val DECODER: SolidityBase.StaticBytes.Decoder = 928 | SolidityBase.StaticBytes.Decoder({ Bytes26(it) }, 26) 929 | } 930 | } 931 | 932 | class Bytes27( 933 | val bytes: ByteArray 934 | ) : SolidityBase.StaticBytes(bytes, 27) { 935 | companion object { 936 | val DECODER: SolidityBase.StaticBytes.Decoder = 937 | SolidityBase.StaticBytes.Decoder({ Bytes27(it) }, 27) 938 | } 939 | } 940 | 941 | class Bytes28( 942 | val bytes: ByteArray 943 | ) : SolidityBase.StaticBytes(bytes, 28) { 944 | companion object { 945 | val DECODER: SolidityBase.StaticBytes.Decoder = 946 | SolidityBase.StaticBytes.Decoder({ Bytes28(it) }, 28) 947 | } 948 | } 949 | 950 | class Bytes29( 951 | val bytes: ByteArray 952 | ) : SolidityBase.StaticBytes(bytes, 29) { 953 | companion object { 954 | val DECODER: SolidityBase.StaticBytes.Decoder = 955 | SolidityBase.StaticBytes.Decoder({ Bytes29(it) }, 29) 956 | } 957 | } 958 | 959 | class Bytes30( 960 | val bytes: ByteArray 961 | ) : SolidityBase.StaticBytes(bytes, 30) { 962 | companion object { 963 | val DECODER: SolidityBase.StaticBytes.Decoder = 964 | SolidityBase.StaticBytes.Decoder({ Bytes30(it) }, 30) 965 | } 966 | } 967 | 968 | class Bytes31( 969 | val bytes: ByteArray 970 | ) : SolidityBase.StaticBytes(bytes, 31) { 971 | companion object { 972 | val DECODER: SolidityBase.StaticBytes.Decoder = 973 | SolidityBase.StaticBytes.Decoder({ Bytes31(it) }, 31) 974 | } 975 | } 976 | 977 | class Bytes32( 978 | val bytes: ByteArray 979 | ) : SolidityBase.StaticBytes(bytes, 32) { 980 | companion object { 981 | val DECODER: SolidityBase.StaticBytes.Decoder = 982 | SolidityBase.StaticBytes.Decoder({ Bytes32(it) }, 32) 983 | } 984 | } 985 | 986 | data class Address( 987 | val value: BigInteger 988 | ) : SolidityBase.UIntBase(value, 160) { 989 | companion object { 990 | val DECODER: SolidityBase.UIntBase.Decoder
= 991 | SolidityBase.UIntBase.Decoder
({ Address(it) }) 992 | } 993 | } 994 | 995 | data class Bool( 996 | val value: Boolean 997 | ) : SolidityBase.UIntBase(if (value) BigInteger.ONE else BigInteger.ZERO, 8) { 998 | class Decoder : SolidityBase.TypeDecoder { 999 | override fun isDynamic(): Boolean = false 1000 | override fun decode(source: SolidityBase.PartitionData): Bool = 1001 | Bool(SolidityBase.decodeBool(source.consume())) 1002 | } 1003 | 1004 | companion object { 1005 | val DECODER: Decoder = Decoder() 1006 | } 1007 | } 1008 | 1009 | open class Bytes( 1010 | val items: ByteArray 1011 | ) : SolidityBase.DynamicType { 1012 | init { 1013 | if (BigInteger(items.size.toString(10)) > BigInteger.valueOf(2).pow(256)) throw 1014 | Exception() 1015 | } 1016 | 1017 | override fun encode(): kotlin.String { 1018 | val parts = encodeParts() 1019 | return parts.static + parts.dynamic 1020 | } 1021 | 1022 | private fun encodeParts(): SolidityBase.DynamicType.Parts { 1023 | val length = items.size.toString(16).padStart(64, '0') 1024 | val contents = items.toHex().padEndMultiple(64, '0') 1025 | return SolidityBase.DynamicType.Parts(length, contents) 1026 | } 1027 | 1028 | override fun encodePacked(): kotlin.String = items.toHex() 1029 | 1030 | class Decoder : SolidityBase.TypeDecoder { 1031 | override fun isDynamic(): Boolean = true 1032 | override fun decode(source: SolidityBase.PartitionData): Bytes = 1033 | Bytes(SolidityBase.decodeBytes(source)) 1034 | } 1035 | 1036 | companion object { 1037 | val DECODER: Decoder = Decoder() 1038 | } 1039 | } 1040 | 1041 | data class String( 1042 | val value: kotlin.String 1043 | ) : Bytes(value.toByteArray()) { 1044 | class Decoder : SolidityBase.TypeDecoder { 1045 | override fun isDynamic(): Boolean = true 1046 | override fun decode(source: SolidityBase.PartitionData): String = 1047 | String(SolidityBase.decodeString(source)) 1048 | } 1049 | 1050 | companion object { 1051 | val DECODER: Decoder = Decoder() 1052 | } 1053 | } 1054 | } 1055 | -------------------------------------------------------------------------------- /bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/SolidityBase.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.model 2 | 3 | import pm.gnosis.exceptions.InvalidBitLengthException 4 | import pm.gnosis.utils.hexToByteArray 5 | import pm.gnosis.utils.padStartMultiple 6 | import pm.gnosis.utils.toHex 7 | import java.math.BigDecimal 8 | import java.math.BigInteger 9 | import java.nio.charset.Charset 10 | import kotlin.collections.ArrayList 11 | 12 | object SolidityBase { 13 | const val BYTES_PAD = 32 14 | const val PADDED_HEX_LENGTH = BYTES_PAD * 2 15 | 16 | val dynamicTypes: List = listOf("bytes", "string") 17 | 18 | interface Type { 19 | fun encode(): String 20 | fun encodePacked(): String 21 | } 22 | 23 | interface StaticType : Type 24 | 25 | interface DynamicType : Type { 26 | data class Parts(val static: String, val dynamic: String) 27 | } 28 | 29 | abstract class Collection(val items: List) : Type { 30 | 31 | override fun encodePacked(): String { 32 | if (items.any { isDynamic(it) }) throw IllegalArgumentException("Cannot encode dynamic items packed!") 33 | return encodeTuple(items) 34 | } 35 | 36 | override fun equals(other: Any?): Boolean { 37 | if (this === other) return true 38 | if (javaClass != other?.javaClass) return false 39 | 40 | other as Collection<*> 41 | 42 | if (items != other.items) return false 43 | 44 | return true 45 | } 46 | 47 | override fun hashCode(): Int { 48 | return items.hashCode() 49 | } 50 | 51 | abstract fun isDynamic(): Boolean 52 | } 53 | 54 | abstract class UIntBase(private val value: BigInteger, private val bitLength: Int) : StaticType { 55 | init { 56 | when { 57 | bitLength % 8 != 0 -> throw InvalidBitLengthException.NOT_MULTIPLE_OF_EIGHT 58 | value.bitLength() > bitLength -> throw InvalidBitLengthException.BIG_VALUE 59 | value.signum() == -1 -> throw IllegalArgumentException("UInt doesn't allow negative numbers") 60 | } 61 | } 62 | 63 | override fun encode(): String { 64 | val string = value.toString(16) 65 | return string.padStartMultiple(PADDED_HEX_LENGTH, '0') 66 | } 67 | 68 | override fun encodePacked(): String { 69 | val string = value.toString(16) 70 | return string.padStart(bitLength / 8 * 2, '0') 71 | } 72 | 73 | override fun equals(other: Any?): Boolean { 74 | if (this === other) return true 75 | if (javaClass != other?.javaClass) return false 76 | 77 | other as UIntBase 78 | 79 | if (value != other.value) return false 80 | 81 | return true 82 | } 83 | 84 | override fun hashCode(): Int { 85 | return value.hashCode() 86 | } 87 | 88 | open class Decoder(private val factory: (BigInteger) -> T) : TypeDecoder { 89 | override fun isDynamic(): Boolean { 90 | return false 91 | } 92 | 93 | override fun decode(source: PartitionData): T { 94 | return factory.invoke(decodeUInt(source.consume())) 95 | } 96 | } 97 | 98 | } 99 | 100 | abstract class IntBase(private val value: BigInteger, private val bitLength: Int) : StaticType { 101 | init { 102 | if (bitLength % 8 != 0) throw InvalidBitLengthException.NOT_MULTIPLE_OF_EIGHT 103 | val min = BigInteger.valueOf(2).pow(bitLength - 1).negate() 104 | val max = BigInteger.valueOf(2).pow(bitLength - 1) - BigInteger.ONE 105 | 106 | if (value < min || value > max) throw IllegalArgumentException("Value is not within bit range [$min, $max]") 107 | } 108 | 109 | private fun encodeWithPadding(paddingLength: Int): String { 110 | return if (value.signum() == -1) { 111 | val bits = value.toString(2).removePrefix("-").padStart(bitLength, '0') 112 | val x = bits.map { if (it == '0') '1' else '0' }.joinToString("") 113 | BigInteger(x, 2).add(BigInteger.ONE).toString(16).padStartMultiple(paddingLength, 'f') 114 | } else { 115 | value.toString(16).padStartMultiple(paddingLength, '0') 116 | } 117 | 118 | } 119 | 120 | override fun encode(): String = encodeWithPadding(PADDED_HEX_LENGTH) 121 | 122 | override fun encodePacked(): String = encodeWithPadding(bitLength / 8 * 2) 123 | 124 | override fun equals(other: Any?): Boolean { 125 | if (this === other) return true 126 | if (javaClass != other?.javaClass) return false 127 | 128 | other as IntBase 129 | 130 | if (value != other.value) return false 131 | 132 | return true 133 | } 134 | 135 | override fun hashCode(): Int { 136 | return value.hashCode() 137 | } 138 | 139 | open class Decoder(private val factory: (BigInteger) -> T) : TypeDecoder { 140 | override fun isDynamic(): Boolean { 141 | return false 142 | } 143 | 144 | override fun decode(source: PartitionData): T { 145 | return factory.invoke(decodeInt(source.consume())) 146 | } 147 | } 148 | } 149 | 150 | abstract class StaticBytes(val byteArray: ByteArray, nBytes: Int) : StaticType { 151 | init { 152 | if (byteArray.size > nBytes) throw IllegalArgumentException("Byte array has ${byteArray.size} bytes. It should have no more than $nBytes bytes.") 153 | } 154 | 155 | override fun encode(): String { 156 | return byteArray.toHex().padEnd(PADDED_HEX_LENGTH, '0') 157 | } 158 | 159 | override fun encodePacked(): String { 160 | return byteArray.toHex() 161 | } 162 | 163 | override fun equals(other: Any?): Boolean { 164 | if (this === other) return true 165 | if (javaClass != other?.javaClass) return false 166 | 167 | other as StaticBytes 168 | 169 | return byteArray.contentEquals(other.byteArray) 170 | } 171 | 172 | override fun hashCode(): Int { 173 | return byteArray.hashCode() 174 | } 175 | 176 | open class Decoder(private val factory: (ByteArray) -> T, val nBytes: Int) : TypeDecoder { 177 | override fun isDynamic(): Boolean { 178 | return false 179 | } 180 | 181 | override fun decode(source: PartitionData): T { 182 | return factory.invoke(decodeStaticBytes(source.consume(), nBytes)) 183 | } 184 | } 185 | } 186 | 187 | class PartitionData(data: String, private val offset: Int = 0) { 188 | private val cleanData = data.removePrefix("0x") 189 | private var index: Int = 0 190 | 191 | fun consume(): String { 192 | return cleanData.substring(offset + index * 64, offset + (index + 1) * 64).apply { 193 | index++ 194 | } 195 | } 196 | 197 | fun reset() { 198 | index = 0 199 | } 200 | 201 | fun subData() = PartitionData(cleanData, offset + index * 64) 202 | 203 | fun subData(bytesOffset: Int) = PartitionData(cleanData, offset + bytesOffset * 2) 204 | 205 | companion object { 206 | fun of(data: String): PartitionData { 207 | return PartitionData(data) 208 | } 209 | } 210 | } 211 | 212 | interface TypeDecoder { 213 | 214 | fun isDynamic(): Boolean 215 | 216 | fun decode(source: PartitionData): T 217 | } 218 | 219 | fun decodeList(source: PartitionData, capacity: Int, itemDecoder: TypeDecoder): List { 220 | return (0 until capacity).map { 221 | if (itemDecoder.isDynamic()) { 222 | // Get offset 223 | val offset = BigInteger(source.consume(), 16).intValueExact() 224 | // Decode dynamic data at offset 225 | itemDecoder.decode(source.subData(offset)) 226 | } else { 227 | itemDecoder.decode(source) 228 | } 229 | } 230 | } 231 | 232 | abstract class Array(items: List, val capacity: Int) : Collection(checkCapacity(items, capacity)) { 233 | override fun encode(): String { 234 | if (items.size != capacity) { 235 | throw IllegalStateException("Capacity mismatch!") 236 | } 237 | // Encode the fixed array as a tuple where all parts are of the same time 238 | return encodeTuple(items) 239 | } 240 | 241 | override fun isDynamic(): Boolean { 242 | if (capacity == 0) { 243 | return false 244 | } 245 | return items.any { isDynamic(it) } 246 | } 247 | 248 | companion object { 249 | private fun checkCapacity(items: List, capacity: Int): List { 250 | if (items.size != capacity) { 251 | throw IllegalStateException("Array is of wrong capacity!") 252 | } 253 | return ArrayList(items) 254 | } 255 | 256 | } 257 | } 258 | 259 | class Vector(items: List) : Collection(items), DynamicType { 260 | 261 | override fun encode(): String { 262 | val parts = encodeParts() 263 | return parts.static + parts.dynamic 264 | } 265 | 266 | private fun encodeParts(): DynamicType.Parts { 267 | val length = items.size.toString(16).padStart(PADDED_HEX_LENGTH, '0') 268 | // Encode the dynamic array as the length and a tuple where all parts are of the same time 269 | return DynamicType.Parts(length, encodeTuple(items)) 270 | } 271 | 272 | override fun isDynamic(): Boolean { 273 | return true 274 | } 275 | 276 | class Decoder(private val itemDecoder: TypeDecoder) : TypeDecoder> { 277 | override fun isDynamic(): Boolean { 278 | return true 279 | } 280 | 281 | override fun decode(source: PartitionData): Vector { 282 | val capacity = decodeUInt(source.consume()).toInt() 283 | return Vector(decodeList(source.subData(), capacity, itemDecoder)) 284 | } 285 | } 286 | } 287 | 288 | @Suppress("unused") 289 | fun encodeFunctionArguments(vararg args: Type): String { 290 | return encodeTuple(args.toList()) 291 | } 292 | 293 | @Suppress("unused") 294 | fun encodeTuple(parts: List): String { 295 | val encodedParts = ArrayList>() 296 | 297 | var sizeOfStaticBlock = 0 298 | parts.forEach { 299 | val encoded = it.encode() 300 | if (isDynamic(it)) { 301 | encodedParts += Pair(encoded, true) 302 | // Add length of an address to static block size 303 | sizeOfStaticBlock += BYTES_PAD 304 | } else { 305 | encodedParts += Pair(encoded, false) 306 | sizeOfStaticBlock += encoded.length / 2 307 | } 308 | } 309 | 310 | val staticArgsBuilder = StringBuilder() 311 | val dynamicArgsBuilder = StringBuilder() 312 | encodedParts.forEach { (encoded, dynamic) -> 313 | if (dynamic) { 314 | val location = sizeOfStaticBlock + dynamicArgsBuilder.length / 2 315 | staticArgsBuilder.append(location.toString(16).padStart(PADDED_HEX_LENGTH, '0')) 316 | dynamicArgsBuilder.append(encoded) 317 | } else { 318 | staticArgsBuilder.append(encoded) 319 | } 320 | } 321 | 322 | return staticArgsBuilder.toString() + dynamicArgsBuilder.toString() 323 | } 324 | 325 | private fun isDynamic(type: Type): Boolean { 326 | if (type is DynamicType) { 327 | return true 328 | } 329 | return (type as? Collection<*>)?.isDynamic() ?: false || (type is Vector<*>) 330 | } 331 | 332 | fun decodeUInt(data: String): BigInteger { 333 | return BigInteger(data, 16) 334 | } 335 | 336 | fun decodeBool(data: String): Boolean { 337 | val value = BigInteger(data) 338 | return when (value) { 339 | BigInteger.ZERO -> false 340 | BigInteger.ONE -> true 341 | else -> throw IllegalArgumentException("${value.toString(10)} is not a valid boolean representation. It should either be 0 (false) or 1 (true)") 342 | } 343 | } 344 | 345 | fun decodeInt(data: String): BigInteger { 346 | val value = BigInteger(data, 16) 347 | if (data.startsWith("8") || 348 | data.startsWith("9") || 349 | data.startsWith("A", true) || 350 | data.startsWith("B", true) || 351 | data.startsWith("C", true) || 352 | data.startsWith("D", true) || 353 | data.startsWith("E", true) || 354 | data.startsWith("F", true)) { 355 | val x = value.toString(2).map { if (it == '0') '1' else '0' }.joinToString("") 356 | return BigInteger(x, 2).add(BigInteger.ONE).multiply(BigInteger("-1")) 357 | } 358 | return value 359 | } 360 | 361 | fun decodeStaticBytes(data: String, nBytes: kotlin.Int): ByteArray { 362 | return data.substring(0, nBytes * 2).hexToByteArray() 363 | } 364 | 365 | fun decodeBytes(source: PartitionData): ByteArray { 366 | val contentSize = BigDecimal(BigInteger(source.consume(), 16)).intValueExact() * 2 367 | if (contentSize == 0) return kotlin.ByteArray(0) 368 | val sb = StringBuilder() 369 | while (sb.length < contentSize) { 370 | sb.append(source.consume()) 371 | } 372 | return sb.substring(0, contentSize).hexToByteArray() 373 | } 374 | 375 | fun decodeString(source: PartitionData) = 376 | decodeBytes(source).toString(Charset.forName("UTF-8")) 377 | 378 | @Deprecated("Deprecated for decodeList") 379 | fun decodeArray(data: String, itemDecoder: (String) -> T): List { 380 | val params = PartitionData.of(data) 381 | val contentSize = BigInteger(params.consume()).intValueExact() 382 | if (contentSize == 0) return emptyList() 383 | return (0 until contentSize).map { itemDecoder(params.consume()) } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /bivrost-solidity-types/src/main/kotlin/pm/gnosis/utils/DataTypeExtensions.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis.utils 2 | 3 | import java.math.BigInteger 4 | import kotlin.experimental.and 5 | 6 | fun String.padStartMultiple(multiple: Int, padChar: Char = ' ') = 7 | this.padStart(if (this.length % multiple != 0) this.length + multiple - this.length % multiple else 0, padChar) 8 | 9 | fun String.padEndMultiple(multiple: Int, padChar: Char = ' ') = 10 | this.padEnd(if (this.length % multiple != 0) this.length + multiple - this.length % multiple else 0, padChar) 11 | 12 | 13 | private val hexArray = "0123456789abcdef".toCharArray() 14 | 15 | fun ByteArray.toHex(): String { 16 | val hexChars = CharArray(this.size * 2) 17 | for (j in this.indices) { 18 | val v = ((this[j] and 0xFF.toByte()).toInt() + 256) % 256 19 | hexChars[j * 2] = hexArray[v ushr 4] 20 | hexChars[j * 2 + 1] = hexArray[v and 0x0F] 21 | } 22 | return String(hexChars) 23 | } 24 | 25 | fun String.hexToByteArray(): ByteArray { 26 | val s = this.removePrefix("0x") 27 | val len = s.length 28 | val data = ByteArray(len / 2) 29 | var i = 0 30 | while (i < len) { 31 | data[i / 2] = ((Character.digit(s[i], 16) shl 4) + Character.digit(s[i + 1], 16)).toByte() 32 | i += 2 33 | } 34 | return data 35 | } 36 | 37 | // Compatibility method for pre Java8 38 | object BigIntegerUtils { 39 | fun exact(bigInteger: BigInteger): Int = 40 | if (bigInteger.bitLength() <= 31) bigInteger.toInt() 41 | else throw ArithmeticException("BigInteger out of int range") 42 | } 43 | -------------------------------------------------------------------------------- /bivrost-utils/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'kotlin' 3 | apply plugin: 'maven' 4 | 5 | dependencies { 6 | implementation project(':bivrost-solidity-types') 7 | 8 | implementation "org.jetbrains.kotlin:kotlin-stdlib" 9 | 10 | implementation "com.squareup:kotlinpoet:${versions.kotlinPoet}" 11 | implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.61' 12 | 13 | testImplementation group: 'junit', name: 'junit', version: '4.12' 14 | } 15 | 16 | uploadArchives { 17 | repositories { 18 | mavenDeployer { 19 | repository(url: uri('../repo')) 20 | } 21 | } 22 | } 23 | 24 | task sourcesJar(type: Jar, dependsOn:classes) { 25 | from sourceSets.main.allSource 26 | classifier = 'sources' 27 | } 28 | 29 | artifacts { 30 | archives sourcesJar 31 | } 32 | -------------------------------------------------------------------------------- /bivrost-utils/src/main/kotlin/pm/gnosis/GeneratorUtils.kt: -------------------------------------------------------------------------------- 1 | package pm.gnosis 2 | 3 | import com.squareup.kotlinpoet.* 4 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 5 | import pm.gnosis.model.SolidityBase 6 | 7 | object GeneratorUtils { 8 | fun generateDecoderCompanion(decoderTypeName: TypeName, decoderInit: CodeBlock) = TypeSpec.companionObjectBuilder() 9 | .addProperty( 10 | PropertySpec.builder("DECODER", decoderTypeName) 11 | .initializer(decoderInit) 12 | .build() 13 | ) 14 | .build() 15 | 16 | fun generateDecoder(name: String, decodeCode: CodeBlock, isDynamic: Boolean = true, paramName: String = "source") = 17 | generateDecoderBuilder(ClassName("", name), decodeCode, CodeBlock.of("return %L", isDynamic), paramName).build() 18 | 19 | fun generateDecoderBuilder(forClass: TypeName, decodeCode: CodeBlock, isDynamicBlock: CodeBlock, paramName: String = "source"): TypeSpec.Builder { 20 | return TypeSpec.classBuilder("Decoder") 21 | .addSuperinterface(SolidityBase.TypeDecoder::class.asClassName().parameterizedBy(forClass)) 22 | .addFunction(generateIsDynamicFunction(isDynamicBlock)) 23 | .addFunction(generateDecodeSourceFunction(decodeCode, forClass, paramName)) 24 | } 25 | 26 | fun generateDecodeSourceFunction(decodeCode: CodeBlock, returnType: TypeName, paramName: String) = 27 | FunSpec.builder("decode") 28 | .addParameter(paramName, SolidityBase.PartitionData::class) 29 | .addModifiers(KModifier.OVERRIDE) 30 | .returns(returnType) 31 | .addCode(decodeCode) 32 | .build() 33 | 34 | fun generateIsDynamicFunction(isDynamicBlock: CodeBlock): FunSpec = FunSpec.builder("isDynamic") 35 | .addModifiers(KModifier.OVERRIDE) 36 | .returns(Boolean::class) 37 | .addCode(isDynamicBlock) 38 | .build() 39 | } 40 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.versions = [ 3 | 'minSdk' : 14, 4 | 'compileSdk' : 28, 5 | 'buildTools' : '28.0.3', 6 | 7 | 'kotlin' : '1.3.21', 8 | 'kotlinPoet' : '1.4.4', 9 | 'moshi' : '1.8.0', 10 | 'androidPlugin': '3.3.0', 11 | ] 12 | 13 | ext.deps = [ 14 | android : [ 15 | 'runtime' : 'com.google.android:android:4.1.1.4', 16 | 'gradlePlugin': "com.android.tools.build:gradle:${versions.androidPlugin}", 17 | ] 18 | ] 19 | 20 | repositories { 21 | mavenCentral() 22 | maven { 23 | url 'https://maven.google.com' 24 | } 25 | maven { 26 | url "https://plugins.gradle.org/m2/" 27 | } 28 | } 29 | dependencies { 30 | classpath deps.android.gradlePlugin 31 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" 32 | } 33 | } 34 | 35 | subprojects { project -> 36 | group 'pm.gnosis' 37 | version getKey('LIBRARY_VERSION', '0.0.42') 38 | 39 | 40 | repositories { 41 | mavenCentral() 42 | maven { 43 | url 'https://maven.google.com' 44 | } 45 | maven { 46 | url "https://plugins.gradle.org/m2/" 47 | } 48 | } 49 | } 50 | 51 | static def getKey(String name, String defaultValue = null) { 52 | def value = System.getenv(name) 53 | if (value == null) { 54 | return defaultValue 55 | } 56 | return value 57 | } 58 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@gnosis.pm. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute to Bivrost - Kotlin 2 | 3 | #### **Did you find a bug?** 4 | 5 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues][issue_search] 6 | 7 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/gnosis/bivrost-kotlin/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. 8 | 9 | * Bugs will be labeled *bug*. See all bugs under [Bugs](https://github.com/gnosis/bivrost-kotlin/labels/bug). 10 | 11 | #### **Did you write a patch that fixes a bug?** 12 | 13 | * Open a new GitHub pull request with the patch. 14 | 15 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 16 | 17 | #### **Did you fix whitespace, format code, or make a purely cosmetic patch?** 18 | 19 | Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Bivrost-Kotlin will generally not be accepted. 20 | 21 | #### **Do you intend to add a new feature or change an existing one?** 22 | 23 | * You can suggest new features as a GitHub issue. **Ensure that there is not already a similar request** by searching on GitHub under [Issues][issue_search]. 24 | 25 | * Featured requests will be labeled *enhancement*. See all feature requests under [Feature requests](https://github.com/gnosis/bivrost-kotlin/labels/enhancement). 26 | 27 | #### **Do you have questions about the project?** 28 | 29 | * You can ask questions about this project as a GitHub issue. **Ensure that there is not already a similar question** by searching on GitHub under [Issues][issue_search]. 30 | 31 | * Questions will be labeled *question*. See all questions under [Questions](https://github.com/gnosis/bivrost-kotlin/labels/question). 32 | 33 | #### **Addition info** 34 | 35 | * Please make sure to follow our [Code of Conduct](CODE_OF_CONDUCT.md) 36 | 37 | * Looking for a Swift solution? See [Bivrost Swift](https://github.com/gnosis/bivrost-swift) 38 | 39 | * Want to chat? Contact us on [Gitter](https://gitter.im/gnosis/Bivrost) 40 | 41 | Thanks! :rocket: :tada: 42 | 43 | Gnosis Team 44 | 45 | [issue_search]: https://github.com/gnosis/bivrost-kotlin/issues 46 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected behavior 2 | 3 | ### Actual behavior 4 | 5 | ### Steps to reproduce the behavior 6 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixed # . 2 | 3 | Changes proposed in this pull request: 4 | - 5 | - 6 | - 7 | 8 | @gnosis/mobile-devs 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/5afe/bivrost-kotlin/22cf65d1a177bd1b87b3475370cebd80e3c3ec63/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 11 12:02:10 CET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /sample/app/abi/EtherToken.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "EtherToken", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [], 7 | "name": "name", 8 | "outputs": [ 9 | { 10 | "name": "", 11 | "type": "string" 12 | } 13 | ], 14 | "payable": false, 15 | "type": "function" 16 | }, 17 | { 18 | "constant": false, 19 | "inputs": [ 20 | { 21 | "name": "spender", 22 | "type": "address" 23 | }, 24 | { 25 | "name": "value", 26 | "type": "uint256" 27 | } 28 | ], 29 | "name": "approve", 30 | "outputs": [ 31 | { 32 | "name": "", 33 | "type": "bool" 34 | } 35 | ], 36 | "payable": false, 37 | "type": "function" 38 | }, 39 | { 40 | "constant": true, 41 | "inputs": [], 42 | "name": "totalSupply", 43 | "outputs": [ 44 | { 45 | "name": "", 46 | "type": "uint256" 47 | } 48 | ], 49 | "payable": false, 50 | "type": "function" 51 | }, 52 | { 53 | "constant": false, 54 | "inputs": [ 55 | { 56 | "name": "from", 57 | "type": "address" 58 | }, 59 | { 60 | "name": "to", 61 | "type": "address" 62 | }, 63 | { 64 | "name": "value", 65 | "type": "uint256" 66 | } 67 | ], 68 | "name": "transferFrom", 69 | "outputs": [ 70 | { 71 | "name": "", 72 | "type": "bool" 73 | } 74 | ], 75 | "payable": false, 76 | "type": "function" 77 | }, 78 | { 79 | "constant": false, 80 | "inputs": [ 81 | { 82 | "name": "value", 83 | "type": "uint256" 84 | } 85 | ], 86 | "name": "withdraw", 87 | "outputs": [], 88 | "payable": false, 89 | "type": "function" 90 | }, 91 | { 92 | "constant": true, 93 | "inputs": [], 94 | "name": "decimals", 95 | "outputs": [ 96 | { 97 | "name": "", 98 | "type": "uint8" 99 | } 100 | ], 101 | "payable": false, 102 | "type": "function" 103 | }, 104 | { 105 | "constant": true, 106 | "inputs": [ 107 | { 108 | "name": "owner", 109 | "type": "address" 110 | } 111 | ], 112 | "name": "balanceOf", 113 | "outputs": [ 114 | { 115 | "name": "", 116 | "type": "uint256" 117 | } 118 | ], 119 | "payable": false, 120 | "type": "function" 121 | }, 122 | { 123 | "constant": true, 124 | "inputs": [], 125 | "name": "symbol", 126 | "outputs": [ 127 | { 128 | "name": "", 129 | "type": "string" 130 | } 131 | ], 132 | "payable": false, 133 | "type": "function" 134 | }, 135 | { 136 | "constant": false, 137 | "inputs": [ 138 | { 139 | "name": "to", 140 | "type": "address" 141 | }, 142 | { 143 | "name": "value", 144 | "type": "uint256" 145 | } 146 | ], 147 | "name": "transfer", 148 | "outputs": [ 149 | { 150 | "name": "", 151 | "type": "bool" 152 | } 153 | ], 154 | "payable": false, 155 | "type": "function" 156 | }, 157 | { 158 | "constant": false, 159 | "inputs": [], 160 | "name": "deposit", 161 | "outputs": [], 162 | "payable": true, 163 | "type": "function" 164 | }, 165 | { 166 | "constant": true, 167 | "inputs": [ 168 | { 169 | "name": "owner", 170 | "type": "address" 171 | }, 172 | { 173 | "name": "spender", 174 | "type": "address" 175 | } 176 | ], 177 | "name": "allowance", 178 | "outputs": [ 179 | { 180 | "name": "", 181 | "type": "uint256" 182 | } 183 | ], 184 | "payable": false, 185 | "type": "function" 186 | }, 187 | { 188 | "inputs": [], 189 | "payable": false, 190 | "type": "constructor" 191 | }, 192 | { 193 | "anonymous": false, 194 | "inputs": [ 195 | { 196 | "indexed": true, 197 | "name": "sender", 198 | "type": "address" 199 | }, 200 | { 201 | "indexed": false, 202 | "name": "value", 203 | "type": "uint256" 204 | } 205 | ], 206 | "name": "Deposit", 207 | "type": "event" 208 | }, 209 | { 210 | "anonymous": false, 211 | "inputs": [ 212 | { 213 | "indexed": true, 214 | "name": "receiver", 215 | "type": "address" 216 | }, 217 | { 218 | "indexed": false, 219 | "name": "value", 220 | "type": "uint256" 221 | } 222 | ], 223 | "name": "Withdrawal", 224 | "type": "event" 225 | }, 226 | { 227 | "anonymous": false, 228 | "inputs": [ 229 | { 230 | "indexed": true, 231 | "name": "from", 232 | "type": "address" 233 | }, 234 | { 235 | "indexed": true, 236 | "name": "to", 237 | "type": "address" 238 | }, 239 | { 240 | "indexed": false, 241 | "name": "value", 242 | "type": "uint256" 243 | } 244 | ], 245 | "name": "Transfer", 246 | "type": "event" 247 | }, 248 | { 249 | "anonymous": false, 250 | "inputs": [ 251 | { 252 | "indexed": true, 253 | "name": "owner", 254 | "type": "address" 255 | }, 256 | { 257 | "indexed": true, 258 | "name": "spender", 259 | "type": "address" 260 | }, 261 | { 262 | "indexed": false, 263 | "name": "value", 264 | "type": "uint256" 265 | } 266 | ], 267 | "name": "Approval", 268 | "type": "event" 269 | } 270 | ], 271 | "unlinked_binary": "0x6060604052341561000f57600080fd5b5b6a084595161401484a0000006002555b5b610bb2806100306000396000f300606060405236156100935763ffffffff60e060020a60003504166306fdde038114610098578063095ea7b31461012357806318160ddd1461015957806323b872dd1461017e5780632e1a7d4d146101ba578063313ce567146101d257806370a08231146101fb57806395d89b411461022c578063a9059cbb146102b7578063d0e30db0146102ed578063dd62ed3e146102f7575b600080fd5b34156100a357600080fd5b6100ab61032e565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100e85780820151818401525b6020016100cf565b50505050905090810190601f1680156101155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561012e57600080fd5b610145600160a060020a0360043516602435610365565b604051901515815260200160405180910390f35b341561016457600080fd5b61016c6103d2565b60405190815260200160405180910390f35b341561018957600080fd5b610145600160a060020a03600435811690602435166044356103d9565b604051901515815260200160405180910390f35b34156101c557600080fd5b6101d0600435610649565b005b34156101dd57600080fd5b6101e56107e7565b60405160ff909116815260200160405180910390f35b341561020657600080fd5b61016c600160a060020a03600435166107ec565b60405190815260200160405180910390f35b341561023757600080fd5b6100ab61080b565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100e85780820151818401525b6020016100cf565b50505050905090810190601f1680156101155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156102c257600080fd5b610145600160a060020a0360043516602435610842565b604051901515815260200160405180910390f35b6101d06109ed565b005b341561030257600080fd5b61016c600160a060020a0360043581169060243516610b59565b60405190815260200160405180910390f35b60408051908101604052600b81527f457468657220546f6b656e000000000000000000000000000000000000000000602082015281565b600160a060020a03338116600081815260016020908152604080832094871680845294909152808220859055909291907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259085905190815260200160405180910390a35060015b92915050565b6002545b90565b600160a060020a0383166000908152602081905260408082205473__Math__________________________________9163e31c71c4919085908590516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b151561045257600080fd5b6102c65a03f4151561046357600080fd5b5050506040518051905015806105165750600160a060020a0380851660009081526001602090815260408083203390941683529290528181205473__Math__________________________________9263e31c71c4928691516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b15156104f957600080fd5b6102c65a03f4151561050a57600080fd5b50505060405180519050155b806105b05750600160a060020a0383166000908152602081905260408082205473__Math__________________________________92634e30a66c92869190516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b151561059357600080fd5b6102c65a03f415156105a457600080fd5b50505060405180519050155b156105bd57506000610642565b600160a060020a03808516600081815260208181526040808320805488900390556001825280832033861684528252808320805488900390559387168083529082905290839020805486019055917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9085905190815260200160405180910390a35060015b9392505050565b600160a060020a0333166000908152602081905260408082205473__Math__________________________________9263b67d77c592859190516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b15156106c057600080fd5b6102c65a03f415156106d157600080fd5b5050506040518051600160a060020a0333166000908152602081905260408082209290925560025473__Math__________________________________935063b67d77c59290918591516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b151561075857600080fd5b6102c65a03f4151561076957600080fd5b505050604051805160025550600160a060020a03331681156108fc0282604051600060405180830381858888f1935050505015156107a657600080fd5b33600160a060020a03167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658260405190815260200160405180910390a25b50565b601281565b600160a060020a0381166000908152602081905260409020545b919050565b60408051908101604052600381527f4554480000000000000000000000000000000000000000000000000000000000602082015281565b600160a060020a0333166000908152602081905260408082205473__Math__________________________________9163e31c71c4919085908590516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b15156108bb57600080fd5b6102c65a03f415156108cc57600080fd5b5050506040518051905015806109715750600160a060020a0383166000908152602081905260408082205473__Math__________________________________92634e30a66c92869190516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b151561095457600080fd5b6102c65a03f4151561096557600080fd5b50505060405180519050155b1561097e575060006103cc565b600160a060020a033381166000818152602081905260408082208054879003905592861680825290839020805486019055917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9085905190815260200160405180910390a35060015b92915050565b600160a060020a0333166000908152602081905260408082205473__Math__________________________________9263771602f792349190516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b1515610a6457600080fd5b6102c65a03f41515610a7557600080fd5b5050506040518051600160a060020a0333166000908152602081905260408082209290925560025473__Math__________________________________935063771602f79290913491516020015260405160e060020a63ffffffff85160281526004810192909252602482015260440160206040518083038186803b1515610afc57600080fd5b6102c65a03f41515610b0d57600080fd5b505050604051805160025550600160a060020a0333167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3460405190815260200160405180910390a25b565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a72305820360d6393a7e5a4086c689296d8e43f0aaee93013bc09ff6447e35e160731aaab0029", 272 | "networks": { 273 | "1501575358127": { 274 | "events": { 275 | "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c": { 276 | "anonymous": false, 277 | "inputs": [ 278 | { 279 | "indexed": true, 280 | "name": "sender", 281 | "type": "address" 282 | }, 283 | { 284 | "indexed": false, 285 | "name": "value", 286 | "type": "uint256" 287 | } 288 | ], 289 | "name": "Deposit", 290 | "type": "event" 291 | }, 292 | "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65": { 293 | "anonymous": false, 294 | "inputs": [ 295 | { 296 | "indexed": true, 297 | "name": "receiver", 298 | "type": "address" 299 | }, 300 | { 301 | "indexed": false, 302 | "name": "value", 303 | "type": "uint256" 304 | } 305 | ], 306 | "name": "Withdrawal", 307 | "type": "event" 308 | }, 309 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": { 310 | "anonymous": false, 311 | "inputs": [ 312 | { 313 | "indexed": true, 314 | "name": "from", 315 | "type": "address" 316 | }, 317 | { 318 | "indexed": true, 319 | "name": "to", 320 | "type": "address" 321 | }, 322 | { 323 | "indexed": false, 324 | "name": "value", 325 | "type": "uint256" 326 | } 327 | ], 328 | "name": "Transfer", 329 | "type": "event" 330 | }, 331 | "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": { 332 | "anonymous": false, 333 | "inputs": [ 334 | { 335 | "indexed": true, 336 | "name": "owner", 337 | "type": "address" 338 | }, 339 | { 340 | "indexed": true, 341 | "name": "spender", 342 | "type": "address" 343 | }, 344 | { 345 | "indexed": false, 346 | "name": "value", 347 | "type": "uint256" 348 | } 349 | ], 350 | "name": "Approval", 351 | "type": "event" 352 | } 353 | }, 354 | "links": { 355 | "Math": "0x9f9fd010aa20b9b983ea5da5f5b5643b7bf69a42" 356 | }, 357 | "address": "0x13de59675d22fb7befd213324f3f829135c6e8f8", 358 | "updated_at": 1501575470347 359 | }, 360 | "1501575538634": { 361 | "events": { 362 | "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c": { 363 | "anonymous": false, 364 | "inputs": [ 365 | { 366 | "indexed": true, 367 | "name": "sender", 368 | "type": "address" 369 | }, 370 | { 371 | "indexed": false, 372 | "name": "value", 373 | "type": "uint256" 374 | } 375 | ], 376 | "name": "Deposit", 377 | "type": "event" 378 | }, 379 | "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65": { 380 | "anonymous": false, 381 | "inputs": [ 382 | { 383 | "indexed": true, 384 | "name": "receiver", 385 | "type": "address" 386 | }, 387 | { 388 | "indexed": false, 389 | "name": "value", 390 | "type": "uint256" 391 | } 392 | ], 393 | "name": "Withdrawal", 394 | "type": "event" 395 | }, 396 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": { 397 | "anonymous": false, 398 | "inputs": [ 399 | { 400 | "indexed": true, 401 | "name": "from", 402 | "type": "address" 403 | }, 404 | { 405 | "indexed": true, 406 | "name": "to", 407 | "type": "address" 408 | }, 409 | { 410 | "indexed": false, 411 | "name": "value", 412 | "type": "uint256" 413 | } 414 | ], 415 | "name": "Transfer", 416 | "type": "event" 417 | }, 418 | "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": { 419 | "anonymous": false, 420 | "inputs": [ 421 | { 422 | "indexed": true, 423 | "name": "owner", 424 | "type": "address" 425 | }, 426 | { 427 | "indexed": true, 428 | "name": "spender", 429 | "type": "address" 430 | }, 431 | { 432 | "indexed": false, 433 | "name": "value", 434 | "type": "uint256" 435 | } 436 | ], 437 | "name": "Approval", 438 | "type": "event" 439 | } 440 | }, 441 | "links": { 442 | "Math": "0x9f9fd010aa20b9b983ea5da5f5b5643b7bf69a42" 443 | }, 444 | "address": "0x13de59675d22fb7befd213324f3f829135c6e8f8", 445 | "updated_at": 1501575552158 446 | }, 447 | "1501769706021": { 448 | "events": { 449 | "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c": { 450 | "anonymous": false, 451 | "inputs": [ 452 | { 453 | "indexed": true, 454 | "name": "sender", 455 | "type": "address" 456 | }, 457 | { 458 | "indexed": false, 459 | "name": "value", 460 | "type": "uint256" 461 | } 462 | ], 463 | "name": "Deposit", 464 | "type": "event" 465 | }, 466 | "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65": { 467 | "anonymous": false, 468 | "inputs": [ 469 | { 470 | "indexed": true, 471 | "name": "receiver", 472 | "type": "address" 473 | }, 474 | { 475 | "indexed": false, 476 | "name": "value", 477 | "type": "uint256" 478 | } 479 | ], 480 | "name": "Withdrawal", 481 | "type": "event" 482 | }, 483 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": { 484 | "anonymous": false, 485 | "inputs": [ 486 | { 487 | "indexed": true, 488 | "name": "from", 489 | "type": "address" 490 | }, 491 | { 492 | "indexed": true, 493 | "name": "to", 494 | "type": "address" 495 | }, 496 | { 497 | "indexed": false, 498 | "name": "value", 499 | "type": "uint256" 500 | } 501 | ], 502 | "name": "Transfer", 503 | "type": "event" 504 | }, 505 | "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": { 506 | "anonymous": false, 507 | "inputs": [ 508 | { 509 | "indexed": true, 510 | "name": "owner", 511 | "type": "address" 512 | }, 513 | { 514 | "indexed": true, 515 | "name": "spender", 516 | "type": "address" 517 | }, 518 | { 519 | "indexed": false, 520 | "name": "value", 521 | "type": "uint256" 522 | } 523 | ], 524 | "name": "Approval", 525 | "type": "event" 526 | } 527 | }, 528 | "links": { 529 | "Math": "0x9f9fd010aa20b9b983ea5da5f5b5643b7bf69a42" 530 | }, 531 | "address": "0x13de59675d22fb7befd213324f3f829135c6e8f8", 532 | "updated_at": 1501769725942 533 | }, 534 | "1502880859643": { 535 | "events": { 536 | "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c": { 537 | "anonymous": false, 538 | "inputs": [ 539 | { 540 | "indexed": true, 541 | "name": "sender", 542 | "type": "address" 543 | }, 544 | { 545 | "indexed": false, 546 | "name": "value", 547 | "type": "uint256" 548 | } 549 | ], 550 | "name": "Deposit", 551 | "type": "event" 552 | }, 553 | "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65": { 554 | "anonymous": false, 555 | "inputs": [ 556 | { 557 | "indexed": true, 558 | "name": "receiver", 559 | "type": "address" 560 | }, 561 | { 562 | "indexed": false, 563 | "name": "value", 564 | "type": "uint256" 565 | } 566 | ], 567 | "name": "Withdrawal", 568 | "type": "event" 569 | }, 570 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": { 571 | "anonymous": false, 572 | "inputs": [ 573 | { 574 | "indexed": true, 575 | "name": "from", 576 | "type": "address" 577 | }, 578 | { 579 | "indexed": true, 580 | "name": "to", 581 | "type": "address" 582 | }, 583 | { 584 | "indexed": false, 585 | "name": "value", 586 | "type": "uint256" 587 | } 588 | ], 589 | "name": "Transfer", 590 | "type": "event" 591 | }, 592 | "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": { 593 | "anonymous": false, 594 | "inputs": [ 595 | { 596 | "indexed": true, 597 | "name": "owner", 598 | "type": "address" 599 | }, 600 | { 601 | "indexed": true, 602 | "name": "spender", 603 | "type": "address" 604 | }, 605 | { 606 | "indexed": false, 607 | "name": "value", 608 | "type": "uint256" 609 | } 610 | ], 611 | "name": "Approval", 612 | "type": "event" 613 | } 614 | }, 615 | "links": { 616 | "Math": "0xb8d14e36f7b826f6065a69d8b5f161fd6213ace6" 617 | }, 618 | "address": "0xa6d492ff7b5da38cf37b7d9f7aeda884a2be5c34", 619 | "updated_at": 1503002277667 620 | }, 621 | "1504002958171": { 622 | "events": { 623 | "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c": { 624 | "anonymous": false, 625 | "inputs": [ 626 | { 627 | "indexed": true, 628 | "name": "sender", 629 | "type": "address" 630 | }, 631 | { 632 | "indexed": false, 633 | "name": "value", 634 | "type": "uint256" 635 | } 636 | ], 637 | "name": "Deposit", 638 | "type": "event" 639 | }, 640 | "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65": { 641 | "anonymous": false, 642 | "inputs": [ 643 | { 644 | "indexed": true, 645 | "name": "receiver", 646 | "type": "address" 647 | }, 648 | { 649 | "indexed": false, 650 | "name": "value", 651 | "type": "uint256" 652 | } 653 | ], 654 | "name": "Withdrawal", 655 | "type": "event" 656 | }, 657 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": { 658 | "anonymous": false, 659 | "inputs": [ 660 | { 661 | "indexed": true, 662 | "name": "from", 663 | "type": "address" 664 | }, 665 | { 666 | "indexed": true, 667 | "name": "to", 668 | "type": "address" 669 | }, 670 | { 671 | "indexed": false, 672 | "name": "value", 673 | "type": "uint256" 674 | } 675 | ], 676 | "name": "Transfer", 677 | "type": "event" 678 | }, 679 | "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": { 680 | "anonymous": false, 681 | "inputs": [ 682 | { 683 | "indexed": true, 684 | "name": "owner", 685 | "type": "address" 686 | }, 687 | { 688 | "indexed": true, 689 | "name": "spender", 690 | "type": "address" 691 | }, 692 | { 693 | "indexed": false, 694 | "name": "value", 695 | "type": "uint256" 696 | } 697 | ], 698 | "name": "Approval", 699 | "type": "event" 700 | } 701 | }, 702 | "links": { 703 | "Math": "0xd5d00d1ee854c49dfce2d7d28d25524a2cc083f0" 704 | }, 705 | "address": "0xa6b6f64e2e2ea9950e9c28d769aa21596257fb9e", 706 | "updated_at": 1504003015791 707 | } 708 | }, 709 | "schema_version": "0.0.5", 710 | "updated_at": 1504003015791 711 | } -------------------------------------------------------------------------------- /sample/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | buildscript { 5 | repositories { 6 | jcenter() 7 | google() 8 | maven { 9 | url uri('../../repo') 10 | } 11 | } 12 | 13 | dependencies { 14 | classpath "pm.gnosis:bivrost-gradle-plugin:0.0.42" 15 | } 16 | } 17 | 18 | apply plugin: "bivrost" 19 | 20 | android { 21 | compileSdkVersion versions.compileSdk 22 | buildToolsVersion versions.buildTools 23 | 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_7 26 | targetCompatibility JavaVersion.VERSION_1_7 27 | } 28 | 29 | defaultConfig { 30 | applicationId 'com.example.abiparser' 31 | minSdkVersion versions.minSdk 32 | targetSdkVersion versions.compileSdk 33 | versionCode 1 34 | versionName '1.0.0' 35 | } 36 | 37 | dexOptions { 38 | jumboMode = true 39 | } 40 | 41 | lintOptions { 42 | textReport true 43 | textOutput 'stdout' 44 | } 45 | 46 | buildTypes { 47 | debug { 48 | applicationIdSuffix ".debug" 49 | versionNameSuffix "-debug" 50 | minifyEnabled false 51 | } 52 | } 53 | } 54 | 55 | repositories { 56 | maven { 57 | url uri('../../repo') 58 | } 59 | } 60 | 61 | dependencies { 62 | implementation deps.kotlin.stdLibJre8 63 | implementation "pm.gnosis:bivrost-solidity-types:0.0.42" 64 | } -------------------------------------------------------------------------------- /sample/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /sample/app/src/main/java/com/example/abiparser/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.abiparser 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | 6 | 7 | class MainActivity: Activity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | EtherToken.Name.encode() 11 | } 12 | } -------------------------------------------------------------------------------- /sample/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Abi Parser Sample 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':bivrost-abi-parser' 2 | include ':bivrost-gradle-plugin' 3 | include ':bivrost-solidity-types' 4 | include ':bivrost-solidity-types-generator' 5 | include ':bivrost-utils' 6 | //include ':sample:app' 7 | 8 | rootProject.name = 'bivrost-kotlin' 9 | 10 | --------------------------------------------------------------------------------