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.sol
This commit is contained in:
committed by
Nicolás Venturo
parent
bfca6bd379
commit
2c11ed59fa
@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## 2.5.0 (unreleased)
|
||||
|
||||
### New features:
|
||||
* `SafeCast.toUintXX`: new library for integer downcasting, which allows for safe operation on smaller types (e.g. `uint32`) when combined with `SafeMath`. ([#1926](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1926))
|
||||
|
||||
## 2.4.0 (unreleased)
|
||||
|
||||
### New features:
|
||||
|
||||
27
contracts/mocks/SafeCastMock.sol
Normal file
27
contracts/mocks/SafeCastMock.sol
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,8 @@ Miscellaneous contracts containing utility functions, often related to working w
|
||||
|
||||
{{Address}}
|
||||
|
||||
{{SafeCast}}
|
||||
|
||||
{{Arrays}}
|
||||
|
||||
{{ReentrancyGuard}}
|
||||
|
||||
95
contracts/utils/SafeCast.sol
Normal file
95
contracts/utils/SafeCast.sol
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
45
test/utils/SafeCast.test.js
Normal file
45
test/utils/SafeCast.test.js
Normal file
@ -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));
|
||||
});
|
||||
Reference in New Issue
Block a user