SafeMath.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { MAX_UINT256 } = constants;
  3. const { expect } = require('chai');
  4. const SafeMath = artifacts.require('$SafeMath');
  5. const SafeMathMemoryCheck = artifacts.require('$SafeMathMemoryCheck');
  6. function expectStruct(value, expected) {
  7. for (const key in expected) {
  8. if (BN.isBN(value[key])) {
  9. expect(value[key]).to.be.bignumber.equal(expected[key]);
  10. } else {
  11. expect(value[key]).to.be.equal(expected[key]);
  12. }
  13. }
  14. }
  15. contract('SafeMath', function () {
  16. beforeEach(async function () {
  17. this.safeMath = await SafeMath.new();
  18. });
  19. async function testCommutative(fn, lhs, rhs, expected, ...extra) {
  20. expect(await fn(lhs, rhs, ...extra)).to.be.bignumber.equal(expected);
  21. expect(await fn(rhs, lhs, ...extra)).to.be.bignumber.equal(expected);
  22. }
  23. async function testFailsCommutative(fn, lhs, rhs, reason, ...extra) {
  24. if (reason === undefined) {
  25. await expectRevert.unspecified(fn(lhs, rhs, ...extra));
  26. await expectRevert.unspecified(fn(rhs, lhs, ...extra));
  27. } else {
  28. await expectRevert(fn(lhs, rhs, ...extra), reason);
  29. await expectRevert(fn(rhs, lhs, ...extra), reason);
  30. }
  31. }
  32. async function testCommutativeIterable(fn, lhs, rhs, expected, ...extra) {
  33. expectStruct(await fn(lhs, rhs, ...extra), expected);
  34. expectStruct(await fn(rhs, lhs, ...extra), expected);
  35. }
  36. describe('with flag', function () {
  37. describe('add', function () {
  38. it('adds correctly', async function () {
  39. const a = new BN('5678');
  40. const b = new BN('1234');
  41. testCommutativeIterable(this.safeMath.$tryAdd, a, b, [true, a.add(b)]);
  42. });
  43. it('reverts on addition overflow', async function () {
  44. const a = MAX_UINT256;
  45. const b = new BN('1');
  46. testCommutativeIterable(this.safeMath.$tryAdd, a, b, [false, '0']);
  47. });
  48. });
  49. describe('sub', function () {
  50. it('subtracts correctly', async function () {
  51. const a = new BN('5678');
  52. const b = new BN('1234');
  53. expectStruct(await this.safeMath.$trySub(a, b), [true, a.sub(b)]);
  54. });
  55. it('reverts if subtraction result would be negative', async function () {
  56. const a = new BN('1234');
  57. const b = new BN('5678');
  58. expectStruct(await this.safeMath.$trySub(a, b), [false, '0']);
  59. });
  60. });
  61. describe('mul', function () {
  62. it('multiplies correctly', async function () {
  63. const a = new BN('1234');
  64. const b = new BN('5678');
  65. testCommutativeIterable(this.safeMath.$tryMul, a, b, [true, a.mul(b)]);
  66. });
  67. it('multiplies by zero correctly', async function () {
  68. const a = new BN('0');
  69. const b = new BN('5678');
  70. testCommutativeIterable(this.safeMath.$tryMul, a, b, [true, a.mul(b)]);
  71. });
  72. it('reverts on multiplication overflow', async function () {
  73. const a = MAX_UINT256;
  74. const b = new BN('2');
  75. testCommutativeIterable(this.safeMath.$tryMul, a, b, [false, '0']);
  76. });
  77. });
  78. describe('div', function () {
  79. it('divides correctly', async function () {
  80. const a = new BN('5678');
  81. const b = new BN('5678');
  82. expectStruct(await this.safeMath.$tryDiv(a, b), [true, a.div(b)]);
  83. });
  84. it('divides zero correctly', async function () {
  85. const a = new BN('0');
  86. const b = new BN('5678');
  87. expectStruct(await this.safeMath.$tryDiv(a, b), [true, a.div(b)]);
  88. });
  89. it('returns complete number result on non-even division', async function () {
  90. const a = new BN('7000');
  91. const b = new BN('5678');
  92. expectStruct(await this.safeMath.$tryDiv(a, b), [true, a.div(b)]);
  93. });
  94. it('reverts on division by zero', async function () {
  95. const a = new BN('5678');
  96. const b = new BN('0');
  97. expectStruct(await this.safeMath.$tryDiv(a, b), [false, '0']);
  98. });
  99. });
  100. describe('mod', function () {
  101. describe('modulos correctly', async function () {
  102. it('when the dividend is smaller than the divisor', async function () {
  103. const a = new BN('284');
  104. const b = new BN('5678');
  105. expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]);
  106. });
  107. it('when the dividend is equal to the divisor', async function () {
  108. const a = new BN('5678');
  109. const b = new BN('5678');
  110. expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]);
  111. });
  112. it('when the dividend is larger than the divisor', async function () {
  113. const a = new BN('7000');
  114. const b = new BN('5678');
  115. expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]);
  116. });
  117. it('when the dividend is a multiple of the divisor', async function () {
  118. const a = new BN('17034'); // 17034 == 5678 * 3
  119. const b = new BN('5678');
  120. expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]);
  121. });
  122. });
  123. it('reverts with a 0 divisor', async function () {
  124. const a = new BN('5678');
  125. const b = new BN('0');
  126. expectStruct(await this.safeMath.$tryMod(a, b), [false, '0']);
  127. });
  128. });
  129. });
  130. describe('with default revert message', function () {
  131. describe('add', function () {
  132. it('adds correctly', async function () {
  133. const a = new BN('5678');
  134. const b = new BN('1234');
  135. await testCommutative(this.safeMath.$add, a, b, a.add(b));
  136. });
  137. it('reverts on addition overflow', async function () {
  138. const a = MAX_UINT256;
  139. const b = new BN('1');
  140. await testFailsCommutative(this.safeMath.$add, a, b, undefined);
  141. });
  142. });
  143. describe('sub', function () {
  144. it('subtracts correctly', async function () {
  145. const a = new BN('5678');
  146. const b = new BN('1234');
  147. expect(await this.safeMath.$sub(a, b)).to.be.bignumber.equal(a.sub(b));
  148. });
  149. it('reverts if subtraction result would be negative', async function () {
  150. const a = new BN('1234');
  151. const b = new BN('5678');
  152. await expectRevert.unspecified(this.safeMath.$sub(a, b));
  153. });
  154. });
  155. describe('mul', function () {
  156. it('multiplies correctly', async function () {
  157. const a = new BN('1234');
  158. const b = new BN('5678');
  159. await testCommutative(this.safeMath.$mul, a, b, a.mul(b));
  160. });
  161. it('multiplies by zero correctly', async function () {
  162. const a = new BN('0');
  163. const b = new BN('5678');
  164. await testCommutative(this.safeMath.$mul, a, b, '0');
  165. });
  166. it('reverts on multiplication overflow', async function () {
  167. const a = MAX_UINT256;
  168. const b = new BN('2');
  169. await testFailsCommutative(this.safeMath.$mul, a, b, undefined);
  170. });
  171. });
  172. describe('div', function () {
  173. it('divides correctly', async function () {
  174. const a = new BN('5678');
  175. const b = new BN('5678');
  176. expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal(a.div(b));
  177. });
  178. it('divides zero correctly', async function () {
  179. const a = new BN('0');
  180. const b = new BN('5678');
  181. expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal('0');
  182. });
  183. it('returns complete number result on non-even division', async function () {
  184. const a = new BN('7000');
  185. const b = new BN('5678');
  186. expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal('1');
  187. });
  188. it('reverts on division by zero', async function () {
  189. const a = new BN('5678');
  190. const b = new BN('0');
  191. await expectRevert.unspecified(this.safeMath.$div(a, b));
  192. });
  193. });
  194. describe('mod', function () {
  195. describe('modulos correctly', async function () {
  196. it('when the dividend is smaller than the divisor', async function () {
  197. const a = new BN('284');
  198. const b = new BN('5678');
  199. expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b));
  200. });
  201. it('when the dividend is equal to the divisor', async function () {
  202. const a = new BN('5678');
  203. const b = new BN('5678');
  204. expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b));
  205. });
  206. it('when the dividend is larger than the divisor', async function () {
  207. const a = new BN('7000');
  208. const b = new BN('5678');
  209. expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b));
  210. });
  211. it('when the dividend is a multiple of the divisor', async function () {
  212. const a = new BN('17034'); // 17034 == 5678 * 3
  213. const b = new BN('5678');
  214. expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b));
  215. });
  216. });
  217. it('reverts with a 0 divisor', async function () {
  218. const a = new BN('5678');
  219. const b = new BN('0');
  220. await expectRevert.unspecified(this.safeMath.$mod(a, b));
  221. });
  222. });
  223. });
  224. describe('with custom revert message', function () {
  225. describe('sub', function () {
  226. it('subtracts correctly', async function () {
  227. const a = new BN('5678');
  228. const b = new BN('1234');
  229. expect(
  230. await this.safeMath.methods['$sub(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  231. ).to.be.bignumber.equal(a.sub(b));
  232. });
  233. it('reverts if subtraction result would be negative', async function () {
  234. const a = new BN('1234');
  235. const b = new BN('5678');
  236. await expectRevert(
  237. this.safeMath.methods['$sub(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  238. 'MyErrorMessage',
  239. );
  240. });
  241. });
  242. describe('div', function () {
  243. it('divides correctly', async function () {
  244. const a = new BN('5678');
  245. const b = new BN('5678');
  246. expect(
  247. await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  248. ).to.be.bignumber.equal(a.div(b));
  249. });
  250. it('divides zero correctly', async function () {
  251. const a = new BN('0');
  252. const b = new BN('5678');
  253. expect(
  254. await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  255. ).to.be.bignumber.equal('0');
  256. });
  257. it('returns complete number result on non-even division', async function () {
  258. const a = new BN('7000');
  259. const b = new BN('5678');
  260. expect(
  261. await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  262. ).to.be.bignumber.equal('1');
  263. });
  264. it('reverts on division by zero', async function () {
  265. const a = new BN('5678');
  266. const b = new BN('0');
  267. await expectRevert(
  268. this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  269. 'MyErrorMessage',
  270. );
  271. });
  272. });
  273. describe('mod', function () {
  274. describe('modulos correctly', async function () {
  275. it('when the dividend is smaller than the divisor', async function () {
  276. const a = new BN('284');
  277. const b = new BN('5678');
  278. expect(
  279. await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  280. ).to.be.bignumber.equal(a.mod(b));
  281. });
  282. it('when the dividend is equal to the divisor', async function () {
  283. const a = new BN('5678');
  284. const b = new BN('5678');
  285. expect(
  286. await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  287. ).to.be.bignumber.equal(a.mod(b));
  288. });
  289. it('when the dividend is larger than the divisor', async function () {
  290. const a = new BN('7000');
  291. const b = new BN('5678');
  292. expect(
  293. await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  294. ).to.be.bignumber.equal(a.mod(b));
  295. });
  296. it('when the dividend is a multiple of the divisor', async function () {
  297. const a = new BN('17034'); // 17034 == 5678 * 3
  298. const b = new BN('5678');
  299. expect(
  300. await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  301. ).to.be.bignumber.equal(a.mod(b));
  302. });
  303. });
  304. it('reverts with a 0 divisor', async function () {
  305. const a = new BN('5678');
  306. const b = new BN('0');
  307. await expectRevert(
  308. this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  309. 'MyErrorMessage',
  310. );
  311. });
  312. });
  313. });
  314. describe('memory leakage', function () {
  315. beforeEach(async function () {
  316. this.safeMathMemoryCheck = await SafeMathMemoryCheck.new();
  317. });
  318. it('add', async function () {
  319. expect(await this.safeMathMemoryCheck.$addMemoryCheck()).to.be.bignumber.equal('0');
  320. });
  321. it('sub', async function () {
  322. expect(await this.safeMathMemoryCheck.$subMemoryCheck()).to.be.bignumber.equal('0');
  323. });
  324. it('mul', async function () {
  325. expect(await this.safeMathMemoryCheck.$mulMemoryCheck()).to.be.bignumber.equal('0');
  326. });
  327. it('div', async function () {
  328. expect(await this.safeMathMemoryCheck.$divMemoryCheck()).to.be.bignumber.equal('0');
  329. });
  330. it('mod', async function () {
  331. expect(await this.safeMathMemoryCheck.$modMemoryCheck()).to.be.bignumber.equal('0');
  332. });
  333. });
  334. });