Giter VIP home page Giter VIP logo

headlong's Introduction

Maven Central Apache License, Version 2.0, January 2004 jdk1.8+ Java CI Gitter

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-11.1.0.jar): 7718cca29d9b3be24a6472fffb2c185724380c7cbea008f6a05949b20d72720e

Usage

ABI package

Encoding Function Calls

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<Long, Boolean> 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 equality

System.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

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")));
Function fooTwo = Function.parse("fooTwo()", "(uint8)");
int returned = fooTwo.decodeSingletonReturn(FastHex.decode("00000000000000000000000000000000000000000000000000000000000000FF")); // uint8 corresponds to int
System.out.println(returned);

Using TupleType

TupleType<Tuple> 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

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)
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 interface
public Student(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 = 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:

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

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 GraalVM 20.0.1 on ARM

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.10.1. Test suite should take less than one minute to run. Test packages require junit. Jar size is ~126 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

headlong's People

Contributors

esaulpaugh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

headlong's Issues

Illegal backwards jump when decoding OpenSea function calls

Hi @esaulpaugh,

Thanks a lot for having done such a great job.

We use headlong as our core decoding facilitates at our open-source analytics library: https://github.com/datawaves-xyz/blockchain-spark. It is super fast, well maintained, and suits our use case.

Recently, we found some decoding errors from our server log. It almost failed on half of OpenSea's 'atomicMatch' function calls (address: 0x7f268357a8c2552623316e2562d90e642bb538e5). I try to debug the program but hard to fix it by my self. So I am asking you for help.

Here's the stack trace:

java.lang.IllegalArgumentException: illegal backwards jump: (4+2848=2852)<2884
	at com.esaulpaugh.headlong.abi.TupleType.decodeObjects(TupleType.java:306)
	at com.esaulpaugh.headlong.abi.TupleType.decode(TupleType.java:193)
	at com.esaulpaugh.headlong.abi.Function.decodeCall(Function.java:233)
(...)

I retrieve the data from our server. I decode it with Web3.py on my laptop then it works. The following code snippet is for reproducing the problem locally.

Function f = Function.fromJson("{\"constant\": false, \"inputs\": [{\"name\": \"addrs\", \"type\": \"address[14]\"}, {\"name\": \"uints\", \"type\": \"uint256[18]\"}, {\"name\": \"feeMethodsSidesKindsHowToCalls\", \"type\": \"uint8[8]\"}, {\"name\": \"calldataBuy\", \"type\": \"bytes\"}, {\"name\": \"calldataSell\", \"type\": \"bytes\"}, {\"name\": \"replacementPatternBuy\", \"type\": \"bytes\"}, {\"name\": \"replacementPatternSell\", \"type\": \"bytes\"}, {\"name\": \"staticExtradataBuy\", \"type\": \"bytes\"}, {\"name\": \"staticExtradataSell\", \"type\": \"bytes\"}, {\"name\": \"vs\", \"type\": \"uint8[2]\"}, {\"name\": \"rssMetadata\", \"type\": \"bytes32[5]\"}], \"name\": \"atomicMatch_\", \"outputs\": [], \"payable\": true, \"stateMutability\": \"payable\", \"type\": \"function\"}");
Tuple inputTuple = f.decodeCall(FastHex.decode("ab834bab0000000000000000000000007f268357a8c2552623316e2562d90e642bb538e5000000000000000000000000267011df2647c0a465a7d0aacb8257483db6b6c3000000000000000000000000f5962dd1cfb95bd15a598b721091b373fcf1e3d10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f268357a8c2552623316e2562d90e642bb538e5000000000000000000000000f5962dd1cfb95bd15a598b721091b373fcf1e3d100000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000429d069189e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006235fb0800000000000000000000000000000000000000000000000000000000000000006d30435dda06a6f4136149c29818e18c3f3843728e80259fa66f1201fbdb91f000000000000000000000000000000000000000000000000000000000000002ee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000429d069189e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006232b917000000000000000000000000000000000000000000000000000000006246314446231f8dcf1e298e1c8579f603c6fa74fa51f0f886c4ae376377eb08467f3c290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000008e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c7596ed295fccb24c5008d11d537120b1b220dbe835bfd0ae15373d9f8e6a947c46c70ad794e802c20c451e530c4cf6962696174a2169e03f2f2a64000f93ea2a7596ed295fccb24c5008d11d537120b1b220dbe835bfd0ae15373d9f8e6a947c46c70ad794e802c20c451e530c4cf6962696174a2169e03f2f2a64000f93ea2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a5950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000267011df2647c0a465a7d0aacb8257483db6b6c3000000000000000000000000920a8d9e9f0defd6f86e4388a5503b04cac83b5700000000000000000000000000000000000000000000000000000000000031be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a595000000000000000000000000f5962dd1cfb95bd15a598b721091b373fcf1e3d10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000920a8d9e9f0defd6f86e4388a5503b04cac83b5700000000000000000000000000000000000000000000000000000000000031be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));

To get you to the site faster, the error happens when parsing the 8th element of the tuple:

image

Thanks again!

Yichao

ask for helf

Can you tell me the process of calling ABIV2 contract function on the chain of blocks ?

`Event` decoding API

Hi @esaulpaugh,

I initially asked on gitter, but thought I'd create an issue here directly.
Is there a way to decode events, like there is Function.decodeCall() ?
If not, I'd be happy to help implement that.

I really appreciate all the work you've done for this lib, it saved me so much time and the API is very well designed.

`tuple index 4: not enough bytes remaining: 354 < 384`

Hi @esaulpaugh and thanks again for an excellent library.

We're having some troubles using the library on this transaction: 0x996a15ed678a5276dbf446587a9bf53f90c3b961b7f711d58a5ec74d44b7d1c2

As seen in the parity traces number 2, the trace goes to 0xcd9dab5e666de980cecdc180cb31f296733e2587 with the following input data:

0x128acb08000000000000000000000000da3a20aad0c34fa742bd9813d45bbf67c787ae0b0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffdb9328a9ba5635a80846cdf000000000000000000000000fffd8963efd1fc6a506488495d951d5263988d2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000016200163301ee63fb29f863f2333bd4466acb46cd8323e620cd9dab5e666de980cecdc180cb31f296733e258700000246cd75645a9ca57f7b9320004bb0641100000000000000beebe34d7e60da18da3a20aad0c34fa742bd9813d45bbf67c787ae0b0000000000000000c34333f9af9c04005800132f152689a72b0a484b49a37fc85a862006f4138f1b000000000000000a31acd25057ef0000000000e17c19c8ee8c58656d0035189628a3e880960bd07f47bdfeb91de21a4c9b431a00000000000000000dbaac192510fe00940106450dee7fd2fb8e39061434babcfc05599a6fb8202a9d2ba41aba912316d16742f259412b681898db00000007f5368cf7b9ce855960c0004bb0641100000000000000475b46c4aab2f8109f5b65fa97d1fd596e9d111d483f90f69cd8102a000000000004ac23d1c23b55042949007118742f53651043cbab06602299064d59deb60a745300000000000000004e3fe9b51a631e

This maps to the swap function of the UniswapV3Pool with the following ABI:

{
  "inputs": [
    {
      "internalType": "address",
      "name": "recipient",
      "type": "address"
    },
    {
      "internalType": "bool",
      "name": "zeroForOne",
      "type": "bool"
    },
    {
      "internalType": "int256",
      "name": "amountSpecified",
      "type": "int256"
    },
    {
      "internalType": "uint160",
      "name": "sqrtPriceLimitX96",
      "type": "uint160"
    },
    {
      "internalType": "bytes",
      "name": "data",
      "type": "bytes"
    }
  ],
  "name": "swap",
  "outputs": [
    {
      "internalType": "int256",
      "name": "amount0",
      "type": "int256"
    },
    {
      "internalType": "int256",
      "name": "amount1",
      "type": "int256"
    }
  ],
  "stateMutability": "nonpayable",
  "type": "function"
}

The transaction is successful and the corresponding Swap event is emitted.

However, trying to decode this data with headlong gives the following stack trace:

Caused by: java.lang.IllegalArgumentException: tuple index 4: not enough bytes remaining: 354 < 384
	at com.esaulpaugh.headlong.abi.TupleType.decodeException(TupleType.java:316)
	at com.esaulpaugh.headlong.abi.TupleType.decode(TupleType.java:246)
	at com.esaulpaugh.headlong.abi.TupleType.decode(TupleType.java:30)
	at com.esaulpaugh.headlong.abi.ABIType.decode(ABIType.java:176)
	at com.esaulpaugh.headlong.abi.Function.decodeCall(Function.java:231)

After some debugging it seems like the offset for the last bytes array is wrong. It is probably set incorrectly in the data? However, the transaction goes through, so I'm a little unsure whats the expected behaviour for this trace.

Let me know what you think!

Håkon

Encode abi params

Hi again :)

Is there possibility to encode abi params without function name itself? My workaround is empting first 4 bytes for example:

val args = Tuple(Address.wrap("0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32"), 5)
val f = Function("(address,uint8)")
val callData = f.encodeCall(args)

it gives: 0xd6ec702b0000000000000000000000005757371414417b8c6caad45baef941abc7d3ab320000000000000000000000000000000000000000000000000000000000000005
then I clear first bytes. I bet that this library have such functionality.

Thanks!

Keep internal type from ABI files

I'm working in a code generation project and would like to generate the data structures for tuples instead of using Tuple.

Would it be possible to parse and keep the internalType JSON field in the ABI objects?

Thanks

type is "receive"; functions of this type must define name as "receive"

Hi @esaulpaugh ,

Thank you for a great library!

The library requires "receive" ABI types to have name set to "receive". As far as I can tell, this is normally not a part of the ABI (example).

What this means is that if I use the ABIJSON module to parse ABIs, I get errors for the "receive" events, even though they look legit.

I'm curious what's the motivation behind this check and whether we could discuss ways of opting out of it?

Best,
Håkon

Parity with web3js in decoding

using this abi

{
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "name": "specId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "name": "requester",
        "type": "address"
      },
      {
        "indexed": false,
        "name": "requestId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "name": "payment",
        "type": "uint256"
      },
      {
        "indexed": false,
        "name": "callbackAddr",
        "type": "address"
      },
      {
        "indexed": false,
        "name": "callbackFunctionId",
        "type": "bytes4"
      },
      {
        "indexed": false,
        "name": "cancelExpiration",
        "type": "uint256"
      },
      {
        "indexed": false,
        "name": "dataVersion",
        "type": "uint256"
      },
      {
        "indexed": false,
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "OracleRequest",
    "type": "event"
  }

with this event

{
    "logIndex": "0x82",
    "transactionIndex": "0x55",
    "transactionHash": "0xda2b8561b0074d954ea33a26692e93ebff9710d2220e0eb5003abdc468bb0d8d",
    "blockHash": "0x7203fb7525cc8fc2cfa53960013a56fce83f02594fcc691f27e324959d4f2d4e",
    "blockNumber": "0xb7b059",
    "address": "0x60B2582FB902Dff0B99c7AC30ABC08AaEfEEB309",
    "data": "0x000000000000000000000000f9fc2b4a0e487297b05285e9b3327f26e70c4e9b6b7ff4ffa51b345e8459b03fdb280ac7f99caaf432142a1b800c581d8b4ed39f00000000000000000000000000000000000000000000000000ee59ddf89580d6000000000000000000000000f9fc2b4a0e487297b05285e9b3327f26e70c4e9b042f2b650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000604e53520000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000976375726c787e68747470733a2f2f6d61726b65742e6c696e6b2f76312f6e6f6465732f66373662653531392d653431652d343861302d393433302d3437333139656461306634332f766572696669636174696f6e2f726573706f6e73653f746f6b656e3d663336613737333531626533346433323830333965623766643536626630303264706174686d726573706f6e7365546f6b656e",
    "topics": [
      "0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65",
      "0x3431373466353064613337333431306161363430326265313263626538636463"
    ],
    "type": null,
    "timestamp": 1615745574,
    "uuid": "9f04ac61-3d16-48de-917e-95d0a622f404",
    "additionalFields": {
      "removed": "false"
    }
  }

when using web3js web3.eth.abi.decodeLog produces this output

Result {
  '0': '0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65',
  '1': '0xf9fc2b4a0E487297B05285e9B3327f26e70c4E9b',
  '2': '0x6b7ff4ffa51b345e8459b03fdb280ac7f99caaf432142a1b800c581d8b4ed39f',
  '3': '67089854350328022',
  '4': '0xf9fc2b4a0E487297B05285e9B3327f26e70c4E9b',
  '5': '0x042f2b65',
  '6': '1615745874',
  '7': '1',
  '8': '0x6375726c787e68747470733a2f2f6d61726b65742e6c696e6b2f76312f6e6f6465732f66373662653531392d653431652d343861302d393433302d3437333139656461306634332f766572696669636174696f6e2f726573706f6e73653f746f6b656e3d663336613737333531626533346433323830333965623766643536626630303264706174686d726573706f6e7365546f6b656e',
  __length__: 9,
  specId: '0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65',
  requester: '0xf9fc2b4a0E487297B05285e9B3327f26e70c4E9b',
  requestId: '0x6b7ff4ffa51b345e8459b03fdb280ac7f99caaf432142a1b800c581d8b4ed39f',
  payment: '67089854350328022',
  callbackAddr: '0xf9fc2b4a0E487297B05285e9B3327f26e70c4E9b',
  callbackFunctionId: '0x042f2b65',
  cancelExpiration: '1615745874',
  dataVersion: '1',
  data: '0x6375726c787e68747470733a2f2f6d61726b65742e6c696e6b2f76312f6e6f6465732f66373662653531392d653431652d343861302d393433302d3437333139656461306634332f766572696669636174696f6e2f726573706f6e73653f746f6b656e3d663336613737333531626533346433323830333965623766643536626630303264706174686d726573706f6e7365546f6b656e'
}

but we get this error using headlong

tuple index 7: not enough bytes remaining: 151 < 160

Guys, i have a question for how to parse params.

I have a funciton signature like this:

functionName((address,uint256))

i donot know how pass the params. there is always a error named tuple length mismatch really thanks, by the way, may be we need more demos.

bool[] requires boolean[] but found Boolean[]

this version is 9.1.1.

i try f.encodeCall(Tuple.singleton(w)), w is new Boolean[]{false}, An error has occurred, as shown below.

Of course, it's OK to use primitive types, such as new boolean[]{false}.

In addition, it is no problem for non arrays, that is, it does not distinguish between Boolean and boolean

So, please support indistinguishable boolean as method encoding input parameter

The mistake of the original text is

tuple index 0: class mismatch: [Ljava.lang.Boolean; != [Z (bool[] requires boolean[] but found Boolean[]) java.lang.IllegalArgumentException: tuple index 0: class mismatch: [Ljava.lang.Boolean; != [Z (bool[] requires boolean[] but found Boolean[]) at com.esaulpaugh.headlong.abi.TupleType.countBytes(TupleType.java:139) at com.esaulpaugh.headlong.abi.TupleType.countBytes(TupleType.java:127) at com.esaulpaugh.headlong.abi.TupleType.validate(TupleType.java:146) at com.esaulpaugh.headlong.abi.Function.validatedCallLength(Function.java:193) at com.esaulpaugh.headlong.abi.Function.encodeCall(Function.java:205)

Problem with decoding returns when supplying array

Hi, I would like to decode function return. I am testing this returning single element array of structs. Tried using:
public Tuple decodeReturn(ByteBuffer buf) { return outputTypes.decode(buf); } but I am receiving error that unsigned val exceeds bit limit: 160 > 31. Going through stacktrace I am landing in UnitType<J> which as description says is not suitable for arrays... Is there a way that I can decode output of my function?
Thanks!

NoSuchMethodError java.nio.ByteBuffer.position(I)Ljava/nio/ByteBuffer

Hi, I'm getting
java.lang.NoSuchMethodError: java.nio.ByteBuffer.position(I)Ljava/nio/ByteBuffer;
at com.esaulpaugh.headlong.abi.TupleType.decodeObjects(TupleType.java:197)
at com.esaulpaugh.headlong.abi.TupleType.decode(TupleType.java:172)
at com.esaulpaugh.headlong.abi.Function.decodeCall(Function.java:236)
at com.esaulpaugh.headlong.abi.Function.decodeCall(Function.java:218)

when using headlong 5.6.0 from Maven central on Java 1.8.0_45.

If you plan to support Java 8 (as readme says) please consider building using maven.compiler.release=0 (see e.g. https://www.morling.dev/blog/bytebuffer-and-the-dreaded-nosuchmethoderror/).

how to encode address argument

        Function function = new Function("foo(address)");
        ByteBuffer byteBuffer = function.encodeCallWithArgs("f85a7c4b088c07ec9e982f158b63385ff20d6757");

throw exception as follow:

Exception in thread "main" java.lang.IllegalArgumentException: tuple index 0: class mismatch: java.lang.String not assignable to java.math.BigInteger (String not instanceof BigInteger/address)

please help me, thank you

Encoding a pure uint256[] function parameter

Hi, I have a question on how to encode a uint256[] parameter. Creating a tuple for the following works just fine:

Function func = new Function("test(uint256[],bool)");
BigInteger[] bigints = new BigInteger[] { BigInteger.valueOf(7), BigInteger.valueOf(8), BigInteger.valueOf(9) };
Tuple args = Tuple.of(bigints, true);
ByteBuffer bb = func.encodeCall(args);
System.out.println(Function.formatCall(bb.array())); 

but if the function argument is a pure uint256[] such as

Function func = new Function("test(uint256[])");
BigInteger[] bigints = new BigInteger[] { BigInteger.valueOf(7), BigInteger.valueOf(8), BigInteger.valueOf(9) };
Tuple args = Tuple.of(bigints);
ByteBuffer bb = func.encodeCall(args);
System.out.println(Function.formatCall(bb.array())); 

the encoding fails with the following trace:

java.lang.IllegalArgumentException: tuple length mismatch: actual != expected: 3 != 1
	at com.esaulpaugh.headlong.abi.TupleType.validate(TupleType.java:146)
	at com.esaulpaugh.headlong.abi.Function.validatedCallLength(Function.java:193)
	at com.esaulpaugh.headlong.abi.Function.encodeCall(Function.java:205)

Any suggestions on how to pass the pure uint256[] correctly in this case?

Thank you

Issues decoding event with multiple indexed arguments and one non indexed argument.

Here is the abi and it happens for Transfer and Approval events: uniswappairabi

    val blockNumber = 14918135
    val transactionHash = "0x72bf1f4c584163e06f951a92fda5e790cb6a0c87cf549866a6043699d8cc5053"

    val data = Array[Byte]()
    var topics = Array("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", "0x000000000000000000000000bdd95abe8a7694ccd77143376b0fbea183e6a740","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000002646")

    val eventName = "Approval"
    val event = AbiEvent(eventABI).toHeadlongEvent
    val topics = topicsHex.map(decodeHex)
    Some(event.decodeArgs(topics, data))

If I change the value parameter in the abi to be indexed then it worked perfectly fine but if not then it throws an exception:
expected topics.length == 3 but found length 4 which I think is related to how that function only counts indexedParams and not nonIndexedParams. Is their a better fix than just changing the abi for each event I want to decode and hoping it gives consistent data?

Some thoughts regarding `IllegalArgumentException("unconsumed bytes: ...")`

Hi @esaulpaugh ,

Thank you so much for a great library!

We've reached an edge case in decoding where we hit the exception mentioned in the title, IllegalArgumentException("unconsumed bytes: ..."), when decoding a function's output.

We have the following input and output data data:

Input: 0xc37f68e200000000000000000000000037cab7add0393689fa50e979cc9bd8db7a9905aa
Output: 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052f1a067938685bf66f0a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

ABI:

     {
        "constant": true,
        "inputs": [
            {
                "internalType": "address",
                "name": "account",
                "type": "address"
            }
        ],
        "name": "getAccountSnapshot",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            },
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            },
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            },
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }

The library throws the following exception with stack trace:

java.lang.IllegalArgumentException: unconsumed bytes: 64 remaining
	at com.esaulpaugh.headlong.abi.ABIType.decode(ABIType.java:190)
	at com.esaulpaugh.headlong.abi.Function.decodeReturn(Function.java:240)

The problem was quite easy to identify. The output data contains 6 x 32 bytes, while the decoder only expects 4 x 32 bytes ((uint256,uint256,uint256,uint256)). This causes the exception.

One could argue that we do have the wrong data in the first place. Which might be the case, but I've triple checked our node provider API and this is what we get.

We do have a golang implementation of decoding which uses geth which doesn't crash on this data. I've also created a quick test script which uses the official eth-abi library. Both implementations consume the data they need and disregard the last 64 bytes.

This begs the question: Does throwing an exception on unconsumed bytes adhere to the standard? Should we consider removing that check?

If not, we would be happy to discuss alternative APIs for us to use which doesn't do this check.

Let me know what you think!

Håkon

Variable length bytes error during log decoding

When trying to decode the data for this log

{
    "logIndex": "0xae",
    "transactionIndex": "0x51",
    "transactionHash": "0xb304d3c6df4222f8d40722b37a88c6f9b25e0dfab931b7df67b7c6e58aa3e59c",
    "blockHash": "0x12dcda41bfeee8190410ac00ead9af24f14d9142e076fb22480a8be383c1bc50",
    "blockNumber": "0xc3757d",
    "address": "0x6758B7d441a9739b98552B373703d8d3d14f9e62",
    "data": "0x000000000000000000000000000000000000000000000034e2c0e23567d500000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100",
    "topics": [
      "0xe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16",
      "0x00000000000000000000000080dfb2ec0c6e1cc2425b47eff16c26e2ff8ca1c8",
      "0x000000000000000000000000d819e948b14ca6aad2b7ffd333ccdf732b129eed"
    ],
    "type": null
  }

using this abi

{
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "name": "value",
        "type": "uint256"
      },
      {
        "indexed": false,
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "Transfer",
    "type": "event"
  }

doing

val event        = Event.fromJson(abi)
 event.getNonIndexedParams
            .asInstanceOf[ABIType[Tuple]]
            .decode(ByteBuffer.wrap(FastHex.decode(log.data.stripLeadingHex)))

we see the error

tuple index 1: not enough bytes remaining: 1 < 32

doing

event.getNonIndexedParams.decode

yields a similar result

tuple index 1: unsigned val exceeds bit limit: 254 > 31

i would expect it to be just returning empty for the data bytes parameter instead, any suggestions?

bytes and string fields not encoded into 32-byte chunks

We are getting several un-decoded logs with the error: tuple index 7: not enough bytes remaining: 151 < 160 whereas these logs are decoded correctly by Etherscan and web3j.

As you mentioned in two of the previous issues we submitted (issue 1 and issue 2), the data length is not a multiple of 32 and should be padded.

We suspect the bytes and string fields are not padded correctly in some logs due to an old issue in Solidity where these were not encoded correctly and not padded resulting in several logs with a truncated data field. Here is the change in question: https://docs.soliditylang.org/en/latest/050-breaking-changes.html (See paragraph: "The ABI encoder now properly pads byte arrays and strings from calldata (msg.data and external function parameters) when used in external function calls and in abi.encode. For unpadded encoding, use abi.encodePacked.").

We believe other libraries are aware of this error and thus decode those logs and we were wondering if you would be considering adding this change to the library as well.

com.esaulpaugh.headlong.abi.ArrayType cannot be cast to com.esaulpaugh.headlong.abi.TupleType

I've a getName() that returns a string.

   val function = com.esaulpaugh.headlong.abi.Function("getName()", "string")
                    val encodedFunction = function.encodeCallWithArgs()
                    val arr: ByteArray = encodedFunction.array()

                    val stringEncode =FastHex.encodeToString(arr,0, arr.size)

                    val response = web3j.ethCall(
                        Transaction.createEthCallTransaction(
                            credentials.address,
                            deployedContractAddress,
                            stringEncode
                        ),
                        DefaultBlockParameterName.LATEST
                    )
                        .sendAsync().get()


                    val decodeResponse = function.decodeReturn(FastHex.decode(response.value))

I'm getting error that ArrayType cannot be cast to TupleType? How to get rid of this?

Function.formatCall() is not returning the right string.

       val args = Tuple.of("My new Name")
        val function = com.esaulpaugh.headlong.abi.Function("setName(string)")
        val encodedFunction = function.encodeCall(args)
        val arr: ByteArray = encodedFunction.array()
        val formattedEncoding =
            com.esaulpaugh.headlong.abi.Function.formatCall(arr, 0, arr.size)
      print(formattedEncoding)

Actual Result: ID c47f0027
0 0000000000000000000000000000000000000000000000000000000000000020
1 000000000000000000000000000000000000000000000000000000000000000b
2 4d79206e6577204e616d65000000000000000000000000000000000000000000

Expected Result: c47f00270000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b4d79206e6577204e616d65000000000000000000000000000000000000000000

I want the above code to return the expected result but it returns the above actual result. Please have a look @esaulpaugh

How to parse struct as input argument?

I've a function that takes a struct as an argument.

    function addUser(User memory newUser) public payable{
        users.push(newUser);
    }

How can i parse this in headlong?

If i do the following, it does not work
val headLongFun = com.esaulpaugh.headlong.abi.Function("addUser(string)")

Maybe a wrong file placement

Hi,

I have been using the RLP library with examples, and discovered that:

FloatingPoint.java is under package com.esaulpaugh.headlong.rlp.util;
But Integers.java and Strings.java is under package com.esaulpaugh.headlong.util;

Maybe it is better to put them together under some package?

Issues decoding an Opensea `atomicMatch_` function call

Hi @esaulpaugh ,

Thanks again for an amazing library!

I'm trying to decode the following.

Input Data:

0xab834bab0000000000000000000000007f268357a8c2552623316e2562d90e642bb538e50000000000000000000000002dba09471e78d26e42cc9b6057745bb0d430e38500000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000007f268357a8c2552623316e2562d90e642bb538e5000000000000000000000000847236b8c34259a9bed67dcc1647cffba8fc01d40000000000000000000000002dba09471e78d26e42cc9b6057745bb0d430e3850000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ba800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062727d6c000000000000000000000000000000000000000000000000000000006272b5fcba245d25e4e86afcbaf548aa790555f3467f38ba3078a2be2d7c057debc92214000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ba8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000627285c000000000000000000000000000000000000000000000000000000000000000006ff1fad6168460880a98b326f4c38e93fba0037fbe4fadf60939acddf28990510000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000008e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c099ee735236c1090e9eb80abde0b4ca69c81f84ba9deeab8710d7df0792ca90a1d302e1894b6bb588ab14ddf4c8c4218c30893ec9f622e7e019af865a0e96f1a099ee735236c1090e9eb80abde0b4ca69c81f84ba9deeab8710d7df0792ca90a1d302e1894b6bb588ab14ddf4c8c4218c30893ec9f622e7e019af865a0e96f1a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a59500000000000000000000000000000000000000000000000000000000000000000000000000000000000000002dba09471e78d26e42cc9b6057745bb0d430e3850000000000000000000000007d8820fa92eb1584636f4f5b8515b5476b75171a0000000000000000000000000000000000000000000000000000000000000d3d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a595000000000000000000000000847236b8c34259a9bed67dcc1647cffba8fc01d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000007d8820fa92eb1584636f4f5b8515b5476b75171a0000000000000000000000000000000000000000000000000000000000000d3d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

ABI:

{
  "constant": false,
  "inputs": [
    {
      "name": "addrs",
      "type": "address[14]"
    },
    {
      "name": "uints",
      "type": "uint256[18]"
    },
    {
      "name": "feeMethodsSidesKindsHowToCalls",
      "type": "uint8[8]"
    },
    {
      "name": "calldataBuy",
      "type": "bytes"
    },
    {
      "name": "calldataSell",
      "type": "bytes"
    },
    {
      "name": "replacementPatternBuy",
      "type": "bytes"
    },
    {
      "name": "replacementPatternSell",
      "type": "bytes"
    },
    {
      "name": "staticExtradataBuy",
      "type": "bytes"
    },
    {
      "name": "staticExtradataSell",
      "type": "bytes"
    },
    {
      "name": "vs",
      "type": "uint8[2]"
    },
    {
      "name": "rssMetadata",
      "type": "bytes32[5]"
    }
  ],
  "name": "atomicMatch_",
  "outputs": [],
  "payable": true,
  "stateMutability": "payable",
  "type": "function"
}

However, I get the following error:

illegal backwards jump: (4+2368=2372)<2884
java.lang.IllegalArgumentException: illegal backwards jump: (4+2368=2372)<2884
	at com.esaulpaugh.headlong.abi.TupleType.decodeObjects(TupleType.java:307)
	at com.esaulpaugh.headlong.abi.TupleType.decode(TupleType.java:194)
	at com.esaulpaugh.headlong.abi.Function.decodeCall(Function.java:230)

Do you happen to know why? The reason why I ask is that geth seems to be able to decode it.

Thank you!

How to create function that returns array?

I've got problem with creation function that returns struct array.
My function looks like: getPoolsInfo(address[]) then the output is array of structs. Struct has fields: address, string, string
I tried to create function like this:

val f = Function("getPoolsInfo(address[])", "(address,string,string)[]")

But ide is screaming that it cannot cast arraytype to tupletype.
Can you tell me how to construct this function properly?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.