Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]Feat: add EIP-1559 transaction type #12

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract Example {
bytes memory rlp = Transactions.encodeRLP(legacyTxn0);

// Decode from RLP
Transactions.Legacy memory legacyTxn1 = Transactions.decodeRLP(rlp);
Transactions.Legacy memory legacyTxn1 = Transactions.decodeLegacyRLP(rlp);
}
}
```
Expand Down
80 changes: 79 additions & 1 deletion src/Transactions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ library Transactions {
bytes v;
}

struct EIP1559 {
address to;
uint64 gas;
uint64 maxFeePerGas;
uint64 maxPriorityFeePerGas;
uint64 value;
uint64 nonce;
bytes data;
uint64 chainId;
bytes accessList;
bytes r;
bytes s;
bytes v;
}

function encodeRLP(Legacy memory txStruct) internal pure returns (bytes memory) {
bytes[] memory items = new bytes[](9);

Expand All @@ -43,7 +58,42 @@ library Transactions {
return RLPWriter.writeList(items);
}

function decodeRLP(bytes memory rlp) internal pure returns (Legacy memory) {
function encodeRLP(EIP1559 memory txStruct) internal pure returns (bytes memory) {
bytes[] memory items = new bytes[](12);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this encoding correct? EIP-1559 transactions are encoded with EIP-2718 format, so it needs to have the transaction type prefixed. You can find a code reference here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just fixed this, lmk if it is still not correct.


items[0] = RLPWriter.writeUint(txStruct.chainId);
items[1] = RLPWriter.writeUint(txStruct.nonce);
items[2] = RLPWriter.writeUint(txStruct.maxPriorityFeePerGas);
items[3] = RLPWriter.writeUint(txStruct.maxFeePerGas);
items[4] = RLPWriter.writeUint(txStruct.gas);
items[5] = RLPWriter.writeAddress(txStruct.to);
items[6] = RLPWriter.writeUint(txStruct.value);
items[7] = RLPWriter.writeBytes(txStruct.data);

// Check if accessList is empty
if (txStruct.accessList.length == 0) {
items[8] = hex"c0"; // Empty list encoding
} else {
items[8] = RLPWriter.writeBytes(txStruct.accessList);
}

items[9] = RLPWriter.writeBytes(txStruct.v);
items[10] = RLPWriter.writeBytes(txStruct.r);
items[11] = RLPWriter.writeBytes(txStruct.s);

bytes memory rlpTxn = RLPWriter.writeList(items);

bytes memory txn = new bytes(1 + rlpTxn.length);
txn[0] = 0x02;

for (uint256 i = 0; i < rlpTxn.length; ++i) {
txn[i + 1] = rlpTxn[i];
}

return txn;
}

function decodeLegacyRLP(bytes memory rlp) internal pure returns (Legacy memory) {
Legacy memory txStruct;

RLPReader.RLPItem[] memory ls = rlp.toRlpItem().toList();
Expand All @@ -67,4 +117,32 @@ library Transactions {

return txStruct;
}

function decodeRLP(bytes memory rlp1) internal pure returns (EIP1559 memory) {
EIP1559 memory txStruct;

bytes memory rlp = new bytes(rlp1.length - 1);

for (uint256 i = 0; i < rlp1.length - 1; ++i) {
rlp[i] = rlp1[i + 1];
}

RLPReader.RLPItem[] memory ls = rlp.toRlpItem().toList();
require(ls.length == 12, "invalid transaction");

txStruct.chainId = uint64(ls[0].toUint());
txStruct.nonce = uint64(ls[1].toUint());
txStruct.maxPriorityFeePerGas = uint64(ls[2].toUint());
txStruct.maxFeePerGas = uint64(ls[3].toUint());
txStruct.gas = uint64(ls[4].toUint());
txStruct.to = ls[5].toAddress();
txStruct.value = uint64(ls[6].toUint());
txStruct.data = ls[7].toBytes();
txStruct.accessList = ls[8].toBytes();
txStruct.v = ls[9].toBytes();
txStruct.r = ls[10].toBytes();
txStruct.s = ls[11].toBytes();

return txStruct;
}
}
51 changes: 50 additions & 1 deletion test/Transactions.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,63 @@ contract TestTransactions is Test {
_testLegacyTransaction(txnWithoutToAddress, expected);
}

function testEIP1559TransactionRLPEncoding() public {
// address to;
// uint64 gas;
// uint64 maxFeePerGas;
// uint64 maxPriorityFeePerGas;
// uint64 value;
// uint64 nonce;
// bytes data;
// uint64 chainId;
// bytes accessList;
// bytes r;
// bytes s;
// bytes v;

Transactions.EIP1559 memory txnWithToAddress = Transactions.EIP1559({
to: address(0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85),
gas: 64744,
maxFeePerGas: 74341019612,
maxPriorityFeePerGas: 74341019612,
value: 0,
nonce: 38,
data: abi.encodePacked(
hex"a9059cbb00000000000000000000000061b7b515c1ec603cf21463bcac992b60fd610ca900000000000000000000000000000000000000000000002dbf877cf6ec677800"
),
chainId: 1,
accessList: bytes(""),
v: bytes(""),
r: abi.encodePacked(hex"8ee28a85ac42174b9e10c49613c0cddcf5d5a5ecb90bd516f81b45a957a64fe2"),
s: abi.encodePacked(hex"05349c1076cc83990f425773d6b5995474782f1fccf1b2e43529ac54ac6ae144")
});

bytes memory expected = abi.encodePacked(
hex"02f8b1012685114f11efdc85114f11efdc82fce894aea46a60368a7bd060eec7df8cba43b7ef41ad8580b844a9059cbb00000000000000000000000061b7b515c1ec603cf21463bcac992b60fd610ca900000000000000000000000000000000000000000000002dbf877cf6ec677800c080a08ee28a85ac42174b9e10c49613c0cddcf5d5a5ecb90bd516f81b45a957a64fe2a005349c1076cc83990f425773d6b5995474782f1fccf1b2e43529ac54ac6ae144"
);

_testEIP1559Transaction(txnWithToAddress, expected);
}

function _testLegacyTransaction(Transactions.Legacy memory legacyTxn, bytes memory expectedRlp) public {
bytes memory rlp = Transactions.encodeRLP(legacyTxn);
assertEq0(rlp, expectedRlp);

Transactions.Legacy memory legacyTxn1 = Transactions.decodeRLP(rlp);
Transactions.Legacy memory legacyTxn1 = Transactions.decodeLegacyRLP(rlp);

// re-encode to validate that the decoding was correct
bytes memory rlp1 = Transactions.encodeRLP(legacyTxn1);
assertEq0(rlp1, expectedRlp);
}

function _testEIP1559Transaction(Transactions.EIP1559 memory eip1559Txn, bytes memory expectedRlp) public {
bytes memory rlp = Transactions.encodeRLP(eip1559Txn);
assertEq0(rlp, expectedRlp);

// Transactions.EIP1559 memory eip1559Txn1 = Transactions.decodeRLP(rlp);

// // re-encode to validate that the decoding was correct
// bytes memory rlp1 = Transactions.encodeRLP(eip1559Txn1);
// assertEq0(rlp1, expectedRlp);
}
}