diff --git a/CHANGELOG.md b/CHANGELOG.md index 34fd61b2cf0..11bb5952987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2.4.0 (unreleased) + +### New features: + * `Address.toPayable`: added a helper to convert between address types without having to resort to low-level casting. ([#1773](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1773)) + ## 2.3.0 (2019-05-27) ### New features: diff --git a/contracts/mocks/AddressImpl.sol b/contracts/mocks/AddressImpl.sol index a73591c370a..7697fdad42e 100644 --- a/contracts/mocks/AddressImpl.sol +++ b/contracts/mocks/AddressImpl.sol @@ -6,4 +6,8 @@ contract AddressImpl { function isContract(address account) external view returns (bool) { return Address.isContract(account); } + + function toPayable(address account) external pure returns (address payable) { + return Address.toPayable(account); + } } diff --git a/contracts/utils/Address.sol b/contracts/utils/Address.sol index 4a07b92da5c..ad2f7e72853 100644 --- a/contracts/utils/Address.sol +++ b/contracts/utils/Address.sol @@ -24,4 +24,12 @@ library Address { assembly { size := extcodesize(account) } return size > 0; } + + /** + * @dev Converts an `address` into `address payable`. Note that this is + * simply a type cast: the actual underlying value is not changed. + */ + function toPayable(address account) internal pure returns (address payable) { + return address(uint160(account)); + } } diff --git a/test/utils/Address.test.js b/test/utils/Address.test.js index cd9fd14533b..f002c39d70d 100644 --- a/test/utils/Address.test.js +++ b/test/utils/Address.test.js @@ -1,19 +1,37 @@ -require('openzeppelin-test-helpers'); +const { constants } = require('openzeppelin-test-helpers'); const AddressImpl = artifacts.require('AddressImpl'); const SimpleToken = artifacts.require('SimpleToken'); contract('Address', function ([_, other]) { + const ALL_ONES_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'; + beforeEach(async function () { this.mock = await AddressImpl.new(); }); - it('should return false for account address', async function () { - (await this.mock.isContract(other)).should.equal(false); + describe('isContract', function () { + it('should return false for account address', async function () { + (await this.mock.isContract(other)).should.equal(false); + }); + + it('should return true for contract address', async function () { + const contract = await SimpleToken.new(); + (await this.mock.isContract(contract.address)).should.equal(true); + }); }); - it('should return true for contract address', async function () { - const contract = await SimpleToken.new(); - (await this.mock.isContract(contract.address)).should.equal(true); + describe('toPayable', function () { + it('should return a payable address when the account is the zero address', async function () { + (await this.mock.toPayable(constants.ZERO_ADDRESS)).should.equal(constants.ZERO_ADDRESS); + }); + + it('should return a payable address when the account is an arbitrary address', async function () { + (await this.mock.toPayable(other)).should.equal(other); + }); + + it('should return a payable address when the account is the all ones address', async function () { + (await this.mock.toPayable(ALL_ONES_ADDRESS)).should.equal(ALL_ONES_ADDRESS); + }); }); });