Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safe Casting Library from uint256 to uintXX #1926

Merged
merged 19 commits into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions contracts/mocks/SafeCastMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pragma solidity ^0.5.0;

import "../util/SafeCast.sol";
bh2smith marked this conversation as resolved.
Show resolved Hide resolved

contract SafeCastMock {
using SafeCast for uint;

function castU128(uint a) public pure returns (uint128) {
return a.castU128();
}

function castU64(uint a) public pure returns (uint64) {
return a.castU64();
}

function castU32(uint a) public pure returns (uint32) {
return a.castU32();
}

function castU16(uint a) public pure returns (uint16) {
return a.castU16();
}

function castU8(uint a) public pure returns (uint8) {
return a.castU8();
}
}
88 changes: 88 additions & 0 deletions contracts/utils/SafeCast.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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 by default 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.
*/
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 cannot overflow.
*/
function castU128(uint a) internal pure returns (uint128) {
require(a < 2**128, "SafeCast: downcast overflow");
bh2smith marked this conversation as resolved.
Show resolved Hide resolved
return uint128(a);
}

/**
* @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 cannot overflow.
bh2smith marked this conversation as resolved.
Show resolved Hide resolved
*/
function castU64(uint a) internal pure returns (uint64) {
require(a < 2**64, "SafeCast: downcast overflow");
bh2smith marked this conversation as resolved.
Show resolved Hide resolved
return uint64(a);
}

/**
* @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 cannot overflow.
*/
function castU32(uint a) internal pure returns (uint32) {
require(a < 2**32, "SafeCast: downcast overflow");
return uint32(a);
}

/**
* @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 cannot overflow.
*/
function castU16(uint a) internal pure returns (uint16) {
require(a < 2**16, "SafeCast: downcast overflow");
return uint16(a);
}

/**
* @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 cannot overflow.
*/
function castU8(uint a) internal pure returns (uint8) {

bh2smith marked this conversation as resolved.
Show resolved Hide resolved
require(a < 2**8, "SafeCast: downcast overflow");
return uint8(a);
}
}
81 changes: 81 additions & 0 deletions test/utils/SafeCast.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const SafeCastMock = artifacts.require('SafeCastMock');

bh2smith marked this conversation as resolved.
Show resolved Hide resolved
const { expectRevert } = require('openzeppelin-test-helpers');

contract('SafeCast', async (accounts) => {
describe('castU128()', () => {
it('reverts on overflow (i.e. input >= 2^128)', async () => {
const contract = await SafeCastMock.new();
await expectRevert(
contract.castU128('340282366920938463463374607431768211456'),
'SafeCast: downcast overflow'
);
await expectRevert(
contract.castU128('999340282366920938463463374607431768211455'),
'SafeCast: downcast overflow'
);
});
it('passes where appropriate (i.e. 0 <= input < 2^128)', async () => {
const contract = await SafeCastMock.new();
assert.equal(await contract.castU128(0), 0);
assert.equal(await contract.castU128(1), 1);
assert.equal(
(await contract.castU128('340282366920938463463374607431768211455')).toString(),
'340282366920938463463374607431768211455'
);
});
});
describe('castU64()', () => {
it('reverts on overflow (i.e. input >= 2^64)', async () => {
const contract = await SafeCastMock.new();
await expectRevert(contract.castU64('18446744073709551616'), 'SafeCast: downcast overflow');
await expectRevert(contract.castU64('18446744073709551617'), 'SafeCast: downcast overflow');
});
it('passes where appropriate (i.e. 0 <= input < 2^64)', async () => {
const contract = await SafeCastMock.new();
assert.equal(await contract.castU64(0), 0);
assert.equal(await contract.castU64(1), 1);
assert.equal((await contract.castU64('18446744073709551615')).toString(), '18446744073709551615');
});
});
describe('castU32()', () => {
it('reverts on overflow (i.e. input >= 2^32)', async () => {
const contract = await SafeCastMock.new();
await expectRevert(contract.castU32('4294967296'), 'SafeCast: downcast overflow');
await expectRevert(contract.castU32('4294967297'), 'SafeCast: downcast overflow');
});
it('passes where appropriate (i.e. 0 <= input < 2^32)', async () => {
const contract = await SafeCastMock.new();
assert.equal(await contract.castU32(0), 0);
assert.equal(await contract.castU32(1), 1);
assert.equal(await contract.castU32(4294967295), 4294967295);
});
});
describe('castU16()', () => {
it('reverts on overflow (i.e. input >= 2^16)', async () => {
const contract = await SafeCastMock.new();
await expectRevert(contract.castU16('65536'), 'SafeCast: downcast overflow');
await expectRevert(contract.castU16('65537'), 'SafeCast: downcast overflow');
});

it('passes where appropriate (i.e. 0 <= input < 2^16)', async () => {
const contract = await SafeCastMock.new();
assert.equal(await contract.castU16(0), 0);
assert.equal(await contract.castU16(1), 1);
assert.equal(await contract.castU16(65535), 65535);
});
});
describe('castU8()', () => {
it('reverts on overflow (i.e. input >= 2^8)', async () => {
const contract = await SafeCastMock.new();
await expectRevert(contract.castU8(256), 'SafeCast: downcast overflow');
await expectRevert(contract.castU8(257), 'SafeCast: downcast overflow');
});
it('passes where appropriate (i.e. 0 <= input < 2^8)', async () => {
const contract = await SafeCastMock.new();
assert.equal(await contract.castU8(0), 0);
assert.equal(await contract.castU8(1), 1);
assert.equal(await contract.castU8(255), 255);
});
});
});