@0x/contracts-zero-ex
: Address review feedback.
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
|
||||
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 LibPuppetRichErrors {
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
|
||||
function PuppetExecuteFailedError(
|
||||
address puppet,
|
||||
address callTarget,
|
||||
bytes memory callData,
|
||||
uint256 callValue,
|
||||
bytes memory errorData
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
bytes4(keccak256("PuppetExecuteFailedError(address,address,bytes,uint256,bytes)")),
|
||||
puppet,
|
||||
callTarget,
|
||||
callData,
|
||||
callValue,
|
||||
errorData
|
||||
);
|
||||
}
|
||||
|
||||
function PuppetExecuteWithFailedError(
|
||||
address puppet,
|
||||
address callTarget,
|
||||
bytes memory callData,
|
||||
bytes memory errorData
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
bytes4(keccak256("PuppetExecuteWithFailedError(address,address,bytes,bytes)")),
|
||||
puppet,
|
||||
callTarget,
|
||||
callData,
|
||||
errorData
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
|
||||
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";
|
||||
|
||||
|
||||
/// @dev A contract that can execute arbitrary calls from its owner.
|
||||
interface IPuppet {
|
||||
|
||||
/// @dev Execute an arbitrary call. Only an authority can call this.
|
||||
/// @param target The call target.
|
||||
/// @param callData The call data.
|
||||
/// @param value Ether to attach to the call.
|
||||
/// @return resultData The data returned by the call.
|
||||
function execute(
|
||||
address payable target,
|
||||
bytes calldata callData,
|
||||
uint256 value
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (bytes memory resultData);
|
||||
|
||||
/// @dev Execute an arbitrary delegatecall, in the context of this puppet.
|
||||
/// Only an authority can call this.
|
||||
/// @param target The call target.
|
||||
/// @param callData The call data.
|
||||
/// @return resultData The data returned by the call.
|
||||
function executeWith(
|
||||
address payable target,
|
||||
bytes calldata callData
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (bytes memory resultData);
|
||||
|
||||
/// @dev Allows the puppet to receive ETH.
|
||||
receive() external payable;
|
||||
|
||||
/// @dev Fetch the immutable owner/deployer of this contract.
|
||||
/// @return owner_ The immutable owner/deployer/
|
||||
function owner() external view returns (address owner_);
|
||||
}
|
175
contracts/zero-ex/contracts/src/external/Puppet.sol
vendored
175
contracts/zero-ex/contracts/src/external/Puppet.sol
vendored
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
|
||||
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 "@0x/contracts-utils/contracts/src/v06/errors/LibOwnableRichErrorsV06.sol";
|
||||
import "../errors/LibPuppetRichErrors.sol";
|
||||
import "./IPuppet.sol";
|
||||
|
||||
|
||||
/// @dev A contract that can execute arbitrary calls from its owner.
|
||||
contract Puppet is
|
||||
IPuppet
|
||||
{
|
||||
// solhint-disable no-unused-vars,indent,no-empty-blocks
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
// solhint-disable
|
||||
/// @dev Store the owner/deployer as an immutable to make this contract stateless.
|
||||
address public override immutable owner;
|
||||
// solhint-enable
|
||||
|
||||
constructor() public {
|
||||
// The deployer is the owner.
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
/// @dev Allows only the (immutable) owner to call a function.
|
||||
modifier onlyOwner() virtual {
|
||||
if (msg.sender != owner) {
|
||||
LibOwnableRichErrorsV06.OnlyOwnerError(
|
||||
msg.sender,
|
||||
owner
|
||||
).rrevert();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Execute an arbitrary call. Only an authority can call this.
|
||||
/// @param target The call target.
|
||||
/// @param callData The call data.
|
||||
/// @param value Ether to attach to the call.
|
||||
/// @return resultData The data returned by the call.
|
||||
function execute(
|
||||
address payable target,
|
||||
bytes calldata callData,
|
||||
uint256 value
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
onlyOwner
|
||||
returns (bytes memory resultData)
|
||||
{
|
||||
bool success;
|
||||
(success, resultData) = target.call{value: value}(callData);
|
||||
if (!success) {
|
||||
LibPuppetRichErrors
|
||||
.PuppetExecuteFailedError(
|
||||
address(this),
|
||||
target,
|
||||
callData,
|
||||
value,
|
||||
resultData
|
||||
)
|
||||
.rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Execute an arbitrary delegatecall, in the context of this puppet.
|
||||
/// Only an authority can call this.
|
||||
/// @param target The call target.
|
||||
/// @param callData The call data.
|
||||
/// @return resultData The data returned by the call.
|
||||
function executeWith(
|
||||
address payable target,
|
||||
bytes calldata callData
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
onlyOwner
|
||||
returns (bytes memory resultData)
|
||||
{
|
||||
bool success;
|
||||
(success, resultData) = target.delegatecall(callData);
|
||||
if (!success) {
|
||||
LibPuppetRichErrors
|
||||
.PuppetExecuteWithFailedError(
|
||||
address(this),
|
||||
target,
|
||||
callData,
|
||||
resultData
|
||||
)
|
||||
.rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
// solhint-disable
|
||||
/// @dev Allows this contract to receive ether.
|
||||
receive() external override payable {}
|
||||
// solhint-enable
|
||||
|
||||
/// @dev Signal support for receiving ERC1155 tokens.
|
||||
/// @param interfaceID The interface ID, as per ERC-165 rules.
|
||||
/// @return hasSupport `true` if this contract supports an ERC-165 interface.
|
||||
function supportsInterface(bytes4 interfaceID)
|
||||
external
|
||||
pure
|
||||
returns (bool hasSupport)
|
||||
{
|
||||
return interfaceID == this.supportsInterface.selector ||
|
||||
interfaceID == this.onERC1155Received.selector ^ this.onERC1155BatchReceived.selector ||
|
||||
interfaceID == this.tokenFallback.selector;
|
||||
}
|
||||
|
||||
/// @dev Allow this contract to receive ERC1155 tokens.
|
||||
/// @return success `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address, // operator,
|
||||
address, // from,
|
||||
uint256, // id,
|
||||
uint256, // value,
|
||||
bytes calldata //data
|
||||
)
|
||||
external
|
||||
pure
|
||||
returns (bytes4 success)
|
||||
{
|
||||
return this.onERC1155Received.selector;
|
||||
}
|
||||
|
||||
/// @dev Allow this contract to receive ERC1155 tokens.
|
||||
/// @return success `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address, // operator,
|
||||
address, // from,
|
||||
uint256[] calldata, // ids,
|
||||
uint256[] calldata, // values,
|
||||
bytes calldata // data
|
||||
)
|
||||
external
|
||||
pure
|
||||
returns (bytes4 success)
|
||||
{
|
||||
return this.onERC1155BatchReceived.selector;
|
||||
}
|
||||
|
||||
/// @dev Allows this contract to receive ERC223 tokens.
|
||||
function tokenFallback(
|
||||
address, // from,
|
||||
uint256, // value,
|
||||
bytes calldata // value
|
||||
)
|
||||
external
|
||||
pure
|
||||
{}
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
|
||||
contract TestPuppetTarget {
|
||||
|
||||
event PuppetTargetCalled(
|
||||
address context,
|
||||
address sender,
|
||||
bytes data,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
bytes4 private constant MAGIC_BYTES = 0x12345678;
|
||||
bytes private constant REVERTING_DATA = hex"1337";
|
||||
|
||||
fallback() external payable {
|
||||
if (keccak256(msg.data) == keccak256(REVERTING_DATA)) {
|
||||
revert("TestPuppetTarget/REVERT");
|
||||
}
|
||||
emit PuppetTargetCalled(
|
||||
address(this),
|
||||
msg.sender,
|
||||
msg.data,
|
||||
msg.value
|
||||
);
|
||||
bytes4 rval = MAGIC_BYTES;
|
||||
assembly {
|
||||
mstore(0, rval)
|
||||
return(0, 32)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,211 +0,0 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { hexUtils, OwnableRevertErrors, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { PuppetContract, TestPuppetTargetContract, TestPuppetTargetEvents } from './wrappers';
|
||||
|
||||
blockchainTests.resets('Puppets', env => {
|
||||
let owner: string;
|
||||
let puppet: PuppetContract;
|
||||
let puppetTarget: TestPuppetTargetContract;
|
||||
|
||||
before(async () => {
|
||||
[owner] = await env.getAccountAddressesAsync();
|
||||
puppet = await PuppetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Puppet,
|
||||
env.provider,
|
||||
{
|
||||
...env.txDefaults,
|
||||
from: owner,
|
||||
},
|
||||
artifacts,
|
||||
);
|
||||
puppetTarget = await TestPuppetTargetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestPuppetTarget,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
const TARGET_RETURN_VALUE = hexUtils.rightPad('0x12345678');
|
||||
const REVERTING_DATA = '0x1337';
|
||||
|
||||
it('owned by deployer', () => {
|
||||
return expect(puppet.owner().callAsync()).to.eventually.eq(owner);
|
||||
});
|
||||
|
||||
describe('execute()', () => {
|
||||
it('non-owner cannot call execute()', async () => {
|
||||
const notOwner = randomAddress();
|
||||
const tx = puppet
|
||||
.execute(randomAddress(), hexUtils.random(), getRandomInteger(0, '100e18'))
|
||||
.callAsync({ from: notOwner });
|
||||
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner));
|
||||
});
|
||||
|
||||
it('owner can call execute()', async () => {
|
||||
const targetData = hexUtils.random(128);
|
||||
const receipt = await puppet
|
||||
.execute(puppetTarget.address, targetData, constants.ZERO_AMOUNT)
|
||||
.awaitTransactionSuccessAsync({ from: owner });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
context: puppetTarget.address,
|
||||
sender: puppet.address,
|
||||
data: targetData,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
},
|
||||
],
|
||||
TestPuppetTargetEvents.PuppetTargetCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('owner can call execute() with attached ETH', async () => {
|
||||
const targetData = hexUtils.random(128);
|
||||
const callValue = getRandomInteger(1, '1e18');
|
||||
const receipt = await puppet
|
||||
.execute(puppetTarget.address, targetData, callValue)
|
||||
.awaitTransactionSuccessAsync({ from: owner, value: callValue });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
context: puppetTarget.address,
|
||||
sender: puppet.address,
|
||||
data: targetData,
|
||||
value: callValue,
|
||||
},
|
||||
],
|
||||
TestPuppetTargetEvents.PuppetTargetCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('owner can call execute() can transfer less ETH than attached', async () => {
|
||||
const targetData = hexUtils.random(128);
|
||||
const callValue = getRandomInteger(1, '1e18');
|
||||
const receipt = await puppet
|
||||
.execute(puppetTarget.address, targetData, callValue.minus(1))
|
||||
.awaitTransactionSuccessAsync({ from: owner, value: callValue });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
context: puppetTarget.address,
|
||||
sender: puppet.address,
|
||||
data: targetData,
|
||||
value: callValue.minus(1),
|
||||
},
|
||||
],
|
||||
TestPuppetTargetEvents.PuppetTargetCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('puppet returns call result', async () => {
|
||||
const result = await puppet
|
||||
.execute(puppetTarget.address, hexUtils.random(128), constants.ZERO_AMOUNT)
|
||||
.callAsync({ from: owner });
|
||||
expect(result).to.eq(TARGET_RETURN_VALUE);
|
||||
});
|
||||
|
||||
it('puppet wraps call revert', async () => {
|
||||
const tx = puppet
|
||||
.execute(puppetTarget.address, REVERTING_DATA, constants.ZERO_AMOUNT)
|
||||
.callAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.Puppet.PuppetExecuteFailedError(
|
||||
puppet.address,
|
||||
puppetTarget.address,
|
||||
REVERTING_DATA,
|
||||
constants.ZERO_AMOUNT,
|
||||
new StringRevertError('TestPuppetTarget/REVERT').encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('puppet can receive ETH', async () => {
|
||||
await env.web3Wrapper.sendTransactionAsync({
|
||||
to: puppet.address,
|
||||
from: owner,
|
||||
value: 1,
|
||||
});
|
||||
const bal = await env.web3Wrapper.getBalanceInWeiAsync(puppet.address);
|
||||
expect(bal).to.bignumber.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeWith()', () => {
|
||||
it('non-owner cannot call executeWith()', async () => {
|
||||
const notOwner = randomAddress();
|
||||
const tx = puppet.executeWith(randomAddress(), hexUtils.random()).callAsync({ from: notOwner });
|
||||
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner));
|
||||
});
|
||||
|
||||
it('owner can call executeWith()', async () => {
|
||||
const targetData = hexUtils.random(128);
|
||||
const receipt = await puppet
|
||||
.executeWith(puppetTarget.address, targetData)
|
||||
.awaitTransactionSuccessAsync({ from: owner });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
context: puppet.address,
|
||||
sender: owner,
|
||||
data: targetData,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
},
|
||||
],
|
||||
TestPuppetTargetEvents.PuppetTargetCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('executeWith() is payable', async () => {
|
||||
const targetData = hexUtils.random(128);
|
||||
const callValue = getRandomInteger(1, '1e18');
|
||||
const receipt = await puppet
|
||||
.executeWith(puppetTarget.address, targetData)
|
||||
.awaitTransactionSuccessAsync({ from: owner, value: callValue });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
context: puppet.address,
|
||||
sender: owner,
|
||||
data: targetData,
|
||||
value: callValue,
|
||||
},
|
||||
],
|
||||
TestPuppetTargetEvents.PuppetTargetCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('puppet returns call result', async () => {
|
||||
const result = await puppet
|
||||
.executeWith(puppetTarget.address, hexUtils.random(128))
|
||||
.callAsync({ from: owner });
|
||||
expect(result).to.eq(TARGET_RETURN_VALUE);
|
||||
});
|
||||
|
||||
it('puppet wraps call revert', async () => {
|
||||
const tx = puppet.executeWith(puppetTarget.address, REVERTING_DATA).callAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.Puppet.PuppetExecuteWithFailedError(
|
||||
puppet.address,
|
||||
puppetTarget.address,
|
||||
REVERTING_DATA,
|
||||
new StringRevertError('TestPuppetTarget/REVERT').encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user