-
Notifications
You must be signed in to change notification settings - Fork 11.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove unnecessary SafeMath call (#1610)
* Refactor Counter to support increment and decrement. * Move Counter out of drafts. * Refactor ERC721 to use Counter. * Rollback Counter returning the current value in increment and decrement. * Update test/drafts/Counter.test.js Co-Authored-By: nventuro <nicolas.venturo@gmail.com> * Improve Counter documentation. * Move Counter.test to utils. * Move back Counter to drafts.
- Loading branch information
Showing
5 changed files
with
90 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,37 @@ | ||
pragma solidity ^0.5.2; | ||
|
||
import "../math/SafeMath.sol"; | ||
|
||
/** | ||
* @title Counters | ||
* @author Matt Condon (@shrugs) | ||
* @dev Provides an incrementing uint256 id acquired by the `Counter#next` getter. | ||
* Use this for issuing ERC721 ids or keeping track of request ids, anything you want, really. | ||
* @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number | ||
* of elements in a mapping, issuing ERC721 ids, or counting request ids | ||
* | ||
* Include with `using Counters` for Counters.Counter;` | ||
* @notice Does not allow an Id of 0, which is popularly used to signify a null state in solidity. | ||
* Does not protect from overflows, but if you have 2^256 ids, you have other problems. | ||
* (But actually, it's generally impossible to increment a counter this many times, energy wise | ||
* so it's not something you have to worry about.) | ||
* Include with `using Counter for Counter.Counter;` | ||
* Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath | ||
* overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never | ||
* directly accessed. | ||
*/ | ||
library Counters { | ||
using SafeMath for uint256; | ||
|
||
struct Counter { | ||
uint256 current; // default: 0 | ||
// This variable should never be directly accessed by users of the library: interactions must be restricted to | ||
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add | ||
// this feature: see https://github.com/ethereum/solidity/issues/4637 | ||
uint256 _value; // default: 0 | ||
} | ||
|
||
function current(Counter storage counter) internal view returns (uint256) { | ||
return counter._value; | ||
} | ||
|
||
function increment(Counter storage counter) internal { | ||
counter._value += 1; | ||
} | ||
|
||
function next(Counter storage index) internal returns (uint256) { | ||
index.current += 1; | ||
return index.current; | ||
function decrement(Counter storage counter) internal { | ||
counter._value = counter._value.sub(1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,58 @@ | ||
const { BN } = require('openzeppelin-test-helpers'); | ||
const { shouldFail } = require('openzeppelin-test-helpers'); | ||
|
||
const CountersImpl = artifacts.require('CountersImpl'); | ||
|
||
const EXPECTED = [new BN(1), new BN(2), new BN(3), new BN(4)]; | ||
const KEY1 = web3.utils.sha3('key1'); | ||
const KEY2 = web3.utils.sha3('key2'); | ||
|
||
contract('Counters', function ([_, owner]) { | ||
contract('Counters', function () { | ||
beforeEach(async function () { | ||
this.mock = await CountersImpl.new({ from: owner }); | ||
this.counter = await CountersImpl.new(); | ||
}); | ||
|
||
it('starts at zero', async function () { | ||
(await this.counter.current()).should.be.bignumber.equal('0'); | ||
}); | ||
|
||
context('custom key', async function () { | ||
it('should return expected values', async function () { | ||
for (const expectedId of EXPECTED) { | ||
await this.mock.doThing(KEY1, { from: owner }); | ||
const actualId = await this.mock.theId(); | ||
actualId.should.be.bignumber.equal(expectedId); | ||
} | ||
describe('increment', function () { | ||
it('increments the current value by one', async function () { | ||
await this.counter.increment(); | ||
(await this.counter.current()).should.be.bignumber.equal('1'); | ||
}); | ||
|
||
it('can be called multiple times', async function () { | ||
await this.counter.increment(); | ||
await this.counter.increment(); | ||
await this.counter.increment(); | ||
|
||
(await this.counter.current()).should.be.bignumber.equal('3'); | ||
}); | ||
}); | ||
|
||
context('parallel keys', async function () { | ||
it('should return expected values for each counter', async function () { | ||
for (const expectedId of EXPECTED) { | ||
await this.mock.doThing(KEY1, { from: owner }); | ||
let actualId = await this.mock.theId(); | ||
actualId.should.be.bignumber.equal(expectedId); | ||
|
||
await this.mock.doThing(KEY2, { from: owner }); | ||
actualId = await this.mock.theId(); | ||
actualId.should.be.bignumber.equal(expectedId); | ||
} | ||
describe('decrement', function () { | ||
beforeEach(async function () { | ||
await this.counter.increment(); | ||
(await this.counter.current()).should.be.bignumber.equal('1'); | ||
}); | ||
|
||
it('decrements the current value by one', async function () { | ||
await this.counter.decrement(); | ||
(await this.counter.current()).should.be.bignumber.equal('0'); | ||
}); | ||
|
||
it('reverts if the current value is 0', async function () { | ||
await this.counter.decrement(); | ||
await shouldFail.reverting(this.counter.decrement()); | ||
}); | ||
|
||
it('can be called multiple times', async function () { | ||
await this.counter.increment(); | ||
await this.counter.increment(); | ||
|
||
(await this.counter.current()).should.be.bignumber.equal('3'); | ||
|
||
await this.counter.decrement(); | ||
await this.counter.decrement(); | ||
await this.counter.decrement(); | ||
|
||
(await this.counter.current()).should.be.bignumber.equal('0'); | ||
}); | ||
}); | ||
}); |