StandardToken.test.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. const { assertRevert } = require('../../helpers/assertRevert');
  2. const expectEvent = require('../../helpers/expectEvent');
  3. const StandardToken = artifacts.require('StandardTokenMock');
  4. const BigNumber = web3.BigNumber;
  5. require('chai')
  6. .use(require('chai-bignumber')(BigNumber))
  7. .should();
  8. contract('StandardToken', function ([_, owner, recipient, anotherAccount]) {
  9. const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
  10. beforeEach(async function () {
  11. this.token = await StandardToken.new(owner, 100);
  12. });
  13. describe('total supply', function () {
  14. it('returns the total amount of tokens', async function () {
  15. const totalSupply = await this.token.totalSupply();
  16. totalSupply.should.be.bignumber.equal(100);
  17. });
  18. });
  19. describe('balanceOf', function () {
  20. describe('when the requested account has no tokens', function () {
  21. it('returns zero', async function () {
  22. const balance = await this.token.balanceOf(anotherAccount);
  23. balance.should.be.bignumber.equal(0);
  24. });
  25. });
  26. describe('when the requested account has some tokens', function () {
  27. it('returns the total amount of tokens', async function () {
  28. const balance = await this.token.balanceOf(owner);
  29. balance.should.be.bignumber.equal(100);
  30. });
  31. });
  32. });
  33. describe('transfer', function () {
  34. describe('when the recipient is not the zero address', function () {
  35. const to = recipient;
  36. describe('when the sender does not have enough balance', function () {
  37. const amount = 101;
  38. it('reverts', async function () {
  39. await assertRevert(this.token.transfer(to, amount, { from: owner }));
  40. });
  41. });
  42. describe('when the sender has enough balance', function () {
  43. const amount = 100;
  44. it('transfers the requested amount', async function () {
  45. await this.token.transfer(to, amount, { from: owner });
  46. const senderBalance = await this.token.balanceOf(owner);
  47. senderBalance.should.be.bignumber.equal(0);
  48. const recipientBalance = await this.token.balanceOf(to);
  49. recipientBalance.should.be.bignumber.equal(amount);
  50. });
  51. it('emits a transfer event', async function () {
  52. const { logs } = await this.token.transfer(to, amount, { from: owner });
  53. const event = expectEvent.inLogs(logs, 'Transfer', {
  54. from: owner,
  55. to: to,
  56. });
  57. event.args.value.should.be.bignumber.equal(amount);
  58. });
  59. });
  60. });
  61. describe('when the recipient is the zero address', function () {
  62. const to = ZERO_ADDRESS;
  63. it('reverts', async function () {
  64. await assertRevert(this.token.transfer(to, 100, { from: owner }));
  65. });
  66. });
  67. });
  68. describe('approve', function () {
  69. describe('when the spender is not the zero address', function () {
  70. const spender = recipient;
  71. describe('when the sender has enough balance', function () {
  72. const amount = 100;
  73. it('emits an approval event', async function () {
  74. const { logs } = await this.token.approve(spender, amount, { from: owner });
  75. logs.length.should.eq(1);
  76. logs[0].event.should.eq('Approval');
  77. logs[0].args.owner.should.eq(owner);
  78. logs[0].args.spender.should.eq(spender);
  79. logs[0].args.value.should.be.bignumber.equal(amount);
  80. });
  81. describe('when there was no approved amount before', function () {
  82. it('approves the requested amount', async function () {
  83. await this.token.approve(spender, amount, { from: owner });
  84. const allowance = await this.token.allowance(owner, spender);
  85. allowance.should.be.bignumber.equal(amount);
  86. });
  87. });
  88. describe('when the spender had an approved amount', function () {
  89. beforeEach(async function () {
  90. await this.token.approve(spender, 1, { from: owner });
  91. });
  92. it('approves the requested amount and replaces the previous one', async function () {
  93. await this.token.approve(spender, amount, { from: owner });
  94. const allowance = await this.token.allowance(owner, spender);
  95. allowance.should.be.bignumber.equal(amount);
  96. });
  97. });
  98. });
  99. describe('when the sender does not have enough balance', function () {
  100. const amount = 101;
  101. it('emits an approval event', async function () {
  102. const { logs } = await this.token.approve(spender, amount, { from: owner });
  103. logs.length.should.eq(1);
  104. logs[0].event.should.eq('Approval');
  105. logs[0].args.owner.should.eq(owner);
  106. logs[0].args.spender.should.eq(spender);
  107. logs[0].args.value.should.be.bignumber.equal(amount);
  108. });
  109. describe('when there was no approved amount before', function () {
  110. it('approves the requested amount', async function () {
  111. await this.token.approve(spender, amount, { from: owner });
  112. const allowance = await this.token.allowance(owner, spender);
  113. allowance.should.be.bignumber.equal(amount);
  114. });
  115. });
  116. describe('when the spender had an approved amount', function () {
  117. beforeEach(async function () {
  118. await this.token.approve(spender, 1, { from: owner });
  119. });
  120. it('approves the requested amount and replaces the previous one', async function () {
  121. await this.token.approve(spender, amount, { from: owner });
  122. const allowance = await this.token.allowance(owner, spender);
  123. allowance.should.be.bignumber.equal(amount);
  124. });
  125. });
  126. });
  127. });
  128. describe('when the spender is the zero address', function () {
  129. const amount = 100;
  130. const spender = ZERO_ADDRESS;
  131. it('approves the requested amount', async function () {
  132. await this.token.approve(spender, amount, { from: owner });
  133. const allowance = await this.token.allowance(owner, spender);
  134. allowance.should.be.bignumber.equal(amount);
  135. });
  136. it('emits an approval event', async function () {
  137. const { logs } = await this.token.approve(spender, amount, { from: owner });
  138. logs.length.should.eq(1);
  139. logs[0].event.should.eq('Approval');
  140. logs[0].args.owner.should.eq(owner);
  141. logs[0].args.spender.should.eq(spender);
  142. logs[0].args.value.should.be.bignumber.equal(amount);
  143. });
  144. });
  145. });
  146. describe('transfer from', function () {
  147. const spender = recipient;
  148. describe('when the recipient is not the zero address', function () {
  149. const to = anotherAccount;
  150. describe('when the spender has enough approved balance', function () {
  151. beforeEach(async function () {
  152. await this.token.approve(spender, 100, { from: owner });
  153. });
  154. describe('when the owner has enough balance', function () {
  155. const amount = 100;
  156. it('transfers the requested amount', async function () {
  157. await this.token.transferFrom(owner, to, amount, { from: spender });
  158. const senderBalance = await this.token.balanceOf(owner);
  159. senderBalance.should.be.bignumber.equal(0);
  160. const recipientBalance = await this.token.balanceOf(to);
  161. recipientBalance.should.be.bignumber.equal(amount);
  162. });
  163. it('decreases the spender allowance', async function () {
  164. await this.token.transferFrom(owner, to, amount, { from: spender });
  165. const allowance = await this.token.allowance(owner, spender);
  166. allowance.should.be.bignumber.equal(0);
  167. });
  168. it('emits a transfer event', async function () {
  169. const { logs } = await this.token.transferFrom(owner, to, amount, { from: spender });
  170. logs.length.should.eq(1);
  171. logs[0].event.should.eq('Transfer');
  172. logs[0].args.from.should.eq(owner);
  173. logs[0].args.to.should.eq(to);
  174. logs[0].args.value.should.be.bignumber.equal(amount);
  175. });
  176. });
  177. describe('when the owner does not have enough balance', function () {
  178. const amount = 101;
  179. it('reverts', async function () {
  180. await assertRevert(this.token.transferFrom(owner, to, amount, { from: spender }));
  181. });
  182. });
  183. });
  184. describe('when the spender does not have enough approved balance', function () {
  185. beforeEach(async function () {
  186. await this.token.approve(spender, 99, { from: owner });
  187. });
  188. describe('when the owner has enough balance', function () {
  189. const amount = 100;
  190. it('reverts', async function () {
  191. await assertRevert(this.token.transferFrom(owner, to, amount, { from: spender }));
  192. });
  193. });
  194. describe('when the owner does not have enough balance', function () {
  195. const amount = 101;
  196. it('reverts', async function () {
  197. await assertRevert(this.token.transferFrom(owner, to, amount, { from: spender }));
  198. });
  199. });
  200. });
  201. });
  202. describe('when the recipient is the zero address', function () {
  203. const amount = 100;
  204. const to = ZERO_ADDRESS;
  205. beforeEach(async function () {
  206. await this.token.approve(spender, amount, { from: owner });
  207. });
  208. it('reverts', async function () {
  209. await assertRevert(this.token.transferFrom(owner, to, amount, { from: spender }));
  210. });
  211. });
  212. });
  213. describe('decrease approval', function () {
  214. describe('when the spender is not the zero address', function () {
  215. const spender = recipient;
  216. describe('when the sender has enough balance', function () {
  217. const amount = 100;
  218. it('emits an approval event', async function () {
  219. const { logs } = await this.token.decreaseApproval(spender, amount, { from: owner });
  220. logs.length.should.eq(1);
  221. logs[0].event.should.eq('Approval');
  222. logs[0].args.owner.should.eq(owner);
  223. logs[0].args.spender.should.eq(spender);
  224. logs[0].args.value.should.be.bignumber.equal(0);
  225. });
  226. describe('when there was no approved amount before', function () {
  227. it('keeps the allowance to zero', async function () {
  228. await this.token.decreaseApproval(spender, amount, { from: owner });
  229. const allowance = await this.token.allowance(owner, spender);
  230. allowance.should.be.bignumber.equal(0);
  231. });
  232. });
  233. describe('when the spender had an approved amount', function () {
  234. const approvedAmount = amount;
  235. beforeEach(async function () {
  236. await this.token.approve(spender, approvedAmount, { from: owner });
  237. });
  238. it('decreases the spender allowance subtracting the requested amount', async function () {
  239. await this.token.decreaseApproval(spender, approvedAmount - 5, { from: owner });
  240. const allowance = await this.token.allowance(owner, spender);
  241. allowance.should.be.bignumber.equal(5);
  242. });
  243. it('sets the allowance to zero when all allowance is removed', async function () {
  244. await this.token.decreaseApproval(spender, approvedAmount, { from: owner });
  245. const allowance = await this.token.allowance(owner, spender);
  246. allowance.should.be.bignumber.equal(0);
  247. });
  248. it('sets the allowance to zero when more than the full allowance is removed', async function () {
  249. await this.token.decreaseApproval(spender, approvedAmount + 5, { from: owner });
  250. const allowance = await this.token.allowance(owner, spender);
  251. allowance.should.be.bignumber.equal(0);
  252. });
  253. });
  254. });
  255. describe('when the sender does not have enough balance', function () {
  256. const amount = 101;
  257. it('emits an approval event', async function () {
  258. const { logs } = await this.token.decreaseApproval(spender, amount, { from: owner });
  259. logs.length.should.eq(1);
  260. logs[0].event.should.eq('Approval');
  261. logs[0].args.owner.should.eq(owner);
  262. logs[0].args.spender.should.eq(spender);
  263. logs[0].args.value.should.be.bignumber.equal(0);
  264. });
  265. describe('when there was no approved amount before', function () {
  266. it('keeps the allowance to zero', async function () {
  267. await this.token.decreaseApproval(spender, amount, { from: owner });
  268. const allowance = await this.token.allowance(owner, spender);
  269. allowance.should.be.bignumber.equal(0);
  270. });
  271. });
  272. describe('when the spender had an approved amount', function () {
  273. beforeEach(async function () {
  274. await this.token.approve(spender, amount + 1, { from: owner });
  275. });
  276. it('decreases the spender allowance subtracting the requested amount', async function () {
  277. await this.token.decreaseApproval(spender, amount, { from: owner });
  278. const allowance = await this.token.allowance(owner, spender);
  279. allowance.should.be.bignumber.equal(1);
  280. });
  281. });
  282. });
  283. });
  284. describe('when the spender is the zero address', function () {
  285. const amount = 100;
  286. const spender = ZERO_ADDRESS;
  287. it('decreases the requested amount', async function () {
  288. await this.token.decreaseApproval(spender, amount, { from: owner });
  289. const allowance = await this.token.allowance(owner, spender);
  290. allowance.should.be.bignumber.equal(0);
  291. });
  292. it('emits an approval event', async function () {
  293. const { logs } = await this.token.decreaseApproval(spender, amount, { from: owner });
  294. logs.length.should.eq(1);
  295. logs[0].event.should.eq('Approval');
  296. logs[0].args.owner.should.eq(owner);
  297. logs[0].args.spender.should.eq(spender);
  298. logs[0].args.value.should.be.bignumber.equal(0);
  299. });
  300. });
  301. });
  302. describe('increase approval', function () {
  303. const amount = 100;
  304. describe('when the spender is not the zero address', function () {
  305. const spender = recipient;
  306. describe('when the sender has enough balance', function () {
  307. it('emits an approval event', async function () {
  308. const { logs } = await this.token.increaseApproval(spender, amount, { from: owner });
  309. logs.length.should.eq(1);
  310. logs[0].event.should.eq('Approval');
  311. logs[0].args.owner.should.eq(owner);
  312. logs[0].args.spender.should.eq(spender);
  313. logs[0].args.value.should.be.bignumber.equal(amount);
  314. });
  315. describe('when there was no approved amount before', function () {
  316. it('approves the requested amount', async function () {
  317. await this.token.increaseApproval(spender, amount, { from: owner });
  318. const allowance = await this.token.allowance(owner, spender);
  319. allowance.should.be.bignumber.equal(amount);
  320. });
  321. });
  322. describe('when the spender had an approved amount', function () {
  323. beforeEach(async function () {
  324. await this.token.approve(spender, 1, { from: owner });
  325. });
  326. it('increases the spender allowance adding the requested amount', async function () {
  327. await this.token.increaseApproval(spender, amount, { from: owner });
  328. const allowance = await this.token.allowance(owner, spender);
  329. allowance.should.be.bignumber.equal(amount + 1);
  330. });
  331. });
  332. });
  333. describe('when the sender does not have enough balance', function () {
  334. const amount = 101;
  335. it('emits an approval event', async function () {
  336. const { logs } = await this.token.increaseApproval(spender, amount, { from: owner });
  337. logs.length.should.eq(1);
  338. logs[0].event.should.eq('Approval');
  339. logs[0].args.owner.should.eq(owner);
  340. logs[0].args.spender.should.eq(spender);
  341. logs[0].args.value.should.be.bignumber.equal(amount);
  342. });
  343. describe('when there was no approved amount before', function () {
  344. it('approves the requested amount', async function () {
  345. await this.token.increaseApproval(spender, amount, { from: owner });
  346. const allowance = await this.token.allowance(owner, spender);
  347. allowance.should.be.bignumber.equal(amount);
  348. });
  349. });
  350. describe('when the spender had an approved amount', function () {
  351. beforeEach(async function () {
  352. await this.token.approve(spender, 1, { from: owner });
  353. });
  354. it('increases the spender allowance adding the requested amount', async function () {
  355. await this.token.increaseApproval(spender, amount, { from: owner });
  356. const allowance = await this.token.allowance(owner, spender);
  357. allowance.should.be.bignumber.equal(amount + 1);
  358. });
  359. });
  360. });
  361. });
  362. describe('when the spender is the zero address', function () {
  363. const spender = ZERO_ADDRESS;
  364. it('approves the requested amount', async function () {
  365. await this.token.increaseApproval(spender, amount, { from: owner });
  366. const allowance = await this.token.allowance(owner, spender);
  367. allowance.should.be.bignumber.equal(amount);
  368. });
  369. it('emits an approval event', async function () {
  370. const { logs } = await this.token.increaseApproval(spender, amount, { from: owner });
  371. logs.length.should.eq(1);
  372. logs[0].event.should.eq('Approval');
  373. logs[0].args.owner.should.eq(owner);
  374. logs[0].args.spender.should.eq(spender);
  375. logs[0].args.value.should.be.bignumber.equal(amount);
  376. });
  377. });
  378. });
  379. describe('_mint', function () {
  380. const initialSupply = new BigNumber(100);
  381. const amount = new BigNumber(50);
  382. it('rejects a null account', async function () {
  383. await assertRevert(this.token.mint(ZERO_ADDRESS, amount));
  384. });
  385. describe('for a non null account', function () {
  386. beforeEach('minting', async function () {
  387. const { logs } = await this.token.mint(recipient, amount);
  388. this.logs = logs;
  389. });
  390. it('increments totalSupply', async function () {
  391. const expectedSupply = initialSupply.plus(amount);
  392. (await this.token.totalSupply()).should.be.bignumber.equal(expectedSupply);
  393. });
  394. it('increments recipient balance', async function () {
  395. (await this.token.balanceOf(recipient)).should.be.bignumber.equal(amount);
  396. });
  397. it('emits Transfer event', async function () {
  398. const event = expectEvent.inLogs(this.logs, 'Transfer', {
  399. from: ZERO_ADDRESS,
  400. to: recipient,
  401. });
  402. event.args.value.should.be.bignumber.equal(amount);
  403. });
  404. });
  405. });
  406. describe('_burn', function () {
  407. const initialSupply = new BigNumber(100);
  408. const amount = new BigNumber(50);
  409. it('rejects a null account', async function () {
  410. await assertRevert(this.token.burn(ZERO_ADDRESS, amount));
  411. });
  412. describe('for a non null account', function () {
  413. it('rejects burning more than balance', async function () {
  414. await assertRevert(this.token.burn(owner, initialSupply.plus(1)));
  415. });
  416. describe('for less amount than balance', function () {
  417. beforeEach('burning', async function () {
  418. const { logs } = await this.token.burn(owner, amount);
  419. this.logs = logs;
  420. });
  421. it('decrements totalSupply', async function () {
  422. const expectedSupply = initialSupply.minus(amount);
  423. (await this.token.totalSupply()).should.be.bignumber.equal(expectedSupply);
  424. });
  425. it('decrements owner balance', async function () {
  426. const expectedBalance = initialSupply.minus(amount);
  427. (await this.token.balanceOf(owner)).should.be.bignumber.equal(expectedBalance);
  428. });
  429. it('emits Transfer event', async function () {
  430. const event = expectEvent.inLogs(this.logs, 'Transfer', {
  431. from: owner,
  432. to: ZERO_ADDRESS,
  433. });
  434. event.args.value.should.be.bignumber.equal(amount);
  435. });
  436. });
  437. });
  438. });
  439. describe('_burnFrom', function () {
  440. const initialSupply = new BigNumber(100);
  441. const allowance = new BigNumber(70);
  442. const amount = new BigNumber(50);
  443. const spender = anotherAccount;
  444. beforeEach('approving', async function () {
  445. await this.token.approve(spender, allowance, { from: owner });
  446. });
  447. it('rejects a null account', async function () {
  448. await assertRevert(this.token.burnFrom(ZERO_ADDRESS, amount));
  449. });
  450. describe('for a non null account', function () {
  451. it('rejects burning more than allowance', async function () {
  452. await assertRevert(this.token.burnFrom(owner, allowance.plus(1)));
  453. });
  454. it('rejects burning more than balance', async function () {
  455. await assertRevert(this.token.burnFrom(owner, initialSupply.plus(1)));
  456. });
  457. describe('for less amount than allowance', function () {
  458. beforeEach('burning', async function () {
  459. const { logs } = await this.token.burnFrom(owner, amount, { from: spender });
  460. this.logs = logs;
  461. });
  462. it('decrements totalSupply', async function () {
  463. const expectedSupply = initialSupply.minus(amount);
  464. (await this.token.totalSupply()).should.be.bignumber.equal(expectedSupply);
  465. });
  466. it('decrements owner balance', async function () {
  467. const expectedBalance = initialSupply.minus(amount);
  468. (await this.token.balanceOf(owner)).should.be.bignumber.equal(expectedBalance);
  469. });
  470. it('decrements spender allowance', async function () {
  471. const expectedAllowance = allowance.minus(amount);
  472. (await this.token.allowance(owner, spender)).should.be.bignumber.equal(expectedAllowance);
  473. });
  474. it('emits Transfer event', async function () {
  475. const event = expectEvent.inLogs(this.logs, 'Transfer', {
  476. from: owner,
  477. to: ZERO_ADDRESS,
  478. });
  479. event.args.value.should.be.bignumber.equal(amount);
  480. });
  481. });
  482. });
  483. });
  484. });