Bytes.test.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { MAX_UINT128, MAX_UINT64, MAX_UINT32, MAX_UINT16 } = require('../helpers/constants');
  5. // Helper functions for fixed bytes types
  6. const bytes32 = value => ethers.toBeHex(value, 32);
  7. const bytes16 = value => ethers.toBeHex(value, 16);
  8. const bytes8 = value => ethers.toBeHex(value, 8);
  9. const bytes4 = value => ethers.toBeHex(value, 4);
  10. const bytes2 = value => ethers.toBeHex(value, 2);
  11. async function fixture() {
  12. const mock = await ethers.deployContract('$Bytes');
  13. return { mock };
  14. }
  15. const lorem = ethers.toUtf8Bytes(
  16. 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
  17. );
  18. const present = lorem.at(1);
  19. const absent = 255;
  20. describe('Bytes', function () {
  21. before(async function () {
  22. Object.assign(this, await loadFixture(fixture));
  23. });
  24. describe('indexOf', function () {
  25. it('first', async function () {
  26. expect(await this.mock.$indexOf(lorem, ethers.toBeHex(present))).to.equal(lorem.indexOf(present));
  27. });
  28. it('from index', async function () {
  29. for (const start in Array(lorem.length + 10).fill()) {
  30. const index = lorem.indexOf(present, start);
  31. const result = index === -1 ? ethers.MaxUint256 : index;
  32. expect(await this.mock.$indexOf(lorem, ethers.toBeHex(present), ethers.Typed.uint256(start))).to.equal(result);
  33. }
  34. });
  35. it('absent', async function () {
  36. expect(await this.mock.$indexOf(lorem, ethers.toBeHex(absent))).to.equal(ethers.MaxUint256);
  37. });
  38. });
  39. describe('lastIndexOf', function () {
  40. it('first', async function () {
  41. expect(await this.mock.$lastIndexOf(lorem, ethers.toBeHex(present))).to.equal(lorem.lastIndexOf(present));
  42. });
  43. it('from index', async function () {
  44. for (const start in Array(lorem.length + 10).fill()) {
  45. const index = lorem.lastIndexOf(present, start);
  46. const result = index === -1 ? ethers.MaxUint256 : index;
  47. expect(await this.mock.$lastIndexOf(lorem, ethers.toBeHex(present), ethers.Typed.uint256(start))).to.equal(
  48. result,
  49. );
  50. }
  51. });
  52. it('absent', async function () {
  53. expect(await this.mock.$lastIndexOf(lorem, ethers.toBeHex(absent))).to.equal(ethers.MaxUint256);
  54. });
  55. });
  56. describe('slice & splice', function () {
  57. describe('slice(bytes, uint256) & splice(bytes, uint256)', function () {
  58. for (const [descr, start] of Object.entries({
  59. 'start = 0': 0,
  60. 'start within bound': 10,
  61. 'start out of bound': 1000,
  62. })) {
  63. it(descr, async function () {
  64. const result = ethers.hexlify(lorem.slice(start));
  65. expect(await this.mock.$slice(lorem, start)).to.equal(result);
  66. expect(await this.mock.$splice(lorem, start)).to.equal(result);
  67. });
  68. }
  69. });
  70. describe('slice(bytes, uint256, uint256) & splice(bytes, uint256, uint256)', function () {
  71. for (const [descr, [start, end]] of Object.entries({
  72. 'start = 0': [0, 42],
  73. 'start and end within bound': [17, 42],
  74. 'end out of bound': [42, 1000],
  75. 'start = end': [17, 17],
  76. 'start > end': [42, 17],
  77. })) {
  78. it(descr, async function () {
  79. const result = ethers.hexlify(lorem.slice(start, end));
  80. expect(await this.mock.$slice(lorem, start, ethers.Typed.uint256(end))).to.equal(result);
  81. expect(await this.mock.$splice(lorem, start, ethers.Typed.uint256(end))).to.equal(result);
  82. });
  83. }
  84. });
  85. });
  86. describe('reverseBits', function () {
  87. describe('reverseBytes32', function () {
  88. it('reverses bytes correctly', async function () {
  89. await expect(this.mock.$reverseBytes32(bytes32(0))).to.eventually.equal(bytes32(0));
  90. await expect(this.mock.$reverseBytes32(bytes32(ethers.MaxUint256))).to.eventually.equal(
  91. bytes32(ethers.MaxUint256),
  92. );
  93. // Test complex pattern that clearly shows byte reversal
  94. await expect(
  95. this.mock.$reverseBytes32('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'),
  96. ).to.eventually.equal('0xefcdab8967452301efcdab8967452301efcdab8967452301efcdab8967452301');
  97. });
  98. it('double reverse returns original', async function () {
  99. const values = [0n, 1n, 0x12345678n, ethers.MaxUint256];
  100. for (const value of values) {
  101. const reversed = await this.mock.$reverseBytes32(bytes32(value));
  102. await expect(this.mock.$reverseBytes32(reversed)).to.eventually.equal(bytes32(value));
  103. }
  104. });
  105. });
  106. describe('reverseBytes16', function () {
  107. it('reverses bytes correctly', async function () {
  108. await expect(this.mock.$reverseBytes16(bytes16(0))).to.eventually.equal(bytes16(0));
  109. await expect(this.mock.$reverseBytes16(bytes16(MAX_UINT128))).to.eventually.equal(bytes16(MAX_UINT128));
  110. // Test complex pattern that clearly shows byte reversal
  111. await expect(this.mock.$reverseBytes16('0x0123456789abcdef0123456789abcdef')).to.eventually.equal(
  112. '0xefcdab8967452301efcdab8967452301',
  113. );
  114. });
  115. it('double reverse returns original', async function () {
  116. const values = [0n, 1n, 0x12345678n, MAX_UINT128];
  117. for (const value of values) {
  118. const reversed = await this.mock.$reverseBytes16(bytes16(value));
  119. // Cast back to uint128 for comparison since function returns uint256
  120. await expect(this.mock.$reverseBytes16(reversed)).to.eventually.equal(bytes16(value & MAX_UINT128));
  121. }
  122. });
  123. });
  124. describe('reverseBytes8', function () {
  125. it('reverses bytes correctly', async function () {
  126. await expect(this.mock.$reverseBytes8(bytes8(0))).to.eventually.equal(bytes8(0));
  127. await expect(this.mock.$reverseBytes8(bytes8(MAX_UINT64))).to.eventually.equal(bytes8(MAX_UINT64));
  128. // Test known pattern: 0x123456789ABCDEF0 -> 0xF0DEBC9A78563412
  129. await expect(this.mock.$reverseBytes8('0x123456789abcdef0')).to.eventually.equal('0xf0debc9a78563412');
  130. });
  131. it('double reverse returns original', async function () {
  132. const values = [0n, 1n, 0x12345678n, MAX_UINT64];
  133. for (const value of values) {
  134. const reversed = await this.mock.$reverseBytes8(bytes8(value));
  135. // Cast back to uint64 for comparison since function returns uint256
  136. await expect(this.mock.$reverseBytes8(reversed)).to.eventually.equal(bytes8(value & MAX_UINT64));
  137. }
  138. });
  139. });
  140. describe('reverseBytes4', function () {
  141. it('reverses bytes correctly', async function () {
  142. await expect(this.mock.$reverseBytes4(bytes4(0))).to.eventually.equal(bytes4(0));
  143. await expect(this.mock.$reverseBytes4(bytes4(MAX_UINT32))).to.eventually.equal(bytes4(MAX_UINT32));
  144. // Test known pattern: 0x12345678 -> 0x78563412
  145. await expect(this.mock.$reverseBytes4(bytes4(0x12345678))).to.eventually.equal(bytes4(0x78563412));
  146. });
  147. it('double reverse returns original', async function () {
  148. const values = [0n, 1n, 0x12345678n, MAX_UINT32];
  149. for (const value of values) {
  150. const reversed = await this.mock.$reverseBytes4(bytes4(value));
  151. // Cast back to uint32 for comparison since function returns uint256
  152. await expect(this.mock.$reverseBytes4(reversed)).to.eventually.equal(bytes4(value & MAX_UINT32));
  153. }
  154. });
  155. });
  156. describe('reverseBytes2', function () {
  157. it('reverses bytes correctly', async function () {
  158. await expect(this.mock.$reverseBytes2(bytes2(0))).to.eventually.equal(bytes2(0));
  159. await expect(this.mock.$reverseBytes2(bytes2(MAX_UINT16))).to.eventually.equal(bytes2(MAX_UINT16));
  160. // Test known pattern: 0x1234 -> 0x3412
  161. await expect(this.mock.$reverseBytes2(bytes2(0x1234))).to.eventually.equal(bytes2(0x3412));
  162. });
  163. it('double reverse returns original', async function () {
  164. const values = [0n, 1n, 0x1234n, MAX_UINT16];
  165. for (const value of values) {
  166. const reversed = await this.mock.$reverseBytes2(bytes2(value));
  167. // Cast back to uint16 for comparison since function returns uint256
  168. await expect(this.mock.$reverseBytes2(reversed)).to.eventually.equal(bytes2(value & MAX_UINT16));
  169. }
  170. });
  171. });
  172. describe('edge cases', function () {
  173. it('handles single byte values', async function () {
  174. await expect(this.mock.$reverseBytes2(bytes2(0x00ff))).to.eventually.equal(bytes2(0xff00));
  175. await expect(this.mock.$reverseBytes4(bytes4(0x000000ff))).to.eventually.equal(bytes4(0xff000000));
  176. });
  177. it('handles alternating patterns', async function () {
  178. await expect(this.mock.$reverseBytes2(bytes2(0xaaaa))).to.eventually.equal(bytes2(0xaaaa));
  179. await expect(this.mock.$reverseBytes2(bytes2(0x5555))).to.eventually.equal(bytes2(0x5555));
  180. await expect(this.mock.$reverseBytes4(bytes4(0xaaaaaaaa))).to.eventually.equal(bytes4(0xaaaaaaaa));
  181. await expect(this.mock.$reverseBytes4(bytes4(0x55555555))).to.eventually.equal(bytes4(0x55555555));
  182. });
  183. });
  184. });
  185. });