Merge pull request #2592 from 0xProject/feat/zero-ex/transformer-deployer
Exchange Proxy: TransformerDeployer
This commit is contained in:
commit
f81697527c
82
contracts/zero-ex/contracts/src/external/TransformerDeployer.sol
vendored
Normal file
82
contracts/zero-ex/contracts/src/external/TransformerDeployer.sol
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
|
||||
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/AuthorizableV06.sol";
|
||||
|
||||
|
||||
/// @dev A contract with a `die()` function.
|
||||
interface IKillable {
|
||||
function die() external;
|
||||
}
|
||||
|
||||
/// @dev Deployer contract for ERC20 transformers.
|
||||
/// Only authorities may call `deploy()` and `kill()`.
|
||||
contract TransformerDeployer is
|
||||
AuthorizableV06
|
||||
{
|
||||
/// @dev Emitted when a contract is deployed via `deploy()`.
|
||||
/// @param deployedAddress The address of the deployed contract.
|
||||
/// @param nonce The deployment nonce.
|
||||
/// @param sender The caller of `deploy()`.
|
||||
event Deployed(address deployedAddress, uint256 nonce, address sender);
|
||||
/// @dev Emitted when a contract is killed via `kill()`.
|
||||
/// @param target The address of the contract being killed..
|
||||
/// @param sender The caller of `kill()`.
|
||||
event Killed(address target, address sender);
|
||||
|
||||
// @dev The current nonce of this contract.
|
||||
uint256 public nonce = 1;
|
||||
// @dev Mapping of deployed contract address to deployment nonce.
|
||||
mapping (address => uint256) public toDeploymentNonce;
|
||||
|
||||
/// @dev Create this contract and register authorities.
|
||||
constructor(address[] memory authorities) public {
|
||||
for (uint256 i = 0; i < authorities.length; ++i) {
|
||||
_addAuthorizedAddress(authorities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Deploy a new contract. Only callable by an authority.
|
||||
/// Any attached ETH will also be forwarded.
|
||||
function deploy(bytes memory bytecode)
|
||||
public
|
||||
payable
|
||||
onlyAuthorized
|
||||
returns (address deployedAddress)
|
||||
{
|
||||
uint256 deploymentNonce = nonce;
|
||||
nonce += 1;
|
||||
assembly {
|
||||
deployedAddress := create(callvalue(), add(bytecode, 32), mload(bytecode))
|
||||
}
|
||||
toDeploymentNonce[deployedAddress] = deploymentNonce;
|
||||
emit Deployed(deployedAddress, deploymentNonce, msg.sender);
|
||||
}
|
||||
|
||||
/// @dev Call `die()` on a contract. Only callable by an authority.
|
||||
function kill(IKillable target)
|
||||
public
|
||||
onlyAuthorized
|
||||
{
|
||||
target.die();
|
||||
emit Killed(address(target), msg.sender);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
|
||||
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/transformers/LibERC20Transformer.sol";
|
||||
|
||||
|
||||
contract TestTransformerDeployerTransformer {
|
||||
|
||||
address payable public immutable deployer;
|
||||
|
||||
constructor() public payable {
|
||||
deployer = msg.sender;
|
||||
}
|
||||
|
||||
modifier onlyDeployer() {
|
||||
require(msg.sender == deployer, "TestTransformerDeployerTransformer/ONLY_DEPLOYER");
|
||||
_;
|
||||
}
|
||||
|
||||
function die()
|
||||
external
|
||||
onlyDeployer
|
||||
{
|
||||
selfdestruct(deployer);
|
||||
}
|
||||
|
||||
function isDeployedByDeployer(uint32 nonce)
|
||||
external
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return LibERC20Transformer.getDeployedAddress(deployer, nonce) == address(this);
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnable,ISimpleFunctionRegistry,ITokenSpender,ITransformERC20,FillQuoteTransformer,PayTakerTransformer,WethTransformer",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Transformer|IExchange|IFeature|IFlashWallet|IOwnable|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|Ownable|PayTakerTransformer|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|WethTransformer|ZeroEx).json"
|
||||
"abis": "./test/generated-artifacts/@(AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Transformer|IExchange|IFeature|IFlashWallet|IOwnable|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|Ownable|PayTakerTransformer|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -57,6 +57,7 @@ import * as TestTokenSpender from '../test/generated-artifacts/TestTokenSpender.
|
||||
import * as TestTokenSpenderERC20Token from '../test/generated-artifacts/TestTokenSpenderERC20Token.json';
|
||||
import * as TestTransformerBase from '../test/generated-artifacts/TestTransformerBase.json';
|
||||
import * as TestTransformERC20 from '../test/generated-artifacts/TestTransformERC20.json';
|
||||
import * as TestTransformerDeployerTransformer from '../test/generated-artifacts/TestTransformerDeployerTransformer.json';
|
||||
import * as TestTransformerHost from '../test/generated-artifacts/TestTransformerHost.json';
|
||||
import * as TestWeth from '../test/generated-artifacts/TestWeth.json';
|
||||
import * as TestWethTransformerHost from '../test/generated-artifacts/TestWethTransformerHost.json';
|
||||
@ -64,6 +65,7 @@ import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeatur
|
||||
import * as TokenSpender from '../test/generated-artifacts/TokenSpender.json';
|
||||
import * as Transformer from '../test/generated-artifacts/Transformer.json';
|
||||
import * as TransformERC20 from '../test/generated-artifacts/TransformERC20.json';
|
||||
import * as TransformerDeployer from '../test/generated-artifacts/TransformerDeployer.json';
|
||||
import * as WethTransformer from '../test/generated-artifacts/WethTransformer.json';
|
||||
import * as ZeroEx from '../test/generated-artifacts/ZeroEx.json';
|
||||
export const artifacts = {
|
||||
@ -79,6 +81,7 @@ export const artifacts = {
|
||||
FlashWallet: FlashWallet as ContractArtifact,
|
||||
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
|
||||
IFlashWallet: IFlashWallet as ContractArtifact,
|
||||
TransformerDeployer: TransformerDeployer as ContractArtifact,
|
||||
Bootstrap: Bootstrap as ContractArtifact,
|
||||
IBootstrap: IBootstrap as ContractArtifact,
|
||||
IFeature: IFeature as ContractArtifact,
|
||||
@ -124,6 +127,7 @@ export const artifacts = {
|
||||
TestTokenSpenderERC20Token: TestTokenSpenderERC20Token as ContractArtifact,
|
||||
TestTransformERC20: TestTransformERC20 as ContractArtifact,
|
||||
TestTransformerBase: TestTransformerBase as ContractArtifact,
|
||||
TestTransformerDeployerTransformer: TestTransformerDeployerTransformer as ContractArtifact,
|
||||
TestTransformerHost: TestTransformerHost as ContractArtifact,
|
||||
TestWeth: TestWeth as ContractArtifact,
|
||||
TestWethTransformerHost: TestWethTransformerHost as ContractArtifact,
|
||||
|
109
contracts/zero-ex/test/transformer_deployer_test.ts
Normal file
109
contracts/zero-ex/test/transformer_deployer_test.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import { AuthorizableRevertErrors, BigNumber } from '@0x/utils';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import {
|
||||
TestTransformerDeployerTransformerContract,
|
||||
TransformerDeployerContract,
|
||||
TransformerDeployerEvents,
|
||||
} from './wrappers';
|
||||
|
||||
blockchainTests.resets('TransformerDeployer', env => {
|
||||
let owner: string;
|
||||
let authority: string;
|
||||
let deployer: TransformerDeployerContract;
|
||||
const deployBytes = artifacts.TestTransformerDeployerTransformer.compilerOutput.evm.bytecode.object;
|
||||
|
||||
before(async () => {
|
||||
[owner, authority] = await env.getAccountAddressesAsync();
|
||||
deployer = await TransformerDeployerContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TransformerDeployer,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
[authority],
|
||||
);
|
||||
});
|
||||
|
||||
describe('deploy()', () => {
|
||||
it('non-authority cannot call', async () => {
|
||||
const nonAuthority = randomAddress();
|
||||
const tx = deployer.deploy(deployBytes).callAsync({ from: nonAuthority });
|
||||
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(nonAuthority));
|
||||
});
|
||||
|
||||
it('authority can deploy', async () => {
|
||||
const targetAddress = await deployer.deploy(deployBytes).callAsync({ from: authority });
|
||||
const target = new TestTransformerDeployerTransformerContract(targetAddress, env.provider);
|
||||
const receipt = await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||
expect(await target.deployer().callAsync()).to.eq(deployer.address);
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[{ deployedAddress: targetAddress, nonce: new BigNumber(1), sender: authority }],
|
||||
TransformerDeployerEvents.Deployed,
|
||||
);
|
||||
});
|
||||
|
||||
it('authority can deploy with value', async () => {
|
||||
const targetAddress = await deployer.deploy(deployBytes).callAsync({ from: authority, value: 1 });
|
||||
const target = new TestTransformerDeployerTransformerContract(targetAddress, env.provider);
|
||||
const receipt = await deployer
|
||||
.deploy(deployBytes)
|
||||
.awaitTransactionSuccessAsync({ from: authority, value: 1 });
|
||||
expect(await target.deployer().callAsync()).to.eq(deployer.address);
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[{ deployedAddress: targetAddress, nonce: new BigNumber(1), sender: authority }],
|
||||
TransformerDeployerEvents.Deployed,
|
||||
);
|
||||
expect(await env.web3Wrapper.getBalanceInWeiAsync(targetAddress)).to.bignumber.eq(1);
|
||||
});
|
||||
|
||||
it('updates nonce', async () => {
|
||||
expect(await deployer.nonce().callAsync()).to.bignumber.eq(1);
|
||||
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||
expect(await deployer.nonce().callAsync()).to.bignumber.eq(2);
|
||||
});
|
||||
|
||||
it('nonce can predict deployment address', async () => {
|
||||
const nonce = await deployer.nonce().callAsync();
|
||||
const targetAddress = await deployer.deploy(deployBytes).callAsync({ from: authority });
|
||||
const target = new TestTransformerDeployerTransformerContract(targetAddress, env.provider);
|
||||
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||
expect(await target.isDeployedByDeployer(nonce).callAsync()).to.eq(true);
|
||||
});
|
||||
|
||||
it('can retrieve deployment nonce from contract address', async () => {
|
||||
const nonce = await deployer.nonce().callAsync();
|
||||
const targetAddress = await deployer.deploy(deployBytes).callAsync({ from: authority });
|
||||
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||
expect(await deployer.toDeploymentNonce(targetAddress).callAsync()).to.bignumber.eq(nonce);
|
||||
});
|
||||
});
|
||||
|
||||
describe('kill()', () => {
|
||||
let target: TestTransformerDeployerTransformerContract;
|
||||
|
||||
before(async () => {
|
||||
const targetAddress = await deployer.deploy(deployBytes).callAsync({ from: authority });
|
||||
target = new TestTransformerDeployerTransformerContract(targetAddress, env.provider);
|
||||
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||
});
|
||||
|
||||
it('authority cannot call', async () => {
|
||||
const nonAuthority = randomAddress();
|
||||
const tx = deployer.kill(target.address).callAsync({ from: nonAuthority });
|
||||
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(nonAuthority));
|
||||
});
|
||||
|
||||
it('authority can kill a contract', async () => {
|
||||
const receipt = await deployer.kill(target.address).awaitTransactionSuccessAsync({ from: authority });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[{ target: target.address, sender: authority }],
|
||||
TransformerDeployerEvents.Killed,
|
||||
);
|
||||
return expect(env.web3Wrapper.getContractCodeAsync(target.address)).to.become(constants.NULL_BYTES);
|
||||
});
|
||||
});
|
||||
});
|
@ -55,6 +55,7 @@ export * from '../test/generated-wrappers/test_token_spender';
|
||||
export * from '../test/generated-wrappers/test_token_spender_erc20_token';
|
||||
export * from '../test/generated-wrappers/test_transform_erc20';
|
||||
export * from '../test/generated-wrappers/test_transformer_base';
|
||||
export * from '../test/generated-wrappers/test_transformer_deployer_transformer';
|
||||
export * from '../test/generated-wrappers/test_transformer_host';
|
||||
export * from '../test/generated-wrappers/test_weth';
|
||||
export * from '../test/generated-wrappers/test_weth_transformer_host';
|
||||
@ -62,5 +63,6 @@ export * from '../test/generated-wrappers/test_zero_ex_feature';
|
||||
export * from '../test/generated-wrappers/token_spender';
|
||||
export * from '../test/generated-wrappers/transform_erc20';
|
||||
export * from '../test/generated-wrappers/transformer';
|
||||
export * from '../test/generated-wrappers/transformer_deployer';
|
||||
export * from '../test/generated-wrappers/weth_transformer';
|
||||
export * from '../test/generated-wrappers/zero_ex';
|
||||
|
@ -68,6 +68,7 @@
|
||||
"test/generated-artifacts/TestTokenSpenderERC20Token.json",
|
||||
"test/generated-artifacts/TestTransformERC20.json",
|
||||
"test/generated-artifacts/TestTransformerBase.json",
|
||||
"test/generated-artifacts/TestTransformerDeployerTransformer.json",
|
||||
"test/generated-artifacts/TestTransformerHost.json",
|
||||
"test/generated-artifacts/TestWeth.json",
|
||||
"test/generated-artifacts/TestWethTransformerHost.json",
|
||||
@ -75,6 +76,7 @@
|
||||
"test/generated-artifacts/TokenSpender.json",
|
||||
"test/generated-artifacts/TransformERC20.json",
|
||||
"test/generated-artifacts/Transformer.json",
|
||||
"test/generated-artifacts/TransformerDeployer.json",
|
||||
"test/generated-artifacts/WethTransformer.json",
|
||||
"test/generated-artifacts/ZeroEx.json"
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user