Skip to content

Instantly share code, notes, and snippets.

Created November 18, 2019 21:33
Show Gist options
  • Save wadealexc/7820c0cd82fd5fdc11a0ad58a84165ae to your computer and use it in GitHub Desktop.
Save wadealexc/7820c0cd82fd5fdc11a0ad58a84165ae to your computer and use it in GitHub Desktop.
pragma solidity 0.5.11;
import "../utils/RLPReader.sol";
* @notice Data structure and its decode function for Payment transaction
library PaymentTransactionModel {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant public MAX_INPUT_NUM = 4;
uint8 constant public MAX_OUTPUT_NUM = 4;
uint8 constant private ENCODED_LENGTH = 4;
struct Output {
uint256 outputType;
bytes20 outputGuard;
address token;
uint256 amount;
struct Transaction {
uint256 txType;
bytes32[] inputs;
PaymentOutputModel.Output[] outputs;
bytes32 metaData;
function decodeSpendTx(bytes memory _tx) internal pure returns (PaymentTransactionModel.Transaction memory) {
// NOTE - With a fixed-length encoding, each function could include a length check like this
require(_tx.length == 646);
// NOTE - Specifying .toLongList rather than .toList allows RLPReader to reject some invalid encodings
// almost immediately
RLPReader.RLPItem[] memory rlpTx = _tx.toRlpItem().toLongList();
require(rlpTx.length == ENCODED_LENGTH);
// NOTE - While some inputs and outputs may be zeroed-out, a long list with 4 elements
// is still expected
// - One possible option here is to use a method to check payload size for each element
// as the list is created. toLongListWithPayload(uint _size) would check that each
// element in the list has a certain size.
RLPReader.RLPItem[] memory rlpInputs = rlpTx[1].toLongList();
require(rlpInputs.length == ENCODED_LENGTH);
RLPReader.RLPItem[] memory rlpOutputs = rlpTx[2].toLongList();
require(rlpOutputs.length == ENCODED_LENGTH);
// NOTE - If using an RLP byte as the txType, use an explicit method
uint txType = rlpTx[0].byteToUint();
// NOTE - References to these arrays may reference zeroed-out values.
// This could have been true before as well (but make sure to check on access!)
bytes32[] memory inputs = new bytes32[](ENCODED_LENGTH);
Output[] memory outputs = new Output[](ENCODED_LENGTH);
for (uint i = 0; i < ENCODED_LENGTH; i++) {
bytes32 input = bytes32(rlpInputs[i].shortStringToUint());
inputs[i] = input;
Output memory output = decodeOutput(rlpOutputs[i]);
outputs[i] = output;
bytes32 metaData = bytes32(rlpTx[3].shortStringToUint());
return Transaction({txType: txType, inputs: inputs, outputs: outputs, metaData: metaData});
function decodeOutput(RLPReader.RLPItem memory encoded) internal pure returns (Output memory) {
// NOTE - Again, a check for the size of each element would be helpful (as well as a
// check on the overall payload size)
RLPReader.RLPItem[] memory rlpEncoded = encoded.toLongList();
require(rlpEncoded.length == 4, "Invalid output encoding");
// NOTE - Each of these conversion methods should expect a fixed-length payload
// (32 bytes for uint, 20 for address)
Output memory output = Output({
outputType: rlpEncoded[0].shortStringToUint(),
outputGuard: bytes20(rlpEncoded[1].shortStringToAddress()),
token: rlpEncoded[2].shortStringToAddress(),
amount: rlpEncoded[3].shortStringToUint()
return output;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment