Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

High-performance Contract ABI and RLP for Ethereum

License

NotificationsYou must be signed in to change notification settings

esaulpaugh/headlong

Repository files navigation

Maven CentralApache License, Version 2.0, January 2004jdk1.8+Java CI GraalVM MavenGitter

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-13.1.1.jar): e544a0fa0c6bf341434d9a204628ddf216a45999bd489dd41274264c06a4ef5c

Usage

ABI package

Encoding Function Calls

Functionbaz =Function.parse("baz(uint32,bool)");// canonicalizes and parses any signature// orFunctionf2 =Function.fromJson("{\"type\":\"function\",\"name\":\"foo\",\"inputs\":[{\"name\":\"complex_nums\",\"type\":\"tuple[]\",\"components\":[{\"name\":\"real\",\"type\":\"fixed168x10\"},{\"name\":\"imaginary\",\"type\":\"fixed168x10\"}]}]}");Pair<Long,Boolean>bazArgs =Tuple.of(69L,true);TuplecomplexNums =Single.of(newTuple[] {Tuple.of(newBigDecimal("0.0090000000"),newBigDecimal("1.9500000000")) });// Two equivalent styles:ByteBufferbazCall =baz.encodeCall(bazArgs);ByteBufferbazCall2 =baz.encodeCallWithArgs(69L,true);System.out.println("baz call hex:\n" +Strings.encode(bazCall) +"\n");// hexadecimal encoding (without 0x prefix)TuplerecoveredArgs =baz.decodeCall(bazCall2);// decode the encoding back to the original argsSystem.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 selectorSystem.out.println("f2 call debug:\n" +f2.annotateCall(complexNums) +"\n");System.out.println("f2 args debug:\n" +f2.getInputs().annotate(complexNums));

Decoding Return Values

Functionfoo =Function.parse("foo((fixed[],int8)[1][][5])","(int,string)");// decode return type (int256,string)Tupledecoded =foo.decodeReturn(FastHex.decode("000000000000000000000000000000000000000000000000000000000000002A"        +"0000000000000000000000000000000000000000000000000000000000000040"        +"000000000000000000000000000000000000000000000000000000000000000e"        +"59616f62616e6745696768747939000000000000000000000000000000000000"    ));System.out.println(decoded.equals(Tuple.of(BigInteger.valueOf(42L),"YaobangEighty9")));
FunctionfooTwo =Function.parse("fooTwo()","(uint8)");intreturned =fooTwo.decodeSingletonReturn(FastHex.decode("00000000000000000000000000000000000000000000000000000000000000FF"));// uint8 corresponds to intSystem.out.println(returned);

Using TupleType

TupleType<Tuple>tt =TupleType.parse("(bool,address,int72[][])");ByteBufferb0 =tt.encode(Tuple.of(false,Address.wrap("0x52908400098527886E0F7030069857D2E4169EE7"),newBigInteger[0][]));// Tuple t = tt.decode(b0); // decode the tuple (has the side effect of advancing the ByteBuffer's position)// or...Addressa =tt.decode(b0,1);// decode only index 1System.out.println(a);Tuplet2 =tt.decode(b0,0,2);// decode only indices 0 and 2System.out.println(t2);ByteBufferb1 =tt.<ABIType<BigInteger[][]>>get(2).encode(newBigInteger[][] {  });// encode only int72[][]

Misc

Event<?>event =Event.fromJson("{\"type\":\"event\",\"name\":\"an_event\",\"inputs\":[{\"name\":\"a\",\"type\":\"bytes\",\"indexed\":true},{\"name\":\"b\",\"type\":\"uint256\",\"indexed\":false}],\"anonymous\":true}");Tupleargs =event.decodeArgs(newbyte[][] {newbyte[32] },newbyte[32]);System.out.println(event);System.out.println(args);// create any type directly (advanced)ArrayType<ABIType<Object>, ?,Object>at =TypeFactory.create("(address,int)[]");ArrayType<TupleType<Tuple>,Tuple,Tuple[]>at2 =TypeFactory.create("(address,int)[]");ArrayType<TupleType<Pair<Address,BigInteger>>,Pair<Address,BigInteger>,Pair<Address,BigInteger>[]>at3 =TypeFactory.create("(address,int)[]");ABIType<Object>unknown =TypeFactory.create(at.getCanonicalType());

RLP package

// for an example class Student implementing some example interfacepublicStudent(byte[]rlp) {Iterator<RLPItem>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 =newBigDecimal(iter.next().asBigInt(),iter.next().asInt());}@OverridepublicObject[]toObjectArray() {returnnewObject[] {// 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)    };}@Overridepublicbyte[]toRLP() {returnRLPEncoder.sequence(toObjectArray());}

Build

Now available in Maven Central Repository.

Or build locally:

Clone the project and install to your local maven repository usinggradle publishToMavenLocal ormvn install, then declare it as a dependency:

implementation("com.esaulpaugh:headlong:13.1.2-SNAPSHOT")
<dependency>    <groupId>com.esaulpaugh</groupId>    <artifactId>headlong</artifactId>    <version>13.1.2-SNAPSHOT</version></dependency>

Alternatively:

  • Rungradle build orgradle jar which output tobuild/libs
  • Usemvn package which outputs totarget
  • Executeant all build-jar which outputs tobuild/lib
  • Add headlong as a project dependency

Benchmarks

Screenshot

temurin 1.8.0_442 on Intel Xeon Gold 6140, Ubuntu 24.10

Screenshotgraalvm-jdk-23.0.2 (aarch64 JIT) on Apple M3 Max

Command line interface

https://github.com/esaulpaugh/headlong-cli

Example Android app

https://github.com/esaulpaugh/headlong-android

Misc

Also includes optimized implementations of:

  • EIP-778 Ethereum Node Records
  • EIP-55 Mixed-case checksum address encoding
  • Keccak
  • hexadecimal

headlong 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+.

For better contract ABI JSON parsing performance, consider constructing anABIParser with aSet<TypeEnum> 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 viaABIJSON.optimize(String).

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


[8]ページ先頭

©2009-2025 Movatter.jp