@0x/contracts-zero-ex
: Make TransformerDeployer
boring.
This commit is contained in:
@@ -19,208 +19,64 @@
|
||||
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 Shared deployer contract for ERC20 transformers implementing some
|
||||
/// basic membership governance.
|
||||
/// All members can create or kill transformers individually.
|
||||
/// A majority vote is required to add or remove memebers.
|
||||
contract TransformerDeployer {
|
||||
|
||||
/// @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()`.
|
||||
event Deployed(address deployedAddress, address member);
|
||||
/// @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()`.
|
||||
event Kill(address target, address member);
|
||||
/// @param target The address of the contract being killed..
|
||||
/// @param sender The caller of `kill()`.
|
||||
event Killed(address target, address sender);
|
||||
|
||||
/// @dev Represents a member (de)registration vote.
|
||||
/// Should be passed into `addMembers()` and `removeMembers()` with
|
||||
/// accompanying signature.
|
||||
struct Vote {
|
||||
// Members to add or remove.
|
||||
address[] members;
|
||||
// How long this vote is valid for.
|
||||
uint256 expirationTime;
|
||||
// @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 The EIP712 typehash for `addMembers()` votes.
|
||||
bytes32 private immutable ADD_MEMBERS_VOTE_TYPEHASH = keccak256(
|
||||
"AddMembersVote(address[] member,uint256 nonce,bytes signature)"
|
||||
);
|
||||
/// @dev The EIP712 typehash for `removeMembers()` votes.
|
||||
bytes32 private immutable REMOVE_MEMBERS_VOTE_TYPEHASH = keccak256(
|
||||
"RemoveMembersVote(address[] member,uint256 nonce,bytes signature)"
|
||||
);
|
||||
|
||||
/// @dev The EIP712 domain separator.
|
||||
bytes32 public immutable EIP712_DOMAIN_SEPARATOR;
|
||||
/// @dev The current deployment nonce of this contract.
|
||||
/// Unlike EOAs, contracts start with a nonce of 1.
|
||||
uint256 public deploymentNonce = 1;
|
||||
/// @dev The number of registered members.
|
||||
uint256 public memberCount;
|
||||
/// @dev Whether an address is a member.
|
||||
mapping(address => bool) public isMember;
|
||||
/// @dev Whether a vote was consumed.
|
||||
mapping(bytes32 => bool) public isVoteConsumed;
|
||||
|
||||
/// @dev Only a valid member can call the function.
|
||||
modifier onlyMember() {
|
||||
require(isMember[msg.sender], "TransformerDeployer/ONLY_CALLABLE_By_MEMBER");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Create this contract and seed the initial members.
|
||||
constructor(address[] memory members) public {
|
||||
uint256 chainId;
|
||||
assembly { chainId := chainid() }
|
||||
EIP712_DOMAIN_SEPARATOR = keccak256(abi.encode(
|
||||
keccak256(
|
||||
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
||||
),
|
||||
keccak256(bytes('TransformerDeployer')),
|
||||
keccak256(bytes('1.0.0')),
|
||||
chainId,
|
||||
address(this)
|
||||
));
|
||||
_addMembers(members);
|
||||
}
|
||||
|
||||
/// @dev Registers new members. Must attach majority votes.
|
||||
function addMembers(Vote[] memory votes, bytes[] memory signatures)
|
||||
public
|
||||
{
|
||||
_consumeVotes(ADD_MEMBERS_VOTE_TYPEHASH, votes, signatures);
|
||||
_addMembers(votes[0].members);
|
||||
}
|
||||
|
||||
/// @dev Removes existing members. Must attach majority votes.
|
||||
function removeMembers(Vote[] memory votes, bytes[] memory signatures)
|
||||
public
|
||||
{
|
||||
_consumeVotes(REMOVE_MEMBERS_VOTE_TYPEHASH, votes, signatures);
|
||||
_removeMembers(votes[0].members);
|
||||
}
|
||||
|
||||
/// @dev Deploy a new contract. Only callable by a member.
|
||||
/// @dev Deploy a new contract. Only callable by an authority.
|
||||
/// Any attached ETH will also be forwarded.
|
||||
function deploy(bytes memory bytecode)
|
||||
public
|
||||
payable
|
||||
onlyMember
|
||||
onlyAuthorized
|
||||
returns (address deployedAddress)
|
||||
{
|
||||
deploymentNonce += 1;
|
||||
uint256 deploymentNonce = nonce;
|
||||
nonce += 1;
|
||||
assembly {
|
||||
deployedAddress := create(callvalue(), add(bytecode, 32), mload(bytecode))
|
||||
}
|
||||
emit Deployed(deployedAddress, msg.sender);
|
||||
toDeploymentNonce[deployedAddress] = deploymentNonce;
|
||||
emit Deployed(deployedAddress, deploymentNonce, msg.sender);
|
||||
}
|
||||
|
||||
/// @dev Call `die()` on a contract. Only callable by a member.
|
||||
/// @dev Call `die()` on a contract. Only callable by an authority.
|
||||
function kill(IKillable target)
|
||||
public
|
||||
onlyMember
|
||||
onlyAuthorized
|
||||
{
|
||||
target.die();
|
||||
}
|
||||
|
||||
/// @dev Check that votes are valid and have majority and consumes them.
|
||||
function _consumeVotes(
|
||||
bytes32 typeHash,
|
||||
Vote[] memory votes,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
internal
|
||||
{
|
||||
require(votes.length >= memberCount / 2 + 1, "TransformerDeployer/INSUFFICIENT_VOTES");
|
||||
bytes32 membersHash = keccak256(abi.encode(votes[0].members));
|
||||
address[] memory signers = new address[](votes.length);
|
||||
for (uint256 i = 0; i < votes.length; ++i) {
|
||||
// Ensure the vote isn't expired.
|
||||
require(votes[i].expirationTime > block.timestamp, "TransformerDeployer/VOTE_EXPIRED");
|
||||
// Ensure the members are the same across all votes.
|
||||
require(
|
||||
membersHash == keccak256(abi.encode(votes[i].members)),
|
||||
"TransformerDeployer/NONHOMOGENOUS_VOTE"
|
||||
);
|
||||
bytes32 voteHash = _getVoteHash(typeHash, votes[i]);
|
||||
// Get the signer of the vote.
|
||||
address signer = signers[i] = _getVoteSigner(voteHash, signatures[i]);
|
||||
// Check for duplicates.
|
||||
for (uint256 j = 0; j < i; ++j) {
|
||||
require(signers[j] != signer, "TransformerDeployer/DUPLICATE_SIGNER");
|
||||
}
|
||||
// Ensure signer is a member.
|
||||
require(isMember[signer], "TransformerDeployer/NOT_A_MEMBER");
|
||||
// Ensure the vote wasn't already consumed.
|
||||
require(!isVoteConsumed[voteHash], "TransformerDeployer/ALREADY_VOTED");
|
||||
// Mark the vote consumed.
|
||||
isVoteConsumed[voteHash] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Get the signer given a vote and signature.
|
||||
function _getVoteSigner(
|
||||
bytes32 voteHash,
|
||||
bytes memory signature
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (address signer)
|
||||
{
|
||||
require(signature.length == 65, "TransformerDeployer/INVALID_SIGNATURE");
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
uint8 v;
|
||||
assembly {
|
||||
r := mload(add(signature, 32))
|
||||
s := mload(add(signature, 64))
|
||||
v := and(mload(add(signature, 65)), 0x00000000000000000000000000000000000000000000000000000000000000ff)
|
||||
}
|
||||
return ecrecover(voteHash, v, r, s);
|
||||
}
|
||||
|
||||
/// @dev Get the EIP712 hash of a vote.
|
||||
function _getVoteHash(bytes32 typeHash, Vote memory vote)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 voteHash)
|
||||
{
|
||||
bytes32 messageHash = keccak256(abi.encode(
|
||||
typeHash,
|
||||
vote.members,
|
||||
vote.expirationTime
|
||||
));
|
||||
return keccak256(abi.encodePacked(
|
||||
'\x19\x01',
|
||||
EIP712_DOMAIN_SEPARATOR,
|
||||
messageHash
|
||||
));
|
||||
}
|
||||
|
||||
/// @dev Register new members.
|
||||
function _addMembers(address[] memory members)
|
||||
private
|
||||
{
|
||||
for (uint256 i = 0; i < members.length; ++i) {
|
||||
require(!isMember[members[i]], "TransformerDeployer/ALREADY_MEMBER");
|
||||
isMember[members[i]] = true;
|
||||
}
|
||||
memberCount += members.length;
|
||||
}
|
||||
|
||||
/// @dev Deregister existing members.
|
||||
function _removeMembers(address[] memory members)
|
||||
private
|
||||
{
|
||||
for (uint256 i = 0; i < members.length; ++i) {
|
||||
require(isMember[members[i]], "TransformerDeployer/NOT_A_MEMBER");
|
||||
isMember[members[i]] = false;
|
||||
}
|
||||
memberCount -= members.length;
|
||||
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"
|
||||
],
|
||||
|
Reference in New Issue
Block a user