SafeMath.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  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(await this.safeMath.methods['$sub(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  230. .to.be.bignumber.equal(a.sub(b));
  231. });
  232. it('reverts if subtraction result would be negative', async function () {
  233. const a = new BN('1234');
  234. const b = new BN('5678');
  235. await expectRevert(
  236. this.safeMath.methods['$sub(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  237. 'MyErrorMessage',
  238. );
  239. });
  240. });
  241. describe('div', function () {
  242. it('divides correctly', async function () {
  243. const a = new BN('5678');
  244. const b = new BN('5678');
  245. expect(await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  246. .to.be.bignumber.equal(a.div(b));
  247. });
  248. it('divides zero correctly', async function () {
  249. const a = new BN('0');
  250. const b = new BN('5678');
  251. expect(await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  252. .to.be.bignumber.equal('0');
  253. });
  254. it('returns complete number result on non-even division', async function () {
  255. const a = new BN('7000');
  256. const b = new BN('5678');
  257. expect(await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  258. .to.be.bignumber.equal('1');
  259. });
  260. it('reverts on division by zero', async function () {
  261. const a = new BN('5678');
  262. const b = new BN('0');
  263. await expectRevert(
  264. this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  265. 'MyErrorMessage',
  266. );
  267. });
  268. });
  269. describe('mod', function () {
  270. describe('modulos correctly', async function () {
  271. it('when the dividend is smaller than the divisor', async function () {
  272. const a = new BN('284');
  273. const b = new BN('5678');
  274. expect(await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  275. .to.be.bignumber.equal(a.mod(b));
  276. });
  277. it('when the dividend is equal to the divisor', async function () {
  278. const a = new BN('5678');
  279. const b = new BN('5678');
  280. expect(await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  281. .to.be.bignumber.equal(a.mod(b));
  282. });
  283. it('when the dividend is larger than the divisor', async function () {
  284. const a = new BN('7000');
  285. const b = new BN('5678');
  286. expect(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 a multiple of the divisor', async function () {
  290. const a = new BN('17034'); // 17034 == 5678 * 3
  291. const b = new BN('5678');
  292. expect(await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'))
  293. .to.be.bignumber.equal(a.mod(b));
  294. });
  295. });
  296. it('reverts with a 0 divisor', async function () {
  297. const a = new BN('5678');
  298. const b = new BN('0');
  299. await expectRevert(
  300. this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'),
  301. 'MyErrorMessage',
  302. );
  303. });
  304. });
  305. });
  306. describe('memory leakage', function () {
  307. beforeEach(async function () {
  308. this.safeMathMemoryCheck = await SafeMathMemoryCheck.new();
  309. });
  310. it('add', async function () {
  311. expect(await this.safeMathMemoryCheck.$addMemoryCheck()).to.be.bignumber.equal('0');
  312. });
  313. it('sub', async function () {
  314. expect(await this.safeMathMemoryCheck.$subMemoryCheck()).to.be.bignumber.equal('0');
  315. });
  316. it('mul', async function () {
  317. expect(await this.safeMathMemoryCheck.$mulMemoryCheck()).to.be.bignumber.equal('0');
  318. });
  319. it('div', async function () {
  320. expect(await this.safeMathMemoryCheck.$divMemoryCheck()).to.be.bignumber.equal('0');
  321. });
  322. it('mod', async function () {
  323. expect(await this.safeMathMemoryCheck.$modMemoryCheck()).to.be.bignumber.equal('0');
  324. });
  325. });
  326. });