Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/esaulpaugh/headlong
High-performance Contract ABI and RLP for Ethereum
https://github.com/esaulpaugh/headlong
abi blockchain codec contract contract-abi encoding enr ether ethereum java rlp
Last synced: about 2 months ago
JSON representation
High-performance Contract ABI and RLP for Ethereum
- Host: GitHub
- URL: https://github.com/esaulpaugh/headlong
- Owner: esaulpaugh
- License: apache-2.0
- Created: 2018-08-05T20:11:14.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2024-05-20T09:10:55.000Z (4 months ago)
- Last Synced: 2024-05-20T12:53:01.468Z (4 months ago)
- Topics: abi, blockchain, codec, contract, contract-abi, encoding, enr, ether, ethereum, java, rlp
- Language: Java
- Homepage:
- Size: 6.97 MB
- Stars: 75
- Watchers: 10
- Forks: 20
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
Awesome Lists containing this project
- DeFi-Developer-Road-Map - headlong - type-safe Contract ABI and Recursive Length Prefix library in Java (Roadmap)
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)
[![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)
[![jdk1.8+](https://img.shields.io/badge/JDK-1.8+-blue.svg)](https://openjdk.java.net/)
[![Java CI](https://github.com/esaulpaugh/headlong/workflows/Java%20CI%20gradle/badge.svg)](https://github.com/esaulpaugh/headlong/actions?query=workflow%3A"Java+CI")
[![Gitter](https://badges.gitter.im/esaulpaugh-headlong/community.svg)](https://gitter.im/esaulpaugh-headlong/community)Contract ABI and Recursive Length Prefix made easy for the JVM.
ABI spec: https://solidity.readthedocs.io/en/latest/abi-spec.html
RLP spec: https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp
SHA-256 (headlong-12.1.0.jar): f44ade364c02259ae92c4d6720171f7565866cdc7c415b43501744f5311062ea
## Usage
### ABI package
#### Encoding Function Calls
```java
Function baz = Function.parse("baz(uint32,bool)"); // canonicalizes and parses any signature
// or
Function f2 = Function.fromJson("{\"type\":\"function\",\"name\":\"foo\",\"inputs\":[{\"name\":\"complex_nums\",\"type\":\"tuple[]\",\"components\":[{\"name\":\"real\",\"type\":\"fixed168x10\"},{\"name\":\"imaginary\",\"type\":\"fixed168x10\"}]}]}");Pair bazArgs = Tuple.of(69L, true);
Tuple complexNums = Single.of(new Tuple[] { Tuple.of(new BigDecimal("0.0090000000"), new BigDecimal("1.9500000000")) });// Two equivalent styles:
ByteBuffer bazCall = baz.encodeCall(bazArgs);
ByteBuffer bazCall2 = baz.encodeCallWithArgs(69L, true);System.out.println("baz call hex:\n" + Strings.encode(bazCall) + "\n"); // hexadecimal encoding (without 0x prefix)
Tuple recoveredArgs = baz.decodeCall(bazCall2); // decode the encoding back to the original args
System.out.println("baz args:\n" + recoveredArgs + "\n"); // toString()
System.out.println("equal:\n" + recoveredArgs.equals(bazArgs) + "\n"); // test for equalitySystem.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)
System.out.println("baz args debug:\n" + baz.getInputs().annotate(bazArgs) + "\n"); // human-readable, for debugging encodings without a selector
System.out.println("f2 call debug:\n" + f2.annotateCall(complexNums) + "\n");
System.out.println("f2 args debug:\n" + f2.getInputs().annotate(complexNums));
```#### Decoding Return Values
```java
Function foo = Function.parse("foo((fixed[],int8)[1][][5])", "(int,string)");// decode return type (int256,string)
Tuple decoded = foo.decodeReturn(
FastHex.decode(
"000000000000000000000000000000000000000000000000000000000000002A"
+ "0000000000000000000000000000000000000000000000000000000000000040"
+ "000000000000000000000000000000000000000000000000000000000000000e"
+ "59616f62616e6745696768747939000000000000000000000000000000000000"
)
);System.out.println(decoded.equals(Tuple.of(BigInteger.valueOf(42L), "YaobangEighty9")));
``````java
Function fooTwo = Function.parse("fooTwo()", "(uint8)");
int returned = fooTwo.decodeSingletonReturn(FastHex.decode("00000000000000000000000000000000000000000000000000000000000000FF")); // uint8 corresponds to int
System.out.println(returned);
```#### Using TupleType
```java
TupleType tt = TupleType.parse("(bool,address,int72[][])");
ByteBuffer b0 = tt.encode(Tuple.of(false, Address.wrap("0x52908400098527886E0F7030069857D2E4169EE7"), new BigInteger[0][]));
// Tuple t = tt.decode(b0); // decode the tuple (has the side effect of advancing the ByteBuffer's position)
// or...
Address a = tt.decode(b0, 1); // decode only index 1
System.out.println(a);
Tuple t2 = tt.decode(b0, 0, 2); // decode only indices 0 and 2
System.out.println(t2);ByteBuffer b1 = tt.getNonCapturing(2).encode(new BigInteger[][] { }); // encode only int72[][]
```#### Misc
```java
Event> event = Event.fromJson("{\"type\":\"event\",\"name\":\"an_event\",\"inputs\":[{\"name\":\"a\",\"type\":\"bytes\",\"indexed\":true},{\"name\":\"b\",\"type\":\"uint256\",\"indexed\":false}],\"anonymous\":true}");
Tuple args = event.decodeArgs(new byte[][] { new byte[32] }, new byte[32]);
System.out.println(event);
System.out.println(args);// create any type directly (advanced)
, Pair[]> at3 = TypeFactory.create("(address,int)[]");
ArrayType, ?, Object> at = TypeFactory.create("(address,int)[]");
ArrayType, Tuple, Tuple[]> at2 = TypeFactory.create("(address,int)[]");
ArrayType>, Pair
ABIType unknown = TypeFactory.create(at.getCanonicalType());
```### RLP package
```java
// for an example class Student implementing some example interface
public Student(byte[] rlp) {
Iterator iter = RLPDecoder.RLP_STRICT.sequenceIterator(rlp);
this.name = iter.next().asString(Strings.UTF_8);
this.gpa = iter.next().asFloat(false);
this.publicKey = iter.next().asBytes();
this.balance = new BigDecimal(iter.next().asBigInt(), iter.next().asInt());
}@Override
public Object[] toObjectArray() {
return new Object[] {
// instances of byte[]
Strings.decode(name, Strings.UTF_8),
FloatingPoint.toBytes(gpa),
publicKey,
balance.unscaledValue().toByteArray(),
Integers.toBytes(balance.scale())
// include an Object[] or Iterable and its elements will be encoded as an RLP list (which may include other lists)
};
}@Override
public byte[] toRLP() {
return RLPEncoder.sequence(toObjectArray());
}
```## Build
Now available in Maven Central Repository.
Or build locally:
Clone the project and install to your local maven repository using `gradle publishToMavenLocal` or `mvn install`, then declare it as a dependency:
```kotlin
implementation("com.esaulpaugh:headlong:12.1.1-SNAPSHOT")
``````xml
com.esaulpaugh
headlong
12.1.1-SNAPSHOT```
Alternatively:* Run `gradle build` or `gradle jar` which output to `build/libs`
* Use `mvn package` which outputs to `target`
* Execute `ant all build-jar` which outputs to `build/lib`
* Add headlong as a project dependency## Command line interface
https://github.com/esaulpaugh/headlong-cli
## Benchmarks
![Screenshot](https://github.com/esaulpaugh/headlong/blob/master/benchmark_results.PNG)
GraalVM 21.0.3 on x86-64## Misc
Also includes optimized implementations of:
* EIP-778 Ethereum Node Records
* EIP-55 Mixed-case checksum address encoding
* Keccak
* hexadecimalheadlong depends on gson v2.10.1. Test suite should take less than one minute to run. Test packages require junit. Jar size is ~128 KiB. Java 8+.
See the wiki for more, such as packed encoding (and decoding) and RLP Object Notation: https://github.com/esaulpaugh/headlong/wiki
Licensed under Apache 2.0 terms