{"id":13792565,"url":"https://github.com/esaulpaugh/headlong","last_synced_at":"2025-04-05T14:08:11.000Z","repository":{"id":37390685,"uuid":"143644382","full_name":"esaulpaugh/headlong","owner":"esaulpaugh","description":"High-performance Contract ABI and RLP for Ethereum","archived":false,"fork":false,"pushed_at":"2024-10-29T08:06:51.000Z","size":7720,"stargazers_count":79,"open_issues_count":0,"forks_count":20,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-29T09:24:25.522Z","etag":null,"topics":["abi","blockchain","codec","contract","contract-abi","encoding","enr","ether","ethereum","java","rlp"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/esaulpaugh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-05T20:11:14.000Z","updated_at":"2024-10-29T08:06:55.000Z","dependencies_parsed_at":"2023-02-19T01:01:51.186Z","dependency_job_id":"374d0d32-4540-4e63-8b37-d9d941418f0c","html_url":"https://github.com/esaulpaugh/headlong","commit_stats":null,"previous_names":[],"tags_count":90,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esaulpaugh%2Fheadlong","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esaulpaugh%2Fheadlong/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esaulpaugh%2Fheadlong/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esaulpaugh%2Fheadlong/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/esaulpaugh","download_url":"https://codeload.github.com/esaulpaugh/headlong/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247345853,"owners_count":20924102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abi","blockchain","codec","contract","contract-abi","encoding","enr","ether","ethereum","java","rlp"],"created_at":"2024-08-03T22:01:13.775Z","updated_at":"2025-04-05T14:08:10.870Z","avatar_url":"https://github.com/esaulpaugh.png","language":"Java","funding_links":[],"categories":["Roadmap"],"sub_categories":[],"readme":"[![Maven Central](https://img.shields.io/maven-central/v/com.esaulpaugh/headlong.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/com.esaulpaugh/headlong)\n[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)](https://www.apache.org/licenses/LICENSE-2.0)\n[![jdk1.8+](https://img.shields.io/badge/JDK-1.8+-blue.svg)](https://openjdk.java.net/)\n[![Java CI GraalVM Maven](https://github.com/esaulpaugh/headlong/actions/workflows/graalvm.yml/badge.svg)](https://github.com/esaulpaugh/headlong/actions/workflows/graalvm.yml)\n[![Gitter](https://badges.gitter.im/esaulpaugh-headlong/community.svg)](https://gitter.im/esaulpaugh-headlong/community)\n\nContract ABI and Recursive Length Prefix made easy for the JVM.\n\nABI spec: https://solidity.readthedocs.io/en/latest/abi-spec.html\n\nRLP spec: https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp\n\nSHA-256 (headlong-13.1.1.jar): e544a0fa0c6bf341434d9a204628ddf216a45999bd489dd41274264c06a4ef5c\n\n## Usage\n\n### ABI package\n\n#### Encoding Function Calls\n\n```java\nFunction baz = Function.parse(\"baz(uint32,bool)\"); // canonicalizes and parses any signature\n// or\nFunction f2 = Function.fromJson(\"{\\\"type\\\":\\\"function\\\",\\\"name\\\":\\\"foo\\\",\\\"inputs\\\":[{\\\"name\\\":\\\"complex_nums\\\",\\\"type\\\":\\\"tuple[]\\\",\\\"components\\\":[{\\\"name\\\":\\\"real\\\",\\\"type\\\":\\\"fixed168x10\\\"},{\\\"name\\\":\\\"imaginary\\\",\\\"type\\\":\\\"fixed168x10\\\"}]}]}\");\n\nPair\u003cLong, Boolean\u003e bazArgs = Tuple.of(69L, true);\nTuple complexNums = Single.of(new Tuple[] { Tuple.of(new BigDecimal(\"0.0090000000\"), new BigDecimal(\"1.9500000000\")) });\n\n// Two equivalent styles:\nByteBuffer bazCall = baz.encodeCall(bazArgs);\nByteBuffer bazCall2 = baz.encodeCallWithArgs(69L, true);\n\nSystem.out.println(\"baz call hex:\\n\" + Strings.encode(bazCall) + \"\\n\"); // hexadecimal encoding (without 0x prefix)\n\nTuple recoveredArgs = baz.decodeCall(bazCall2); // decode the encoding back to the original args\n\nSystem.out.println(\"baz args:\\n\" + recoveredArgs + \"\\n\"); // toString()\nSystem.out.println(\"equal:\\n\" + recoveredArgs.equals(bazArgs) + \"\\n\"); // test for equality\n\nSystem.out.println(\"baz call debug:\\n\" + baz.annotateCall(bazCall.array()) + \"\\n\"); // human-readable, for debugging function calls (expects input to start with 4-byte selector)\nSystem.out.println(\"baz args debug:\\n\" + baz.getInputs().annotate(bazArgs) + \"\\n\"); // human-readable, for debugging encodings without a selector\nSystem.out.println(\"f2 call debug:\\n\" + f2.annotateCall(complexNums) + \"\\n\");\nSystem.out.println(\"f2 args debug:\\n\" + f2.getInputs().annotate(complexNums));\n```\n\n#### Decoding Return Values\n\n```java\nFunction foo = Function.parse(\"foo((fixed[],int8)[1][][5])\", \"(int,string)\");\n\n// decode return type (int256,string)\nTuple decoded = foo.decodeReturn(\n    FastHex.decode(\n          \"000000000000000000000000000000000000000000000000000000000000002A\"\n        + \"0000000000000000000000000000000000000000000000000000000000000040\"\n        + \"000000000000000000000000000000000000000000000000000000000000000e\"\n        + \"59616f62616e6745696768747939000000000000000000000000000000000000\"\n    )\n);\n\nSystem.out.println(decoded.equals(Tuple.of(BigInteger.valueOf(42L), \"YaobangEighty9\")));\n```\n\n```java\nFunction fooTwo = Function.parse(\"fooTwo()\", \"(uint8)\");\nint returned = fooTwo.decodeSingletonReturn(FastHex.decode(\"00000000000000000000000000000000000000000000000000000000000000FF\")); // uint8 corresponds to int\nSystem.out.println(returned);\n```\n\n#### Using TupleType\n\n```java\nTupleType\u003cTuple\u003e tt = TupleType.parse(\"(bool,address,int72[][])\");\nByteBuffer b0 = tt.encode(Tuple.of(false, Address.wrap(\"0x52908400098527886E0F7030069857D2E4169EE7\"), new BigInteger[0][]));\n// Tuple t = tt.decode(b0); // decode the tuple (has the side effect of advancing the ByteBuffer's position)\n// or...\nAddress a = tt.decode(b0, 1); // decode only index 1\nSystem.out.println(a);\nTuple t2 = tt.decode(b0, 0, 2); // decode only indices 0 and 2\nSystem.out.println(t2);\n\nByteBuffer b1 = tt.\u003cABIType\u003cBigInteger[][]\u003e\u003eget(2).encode(new BigInteger[][] {  }); // encode only int72[][]\n```\n\n#### Misc\n\n```java\nEvent\u003c?\u003e event = Event.fromJson(\"{\\\"type\\\":\\\"event\\\",\\\"name\\\":\\\"an_event\\\",\\\"inputs\\\":[{\\\"name\\\":\\\"a\\\",\\\"type\\\":\\\"bytes\\\",\\\"indexed\\\":true},{\\\"name\\\":\\\"b\\\",\\\"type\\\":\\\"uint256\\\",\\\"indexed\\\":false}],\\\"anonymous\\\":true}\");\nTuple args = event.decodeArgs(new byte[][] { new byte[32] }, new byte[32]);\nSystem.out.println(event);\nSystem.out.println(args);\n\n// create any type directly (advanced)\nArrayType\u003cABIType\u003cObject\u003e, ?, Object\u003e at = TypeFactory.create(\"(address,int)[]\");\nArrayType\u003cTupleType\u003cTuple\u003e, Tuple, Tuple[]\u003e at2 = TypeFactory.create(\"(address,int)[]\");\nArrayType\u003cTupleType\u003cPair\u003cAddress, BigInteger\u003e\u003e, Pair\u003cAddress, BigInteger\u003e, Pair\u003cAddress, BigInteger\u003e[]\u003e at3 = TypeFactory.create(\"(address,int)[]\");\nABIType\u003cObject\u003e unknown = TypeFactory.create(at.getCanonicalType());\n```\n\n### RLP package\n\n```java\n// for an example class Student implementing some example interface\npublic Student(byte[] rlp) {\n    Iterator\u003cRLPItem\u003e iter = RLPDecoder.RLP_STRICT.sequenceIterator(rlp);\n    \n    this.name = iter.next().asString(Strings.UTF_8);\n    this.gpa = iter.next().asFloat(false);\n    this.publicKey = iter.next().asBytes();\n    this.balance = new BigDecimal(iter.next().asBigInt(), iter.next().asInt());\n}\n\n@Override\npublic Object[] toObjectArray() {\n    return new Object[] {\n            // instances of byte[]\n            Strings.decode(name, Strings.UTF_8),\n            FloatingPoint.toBytes(gpa),\n            publicKey,\n            balance.unscaledValue().toByteArray(),\n            Integers.toBytes(balance.scale())\n            // include an Object[] or Iterable and its elements will be encoded as an RLP list (which may include other lists)\n    };\n}\n\n@Override\npublic byte[] toRLP() {\n    return RLPEncoder.sequence(toObjectArray());\n}\n```\n\n## Build\n\nNow available in Maven Central Repository.\n\nOr build locally:\n\nClone the project and install to your local maven repository using `gradle publishToMavenLocal` or `mvn install`, then declare it as a dependency:\n\n```kotlin\nimplementation(\"com.esaulpaugh:headlong:13.2.0-SNAPSHOT\")\n```\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.esaulpaugh\u003c/groupId\u003e\n    \u003cartifactId\u003eheadlong\u003c/artifactId\u003e\n    \u003cversion\u003e13.2.0-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\nAlternatively:\n\n* Run `gradle build` or `gradle jar` which output to `build/libs`\n* Use `mvn package` which outputs to `target`\n* Execute `ant all build-jar` which outputs to `build/lib`\n* Add headlong as a project dependency\n\n## Benchmarks\n\n![Screenshot](https://github.com/esaulpaugh/headlong/blob/master/temurin-1.8.0_442_ubuntu24.10_xeon_gold_6140.png)\n\ntemurin 1.8.0_442 on Intel Xeon Gold 6140, Ubuntu 24.10\n\n![Screenshot](https://github.com/esaulpaugh/headlong/blob/master/benchmark_results.PNG)\ngraalvm-jdk-23.0.2 (aarch64 JIT) on Apple M3 Max\n\n## Command line interface\n\nhttps://github.com/esaulpaugh/headlong-cli\n\n## Example Android app\n\nhttps://github.com/esaulpaugh/headlong-android\n\n## Misc\n\nAlso includes optimized implementations of:\n\n* EIP-778 Ethereum Node Records\n* EIP-55 Mixed-case checksum address encoding\n* Keccak\n* hexadecimal\n\nheadlong depends on gson v2.1 or greater at runtime and v2.12.0 or greater at compile time. Test suite should take less than one minute to run. Test packages require junit. Jar size is ~133 KiB. Java 8+.\n\nFor better contract ABI JSON parsing performance, consider constructing an `ABIParser` with a `Set\u003cTypeEnum\u003e` by which to filter objects by type. For best performance, json should be compact and \"type\" should be the first key in functions, events, and errors. This can be done via `ABIJSON.optimize(String)`.\n\nSee the wiki for more, such as packed encoding (and decoding) and RLP Object Notation: https://github.com/esaulpaugh/headlong/wiki\n\nLicensed under Apache 2.0 terms\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesaulpaugh%2Fheadlong","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fesaulpaugh%2Fheadlong","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesaulpaugh%2Fheadlong/lists"}