From bd4bb3fa9cc25b1e90b720b659b0668478cac5ba Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Fri, 15 Mar 2024 23:00:49 +0000 Subject: [PATCH] solution: RLP encoding for EIP-4844 transactions --- .../emeraldpay/etherjar/tx/Transaction.java | 2 +- .../etherjar/tx/TransactionDecoder.java | 41 ++++++++ .../etherjar/tx/TransactionEncoder.java | 89 +++++++++++++----- .../etherjar/tx/TransactionType.java | 11 ++- .../etherjar/tx/TransactionWithBlob.java | 51 ++++++++++ .../etherjar/tx/TransactionDecoderSpec.groovy | 94 +++++++++++++++++++ .../etherjar/tx/TransactionEncoderSpec.groovy | 4 +- .../src/test/resources/tx-blob-0x109332.hex | 1 + .../src/test/resources/tx-blob-0x6792c2.hex | 1 + 9 files changed, 266 insertions(+), 28 deletions(-) create mode 100644 etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionWithBlob.java create mode 100644 etherjar-tx/src/test/resources/tx-blob-0x109332.hex create mode 100644 etherjar-tx/src/test/resources/tx-blob-0x6792c2.hex diff --git a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/Transaction.java b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/Transaction.java index a3e4ecff..057ad3ca 100644 --- a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/Transaction.java +++ b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/Transaction.java @@ -152,7 +152,7 @@ public byte[] hash() { } public byte[] hash(Integer chainId) { - byte[] rlp = TransactionEncoder.DEFAULT.encode(this, false, chainId); + byte[] rlp = TransactionEncoder.DEFAULT.encodeStandard(this, false, chainId); Keccak.Digest256 keccak = new Keccak.Digest256(); keccak.update(rlp); diff --git a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionDecoder.java b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionDecoder.java index 784201aa..8dd77296 100644 --- a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionDecoder.java +++ b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionDecoder.java @@ -41,6 +41,9 @@ public Transaction decode(byte[] raw) { if (type == TransactionType.GAS_PRIORITY) { return decodeGasPriority(raw); } + if (type == TransactionType.BLOB) { + return decodeBlob(raw); + } if (type == TransactionType.STANDARD) { return decodeStandard(raw); } @@ -130,6 +133,24 @@ public TransactionWithGasPriority decodeGasPriority(byte[] raw) { return tx; } + public TransactionWithBlob decodeBlob(byte[] raw) { + // rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s]) + RlpReader rdr = startReader(raw, 1); + TransactionWithBlob tx = new TransactionWithBlob(); + readChainId(rdr, tx); + readNonce(rdr, tx); + readPriorityGasPrice(rdr, tx); + readMaxGasPrice(rdr, tx); + readGasLimit(rdr, tx); + readBodyPart(rdr, tx); + + readAccessList(rdr, tx); + readBlob(rdr, tx); + tryReadSignature(rdr, tx); + ensureFullyRead(rdr); + return tx; + } + private RlpReader startReader(byte[] raw, int position) { RlpReader toprdr = new RlpReader(raw, position, raw.length - position); if (toprdr.getType() != RlpType.LIST) { @@ -210,6 +231,26 @@ private void readAccessList(RlpReader rdr, TransactionWithAccess tx) { } } + protected void readBlob(RlpReader rdr, TransactionWithBlob tx) { + // The field max_fee_per_blob_gas is a uint256 + if (rdr.hasNext() && rdr.getType() == RlpType.BYTES) { + tx.setMaxFeePerBlobGas(new Wei(rdr.nextBigInt())); + } else { + throw new IllegalArgumentException("Transaction has invalid RLP encoding. Cannot extract: Max Fee Per Blob Gas"); + } + // the field blob_versioned_hashes represents a list of hash outputs from kzg_to_versioned_hash. + if (rdr.hasNext() && rdr.getType() == RlpType.LIST) { + RlpReader blobVersionedHashesRdr = rdr.nextList(); + List blobVersionedHashes = new ArrayList<>(); + while (blobVersionedHashesRdr.hasNext()) { + blobVersionedHashes.add(Hex32.from(blobVersionedHashesRdr.next())); + } + tx.setBlobVersionedHashes(blobVersionedHashes); + } else { + throw new IllegalArgumentException("Transaction has invalid RLP encoding. Not a list: Blob Versioned Hashes"); + } + } + protected void readDefinitionsPart(RlpReader rdr, Transaction tx) { readNonce(rdr, tx); readGasPrice(rdr, tx); diff --git a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionEncoder.java b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionEncoder.java index b9d32206..8b1ac03b 100644 --- a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionEncoder.java +++ b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionEncoder.java @@ -29,17 +29,20 @@ public byte[] encode(Transaction tx, boolean includeSignature) { if (tx.getType() == TransactionType.GAS_PRIORITY) { return encode((TransactionWithGasPriority) tx, includeSignature); } + if (tx.getType() == TransactionType.BLOB) { + return encode((TransactionWithBlob) tx, includeSignature); + } if (tx.getType() == TransactionType.ACCESS_LIST) { return encode((TransactionWithAccess) tx, includeSignature); } if (tx.getType() == TransactionType.STANDARD) { if (includeSignature) { - return encode(tx, true, null); + return encodeStandard(tx, true, null); } else { Signature signature = tx.getSignature(); if (signature.getType() == SignatureType.EIP155) { int chainId = ((SignatureEIP155)signature).getChainId(); - return encode(tx, false, chainId); + return encodeStandard(tx, false, chainId); } else { throw new IllegalStateException("Neither signature nor chainId specified"); } @@ -48,7 +51,7 @@ public byte[] encode(Transaction tx, boolean includeSignature) { throw new IllegalStateException("Unsupported transaction type: " + tx.getType()); } - public byte[] encode(Transaction tx, boolean includeSignature, Integer chainId) { + protected byte[] encodeStandard(Transaction tx, boolean includeSignature, Integer chainId) { RlpWriter wrt = new RlpWriter(); wrt.startList() .write(tx.getNonce()) @@ -90,7 +93,7 @@ public byte[] encode(Transaction tx, boolean includeSignature, Integer chainId) return wrt.toByteArray(); } - protected void writeBody(RlpWriter wrt, TransactionWithAccess tx, boolean includeSignature) { + protected void writeBody(RlpWriter wrt, Transaction tx) { if (tx.getTo() != null) { wrt.write(tx.getTo().getBytes()); } else { @@ -108,7 +111,9 @@ protected void writeBody(RlpWriter wrt, TransactionWithAccess tx, boolean includ } else { wrt.write(new byte[0]); } + } + private static void writeAccessList(RlpWriter wrt, TransactionWithAccess tx) { wrt.startList(); for (TransactionWithAccess.Access access: tx.getAccessList()) { wrt.startList(); @@ -121,27 +126,35 @@ protected void writeBody(RlpWriter wrt, TransactionWithAccess tx, boolean includ wrt.closeList(); } wrt.closeList(); + } - if (includeSignature) { - Signature signature = tx.getSignature(); - if (signature == null) { - // just empty signature - wrt.write(0) - .write(0) - .write(0); - } else { - if (signature.getType() == SignatureType.EIP2930) { - int yParity = ((SignatureEIP2930)signature).getYParity(); - if (yParity == 0) { - wrt.write(0); - } else { - wrt.write(Integer.valueOf(yParity).byteValue()); - } - wrt.write(signature.getR()); - wrt.write(signature.getS()); + private static void writeBlob(RlpWriter wrt, TransactionWithBlob tx) { + wrt.write(tx.getMaxFeePerBlobGas().getAmount()); + wrt.startList(); + for (Hex32 hash: tx.getBlobVersionedHashes()) { + wrt.write(hash.getBytes()); + } + wrt.closeList(); + } + + private static void writeSignature(RlpWriter wrt, Signature signature) { + if (signature == null) { + // just empty signature + wrt.write(0) + .write(0) + .write(0); + } else { + if (signature.getType() == SignatureType.EIP2930) { + int yParity = ((SignatureEIP2930)signature).getYParity(); + if (yParity == 0) { + wrt.write(0); } else { - throw new ClassCastException("Required signature " + SignatureEIP2930.class.getName() + " but have " + signature.getClass().getName()); + wrt.write(Integer.valueOf(yParity).byteValue()); } + wrt.write(signature.getR()); + wrt.write(signature.getS()); + } else { + throw new ClassCastException("Required signature " + SignatureEIP2930.class.getName() + " but have " + signature.getClass().getName()); } } } @@ -155,7 +168,11 @@ public byte[] encode(TransactionWithAccess tx, boolean includeSignature) { .write(tx.getNonce()) .write(tx.getGasPrice().getAmount()) .write(tx.getGas()); - writeBody(wrt, tx, includeSignature); + writeBody(wrt, tx); + writeAccessList(wrt, tx); + if (includeSignature) { + writeSignature(wrt, tx.getSignature()); + } wrt.closeList(); return buffer.toByteArray(); } @@ -170,7 +187,31 @@ public byte[] encode(TransactionWithGasPriority tx, boolean includeSignature) { .write(tx.getPriorityGasPrice().getAmount()) .write(tx.getMaxGasPrice().getAmount()) .write(tx.getGas()); - writeBody(wrt, tx, includeSignature); + writeBody(wrt, tx); + writeAccessList(wrt, tx); + if (includeSignature) { + writeSignature(wrt, tx.getSignature()); + } + wrt.closeList(); + return buffer.toByteArray(); + } + + public byte[] encode(TransactionWithBlob tx, boolean includeSignature) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + buffer.write(TransactionType.BLOB.getFlag()); + RlpWriter wrt = new RlpWriter(buffer); + wrt.startList() + .write(tx.getChainId()) + .write(tx.getNonce()) + .write(tx.getPriorityGasPrice().getAmount()) + .write(tx.getMaxGasPrice().getAmount()) + .write(tx.getGas()); + writeBody(wrt, tx); + writeAccessList(wrt, tx); + writeBlob(wrt, tx); + if (includeSignature) { + writeSignature(wrt, tx.getSignature()); + } wrt.closeList(); return buffer.toByteArray(); } diff --git a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionType.java b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionType.java index 5fb44f22..e59a6060 100644 --- a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionType.java +++ b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionType.java @@ -34,7 +34,13 @@ public enum TransactionType { * Transaction with Gas Max and Priority prices. * @see EIP-1559 */ - GAS_PRIORITY((byte)2); + GAS_PRIORITY((byte)2), + + /** + * Blob transaction + * @see EIP-4844 + */ + BLOB((byte)3); private final byte flag; @@ -63,6 +69,9 @@ public static TransactionType fromPrefix(byte prefix) { if (u == 2) { return TransactionType.GAS_PRIORITY; } + if (u == 3) { + return TransactionType.BLOB; + } if (u == 1) { return TransactionType.ACCESS_LIST; } diff --git a/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionWithBlob.java b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionWithBlob.java new file mode 100644 index 00000000..7be01c34 --- /dev/null +++ b/etherjar-tx/src/main/java/io/emeraldpay/etherjar/tx/TransactionWithBlob.java @@ -0,0 +1,51 @@ +package io.emeraldpay.etherjar.tx; + +import io.emeraldpay.etherjar.domain.Wei; +import io.emeraldpay.etherjar.hex.Hex32; +import org.bouncycastle.jcajce.provider.digest.Keccak; + +import java.util.List; + +/** + * Transaction with Blobs (EIP-2718) + * + * @see EIP-2718 + */ +public class TransactionWithBlob extends TransactionWithGasPriority { + + private Wei maxFeePerBlobGas; + + /** + * Represents a list of hash outputs from kzg_to_versioned_hash + */ + private List blobVersionedHashes; + + public Wei getMaxFeePerBlobGas() { + return maxFeePerBlobGas; + } + + public void setMaxFeePerBlobGas(Wei maxFeePerBlobGas) { + this.maxFeePerBlobGas = maxFeePerBlobGas; + } + + public List getBlobVersionedHashes() { + return blobVersionedHashes; + } + + public void setBlobVersionedHashes(List blobVersionedHashes) { + this.blobVersionedHashes = blobVersionedHashes; + } + + @Override + public TransactionType getType() { + return TransactionType.BLOB; + } + + @Override + public byte[] hash() { + byte[] rlp = TransactionEncoder.DEFAULT.encode(this, false); + Keccak.Digest256 keccak = new Keccak.Digest256(); + keccak.update(rlp); + return keccak.digest(); + } +} diff --git a/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionDecoderSpec.groovy b/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionDecoderSpec.groovy index 78c865f1..e9d2ccdc 100644 --- a/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionDecoderSpec.groovy +++ b/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionDecoderSpec.groovy @@ -16,9 +16,12 @@ package io.emeraldpay.etherjar.tx import io.emeraldpay.etherjar.domain.Address +import io.emeraldpay.etherjar.domain.TransactionId import io.emeraldpay.etherjar.domain.Wei import io.emeraldpay.etherjar.hex.Hex32 +import io.emeraldpay.etherjar.hex.HexData import org.apache.commons.codec.binary.Hex +import org.bouncycastle.jcajce.provider.digest.Keccak import spock.lang.Specification class TransactionDecoderSpec extends Specification { @@ -371,4 +374,95 @@ class TransactionDecoderSpec extends Specification { recoverAddress() == Address.from("0x72e96f7033c40cacc2fd554836846c2258f39909") } } + + def "Parse tx type 0x6792c2"() { + // tx 0x6792c2e7a345b10a1f8d5cc4897f363602576e016a68a1e5e61729967b92f7f5 + // data 0x03f9013b018310dd0f84773594008513d978f31082520894ff000000000000000000000000000000000000108080c0843b9aca00f8c6a0012e5ded2e690630287c8adf37fa41eaf98d887a29e2681a217d5ee511e86662a00152dc10942b02211cb44f21de65b7a166baa4f4c890eff4b39e0a79241e888ba001ef5bb4e3786697e4015d6d36e40490c3b01afad29be3ae0d470dbae8733a8fa001fa161dedc837ebd8615c7039cebea1dadb643e47ddd5d5e10f1e46ca355ca1a00100b76767b31dd57588183d2ad854802a0ae2e0839e7ecfa09f81af97435f1ea001222a1924007ab3f7e229cbf0254491b64621d96e0cc8da96e0752b3f00188b80a0d6919cc4c26508304c85e7ff91defe79a653a69619b068fa60bd49955c62adcfa040ccb84aebe0138ac45445759a82204ca197a2b1bd41394dd92e5696e441459b + // see https://etherscan.io/tx/0x6792c2e7a345b10a1f8d5cc4897f363602576e016a68a1e5e61729967b92f7f5#blobs + setup: + def txHex = TransactionDecoderSpec.class.getClassLoader().getResourceAsStream("tx-blob-0x6792c2.hex").text.trim() + def tx = Hex.decodeHex(txHex) + Keccak.Digest256 keccak = new Keccak.Digest256(); + keccak.update(tx); + println("Exp: " + TransactionId.from(keccak.digest())); + + + when: + def act = decoder.decode(tx) + println("Hash: " + act.transactionId()) + + then: + act.type == TransactionType.BLOB + with((TransactionWithBlob)act) { + value == Wei.ZERO + chainId == 1 + extractFrom() == Address.from("0x6887246668a3b87F54DeB3b94Ba47a6f63F32985") + to == Address.from("0xFF00000000000000000000000000000000000010") + gas == 21000 + nonce == 0x10dd0f + accessList.isEmpty() + maxGasPrice.toHex() == "0x13d978f310" + priorityGasPrice.toHex() == "0x77359400" + maxFeePerBlobGas.toHex() == "0x3b9aca00" + transactionId().toHex() == "0x6792c2e7a345b10a1f8d5cc4897f363602576e016a68a1e5e61729967b92f7f5" + blobVersionedHashes.size() == 6 + blobVersionedHashes[0].toHex() == "0x012e5ded2e690630287c8adf37fa41eaf98d887a29e2681a217d5ee511e86662" + blobVersionedHashes[1].toHex() == "0x0152dc10942b02211cb44f21de65b7a166baa4f4c890eff4b39e0a79241e888b" + blobVersionedHashes[2].toHex() == "0x01ef5bb4e3786697e4015d6d36e40490c3b01afad29be3ae0d470dbae8733a8f" + blobVersionedHashes[3].toHex() == "0x01fa161dedc837ebd8615c7039cebea1dadb643e47ddd5d5e10f1e46ca355ca1" + blobVersionedHashes[4].toHex() == "0x0100b76767b31dd57588183d2ad854802a0ae2e0839e7ecfa09f81af97435f1e" + blobVersionedHashes[5].toHex() == "0x01222a1924007ab3f7e229cbf0254491b64621d96e0cc8da96e0752b3f00188b" + } + with((SignatureEIP2930)act.getSignature()) { + r.toString(16) == "d6919cc4c26508304c85e7ff91defe79a653a69619b068fa60bd49955c62adcf" + s.toString(16) == "40ccb84aebe0138ac45445759a82204ca197a2b1bd41394dd92e5696e441459b" + getYParity() == 0 + } + + when: + def encoded = TransactionEncoder.DEFAULT.encode(act, true) + + then: + Hex.encodeHexString(encoded) == txHex + } + + def "Parse tx type 0x109332"() { + // tx 0x109332e227bb505e5731fcbe231dc8d9c0136c14300cd34e40097abf72c51105 + setup: + def txHex = TransactionDecoderSpec.class.getClassLoader().getResourceAsStream("tx-blob-0x109332.hex").text.trim() + def tx = Hex.decodeHex(txHex) + + when: + def act = decoder.decode(tx) + + then: + act.type == TransactionType.BLOB + with((TransactionWithBlob)act) { + value == Wei.ZERO + chainId == 1 + extractFrom() == Address.from("0x2c169dfe5fbba12957bdd0ba47d9cedbfe260ca7") + to == Address.from("0xc662c410c0ecf747543f5ba90660f6abebd9c8c4") + gas == 0x53ec60 + nonce == 0x96f2c + accessList.isEmpty() + maxGasPrice.toHex() == "0x22ecb25c00" + priorityGasPrice.toHex() == "0x5f5e100" + maxFeePerBlobGas.toHex() == "0x22ecb25c00" + transactionId().toHex() == "0x109332e227bb505e5731fcbe231dc8d9c0136c14300cd34e40097abf72c51105" + data.toHex() == "0xb72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000d00c61b69ddec675e4b7ffeb185d154a8ed3623d4df8786b555a7bf44abecfe0f03d30fea5a885673f5647975be75e656672f176580385a21f4a27c23664112110000000000000000000000000000000000000000000000000000000000096f2b0773f23b4b16001938abff13310bc1eac3a820c0c05944224cf7f5479700be6c05ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a200000000000000000000000000000000000000000000000000000000000000010000000000000000cb544616679c59d793a8814c7605b76f121e350626ea6cba0000000000000000963c4cd9d646d98528c052d384fe425af01f7f8bec0203e90304a6d9bc75a700597be71c9fa71286f76a3d5b0fd40f0eb7ef22ec19b08df200000000000000000000000000000000eacbe294cd474ee851cd8634ab96e968000000000000000000000000000000001321355a1f29da6c49b6267eefabb2c4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030a246ff605cb19ea1fa86c847d516c797fd5f98c913ab601de00e831f059661fe932e6e5de8b3a36d9ad2daf04744e10500000000000000000000000000000000" + blobVersionedHashes.size() == 1 + blobVersionedHashes[0].toHex() == "0x01ebcede241d2473b6b1a6fa08f0a281858bc8901f742a39ff74d371e2f2664f" + } + with((SignatureEIP2930)act.getSignature()) { + r.toString(16) == "a0933938cbf7279651014732d845f7bfda8575e543c6f6350640d2e3a090db42" + s.toString(16) == "767e7e993e6e7e1c98d23b7dd782f734e2a8e54dc3083c7fabcab925759d2814" + getYParity() == 0 + } + + when: + def encoded = TransactionEncoder.DEFAULT.encode(act, true) + + then: + Hex.encodeHexString(encoded) == txHex + } } diff --git a/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionEncoderSpec.groovy b/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionEncoderSpec.groovy index 4b78f46d..78b1bf2a 100644 --- a/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionEncoderSpec.groovy +++ b/etherjar-tx/src/test/groovy/io/emeraldpay/etherjar/tx/TransactionEncoderSpec.groovy @@ -65,7 +65,7 @@ class TransactionEncoderSpec extends Specification { value = Wei.ofEthers(1) } when: - def act = encoder.encode(tx, false, 1) + def act = encoder.encodeStandard(tx, false, 1) then: Hex.encodeHexString(act) == "ec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080" } @@ -92,7 +92,7 @@ class TransactionEncoderSpec extends Specification { "80" + //r "80" //s when: - def act = encoder.encode(tx,false, 0x25) + def act = encoder.encodeStandard(tx,false, 0x25) then: Hex.encodeHexString(act) == exp } diff --git a/etherjar-tx/src/test/resources/tx-blob-0x109332.hex b/etherjar-tx/src/test/resources/tx-blob-0x109332.hex new file mode 100644 index 00000000..8fad8290 --- /dev/null +++ b/etherjar-tx/src/test/resources/tx-blob-0x109332.hex @@ -0,0 +1 @@ +03f902fd0183096f2c8405f5e1008522ecb25c008353ec6094c662c410c0ecf747543f5ba90660f6abebd9c8c480b90264b72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000d00c61b69ddec675e4b7ffeb185d154a8ed3623d4df8786b555a7bf44abecfe0f03d30fea5a885673f5647975be75e656672f176580385a21f4a27c23664112110000000000000000000000000000000000000000000000000000000000096f2b0773f23b4b16001938abff13310bc1eac3a820c0c05944224cf7f5479700be6c05ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a200000000000000000000000000000000000000000000000000000000000000010000000000000000cb544616679c59d793a8814c7605b76f121e350626ea6cba0000000000000000963c4cd9d646d98528c052d384fe425af01f7f8bec0203e90304a6d9bc75a700597be71c9fa71286f76a3d5b0fd40f0eb7ef22ec19b08df200000000000000000000000000000000eacbe294cd474ee851cd8634ab96e968000000000000000000000000000000001321355a1f29da6c49b6267eefabb2c4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030a246ff605cb19ea1fa86c847d516c797fd5f98c913ab601de00e831f059661fe932e6e5de8b3a36d9ad2daf04744e10500000000000000000000000000000000c08522ecb25c00e1a001ebcede241d2473b6b1a6fa08f0a281858bc8901f742a39ff74d371e2f2664f80a0a0933938cbf7279651014732d845f7bfda8575e543c6f6350640d2e3a090db42a0767e7e993e6e7e1c98d23b7dd782f734e2a8e54dc3083c7fabcab925759d2814 diff --git a/etherjar-tx/src/test/resources/tx-blob-0x6792c2.hex b/etherjar-tx/src/test/resources/tx-blob-0x6792c2.hex new file mode 100644 index 00000000..084d3e30 --- /dev/null +++ b/etherjar-tx/src/test/resources/tx-blob-0x6792c2.hex @@ -0,0 +1 @@ +03f9013b018310dd0f84773594008513d978f31082520894ff000000000000000000000000000000000000108080c0843b9aca00f8c6a0012e5ded2e690630287c8adf37fa41eaf98d887a29e2681a217d5ee511e86662a00152dc10942b02211cb44f21de65b7a166baa4f4c890eff4b39e0a79241e888ba001ef5bb4e3786697e4015d6d36e40490c3b01afad29be3ae0d470dbae8733a8fa001fa161dedc837ebd8615c7039cebea1dadb643e47ddd5d5e10f1e46ca355ca1a00100b76767b31dd57588183d2ad854802a0ae2e0839e7ecfa09f81af97435f1ea001222a1924007ab3f7e229cbf0254491b64621d96e0cc8da96e0752b3f00188b80a0d6919cc4c26508304c85e7ff91defe79a653a69619b068fa60bd49955c62adcfa040ccb84aebe0138ac45445759a82204ca197a2b1bd41394dd92e5696e441459b