diff --git a/contracts/utils/compiler.json b/contracts/utils/compiler.json index e0b173967b..7d494ae78d 100644 --- a/contracts/utils/compiler.json +++ b/contracts/utils/compiler.json @@ -36,6 +36,7 @@ "test/TestConstants.sol", "test/TestLibAddressArray.sol", "test/TestLibBytes.sol", + "test/TestOwnable.sol", "test/TestSafeMath.sol" ] } diff --git a/contracts/utils/contracts/test/TestOwnable.sol b/contracts/utils/contracts/test/TestOwnable.sol new file mode 100644 index 0000000000..e79033c5cc --- /dev/null +++ b/contracts/utils/contracts/test/TestOwnable.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.5.9; + +import "../src/Ownable.sol"; + + +contract TestOwnable is + Ownable +{ + function externalOnlyOwner() + external + onlyOwner + {} // solhint-disable-line no-empty-blocks +} diff --git a/contracts/utils/package.json b/contracts/utils/package.json index 73e2f9308e..9ddbceb9d1 100644 --- a/contracts/utils/package.json +++ b/contracts/utils/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|RichErrors|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes|TestSafeMath).json", + "abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|RichErrors|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes|TestOwnable|TestSafeMath).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/utils/src/artifacts.ts b/contracts/utils/src/artifacts.ts index 4028caad50..9267a5b6b7 100644 --- a/contracts/utils/src/artifacts.ts +++ b/contracts/utils/src/artifacts.ts @@ -17,6 +17,7 @@ import * as SafeMath from '../generated-artifacts/SafeMath.json'; import * as TestConstants from '../generated-artifacts/TestConstants.json'; import * as TestLibAddressArray from '../generated-artifacts/TestLibAddressArray.json'; import * as TestLibBytes from '../generated-artifacts/TestLibBytes.json'; +import * as TestOwnable from '../generated-artifacts/TestOwnable.json'; import * as TestSafeMath from '../generated-artifacts/TestSafeMath.json'; export const artifacts = { LibAddress: LibAddress as ContractArtifact, @@ -31,5 +32,6 @@ export const artifacts = { TestConstants: TestConstants as ContractArtifact, TestLibAddressArray: TestLibAddressArray as ContractArtifact, TestLibBytes: TestLibBytes as ContractArtifact, + TestOwnable: TestOwnable as ContractArtifact, TestSafeMath: TestSafeMath as ContractArtifact, }; diff --git a/contracts/utils/src/wrappers.ts b/contracts/utils/src/wrappers.ts index 2adf5244da..5dffb7b8f4 100644 --- a/contracts/utils/src/wrappers.ts +++ b/contracts/utils/src/wrappers.ts @@ -15,4 +15,5 @@ export * from '../generated-wrappers/safe_math'; export * from '../generated-wrappers/test_constants'; export * from '../generated-wrappers/test_lib_address_array'; export * from '../generated-wrappers/test_lib_bytes'; +export * from '../generated-wrappers/test_ownable'; export * from '../generated-wrappers/test_safe_math'; diff --git a/contracts/utils/test/ownable.ts b/contracts/utils/test/ownable.ts new file mode 100644 index 0000000000..451f9632bc --- /dev/null +++ b/contracts/utils/test/ownable.ts @@ -0,0 +1,65 @@ +import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { OwnableRevertErrors } from '@0x/utils'; +import * as chai from 'chai'; +import * as _ from 'lodash'; + +import { artifacts, TestOwnableContract } from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Ownable', () => { + let ownable: TestOwnableContract; + let owner: string; + let nonOwner: string; + + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + owner = await accounts[0]; + nonOwner = await accounts[1]; + await blockchainLifecycle.startAsync(); + // Deploy SafeMath from the owner address + txDefaults.from = owner; + ownable = await TestOwnableContract.deployFrom0xArtifactAsync( + artifacts.TestOwnable, + provider, + txDefaults, + ); + }); + + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + + // tslint:disable:no-unused-expression + describe('onlyOwner', () => { + it('should throw if sender is not owner', async () => { + const expectedError = new OwnableRevertErrors.OnlyOwnerError( + nonOwner, + owner, + ); + return expect(ownable.externalOnlyOwner.callAsync({ from: nonOwner })).to.revertWith(expectedError); + }); + + it('should succeed if sender is owner', async () => { + expect(ownable.externalOnlyOwner.callAsync({ from: owner })).to.be.fulfilled; + }); + }); + + describe('transferOwnership', () => { + it('should not transfer ownership if the specified new owner is the zero address', async () => { + expect(ownable.transferOwnership.sendTransactionAsync(constants.NULL_ADDRESS, { from: owner })).to.be.fulfilled; + const updatedOwner = await ownable.owner.callAsync(); + expect(updatedOwner).to.be.eq(owner); + }); + + it('should transfer ownership if the specified new owner is not the zero address', async () => { + expect(ownable.transferOwnership.sendTransactionAsync(nonOwner, { from: owner })).to.be.fulfilled; + const updatedOwner = await ownable.owner.callAsync(); + expect(updatedOwner).to.be.eq(nonOwner); + }); + }); + // tslint:enable:no-unused-expression +}); diff --git a/contracts/utils/tsconfig.json b/contracts/utils/tsconfig.json index c89e534ee4..3f1913dd7e 100644 --- a/contracts/utils/tsconfig.json +++ b/contracts/utils/tsconfig.json @@ -15,6 +15,7 @@ "generated-artifacts/TestConstants.json", "generated-artifacts/TestLibAddressArray.json", "generated-artifacts/TestLibBytes.json", + "generated-artifacts/TestOwnable.json", "generated-artifacts/TestSafeMath.json" ], "exclude": ["./deploy/solc/solc_bin"]