@0x/contracts-zero-ex`: Create ZeroEx (proxy) contracts

This commit is contained in:
Lawrence Forman 2020-04-07 12:34:50 -04:00
parent 4212a08337
commit c11d661b39
42 changed files with 1959 additions and 0 deletions

View File

@ -0,0 +1,10 @@
# Blacklist all files
.*
*
# Whitelist lib
!lib/**/*
# Whitelist Solidity contracts
!contracts/src/**/*
# Blacklist tests in lib
/lib/test/*
# Package specific ignore

View File

@ -0,0 +1,11 @@
[
{
"version": "0.1.0",
"changes": [
{
"note": "Create this package",
"pr": 2540
}
]
}
]

View File

@ -0,0 +1,6 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1,65 @@
## ERC20BridgeSampler
This package contains contracts contracts for the ZeroEx extensible contract architecture.
## Installation
**Install**
```bash
npm install @0x/contracts-zero-ex --save
```
## Contributing
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
### Install Dependencies
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
```bash
yarn config set workspaces-experimental true
```
Then install dependencies
```bash
yarn install
```
### Build
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
PKG=@0x/contracts-zero-ex yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/contracts-zero-ex yarn watch
```
### Clean
```bash
yarn clean
```
### Lint
```bash
yarn lint
```
### Run Tests
```bash
yarn test
```

View File

@ -0,0 +1,27 @@
{
"artifactsDir": "./test/generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"isOfflineMode": false,
"shouldSaveStandardInput": true,
"compilerSettings": {
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
}

View File

@ -0,0 +1,122 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "./interfaces/IZeroExBootstrapper.sol";
import "./storage/LibProxyStorage.sol";
import "./errors/LibProxyRichErrors.sol";
/// @dev An extensible proxy contract that serves as a universal entry point for
/// interacting with the 0x protocol.
contract ZeroEx {
// solhint-disable separate-by-one-line-in-contract,indent,var-name-mixedcase
using LibBytesV06 for bytes;
/// @dev Construct this contract.
/// After constructing this contract, the deployer should call
/// `bootstrap()` to seed the initial feature set.
constructor() public {
// Set the `bootstrap()` caller to the deployer.
LibProxyStorage.getStorage().bootstrapCaller = msg.sender;
}
// solhint-disable state-visibility
/// @dev Forwards calls to the appropriate implementation contract.
fallback() external payable {
bytes4 selector = msg.data.readBytes4(0);
address impl = getFunctionImplementation(selector);
if (impl == address(0)) {
_revertWithData(LibProxyRichErrors.NotImplementedError(selector));
}
(bool success, bytes memory resultData) = impl.delegatecall(msg.data);
if (!success) {
_revertWithData(resultData);
}
_returnWithData(resultData);
}
/// @dev Fallback for just receiving ether.
receive() external payable {}
// solhint-enable state-visibility
/// @dev Bootstrap the initial feature set of this contract.
/// This can only be called once by the deployer of this contract.
/// @param bootstrappers Array of bootstrapping contracts to delegatecall into.
function bootstrap(IZeroExBootstrapper[] calldata bootstrappers) external {
LibProxyStorage.Storage storage stor = LibProxyStorage.getStorage();
// If `bootstrapCaller` is zero, the contract has already been bootstrapped.
address bootstrapCaller = stor.bootstrapCaller;
if (bootstrapCaller == address(0)) {
_revertWithData(LibProxyRichErrors.AlreadyBootstrappedError());
}
// Only the deployer caller can call this function.
if (bootstrapCaller != msg.sender) {
_revertWithData(
LibProxyRichErrors.InvalidBootstrapCallerError(msg.sender, bootstrapCaller)
);
}
// Prevent calling `bootstrap()` again.
stor.bootstrapCaller = address(0);
// Call the bootstrap contracts.
for (uint256 i = 0; i < bootstrappers.length; ++i) {
// Delegatecall into the bootstrap contract.
(bool success, bytes memory resultData) = address(bootstrappers[i])
.delegatecall(abi.encodeWithSelector(
IZeroExBootstrapper.bootstrap.selector,
address(bootstrappers[i])
));
if (!success) {
_revertWithData(resultData);
}
}
}
/// @dev Get the implementation contract of a registered function.
/// @param selector The function selector.
/// @return impl The implementation contract address.
function getFunctionImplementation(bytes4 selector)
public
view
returns (address impl)
{
return LibProxyStorage.getStorage().impls[selector];
}
/// @dev Revert with arbitrary bytes.
/// @param data Revert data.
function _revertWithData(bytes memory data) private pure {
assembly { revert(add(data, 32), mload(data)) }
}
/// @dev Return with arbitrary bytes.
/// @param data Return data.
function _returnWithData(bytes memory data) private pure {
assembly { return(add(data, 32), mload(data)) }
}
}

View File

@ -0,0 +1,36 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
library LibCommonRichErrors {
// solhint-disable func-name-mixedcase
function OnlyCallableBySelfError(address sender)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("OnlyCallableBySelfError(address)")),
sender
);
}
}

View File

@ -0,0 +1,50 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
library LibOwnableRichErrors {
// solhint-disable func-name-mixedcase
function OnlyOwnerError(
address sender,
address owner
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("OnlyOwnerError(address,address)")),
sender,
owner
);
}
function TransferOwnerToZeroError()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("TransferOwnerToZeroError()"))
);
}
}

View File

@ -0,0 +1,58 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
library LibProxyRichErrors {
// solhint-disable func-name-mixedcase
function NotImplementedError(bytes4 selector)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("NotImplementedError(bytes4)")),
selector
);
}
function AlreadyBootstrappedError()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("AlreadyBootstrappedError()"))
);
}
function InvalidBootstrapCallerError(address caller, address expectedCaller)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("InvalidBootstrapCallerError(address,address)")),
caller,
expectedCaller
);
}
}

View File

@ -0,0 +1,38 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
library LibSimpleFunctionRegistryRichErrors {
// solhint-disable func-name-mixedcase
function NoRollbackHistoryError(
bytes4 selector
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("NoRollbackHistoryError(bytes4)")),
selector
);
}
}

View File

@ -0,0 +1,79 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../fixins/FixinOwnable.sol";
import "../interfaces/IFeature.sol";
import "../interfaces/IOwnable.sol";
import "../interfaces/ISimpleFunctionRegistry.sol";
import "../interfaces/IZeroExBootstrapper.sol";
import "../errors/LibOwnableRichErrors.sol";
import "../storage/LibOwnableStorage.sol";
/// @dev Owner management features.
contract Ownable is
IFeature,
IOwnable,
IZeroExBootstrapper,
FixinOwnable
{
// solhint-disable const-name-snakecase
/// @dev Name of this feature.
string constant public override FEATURE_NAME = "Ownable";
/// @dev Version of this feature.
uint256 constant public override FEATURE_VERSION = (1 << 64) | (0 << 32) | (0);
/// @dev Initializes the authority feature.
/// @param impl The actual address of this feature contract.
function bootstrap(address impl) external override {
// Set the owner.
LibOwnableStorage.getStorage().owner = msg.sender;
// Register feature functions.
ISimpleFunctionRegistry(address(this)).extendSelf(this.transferOwnership.selector, impl);
ISimpleFunctionRegistry(address(this)).extendSelf(this.getOwner.selector, impl);
}
/// @dev Change the owner of this contract.
/// Only directly callable by the owner.
/// @param newOwner New owner address.
function transferOwnership(address newOwner)
external
override
onlyOwner
{
LibOwnableStorage.Storage storage proxyStor = LibOwnableStorage.getStorage();
if (newOwner == address(0)) {
_rrevert(LibOwnableRichErrors.TransferOwnerToZeroError());
} else {
proxyStor.owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
/// @dev Get the owner of this contract.
/// @return owner_ The owner of this contract.
function getOwner() external override view returns (address owner_) {
return LibOwnableStorage.getStorage().owner;
}
}

View File

@ -0,0 +1,139 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../interfaces/IFeature.sol";
import "../interfaces/ISimpleFunctionRegistry.sol";
import "../interfaces/IZeroExBootstrapper.sol";
import "../fixins/FixinOwnable.sol";
import "../storage/LibProxyStorage.sol";
import "../storage/LibSimpleFunctionRegistryStorage.sol";
import "../errors/LibSimpleFunctionRegistryRichErrors.sol";
/// @dev Basic registry management features.
contract SimpleFunctionRegistry is
IFeature,
ISimpleFunctionRegistry,
IZeroExBootstrapper,
FixinOwnable
{
// solhint-disable const-name-snakecase
/// @dev Name of this feature.
string constant public override FEATURE_NAME = "SimpleFunctionRegistry";
/// @dev Version of this feature.
uint256 constant public override FEATURE_VERSION = (1 << 64) | (0 << 32) | (0);
/// @dev Initializes the feature implementation registry.
/// @param impl The actual address of this feature contract.
function bootstrap(address impl) external override {
// Register the registration functions (inception vibes).
_extend(this.extend.selector, impl);
_extend(this.extendSelf.selector, impl);
// Register the rollback function.
_extend(this.rollback.selector, impl);
}
/// @dev Roll back to the last implementation of a function.
/// Only directly callable by an authority.
/// @param selector The function selector.
function rollback(bytes4 selector)
external
override
onlyOwner
{
(
LibSimpleFunctionRegistryStorage.Storage storage stor,
LibProxyStorage.Storage storage proxyStor
) = _getStorages();
address[] storage history = stor.implHistory[selector];
if (history.length == 0) {
_rrevert(
LibSimpleFunctionRegistryRichErrors.NoRollbackHistoryError(selector)
);
}
address impl = history[history.length - 1];
address oldImpl = proxyStor.impls[selector];
proxyStor.impls[selector] = impl;
history.pop();
emit ProxyFunctionUpdated(selector, oldImpl, impl);
}
/// @dev Register or replace a function.
/// Only directly callable by an authority.
/// @param selector The function selector.
/// @param impl The implementation contract for the function.
function extend(bytes4 selector, address impl)
external
override
onlyOwner
{
_extend(selector, impl);
}
/// @dev Register or replace a function.
/// Only callable from within.
/// @param selector The function selector.
/// @param impl The implementation contract for the function.
function extendSelf(bytes4 selector, address impl)
external
override
onlySelf
{
_extend(selector, impl);
}
/// @dev Register or replace a function.
/// @param selector The function selector.
/// @param impl The implementation contract for the function.
function _extend(bytes4 selector, address impl)
private
{
(
LibSimpleFunctionRegistryStorage.Storage storage stor,
LibProxyStorage.Storage storage proxyStor
) = _getStorages();
address oldImpl = proxyStor.impls[selector];
address[] storage history = stor.implHistory[selector];
history.push(oldImpl);
proxyStor.impls[selector] = impl;
emit ProxyFunctionUpdated(selector, oldImpl, impl);
}
/// @dev Get the storage buckets for this feature and the proxy.
/// @return stor Storage bucket for this feature.
/// @return proxyStor age bucket for the proxy.
function _getStorages()
private
pure
returns (
LibSimpleFunctionRegistryStorage.Storage storage stor,
LibProxyStorage.Storage storage proxyStor
)
{
return (
LibSimpleFunctionRegistryStorage.getStorage(),
LibProxyStorage.getStorage()
);
}
}

View File

@ -0,0 +1,43 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "../storage/LibProxyStorage.sol";
import "../errors/LibCommonRichErrors.sol";
/// @dev Common utilities.
contract FixinCommon {
/// @dev The caller must be this contract.
modifier onlySelf() {
if (msg.sender != address(this)) {
_rrevert(LibCommonRichErrors.OnlyCallableBySelfError(msg.sender));
}
_;
}
/// @dev Reverts with arbitrary data `errorData`.
/// @param errorData ABI encoded error data.
function _rrevert(bytes memory errorData) internal pure {
LibRichErrorsV06.rrevert(errorData);
}
}

View File

@ -0,0 +1,50 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../errors/LibOwnableRichErrors.sol";
import "../storage/LibOwnableStorage.sol";
import "./FixinCommon.sol";
/// @dev A feature mixin for restricting callers to owners.
contract FixinOwnable is
FixinCommon
{
/// @dev The caller of this function must be the owner.
modifier onlyOwner() {
{
address owner = _getOwner();
if (msg.sender != owner) {
_rrevert(LibOwnableRichErrors.OnlyOwnerError(
msg.sender,
owner
));
}
}
_;
}
/// @dev Get the owner of this contract.
/// @return owner The owner of this contract.
function _getOwner() internal view returns (address owner) {
return LibOwnableStorage.getStorage().owner;
}
}

View File

@ -0,0 +1,35 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../interfaces/IZeroExBootstrapper.sol";
/// @dev Basic interface for a feature contract.
interface IFeature {
// solhint-disable func-name-mixedcase
/// @dev The name of this feature set.
function FEATURE_NAME() external view returns (string memory name);
/// @dev The version of this feature set.
function FEATURE_VERSION() external view returns (uint256 version);
}

View File

@ -0,0 +1,29 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/interfaces/IOwnableV06.sol";
// solhint-disable no-empty-blocks
/// @dev Owner management features.
interface IOwnable is
IOwnableV06
{}

View File

@ -0,0 +1,48 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../interfaces/IZeroExBootstrapper.sol";
/// @dev Basic registry management features.
interface ISimpleFunctionRegistry {
/// @dev A function implementation was updated via `extend()` or `rollback()`.
/// @param selector The function selector.
/// @param oldImpl The implementation contract address being replaced.
/// @param newImpl The replacement implementation contract address.
event ProxyFunctionUpdated(bytes4 indexed selector, address oldImpl, address newImpl);
/// @dev Roll back to the last implementation of a function.
/// @param selector The function selector.
function rollback(bytes4 selector) external;
/// @dev Register or replace a function.
/// @param selector The function selector.
/// @param impl The implementation contract for the function.
function extend(bytes4 selector, address impl) external;
/// @dev Register or replace a function.
/// Only callable from within.
/// @param selector The function selector.
/// @param impl The implementation contract for the function.
function extendSelf(bytes4 selector, address impl) external;
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
/// @dev Interface for a bootstrapping contract that the `ZeroEx` proxy.
interface IZeroExBootstrapper {
/// @dev Sets up the initial state of the `ZeroEx` contract.
/// The `ZeroEx` contract will delegatecall this function so the
/// bootstrapper should use this function to register initial
/// features.
/// @param impl The implementation contract.
function bootstrap(address impl) external;
}

View File

@ -0,0 +1,70 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../ZeroEx.sol";
import "../features/SimpleFunctionRegistry.sol";
import "../features/Ownable.sol";
import "../interfaces/IOwnable.sol";
import "../interfaces/IZeroExBootstrapper.sol";
import "../interfaces/ISimpleFunctionRegistry.sol";
/// @dev A contract for deploying and configuring a minimal ZeroEx contract.
contract BasicMigration {
/// @dev Deploy the `ZeroEx` contract with the minimum feature set.
/// @param owner The owner of the contract.
/// @return zeroEx The deployed and configured `ZeroEx` contract.
function migrate(address owner) external returns (ZeroEx zeroEx) {
// Deploy the ZeroEx contract.
zeroEx = new ZeroEx();
// Bootstrap the initial feature set.
zeroEx.bootstrap(_createBootstrappers());
// Disable the `extendSelf()` function by rolling it back to zero.
ISimpleFunctionRegistry(address(zeroEx))
.rollback(ISimpleFunctionRegistry.extendSelf.selector);
// Call the _postInitialize hook.
_postInitialize(zeroEx);
// Transfer ownership.
if (owner != address(this)) {
IOwnable(address(zeroEx)).transferOwnership(owner);
}
}
function _createBootstrappers()
internal
virtual
returns (IZeroExBootstrapper[] memory bootstrappers)
{
bootstrappers = new IZeroExBootstrapper[](2);
bootstrappers[0] = IZeroExBootstrapper(address(new SimpleFunctionRegistry()));
bootstrappers[1] = IZeroExBootstrapper(address(new Ownable()));
}
// solhint-disable no-empty-blocks
function _postInitialize(ZeroEx zeroEx) internal virtual {
// Override me.
}
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
/// @dev Storage helpers for the `Ownable` feature.
library LibOwnableStorage {
/// @dev Globally unique offset for the storage bucket.
bytes32 constant internal STORAGE_ID =
0xeef73acb590dd70cb88ccc8e9832ea7f198de2f3c87ff92d610497d647795b3c;
/// @dev Storage bucket for this feature.
struct Storage {
// The owner of this contract.
address owner;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
bytes32 storageId = STORAGE_ID;
assembly { stor_slot := storageId }
}
}

View File

@ -0,0 +1,43 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
/// @dev Storage helpers for the proxy contract.
library LibProxyStorage {
/// @dev Globally unique offset for the storage bucket.
bytes32 constant internal STORAGE_ID =
0xd7434ab6df6fb6431870c66fc3fc3d21ddd20b029595942d9e0ffc950ce32f66;
/// @dev Storage bucket for proxy contract.
struct Storage {
// The allowed caller for `bootstrap()`.
address bootstrapCaller;
// Mapping of function selector -> function implementation
mapping(bytes4 => address) impls;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
bytes32 storageId = STORAGE_ID;
assembly { stor_slot := storageId }
}
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
/// @dev Storage helpers for the `SimpleFunctionRegistry` feature.
library LibSimpleFunctionRegistryStorage {
/// @dev Globally unique offset for the storage bucket.
bytes32 constant internal STORAGE_ID =
0x9817e79514d088041d4834d6eb535f4fa107927bddb0f3c55c3e6b2bfe43daa5;
/// @dev Storage bucket for this feature.
struct Storage {
// Mapping of function selector -> implementation history.
mapping(bytes4 => address[]) implHistory;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
bytes32 storageId = STORAGE_ID;
assembly { stor_slot := storageId }
}
}

View File

@ -0,0 +1,25 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
interface ITestSimpleFunctionRegistryFeature {
function testFn() external view returns (uint256 id);
}

View File

@ -0,0 +1,33 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/migrations/BasicMigration.sol";
import "../src/interfaces/IZeroExBootstrapper.sol";
contract TestBasicMigration is
BasicMigration
{
function callBootstrap(ZeroEx zeroEx) external {
IZeroExBootstrapper[] memory bootstrappers;
zeroEx.bootstrap(bootstrappers);
}
}

View File

@ -0,0 +1,35 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/fixins/FixinCommon.sol";
contract TestSimpleFunctionRegistryFeatureImpl1 is
FixinCommon
{
function testFn()
external
pure
returns (uint256 id)
{
return 1337;
}
}

View File

@ -0,0 +1,35 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/fixins/FixinCommon.sol";
contract TestSimpleFunctionRegistryFeatureImpl2 is
FixinCommon
{
function testFn()
external
pure
returns (uint256 id)
{
return 1338;
}
}

View File

@ -0,0 +1,54 @@
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/fixins/FixinCommon.sol";
import "../src/ZeroEx.sol";
contract TestZeroExFeature is
FixinCommon
{
event PayableFnCalled(uint256 value);
event NotPayableFnCalled();
function payableFn()
external
payable
{
emit PayableFnCalled(msg.value);
}
function notPayableFn()
external
{
emit NotPayableFnCalled();
}
// solhint-disable no-empty-blocks
function unimplmentedFn()
external
{}
function internalFn()
external
onlySelf
{}
}

View File

@ -0,0 +1,84 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.1.0",
"engines": {
"node": ">=6.12"
},
"description": "Extensible contracts for interacting with the 0x protocol",
"main": "lib/src/index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-wrappers/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html",
"coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test",
"contracts:gen": "contracts-gen generate",
"contracts:copy": "contracts-gen copy",
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"compile:truffle": "truffle compile",
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"publicInterfaceContracts": "ZeroEx,IOwnable,ISimpleFunctionRegistry",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(BasicMigration|FixinCommon|FixinOwnable|IFeature|IOwnable|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|IZeroExBootstrapper|LibCommonRichErrors|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|Ownable|SimpleFunctionRegistry|TestBasicMigration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestZeroExFeature|ZeroEx).json"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/zero-ex/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.2.2",
"@0x/contracts-gen": "^2.0.8",
"@0x/contracts-test-utils": "^5.3.2",
"@0x/dev-utils": "^3.2.1",
"@0x/sol-compiler": "^4.0.8",
"@0x/subproviders": "^6.0.8",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0",
"typedoc": "^0.15.0",
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.1",
"@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.4.1",
"ethereum-types": "^3.1.0"
},
"publishConfig": {
"access": "public"
}
}

View File

@ -0,0 +1,15 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as IOwnable from '../generated-artifacts/IOwnable.json';
import * as ISimpleFunctionRegistry from '../generated-artifacts/ISimpleFunctionRegistry.json';
import * as ZeroEx from '../generated-artifacts/ZeroEx.json';
export const artifacts = {
ZeroEx: ZeroEx as ContractArtifact,
IOwnable: IOwnable as ContractArtifact,
ISimpleFunctionRegistry: ISimpleFunctionRegistry as ContractArtifact,
};

View File

@ -0,0 +1,38 @@
export { artifacts } from './artifacts';
export {
IOwnableContract,
IOwnableEvents,
ISimpleFunctionRegistryContract,
ISimpleFunctionRegistryEvents,
ZeroExContract,
} from './wrappers';
export { ZeroExRevertErrors } from '@0x/utils';
export {
ContractArtifact,
ContractChains,
CompilerOpts,
StandardContractOutput,
CompilerSettings,
ContractChainData,
ContractAbi,
DevdocOutput,
EvmOutput,
CompilerSettingsMetadata,
OptimizerSettings,
OutputField,
ParamDescription,
EvmBytecodeOutput,
EvmBytecodeOutputLinkReferences,
AbiDefinition,
FunctionAbi,
EventAbi,
RevertErrorAbi,
EventParameter,
DataItem,
MethodAbi,
ConstructorAbi,
FallbackAbi,
ConstructorStateMutability,
TupleDataItem,
StateMutability,
} from 'ethereum-types';

View File

@ -0,0 +1,8 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/i_ownable';
export * from '../generated-wrappers/i_simple_function_registry';
export * from '../generated-wrappers/zero_ex';

View File

@ -0,0 +1,53 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as BasicMigration from '../test/generated-artifacts/BasicMigration.json';
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
import * as FixinOwnable from '../test/generated-artifacts/FixinOwnable.json';
import * as IFeature from '../test/generated-artifacts/IFeature.json';
import * as IOwnable from '../test/generated-artifacts/IOwnable.json';
import * as ISimpleFunctionRegistry from '../test/generated-artifacts/ISimpleFunctionRegistry.json';
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
import * as IZeroExBootstrapper from '../test/generated-artifacts/IZeroExBootstrapper.json';
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
import * as LibOwnableRichErrors from '../test/generated-artifacts/LibOwnableRichErrors.json';
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
import * as LibProxyStorage from '../test/generated-artifacts/LibProxyStorage.json';
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
import * as LibSimpleFunctionRegistryStorage from '../test/generated-artifacts/LibSimpleFunctionRegistryStorage.json';
import * as Ownable from '../test/generated-artifacts/Ownable.json';
import * as SimpleFunctionRegistry from '../test/generated-artifacts/SimpleFunctionRegistry.json';
import * as TestBasicMigration from '../test/generated-artifacts/TestBasicMigration.json';
import * as TestSimpleFunctionRegistryFeatureImpl1 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json';
import * as TestSimpleFunctionRegistryFeatureImpl2 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json';
import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeature.json';
import * as ZeroEx from '../test/generated-artifacts/ZeroEx.json';
export const artifacts = {
ZeroEx: ZeroEx as ContractArtifact,
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
LibOwnableRichErrors: LibOwnableRichErrors as ContractArtifact,
LibProxyRichErrors: LibProxyRichErrors as ContractArtifact,
LibSimpleFunctionRegistryRichErrors: LibSimpleFunctionRegistryRichErrors as ContractArtifact,
Ownable: Ownable as ContractArtifact,
SimpleFunctionRegistry: SimpleFunctionRegistry as ContractArtifact,
FixinCommon: FixinCommon as ContractArtifact,
FixinOwnable: FixinOwnable as ContractArtifact,
IFeature: IFeature as ContractArtifact,
IOwnable: IOwnable as ContractArtifact,
ISimpleFunctionRegistry: ISimpleFunctionRegistry as ContractArtifact,
IZeroExBootstrapper: IZeroExBootstrapper as ContractArtifact,
BasicMigration: BasicMigration as ContractArtifact,
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
LibProxyStorage: LibProxyStorage as ContractArtifact,
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
TestBasicMigration: TestBasicMigration as ContractArtifact,
TestSimpleFunctionRegistryFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1 as ContractArtifact,
TestSimpleFunctionRegistryFeatureImpl2: TestSimpleFunctionRegistryFeatureImpl2 as ContractArtifact,
TestZeroExFeature: TestZeroExFeature as ContractArtifact,
};

View File

@ -0,0 +1,62 @@
import { blockchainTests, expect, randomAddress } from '@0x/contracts-test-utils';
import { hexUtils, ZeroExRevertErrors } from '@0x/utils';
import { artifacts } from './artifacts';
import {
IOwnableContract,
ISimpleFunctionRegistryContract,
TestBasicMigrationContract,
ZeroExContract,
} from './wrappers';
blockchainTests.resets('Basic migration', env => {
let owner: string;
let zeroEx: ZeroExContract;
let migrator: TestBasicMigrationContract;
before(async () => {
[owner] = await env.getAccountAddressesAsync();
migrator = await TestBasicMigrationContract.deployFrom0xArtifactAsync(
artifacts.TestBasicMigration,
env.provider,
env.txDefaults,
artifacts,
);
const migrateCall = migrator.migrate(owner);
zeroEx = new ZeroExContract(await migrateCall.callAsync(), env.provider, env.txDefaults);
await migrateCall.awaitTransactionSuccessAsync();
});
describe('bootstrapping', () => {
it('Migrator cannot call bootstrap() again', async () => {
const tx = migrator.callBootstrap(zeroEx.address).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(new ZeroExRevertErrors.Proxy.AlreadyBootstrappedError());
});
});
describe('Ownable feature', () => {
let ownable: IOwnableContract;
before(async () => {
ownable = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
});
it('has the correct owner', async () => {
const actualOwner = await ownable.getOwner().callAsync();
expect(actualOwner).to.eq(owner);
});
});
describe('Registry feature', () => {
let registry: ISimpleFunctionRegistryContract;
before(async () => {
registry = new ISimpleFunctionRegistryContract(zeroEx.address, env.provider, env.txDefaults);
});
it('`extendSelf()` is unregistered', async () => {
const tx = registry.extendSelf(hexUtils.random(4), randomAddress()).callAsync();
return expect(tx).to.revertWith(new ZeroExRevertErrors.Proxy.NotImplementedError());
});
});
});

View File

@ -0,0 +1,41 @@
import { blockchainTests, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { OwnableRevertErrors } from '@0x/utils';
import { basicMigrateAsync } from '../utils/migration';
import { IOwnableContract, IOwnableEvents } from '../wrappers';
blockchainTests.resets('Ownable feature', env => {
const notOwner = randomAddress();
let owner: string;
let auth: IOwnableContract;
before(async () => {
[owner] = await env.getAccountAddressesAsync();
const zeroEx = await basicMigrateAsync(owner, env.provider, env.txDefaults);
auth = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
});
describe('transferOwnership()', () => {
it('non-owner cannot transfer ownership', async () => {
const newOwner = randomAddress();
const tx = auth.transferOwnership(newOwner).callAsync({ from: notOwner });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner, owner));
});
it('owner can transfer ownership', async () => {
const newOwner = randomAddress();
const receipt = await auth.transferOwnership(newOwner).awaitTransactionSuccessAsync({ from: owner });
verifyEventsFromLogs(
receipt.logs,
[
{
previousOwner: owner,
newOwner,
},
],
IOwnableEvents.OwnershipTransferred,
);
expect(await auth.getOwner().callAsync()).to.eq(newOwner);
});
});
});

View File

@ -0,0 +1,118 @@
import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { hexUtils, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils';
import { artifacts } from '../artifacts';
import { basicMigrateAsync } from '../utils/migration';
import {
ISimpleFunctionRegistryContract,
ISimpleFunctionRegistryEvents,
ITestSimpleFunctionRegistryFeatureContract,
TestSimpleFunctionRegistryFeatureImpl1Contract,
TestSimpleFunctionRegistryFeatureImpl2Contract,
} from '../wrappers';
blockchainTests.resets('SimpleFunctionRegistry feature', env => {
const { NULL_ADDRESS } = constants;
const notOwner = randomAddress();
let owner: string;
let registry: ISimpleFunctionRegistryContract;
let testFnSelector: string;
let testFeature: ITestSimpleFunctionRegistryFeatureContract;
let testFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1Contract;
let testFeatureImpl2: TestSimpleFunctionRegistryFeatureImpl2Contract;
before(async () => {
[owner] = await env.getAccountAddressesAsync();
const zeroEx = await basicMigrateAsync(owner, env.provider, env.txDefaults);
registry = new ISimpleFunctionRegistryContract(zeroEx.address, env.provider, {
...env.txDefaults,
from: owner,
});
testFeature = new ITestSimpleFunctionRegistryFeatureContract(zeroEx.address, env.provider, env.txDefaults);
testFnSelector = testFeature.getSelector('testFn');
testFeatureImpl1 = await TestSimpleFunctionRegistryFeatureImpl1Contract.deployFrom0xArtifactAsync(
artifacts.TestSimpleFunctionRegistryFeatureImpl1,
env.provider,
env.txDefaults,
artifacts,
);
testFeatureImpl2 = await TestSimpleFunctionRegistryFeatureImpl2Contract.deployFrom0xArtifactAsync(
artifacts.TestSimpleFunctionRegistryFeatureImpl2,
env.provider,
env.txDefaults,
artifacts,
);
});
it('`extend()` cannot be called by a non-owner', async () => {
const tx = registry.extend(hexUtils.random(4), randomAddress()).callAsync({ from: notOwner });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner, owner));
});
it('`rollback()` cannot be called by a non-owner', async () => {
const tx = registry.rollback(hexUtils.random(4)).callAsync({ from: notOwner });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner, owner));
});
it('`rollback()` reverts for unregistered function', async () => {
const tx = registry.rollback(testFnSelector).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(
new ZeroExRevertErrors.SimpleFunctionRegistry.NoRollbackHistoryError(testFnSelector),
);
});
it('owner can add a new function with `extend()`', async () => {
const { logs } = await registry.extend(testFnSelector, testFeatureImpl1.address).awaitTransactionSuccessAsync();
verifyEventsFromLogs(
logs,
[{ selector: testFnSelector, oldImpl: NULL_ADDRESS, newImpl: testFeatureImpl1.address }],
ISimpleFunctionRegistryEvents.ProxyFunctionUpdated,
);
const r = await testFeature.testFn().callAsync();
expect(r).to.bignumber.eq(1337);
});
it('owner can replace add a function with `extend()`', async () => {
await registry.extend(testFnSelector, testFeatureImpl1.address).awaitTransactionSuccessAsync();
await registry.extend(testFnSelector, testFeatureImpl2.address).awaitTransactionSuccessAsync();
const r = await testFeature.testFn().callAsync();
expect(r).to.bignumber.eq(1338);
});
it('owner can unset a function with `extend()`', async () => {
await registry.extend(testFnSelector, testFeatureImpl1.address).awaitTransactionSuccessAsync();
await registry.extend(testFnSelector, constants.NULL_ADDRESS).awaitTransactionSuccessAsync();
return expect(testFeature.testFn().callAsync()).to.revertWith(
new ZeroExRevertErrors.Proxy.NotImplementedError(testFnSelector),
);
});
it('owner can rollback a new function to unset', async () => {
await registry.extend(testFnSelector, testFeatureImpl1.address).awaitTransactionSuccessAsync();
const { logs } = await registry.rollback(testFnSelector).awaitTransactionSuccessAsync();
verifyEventsFromLogs(
logs,
[{ selector: testFnSelector, oldImpl: testFeatureImpl1.address, newImpl: NULL_ADDRESS }],
ISimpleFunctionRegistryEvents.ProxyFunctionUpdated,
);
return expect(testFeature.testFn().callAsync()).to.revertWith(
new ZeroExRevertErrors.Proxy.NotImplementedError(testFnSelector),
);
});
it('owner can rollback a function to a previous version', async () => {
await registry.extend(testFnSelector, testFeatureImpl1.address).awaitTransactionSuccessAsync();
await registry.extend(testFnSelector, testFeatureImpl2.address).awaitTransactionSuccessAsync();
await registry.rollback(testFnSelector).awaitTransactionSuccessAsync();
const r = await testFeature.testFn().callAsync();
expect(r).to.bignumber.eq(1337);
});
it('owner can rollback an unset function to a previous version', async () => {
await registry.extend(testFnSelector, testFeatureImpl1.address).awaitTransactionSuccessAsync();
await registry.extend(testFnSelector, constants.NULL_ADDRESS).awaitTransactionSuccessAsync();
await registry.rollback(testFnSelector).awaitTransactionSuccessAsync();
const r = await testFeature.testFn().callAsync();
expect(r).to.bignumber.eq(1337);
});
});

View File

@ -0,0 +1,23 @@
import { SupportedProvider } from '@0x/subproviders';
import { TxData } from 'ethereum-types';
import { artifacts } from '../artifacts';
import { BasicMigrationContract, ZeroExContract } from '../wrappers';
// tslint:disable: completed-docs
export async function basicMigrateAsync(
owner: string,
provider: SupportedProvider,
txDefaults: Partial<TxData>,
): Promise<ZeroExContract> {
const migrator = await BasicMigrationContract.deployFrom0xArtifactAsync(
artifacts.BasicMigration,
provider,
txDefaults,
artifacts,
);
const migrateCall = migrator.migrate(owner);
const zeroEx = new ZeroExContract(await migrateCall.callAsync(), provider, {});
await migrateCall.awaitTransactionSuccessAsync();
return zeroEx;
}

View File

@ -0,0 +1,27 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/basic_migration';
export * from '../test/generated-wrappers/fixin_common';
export * from '../test/generated-wrappers/fixin_ownable';
export * from '../test/generated-wrappers/i_feature';
export * from '../test/generated-wrappers/i_ownable';
export * from '../test/generated-wrappers/i_simple_function_registry';
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
export * from '../test/generated-wrappers/i_zero_ex_bootstrapper';
export * from '../test/generated-wrappers/lib_common_rich_errors';
export * from '../test/generated-wrappers/lib_ownable_rich_errors';
export * from '../test/generated-wrappers/lib_ownable_storage';
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
export * from '../test/generated-wrappers/lib_proxy_storage';
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
export * from '../test/generated-wrappers/lib_simple_function_registry_storage';
export * from '../test/generated-wrappers/ownable';
export * from '../test/generated-wrappers/simple_function_registry';
export * from '../test/generated-wrappers/test_basic_migration';
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl1';
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl2';
export * from '../test/generated-wrappers/test_zero_ex_feature';
export * from '../test/generated-wrappers/zero_ex';

View File

@ -0,0 +1,95 @@
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { BigNumber, ZeroExRevertErrors } from '@0x/utils';
import { artifacts } from './artifacts';
import { basicMigrateAsync } from './utils/migration';
import {
IFeatureContract,
IOwnableContract,
ISimpleFunctionRegistryContract,
TestZeroExFeatureContract,
TestZeroExFeatureEvents,
ZeroExContract,
} from './wrappers';
blockchainTests.resets('ZeroEx contract', env => {
let owner: string;
let zeroEx: ZeroExContract;
let ownable: IOwnableContract;
let registry: ISimpleFunctionRegistryContract;
let testFeature: TestZeroExFeatureContract;
before(async () => {
[owner] = await env.getAccountAddressesAsync();
zeroEx = await basicMigrateAsync(owner, env.provider, env.txDefaults);
ownable = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
registry = new ISimpleFunctionRegistryContract(zeroEx.address, env.provider, env.txDefaults);
testFeature = new TestZeroExFeatureContract(zeroEx.address, env.provider, env.txDefaults);
// Register test features.
const testFeatureImpl = await TestZeroExFeatureContract.deployFrom0xArtifactAsync(
artifacts.TestZeroExFeature,
env.provider,
env.txDefaults,
artifacts,
);
for (const fn of ['payableFn', 'notPayableFn', 'internalFn']) {
await registry
.extend(testFeature.getSelector(fn), testFeatureImpl.address)
.awaitTransactionSuccessAsync({ from: owner });
}
});
it('can receive ether', async () => {
const txHash = await env.web3Wrapper.sendTransactionAsync({
from: owner,
to: zeroEx.address,
data: constants.NULL_BYTES,
value: 1,
});
await env.web3Wrapper.awaitTransactionSuccessAsync(txHash);
});
it('can attach ether to a call', async () => {
const wei = Math.floor(Math.random() * 100);
const receipt = await testFeature.payableFn().awaitTransactionSuccessAsync({ value: wei });
verifyEventsFromLogs(receipt.logs, [{ value: new BigNumber(wei) }], TestZeroExFeatureEvents.PayableFnCalled);
});
it('reverts when attaching ether to a non-payable function', async () => {
const wei = Math.floor(Math.random() * 100);
const tx = testFeature.notPayableFn().awaitTransactionSuccessAsync({ value: wei });
// This will cause an empty revert.
return expect(tx).to.be.rejectedWith('revert');
});
it('reverts when calling an unimplmented function', async () => {
const selector = testFeature.getSelector('unimplmentedFn');
const tx = testFeature.unimplmentedFn().awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(new ZeroExRevertErrors.Proxy.NotImplementedError(selector));
});
it('reverts when calling an internal function', async () => {
const tx = testFeature.internalFn().awaitTransactionSuccessAsync({ from: owner });
return expect(tx).to.revertWith(new ZeroExRevertErrors.Common.OnlyCallableBySelfError(owner));
});
describe('getFunctionImplementation()', () => {
it('returns the correct implementations of the initial features', async () => {
const ownableSelectors = [ownable.getSelector('transferOwnership')];
const registrySelectors = [
registry.getSelector('rollback'),
registry.getSelector('extend'),
// registry.getSelector('extendSelf'),
];
const selectors = [...ownableSelectors, ...registrySelectors];
const impls = await Promise.all(selectors.map(s => zeroEx.getFunctionImplementation(s).callAsync()));
for (let i = 0; i < impls.length; ++i) {
const selector = selectors[i];
const impl = impls[i];
const feat = new IFeatureContract(impl, env.provider, env.txDefaults);
const featName = ownableSelectors.includes(selector) ? 'Ownable' : 'SimpleFunctionRegistry';
expect(await feat.FEATURE_NAME().callAsync()).to.eq(featName);
}
});
});
});

View File

@ -0,0 +1,96 @@
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* truffleframework.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
// const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
// Another network with more advanced options...
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send txs from (default: accounts[0])
// websockets: true // Enable EventEmitter interface for web3 (default: false)
// },
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
// ropsten: {
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
// network_id: 3, // Ropsten's id
// gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: '0.6.5',
settings: {
evmVersion: 'istanbul',
optimizer: {
enabled: true,
runs: 1000000,
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
},
},
},
},
};

View File

@ -0,0 +1,33 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/IOwnable.json",
"generated-artifacts/ISimpleFunctionRegistry.json",
"generated-artifacts/ZeroEx.json",
"test/generated-artifacts/BasicMigration.json",
"test/generated-artifacts/FixinCommon.json",
"test/generated-artifacts/FixinOwnable.json",
"test/generated-artifacts/IFeature.json",
"test/generated-artifacts/IOwnable.json",
"test/generated-artifacts/ISimpleFunctionRegistry.json",
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
"test/generated-artifacts/IZeroExBootstrapper.json",
"test/generated-artifacts/LibCommonRichErrors.json",
"test/generated-artifacts/LibOwnableRichErrors.json",
"test/generated-artifacts/LibOwnableStorage.json",
"test/generated-artifacts/LibProxyRichErrors.json",
"test/generated-artifacts/LibProxyStorage.json",
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
"test/generated-artifacts/LibSimpleFunctionRegistryStorage.json",
"test/generated-artifacts/Ownable.json",
"test/generated-artifacts/SimpleFunctionRegistry.json",
"test/generated-artifacts/TestBasicMigration.json",
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json",
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json",
"test/generated-artifacts/TestZeroExFeature.json",
"test/generated-artifacts/ZeroEx.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -0,0 +1,10 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"custom-no-magic-numbers": false,
"max-file-line-count": false
},
"linterOptions": {
"exclude": ["src/artifacts.ts", "test/artifacts.ts"]
}
}