Safe Casting Library from uint256 to uintXX (#1926)
* Include Safe Casting Library with complete and exhaustive test-suite. * linting test file. * Typo in SafeCast import statement * Update test/utils/SafeCast.test.js * Rename `castUXX` to `toUintXX` from suggestion * Tackling the quick and easy suggestions regarding error string improvements etc. * typo and changelog update. * Improve SafeCast tests * Update test/utils/SafeCast.test.js * Update test/utils/SafeCast.test.js * incorrect import * add SafeCast to docs site * Update CHANGELOG.md * Update SafeCast.solpull/1961/head
parent
bfca6bd379
commit
2c11ed59fa
@ -0,0 +1,27 @@ |
|||||||
|
pragma solidity ^0.5.0; |
||||||
|
|
||||||
|
import "../utils/SafeCast.sol"; |
||||||
|
|
||||||
|
contract SafeCastMock { |
||||||
|
using SafeCast for uint; |
||||||
|
|
||||||
|
function toUint128(uint a) public pure returns (uint128) { |
||||||
|
return a.toUint128(); |
||||||
|
} |
||||||
|
|
||||||
|
function toUint64(uint a) public pure returns (uint64) { |
||||||
|
return a.toUint64(); |
||||||
|
} |
||||||
|
|
||||||
|
function toUint32(uint a) public pure returns (uint32) { |
||||||
|
return a.toUint32(); |
||||||
|
} |
||||||
|
|
||||||
|
function toUint16(uint a) public pure returns (uint16) { |
||||||
|
return a.toUint16(); |
||||||
|
} |
||||||
|
|
||||||
|
function toUint8(uint a) public pure returns (uint8) { |
||||||
|
return a.toUint8(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
pragma solidity ^0.5.0; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Wrappers over Solidity's uintXX casting operators with added overflow |
||||||
|
* checks. |
||||||
|
* |
||||||
|
* Downcasting from uint256 in Solidity does not revert on overflow. This can |
||||||
|
* easily result in undesired exploitation or bugs, since developers usually |
||||||
|
* assume that overflows raise errors. `SafeCast` restores this intuition by |
||||||
|
* reverting the transaction when such an operation overflows. |
||||||
|
* |
||||||
|
* Using this library instead of the unchecked operations eliminates an entire |
||||||
|
* class of bugs, so it's recommended to use it always. |
||||||
|
* |
||||||
|
* Can be combined with {SafeMath} to extend it to smaller types, by performing |
||||||
|
* all math on `uint256` and then downcasting. |
||||||
|
*/ |
||||||
|
library SafeCast { |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Returns the downcasted uint128 from uint256, reverting on |
||||||
|
* overflow (when the input is greater than largest uint128). |
||||||
|
* |
||||||
|
* Counterpart to Solidity's `uint128` operator. |
||||||
|
* |
||||||
|
* Requirements: |
||||||
|
* |
||||||
|
* - input must fit into 128 bits |
||||||
|
*/ |
||||||
|
function toUint128(uint256 value) internal pure returns (uint128) { |
||||||
|
require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); |
||||||
|
return uint128(value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Returns the downcasted uint64 from uint256, reverting on |
||||||
|
* overflow (when the input is greater than largest uint64). |
||||||
|
* |
||||||
|
* Counterpart to Solidity's `uint64` operator. |
||||||
|
* |
||||||
|
* Requirements: |
||||||
|
* |
||||||
|
* - input must fit into 64 bits |
||||||
|
*/ |
||||||
|
function toUint64(uint256 value) internal pure returns (uint64) { |
||||||
|
require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); |
||||||
|
return uint64(value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Returns the downcasted uint32 from uint256, reverting on |
||||||
|
* overflow (when the input is greater than largest uint32). |
||||||
|
* |
||||||
|
* Counterpart to Solidity's `uint32` operator. |
||||||
|
* |
||||||
|
* Requirements: |
||||||
|
* |
||||||
|
* - input must fit into 32 bits |
||||||
|
*/ |
||||||
|
function toUint32(uint256 value) internal pure returns (uint32) { |
||||||
|
require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); |
||||||
|
return uint32(value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Returns the downcasted uint16 from uint256, reverting on |
||||||
|
* overflow (when the input is greater than largest uint16). |
||||||
|
* |
||||||
|
* Counterpart to Solidity's `uint16` operator. |
||||||
|
* |
||||||
|
* Requirements: |
||||||
|
* |
||||||
|
* - input must fit into 16 bits |
||||||
|
*/ |
||||||
|
function toUint16(uint256 value) internal pure returns (uint16) { |
||||||
|
require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); |
||||||
|
return uint16(value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Returns the downcasted uint8 from uint256, reverting on |
||||||
|
* overflow (when the input is greater than largest uint8). |
||||||
|
* |
||||||
|
* Counterpart to Solidity's `uint8` operator. |
||||||
|
* |
||||||
|
* Requirements: |
||||||
|
* |
||||||
|
* - input must fit into 8 bits. |
||||||
|
*/ |
||||||
|
function toUint8(uint256 value) internal pure returns (uint8) { |
||||||
|
require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); |
||||||
|
return uint8(value); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); |
||||||
|
|
||||||
|
const { expect } = require('chai'); |
||||||
|
|
||||||
|
const SafeCastMock = artifacts.require('SafeCastMock'); |
||||||
|
|
||||||
|
contract('SafeCast', async () => { |
||||||
|
beforeEach(async function () { |
||||||
|
this.safeCast = await SafeCastMock.new(); |
||||||
|
}); |
||||||
|
|
||||||
|
function testToUint (bits) { |
||||||
|
describe(`toUint${bits}`, () => { |
||||||
|
const maxValue = new BN('2').pow(new BN(bits)).subn(1); |
||||||
|
|
||||||
|
it('downcasts 0', async function () { |
||||||
|
expect(await this.safeCast[`toUint${bits}`](0)).to.be.bignumber.equal('0'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('downcasts 1', async function () { |
||||||
|
expect(await this.safeCast[`toUint${bits}`](1)).to.be.bignumber.equal('1'); |
||||||
|
}); |
||||||
|
|
||||||
|
it(`downcasts 2^${bits} - 1 (${maxValue})`, async function () { |
||||||
|
expect(await this.safeCast[`toUint${bits}`](maxValue)).to.be.bignumber.equal(maxValue); |
||||||
|
}); |
||||||
|
|
||||||
|
it(`reverts when downcasting 2^${bits} (${maxValue.addn(1)})`, async function () { |
||||||
|
await expectRevert( |
||||||
|
this.safeCast[`toUint${bits}`](maxValue.addn(1)), |
||||||
|
`SafeCast: value doesn't fit in ${bits} bits` |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it(`reverts when downcasting 2^${bits} + 1 (${maxValue.addn(2)})`, async function () { |
||||||
|
await expectRevert( |
||||||
|
this.safeCast[`toUint${bits}`](maxValue.addn(2)), |
||||||
|
`SafeCast: value doesn't fit in ${bits} bits` |
||||||
|
); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
[8, 16, 32, 64, 128].forEach(bits => testToUint(bits)); |
||||||
|
}); |
Loading…
Reference in new issue