@0x:contracts-utils Added RichErrors to LibBytes

This commit is contained in:
James Towle 2019-07-08 21:27:17 -05:00 committed by Amir Bandeali
parent c788db785b
commit d3db2dcfbb
8 changed files with 479 additions and 166 deletions

View File

@ -13,7 +13,7 @@ import {
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { transactionHashUtils } from '@0x/order-utils'; import { transactionHashUtils } from '@0x/order-utils';
import { EIP712DomainWithDefaultSchema, RevertReason, SignatureType, SignedOrder } from '@0x/types'; import { EIP712DomainWithDefaultSchema, RevertReason, SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils'; import { BigNumber, LibBytesRevertErrors, providerUtils } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util'; import * as ethUtil from 'ethereumjs-util';
@ -206,10 +206,12 @@ describe('Mixins tests', () => {
}); });
it('should revert if data is less than 4 bytes long', async () => { it('should revert if data is less than 4 bytes long', async () => {
const data = '0x010203'; const data = '0x010203';
await expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
mixins.decodeOrdersFromFillData.callAsync(data), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, new BigNumber(3), // the length of data
new BigNumber(4),
); );
return expect(mixins.decodeOrdersFromFillData.callAsync(data)).to.revertWith(expectedError);
}); });
}); });

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2018 ZeroEx Intl. Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -18,6 +18,8 @@
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
import "./LibBytesRichErrors.sol";
library LibBytes { library LibBytes {
@ -38,7 +40,7 @@ library LibBytes {
} }
return memoryAddress; return memoryAddress;
} }
/// @dev Gets the memory address for the contents of a byte array. /// @dev Gets the memory address for the contents of a byte array.
/// @param input Byte array to lookup. /// @param input Byte array to lookup.
/// @return memoryAddress Memory address of the contents of the byte array. /// @return memoryAddress Memory address of the contents of the byte array.
@ -121,7 +123,7 @@ library LibBytes {
source := add(source, 32) source := add(source, 32)
dest := add(dest, 32) dest := add(dest, 32)
} }
// Write the last 32 bytes // Write the last 32 bytes
mstore(dEnd, last) mstore(dEnd, last)
} }
@ -152,7 +154,7 @@ library LibBytes {
sEnd := sub(sEnd, 32) sEnd := sub(sEnd, 32)
dEnd := sub(dEnd, 32) dEnd := sub(dEnd, 32)
} }
// Write the first 32 bytes // Write the first 32 bytes
mstore(dest, first) mstore(dest, first)
} }
@ -174,15 +176,23 @@ library LibBytes {
pure pure
returns (bytes memory result) returns (bytes memory result)
{ {
require( // Ensure that the from and to positions are valid positions for a slice within
from <= to, // the byte array that is being used.
"FROM_LESS_THAN_TO_REQUIRED" if (from > to) {
); LibBytesRichErrors.InvalidByteOperationErrorRevert(
require( LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
to <= b.length, from,
"TO_LESS_THAN_LENGTH_REQUIRED" to
); );
}
if (to > b.length) {
LibBytesRichErrors.InvalidByteOperationErrorRevert(
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
to,
b.length
);
}
// Create a new bytes structure and copy contents // Create a new bytes structure and copy contents
result = new bytes(to - from); result = new bytes(to - from);
memCopy( memCopy(
@ -192,7 +202,7 @@ library LibBytes {
); );
return result; return result;
} }
/// @dev Returns a slice from a byte array without preserving the input. /// @dev Returns a slice from a byte array without preserving the input.
/// @param b The byte array to take a slice from. Will be destroyed in the process. /// @param b The byte array to take a slice from. Will be destroyed in the process.
/// @param from The starting index for the slice (inclusive). /// @param from The starting index for the slice (inclusive).
@ -208,15 +218,23 @@ library LibBytes {
pure pure
returns (bytes memory result) returns (bytes memory result)
{ {
require( // Ensure that the from and to positions are valid positions for a slice within
from <= to, // the byte array that is being used.
"FROM_LESS_THAN_TO_REQUIRED" if (from > to) {
); LibBytesRichErrors.InvalidByteOperationErrorRevert(
require( LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
to <= b.length, from,
"TO_LESS_THAN_LENGTH_REQUIRED" to
); );
}
if (to > b.length) {
LibBytesRichErrors.InvalidByteOperationErrorRevert(
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
to,
b.length
);
}
// Create a new bytes structure around [from, to) in-place. // Create a new bytes structure around [from, to) in-place.
assembly { assembly {
result := add(b, from) result := add(b, from)
@ -233,10 +251,13 @@ library LibBytes {
pure pure
returns (bytes1 result) returns (bytes1 result)
{ {
require( if (b.length == 0) {
b.length > 0, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_THAN_ZERO_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
); b.length,
0
);
}
// Store last byte. // Store last byte.
result = b[b.length - 1]; result = b[b.length - 1];
@ -257,10 +278,13 @@ library LibBytes {
pure pure
returns (address result) returns (address result)
{ {
require( if (b.length < 20) {
b.length >= 20, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
); b.length,
20 // 20 is length of address
);
}
// Store last 20 bytes. // Store last 20 bytes.
result = readAddress(b, b.length - 20); result = readAddress(b, b.length - 20);
@ -303,10 +327,13 @@ library LibBytes {
pure pure
returns (address result) returns (address result)
{ {
require( if (b.length < index + 20) {
b.length >= index + 20, // 20 is length of address LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
); b.length,
index + 20 // 20 is length of address
);
}
// Add offset to index: // Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
@ -335,10 +362,13 @@ library LibBytes {
internal internal
pure pure
{ {
require( if (b.length < index + 20) {
b.length >= index + 20, // 20 is length of address LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
); b.length,
index + 20 // 20 is length of address
);
}
// Add offset to index: // Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
@ -359,7 +389,7 @@ library LibBytes {
mload(add(b, index)), mload(add(b, index)),
0xffffffffffffffffffffffff0000000000000000000000000000000000000000 0xffffffffffffffffffffffff0000000000000000000000000000000000000000
) )
// Make sure input address is clean. // Make sure input address is clean.
// (Solidity does not guarantee this) // (Solidity does not guarantee this)
input := and(input, 0xffffffffffffffffffffffffffffffffffffffff) input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
@ -381,10 +411,13 @@ library LibBytes {
pure pure
returns (bytes32 result) returns (bytes32 result)
{ {
require( if (b.length < index + 32) {
b.length >= index + 32, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
); b.length,
index + 32
);
}
// Arrays are prefixed by a 256 bit length parameter // Arrays are prefixed by a 256 bit length parameter
index += 32; index += 32;
@ -408,10 +441,13 @@ library LibBytes {
internal internal
pure pure
{ {
require( if (b.length < index + 32) {
b.length >= index + 32, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
); b.length,
index + 32
);
}
// Arrays are prefixed by a 256 bit length parameter // Arrays are prefixed by a 256 bit length parameter
index += 32; index += 32;
@ -465,10 +501,13 @@ library LibBytes {
pure pure
returns (bytes4 result) returns (bytes4 result)
{ {
require( if (b.length < index + 4) {
b.length >= index + 4, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED" LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
); b.length,
index + 4
);
}
// Arrays are prefixed by a 32 byte length field // Arrays are prefixed by a 32 byte length field
index += 32; index += 32;
@ -503,11 +542,15 @@ library LibBytes {
// Assert length of <b> is valid, given // Assert length of <b> is valid, given
// length of nested bytes // length of nested bytes
require( if (b.length < index + nestedBytesLength) {
b.length >= index + nestedBytesLength, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED" LibBytesRichErrors
); .InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
b.length,
index + nestedBytesLength
);
}
// Return a pointer to the byte array as it exists inside `b` // Return a pointer to the byte array as it exists inside `b`
assembly { assembly {
result := add(b, index) result := add(b, index)
@ -529,10 +572,14 @@ library LibBytes {
{ {
// Assert length of <b> is valid, given // Assert length of <b> is valid, given
// length of input // length of input
require( if (b.length < index + 32 + input.length) {
b.length >= index + 32 + input.length, // 32 bytes to store length LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED" LibBytesRichErrors
); .InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
b.length,
index + 32 + input.length // 32 bytes to store length
);
}
// Copy <input> into <b> // Copy <input> into <b>
memCopy( memCopy(
@ -554,10 +601,14 @@ library LibBytes {
{ {
uint256 sourceLen = source.length; uint256 sourceLen = source.length;
// Dest length must be >= source length, or some bytes would not be copied. // Dest length must be >= source length, or some bytes would not be copied.
require( if (dest.length < sourceLen) {
dest.length >= sourceLen, LibBytesRichErrors.InvalidByteOperationErrorRevert(
"GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED" LibBytesRichErrors
); .InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
dest.length,
sourceLen
);
}
memCopy( memCopy(
dest.contentAddress(), dest.contentAddress(),
source.contentAddress(), source.contentAddress(),

View File

@ -0,0 +1,59 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "./LibRichErrors.sol";
library LibBytesRichErrors {
using LibRichErrors for *;
enum InvalidByteOperationErrorCodes {
FromLessThanOrEqualsToRequired,
ToLessThanOrEqualsLengthRequired,
LengthGreaterThanZeroRequired,
LengthGreaterThanOrEqualsFourRequired,
LengthGreaterThanOrEqualsTwentyRequired,
LengthGreaterThanOrEqualsThirtyTwoRequired,
LengthGreaterThanOrEqualsNestedBytesLengthRequired,
DestinationLengthGreaterThanOrEqualSourceLengthRequired
}
// bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)"))
bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR =
0x28006595;
// solhint-disable func-name-mixedcase
function InvalidByteOperationErrorRevert(
InvalidByteOperationErrorCodes errorCode,
uint256 endpoint,
uint256 required
)
internal
pure
{
abi.encodeWithSelector(
INVALID_BYTE_OPERATION_ERROR_SELECTOR,
errorCode,
endpoint,
required
)._rrevert();
}
}

View File

@ -0,0 +1,58 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
library LibRichErrors {
// bytes4(keccak256("Error(string)"))
bytes4 internal constant STANDARD_ERROR_SELECTOR =
0x08c379a0;
// solhint-disable func-name-mixedcase
/// @dev ABI encode a standard, string revert error payload.
/// This is the same payload that would be included by a `revert(string)`
/// solidity statement. It has the function signature `Error(string)`.
/// @param message The error string.
/// @return The ABI encoded error.
function StandardError(
string memory message
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
STANDARD_ERROR_SELECTOR,
bytes(message)
);
}
// solhint-enable func-name-mixedcase
/// @dev Reverts an encoded rich revert reason `errorData`.
/// @param errorData ABI encoded error data.
function _rrevert(bytes memory errorData)
internal
pure
{
assembly {
revert(add(errorData, 0x20), mload(errorData))
}
}
}

View File

@ -22,9 +22,20 @@ import "../src/LibBytes.sol";
contract TestLibBytes { contract TestLibBytes {
using LibBytes for bytes; using LibBytes for bytes;
/// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The byte that was popped off.
function publicPopLastByteStateful(bytes memory b)
public
returns (bytes memory, bytes1 result)
{
result = b.popLastByte();
return (b, result);
}
/// @dev Pops the last byte off of a byte array by modifying its length. /// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified. /// @param b Byte array that will be modified.
/// @return The byte that was popped off. /// @return The byte that was popped off.
@ -61,7 +72,7 @@ contract TestLibBytes {
equal = lhs.equals(rhs); equal = lhs.equals(rhs);
return equal; return equal;
} }
function publicEqualsPop1(bytes memory lhs, bytes memory rhs) function publicEqualsPop1(bytes memory lhs, bytes memory rhs)
public public
pure pure
@ -236,7 +247,7 @@ contract TestLibBytes {
b.writeBytesWithLength(index, input); b.writeBytesWithLength(index, input);
return b; return b;
} }
/// @dev Copies a block of memory from one location to another. /// @dev Copies a block of memory from one location to another.
/// @param mem Memory contents we want to apply memCopy to /// @param mem Memory contents we want to apply memCopy to
/// @param dest Destination offset into <mem>. /// @param dest Destination offset into <mem>.

View File

@ -1,16 +1,7 @@
import { import { chaiSetup, constants, provider, txDefaults, typeEncodingUtils, web3Wrapper } from '@0x/contracts-test-utils';
chaiSetup,
constants,
expectContractCallFailedAsync,
provider,
txDefaults,
typeEncodingUtils,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils'; import { generatePseudoRandomSalt } from '@0x/order-utils';
import { RevertReason } from '@0x/types'; import { BigNumber, LibBytesRevertErrors } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import BN = require('bn.js'); import BN = require('bn.js');
import * as chai from 'chai'; import * as chai from 'chai';
import ethUtil = require('ethereumjs-util'); import ethUtil = require('ethereumjs-util');
@ -106,10 +97,12 @@ describe('LibBytes', () => {
describe('popLastByte', () => { describe('popLastByte', () => {
it('should revert if length is 0', async () => { it('should revert if length is 0', async () => {
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
RevertReason.LibBytesGreaterThanZeroLengthRequired, constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
); );
return expect(libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES)).to.revertWith(expectedError);
}); });
it('should pop the last byte from the input and return it when array holds more than 1 byte', async () => { it('should pop the last byte from the input and return it when array holds more than 1 byte', async () => {
const [newBytes, poppedByte] = await libBytes.publicPopLastByte.callAsync(byteArrayLongerThan32Bytes); const [newBytes, poppedByte] = await libBytes.publicPopLastByte.callAsync(byteArrayLongerThan32Bytes);
@ -128,9 +121,14 @@ describe('LibBytes', () => {
describe('popLast20Bytes', () => { describe('popLast20Bytes', () => {
it('should revert if length is less than 20', async () => { it('should revert if length is less than 20', async () => {
return expectContractCallFailedAsync( const byteLen = (byteArrayShorterThan20Bytes.length - 2) / 2;
libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
new BigNumber(byteLen), // length of byteArrayShorterThan20Byte
new BigNumber(20),
);
return expect(libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes)).to.revertWith(
expectedError,
); );
}); });
it('should pop the last 20 bytes from the input and return it when array holds more than 20 bytes', async () => { it('should pop the last 20 bytes from the input and return it when array holds more than 20 bytes', async () => {
@ -203,10 +201,16 @@ describe('LibBytes', () => {
describe('deepCopyBytes', () => { describe('deepCopyBytes', () => {
it('should revert if dest is shorter than source', async () => { it('should revert if dest is shorter than source', async () => {
return expectContractCallFailedAsync( const endpointByteLen = (byteArrayShorterThan20Bytes.length - 2) / 2;
libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes), const requiredByteLen = (byteArrayLongerThan32Bytes.length - 2) / 2;
RevertReason.LibBytesGreaterOrEqualToSourceBytesLengthRequired, const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
LibBytesRevertErrors.InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
new BigNumber(endpointByteLen), // length of byteArrayShorterThan20Byte
new BigNumber(requiredByteLen), // length of byteArrayShorterThan20Byte
); );
return expect(
libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes),
).to.revertWith(expectedError);
}); });
it('should overwrite dest with source if source and dest have equal length', async () => { it('should overwrite dest with source if source and dest have equal length', async () => {
const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length - 2)}`; const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length - 2)}`;
@ -256,18 +260,22 @@ describe('LibBytes', () => {
it('should fail if the byte array is too short to hold an address', async () => { it('should fail if the byte array is too short to hold an address', async () => {
const shortByteArray = '0xabcdef'; const shortByteArray = '0xabcdef';
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadAddress.callAsync(shortByteArray, offset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, new BigNumber(3),
new BigNumber(20),
); );
return expect(libBytes.publicReadAddress.callAsync(shortByteArray, offset)).to.revertWith(expectedError);
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
const byteArray = testAddress; const byteArray = testAddress;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadAddress.callAsync(byteArray, badOffset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, new BigNumber(20),
new BigNumber(40),
); );
return expect(libBytes.publicReadAddress.callAsync(byteArray, badOffset)).to.revertWith(expectedError);
}); });
}); });
@ -300,17 +308,26 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold an address', async () => { it('should fail if the byte array is too short to hold an address', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = ethUtil.toBuffer(byteArrayShorterThan20Bytes).byteLength;
libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
new BigNumber(byteLen),
new BigNumber(20),
); );
return expect(
libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress),
).to.revertWith(expectedError);
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
const byteArray = byteArrayLongerThan32Bytes; const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, badOffset,
badOffset.plus(new BigNumber(20)),
);
return expect(libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress)).to.revertWith(
expectedError,
); );
}); });
}); });
@ -332,17 +349,24 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a bytes32', async () => { it('should fail if the byte array is too short to hold a bytes32', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
byteLen,
new BigNumber(32),
);
return expect(libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset)).to.revertWith(
expectedError,
); );
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadBytes32.callAsync(testBytes32, badOffset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, badOffset,
badOffset.plus(new BigNumber(32)),
); );
return expect(libBytes.publicReadBytes32.callAsync(testBytes32, badOffset)).to.revertWith(expectedError);
}); });
}); });
@ -375,17 +399,26 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a bytes32', async () => { it('should fail if the byte array is too short to hold a bytes32', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
byteLen,
new BigNumber(32),
); );
return expect(
libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32),
).to.revertWith(expectedError);
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
const byteArray = byteArrayLongerThan32Bytes; const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, badOffset,
badOffset.plus(new BigNumber(32)),
);
return expect(libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32)).to.revertWith(
expectedError,
); );
}); });
}); });
@ -411,9 +444,14 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a uint256', async () => { it('should fail if the byte array is too short to hold a uint256', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
byteLen,
new BigNumber(32),
);
return expect(libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset)).to.revertWith(
expectedError,
); );
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
@ -421,10 +459,12 @@ describe('LibBytes', () => {
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256); const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
const byteArray = ethUtil.bufferToHex(testUint256AsBuffer); const byteArray = ethUtil.bufferToHex(testUint256AsBuffer);
const badOffset = new BigNumber(testUint256AsBuffer.byteLength); const badOffset = new BigNumber(testUint256AsBuffer.byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadUint256.callAsync(byteArray, badOffset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, badOffset,
badOffset.plus(new BigNumber(32)),
); );
return expect(libBytes.publicReadUint256.callAsync(byteArray, badOffset)).to.revertWith(expectedError);
}); });
}); });
@ -461,17 +501,26 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a uint256', async () => { it('should fail if the byte array is too short to hold a uint256', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
byteLen,
new BigNumber(32),
); );
return expect(
libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256),
).to.revertWith(expectedError);
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
const byteArray = byteArrayLongerThan32Bytes; const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, badOffset,
badOffset.plus(new BigNumber(32)),
);
return expect(libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256)).to.revertWith(
expectedError,
); );
}); });
}); });
@ -480,10 +529,15 @@ describe('LibBytes', () => {
// AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0' // AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0'
it('should revert if byte array has a length < 4', async () => { it('should revert if byte array has a length < 4', async () => {
const byteArrayLessThan4Bytes = '0x010101'; const byteArrayLessThan4Bytes = '0x010101';
const byteLen = (byteArrayLessThan4Bytes.length - 2) / 2;
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, offset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, new BigNumber(byteLen), // length of byteArrayLessThan4Bytes
new BigNumber(4),
);
return expect(libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, offset)).to.revertWith(
expectedError,
); );
}); });
it('should return the first 4 bytes of a byte array of arbitrary length', async () => { it('should return the first 4 bytes of a byte array of arbitrary length', async () => {
@ -507,10 +561,13 @@ describe('LibBytes', () => {
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes4', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a bytes4', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes4).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(testBytes4).byteLength);
return expectContractCallFailedAsync( const byteLen = new BigNumber((testBytes4.length - 2) / 2);
libBytes.publicReadBytes4.callAsync(testBytes4, badOffset), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
byteLen,
badOffset.plus(new BigNumber(4)),
); );
return expect(libBytes.publicReadBytes4.callAsync(testBytes4, badOffset)).to.revertWith(expectedError);
}); });
}); });
@ -557,30 +614,48 @@ describe('LibBytes', () => {
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
// The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read. // The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read.
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
byteLen,
new BigNumber(32),
); );
return expect(
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset),
).to.revertWith(expectedError);
}); });
it('should fail if we store a nested byte array length, without a nested byte array', async () => { it('should fail if we store a nested byte array length, without a nested byte array', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectContractCallFailedAsync( const byteLen = new BigNumber((testBytes32.length - 2) / 2);
libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
byteLen,
(new BigNumber(testBytes32)).plus(32),
);
return expect(libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset)).to.revertWith(
expectedError,
); );
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, badOffset,
badOffset.plus(new BigNumber(32)),
); );
return expect(
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset),
).to.revertWith(expectedError);
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
return expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset), LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, badOffset,
badOffset.plus(new BigNumber(32)),
);
return expect(libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset)).to.revertWith(
expectedError,
); );
}); });
}); });
@ -690,18 +765,28 @@ describe('LibBytes', () => {
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(new Buffer(1)); const emptyByteArray = ethUtil.bufferToHex(new Buffer(1));
return expectContractCallFailedAsync( const inputLen = new BigNumber((longData.length - 2) / 2);
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
new BigNumber(1),
new BigNumber(32).plus(inputLen),
); );
return expect(
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData),
).to.revertWith(expectedError);
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength));
const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength);
return expectContractCallFailedAsync( const inputLen = new BigNumber((shortData.length - 2) / 2);
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData), const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
badOffset,
badOffset.plus(new BigNumber(32)).plus(inputLen),
); );
return expect(
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData),
).to.revertWith(expectedError);
}); });
}); });
@ -875,9 +960,13 @@ describe('LibBytes', () => {
it('should revert if from > to', async () => { it('should revert if from > to', async () => {
const from = new BigNumber(1); const from = new BigNumber(1);
const to = new BigNumber(0); const to = new BigNumber(0);
expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to), LibBytesRevertErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
RevertReason.FromLessThanToRequired, from,
to,
);
return expect(libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to)).to.revertWith(
expectedError,
); );
}); });
it('should return a byte array of length 0 if from == to', async () => { it('should return a byte array of length 0 if from == to', async () => {
@ -896,12 +985,16 @@ describe('LibBytes', () => {
expect(result).to.eq(constants.NULL_BYTES); expect(result).to.eq(constants.NULL_BYTES);
}); });
it('should revert if to > input.length', async () => { it('should revert if to > input.length', async () => {
const byteLen = (byteArrayLongerThan32Bytes.length - 2) / 2; const byteLen: number = (byteArrayLongerThan32Bytes.length - 2) / 2;
const from = new BigNumber(0); const from = new BigNumber(0);
const to = new BigNumber(byteLen).plus(1); const to = new BigNumber(byteLen).plus(1);
expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to), LibBytesRevertErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
RevertReason.ToLessThanLengthRequired, to,
new BigNumber(byteLen),
);
return expect(libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to)).to.revertWith(
expectedError,
); );
}); });
it('should slice a section of the input', async () => { it('should slice a section of the input', async () => {
@ -926,9 +1019,13 @@ describe('LibBytes', () => {
it('should revert if from > to', async () => { it('should revert if from > to', async () => {
const from = new BigNumber(1); const from = new BigNumber(1);
const to = new BigNumber(0); const to = new BigNumber(0);
expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicSliceDestructive.callAsync(byteArrayLongerThan32Bytes, from, to), LibBytesRevertErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
RevertReason.FromLessThanToRequired, from,
to,
);
return expect(libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to)).to.revertWith(
expectedError,
); );
}); });
it('should return a byte array of length 0 if from == to', async () => { it('should return a byte array of length 0 if from == to', async () => {
@ -948,10 +1045,14 @@ describe('LibBytes', () => {
const byteLen = (byteArrayLongerThan32Bytes.length - 2) / 2; const byteLen = (byteArrayLongerThan32Bytes.length - 2) / 2;
const from = new BigNumber(0); const from = new BigNumber(0);
const to = new BigNumber(byteLen).plus(1); const to = new BigNumber(byteLen).plus(1);
expectContractCallFailedAsync( const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
libBytes.publicSliceDestructive.callAsync(byteArrayLongerThan32Bytes, from, to), LibBytesRevertErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
RevertReason.ToLessThanLengthRequired, to,
new BigNumber(byteLen),
); );
return expect(
libBytes.publicSliceDestructive.callAsync(byteArrayLongerThan32Bytes, from, to),
).to.revertWith(expectedError);
}); });
it('should slice a section of the input', async () => { it('should slice a section of the input', async () => {
const from = new BigNumber(1); const from = new BigNumber(1);

View File

@ -1,3 +1,4 @@
import * as LibBytesRevertErrors from './lib_bytes_revert_errors';
import * as OwnableRevertErrors from './ownable_revert_errors'; import * as OwnableRevertErrors from './ownable_revert_errors';
import * as SafeMathRevertErrors from './safe_math_revert_errors'; import * as SafeMathRevertErrors from './safe_math_revert_errors';
@ -28,4 +29,4 @@ export {
AnyRevertError, AnyRevertError,
} from './revert_error'; } from './revert_error';
export { OwnableRevertErrors, SafeMathRevertErrors }; export { LibBytesRevertErrors, OwnableRevertErrors, SafeMathRevertErrors };

View File

@ -0,0 +1,30 @@
import { BigNumber } from './configured_bignumber';
import { RevertError } from './revert_error';
export enum InvalidByteOperationErrorCodes {
FromLessThanOrEqualsToRequired,
ToLessThanOrEqualsLengthRequired,
LengthGreaterThanZeroRequired,
LengthGreaterThanOrEqualsFourRequired,
LengthGreaterThanOrEqualsTwentyRequired,
LengthGreaterThanOrEqualsThirtyTwoRequired,
LengthGreaterThanOrEqualsNestedBytesLengthRequired,
DestinationLengthGreaterThanOrEqualSourceLengthRequired,
}
export class InvalidByteOperationError extends RevertError {
constructor(error?: InvalidByteOperationErrorCodes, endpoint?: BigNumber, required?: BigNumber) {
super(
'InvalidByteOperationError',
'InvalidByteOperationError(uint8 error, uint256 endpoint, uint256 required)',
{
error,
endpoint,
required,
},
);
}
}
// Register the InvalidByteOperationError type
RevertError.registerType(InvalidByteOperationError);