diff --git a/contracts/AutoIncrementing.sol b/contracts/AutoIncrementing.sol new file mode 100644 index 000000000..cf46e2f57 --- /dev/null +++ b/contracts/AutoIncrementing.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.4.24; + + +/** + * @title AutoIncrementing + * @author Matt Condon (@shrugs) + * @dev Provides an auto-incrementing uint256 id acquired by the `Counter#nextId` getter. + * Use this for issuing ERC721Token ids or keeping track of request ids, anything you want, really. + * + * Include with `using AutoIncrementing for AutoIncrementing.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.) + */ +library AutoIncrementing { + + struct Counter { + uint256 prevId; // default: 0 + } + + function nextId(Counter storage _counter) + internal + returns (uint256) + { + _counter.prevId = _counter.prevId + 1; + return _counter.prevId; + } +} diff --git a/contracts/crowdsale/emission/AllowanceCrowdsale.sol b/contracts/crowdsale/emission/AllowanceCrowdsale.sol index 7635ca7d8..e271076a8 100644 --- a/contracts/crowdsale/emission/AllowanceCrowdsale.sol +++ b/contracts/crowdsale/emission/AllowanceCrowdsale.sol @@ -5,6 +5,8 @@ import "../../token/ERC20/ERC20.sol"; import "../../token/ERC20/ERC20Basic.sol"; import "../../token/ERC20/SafeERC20.sol"; import "../../math/SafeMath.sol"; + + /** * @title AllowanceCrowdsale * @dev Extension of Crowdsale where tokens are held by a wallet, which approves an allowance to the crowdsale. diff --git a/contracts/mocks/AutoIncrementingImpl.sol b/contracts/mocks/AutoIncrementingImpl.sol new file mode 100644 index 000000000..9aae333f0 --- /dev/null +++ b/contracts/mocks/AutoIncrementingImpl.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.24; + +import "../AutoIncrementing.sol"; + + +contract AutoIncrementingImpl { + using AutoIncrementing for AutoIncrementing.Counter; + + uint256 public theId; + + // use whatever key you want to track your counters + mapping(string => AutoIncrementing.Counter) private counters; + + function doThing(string _key) + public + returns (uint256) + { + theId = counters[_key].nextId(); + return theId; + } +} diff --git a/test/AutoIncrementing.test.js b/test/AutoIncrementing.test.js new file mode 100644 index 000000000..6ac433a0b --- /dev/null +++ b/test/AutoIncrementing.test.js @@ -0,0 +1,41 @@ +const { hashMessage } = require('./helpers/sign'); + +const AutoIncrementing = artifacts.require('AutoIncrementingImpl'); + +require('chai') + .use(require('chai-bignumber')(web3.BigNumber)) + .should(); + +const EXPECTED = [1, 2, 3, 4]; +const KEY1 = hashMessage('key1'); +const KEY2 = hashMessage('key2'); + +contract('AutoIncrementing', function ([_, owner]) { + beforeEach(async function () { + this.mock = await AutoIncrementing.new({ from: owner }); + }); + + context('custom key', async function () { + it('should return expected values', async function () { + for (let expectedId of EXPECTED) { + await this.mock.doThing(KEY1, { from: owner }); + const actualId = await this.mock.theId(); + actualId.should.be.bignumber.eq(expectedId); + } + }); + }); + + context('parallel keys', async function () { + it('should return expected values for each counter', async function () { + for (let expectedId of EXPECTED) { + await this.mock.doThing(KEY1, { from: owner }); + let actualId = await this.mock.theId(); + actualId.should.be.bignumber.eq(expectedId); + + await this.mock.doThing(KEY2, { from: owner }); + actualId = await this.mock.theId(); + actualId.should.be.bignumber.eq(expectedId); + } + }); + }); +});