diff --git a/crypto/src/main/java/org/web3j/crypto/RawTransaction.java b/crypto/src/main/java/org/web3j/crypto/RawTransaction.java index a34a24b866..bd3f4ff6b1 100644 --- a/crypto/src/main/java/org/web3j/crypto/RawTransaction.java +++ b/crypto/src/main/java/org/web3j/crypto/RawTransaction.java @@ -13,6 +13,7 @@ package org.web3j.crypto; import java.math.BigInteger; +import java.util.Collections; import java.util.List; import org.web3j.crypto.transaction.type.ITransaction; @@ -115,7 +116,32 @@ public static RawTransaction createTransaction( value, data, maxPriorityFeePerGas, - maxFeePerGas)); + maxFeePerGas, + Collections.emptyList())); + } + + public static RawTransaction createTransaction( + long chainId, + BigInteger nonce, + BigInteger gasLimit, + String to, + BigInteger value, + String data, + BigInteger maxPriorityFeePerGas, + BigInteger maxFeePerGas, + List accessList) { + + return new RawTransaction( + Transaction1559.createTransaction( + chainId, + nonce, + gasLimit, + to, + value, + data, + maxPriorityFeePerGas, + maxFeePerGas, + accessList)); } public static RawTransaction createTransaction( diff --git a/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java b/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java index b5ee7cb2ba..3b3dbe6e70 100644 --- a/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java +++ b/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java @@ -64,6 +64,8 @@ private static RawTransaction decodeEIP1559Transaction(final byte[] transaction) final BigInteger value = ((RlpString) values.getValues().get(6)).asPositiveBigInteger(); final String data = ((RlpString) values.getValues().get(7)).asString(); + List accessList = + decodeAccessList(((RlpList) values.getValues().get(8)).getValues()); final RawTransaction rawTransaction = RawTransaction.createTransaction( @@ -74,7 +76,8 @@ private static RawTransaction decodeEIP1559Transaction(final byte[] transaction) value, data, maxPriorityFeePerGas, - maxFeePerGas); + maxFeePerGas, + accessList); if (values.getValues().size() == UNSIGNED_EIP1559TX_RLP_LIST_SIZE) { return rawTransaction; diff --git a/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction1559.java b/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction1559.java index 46b96f208f..ae07684dd3 100644 --- a/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction1559.java +++ b/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction1559.java @@ -14,8 +14,10 @@ import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import org.web3j.crypto.AccessListObject; import org.web3j.crypto.Sign; import org.web3j.rlp.RlpList; import org.web3j.rlp.RlpString; @@ -23,16 +25,13 @@ import org.web3j.utils.Bytes; import org.web3j.utils.Numeric; -import static org.web3j.crypto.transaction.type.TransactionType.EIP1559; - /** * Transaction class used for signing 1559 transactions locally.
* For the specification, refer to p4 of the yellow * paper. */ -public class Transaction1559 extends LegacyTransaction implements ITransaction { +public class Transaction1559 extends Transaction2930 implements ITransaction { - private long chainId; private BigInteger maxPriorityFeePerGas; private BigInteger maxFeePerGas; @@ -45,8 +44,22 @@ public Transaction1559( String data, BigInteger maxPriorityFeePerGas, BigInteger maxFeePerGas) { - super(EIP1559, nonce, null, gasLimit, to, value, data); - this.chainId = chainId; + super(chainId, nonce, null, gasLimit, to, value, data, Collections.emptyList()); + this.maxPriorityFeePerGas = maxPriorityFeePerGas; + this.maxFeePerGas = maxFeePerGas; + } + + public Transaction1559( + long chainId, + BigInteger nonce, + BigInteger gasLimit, + String to, + BigInteger value, + String data, + BigInteger maxPriorityFeePerGas, + BigInteger maxFeePerGas, + List accessList) { + super(chainId, nonce, null, gasLimit, to, value, data, accessList); this.maxPriorityFeePerGas = maxPriorityFeePerGas; this.maxFeePerGas = maxFeePerGas; } @@ -83,7 +96,7 @@ public List asRlpValues(Sign.SignatureData signatureData) { result.add(RlpString.create(data)); // access list - result.add(new RlpList()); + result.add(new RlpList(rlpAccessListRlp())); if (signatureData != null) { result.add(RlpString.create(Sign.getRecId(signatureData, getChainId()))); @@ -103,7 +116,38 @@ public static Transaction1559 createEtherTransaction( BigInteger maxPriorityFeePerGas, BigInteger maxFeePerGas) { return new Transaction1559( - chainId, nonce, gasLimit, to, value, "", maxPriorityFeePerGas, maxFeePerGas); + chainId, + nonce, + gasLimit, + to, + value, + "", + maxPriorityFeePerGas, + maxFeePerGas, + Collections.emptyList()); + } + + public static Transaction1559 createTransaction( + long chainId, + BigInteger nonce, + BigInteger gasLimit, + String to, + BigInteger value, + String data, + BigInteger maxPriorityFeePerGas, + BigInteger maxFeePerGas, + List accessList) { + + return new Transaction1559( + chainId, + nonce, + gasLimit, + to, + value, + data, + maxPriorityFeePerGas, + maxFeePerGas, + accessList); } public static Transaction1559 createTransaction( @@ -121,12 +165,13 @@ public static Transaction1559 createTransaction( } @Override - public BigInteger getGasPrice() { - throw new UnsupportedOperationException("not available for 1559 transaction"); + public TransactionType getType() { + return TransactionType.EIP1559; } - public long getChainId() { - return chainId; + @Override + public BigInteger getGasPrice() { + throw new UnsupportedOperationException("not available for 1559 transaction"); } public BigInteger getMaxPriorityFeePerGas() { diff --git a/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction2930.java b/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction2930.java index b278ea1853..5b1c6849bf 100644 --- a/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction2930.java +++ b/crypto/src/main/java/org/web3j/crypto/transaction/type/Transaction2930.java @@ -71,6 +71,19 @@ public List asRlpValues(Sign.SignatureData signatureData) { result.add(RlpString.create(data)); // access list + result.add(new RlpList(rlpAccessListRlp())); + + if (signatureData != null) { + result.add(RlpString.create(Sign.getRecId(signatureData, getChainId()))); + result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getR()))); + result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getS()))); + } + + return result; + } + + protected List rlpAccessListRlp() { + List accessList = getAccessList(); List rlpAccessList = new ArrayList<>(); accessList.forEach( @@ -89,15 +102,7 @@ public List asRlpValues(Sign.SignatureData signatureData) { rlpAccessListObject.add(new RlpList(keyList)); rlpAccessList.add(new RlpList(rlpAccessListObject)); }); - result.add(new RlpList(rlpAccessList)); - - if (signatureData != null) { - result.add(RlpString.create(Sign.getRecId(signatureData, getChainId()))); - result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getR()))); - result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getS()))); - } - - return result; + return rlpAccessList; } public static Transaction2930 createEtherTransaction( diff --git a/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java b/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java index a6ba23810b..6a4ae23fd1 100644 --- a/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java +++ b/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java @@ -153,6 +153,32 @@ public void testDecoding1559() { assertEquals(transaction1559.getData(), resultTransaction1559.getData()); } + @Test + public void testDecoding1559AccessList() { + final RawTransaction rawTransaction = createEip1559RawTransactionAccessList(); + final Transaction1559 transaction1559 = (Transaction1559) rawTransaction.getTransaction(); + + final byte[] encodedMessage = TransactionEncoder.encode(rawTransaction); + final String hexMessage = Numeric.toHexString(encodedMessage); + + final RawTransaction result = TransactionDecoder.decode(hexMessage); + assertTrue(result.getTransaction() instanceof Transaction1559); + final Transaction1559 resultTransaction1559 = (Transaction1559) result.getTransaction(); + + assertNotNull(result); + assertEquals(transaction1559.getChainId(), resultTransaction1559.getChainId()); + assertEquals(transaction1559.getNonce(), resultTransaction1559.getNonce()); + assertEquals(transaction1559.getMaxFeePerGas(), resultTransaction1559.getMaxFeePerGas()); + assertEquals( + transaction1559.getMaxPriorityFeePerGas(), + resultTransaction1559.getMaxPriorityFeePerGas()); + assertEquals(transaction1559.getGasLimit(), resultTransaction1559.getGasLimit()); + assertEquals(transaction1559.getTo(), resultTransaction1559.getTo()); + assertEquals(transaction1559.getValue(), resultTransaction1559.getValue()); + assertEquals(transaction1559.getData(), resultTransaction1559.getData()); + assertEquals(transaction1559.getAccessList(), resultTransaction1559.getAccessList()); + } + @Test public void testDecodingSigned1559() throws SignatureException { final RawTransaction rawTransaction = createEip1559RawTransaction(); @@ -202,6 +228,31 @@ private static RawTransaction createEip1559RawTransaction() { BigInteger.valueOf(1100000)); } + private static RawTransaction createEip1559RawTransactionAccessList() { + List accessList = + Stream.of( + new AccessListObject( + "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", + Stream.of( + "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000000000000000000000000000000000000000000000000000000000000007") + .collect(toList())), + new AccessListObject( + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + Collections.emptyList())) + .collect(toList()); + return RawTransaction.createTransaction( + 3L, + BigInteger.valueOf(0), + BigInteger.valueOf(30000), + "0x627306090abab3a6e1400e9345bc60c78a8bef57", + BigInteger.valueOf(123), + "0x1000001111100000", + BigInteger.valueOf(5678), + BigInteger.valueOf(1100000), + accessList); + } + @Test public void testDecoding2930() { final RawTransaction rawTransaction = createEip2930RawTransaction();