437 lines
19 KiB
TypeScript
437 lines
19 KiB
TypeScript
import {
|
|
artifacts as assetProxyArtifacts,
|
|
ERC1155ProxyContract,
|
|
ERC20ProxyContract,
|
|
ERC721ProxyContract,
|
|
MultiAssetProxyContract,
|
|
StaticCallProxyContract,
|
|
} from '@0x/contracts-asset-proxy';
|
|
import {
|
|
artifacts as exchangeArtifacts,
|
|
AssetProxyDispatcher,
|
|
Authorizable,
|
|
ExchangeAssetProxyRegisteredEventArgs,
|
|
ExchangeContract,
|
|
ExchangeEvents,
|
|
ExchangeProtocolFeeCollectorAddressEventArgs,
|
|
ExchangeProtocolFeeMultiplierEventArgs,
|
|
Ownable,
|
|
} from '@0x/contracts-exchange';
|
|
import { artifacts as multisigArtifacts, ZeroExGovernorContract } from '@0x/contracts-multisig';
|
|
import {
|
|
artifacts as stakingArtifacts,
|
|
constants as stakingConstants,
|
|
ReadOnlyProxyContract,
|
|
StakingContract,
|
|
StakingEvents,
|
|
StakingExchangeAddedEventArgs,
|
|
StakingProxyContract,
|
|
} from '@0x/contracts-staking';
|
|
import { blockchainTests, constants, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
|
|
import {
|
|
AuthorizableAuthorizedAddressAddedEventArgs,
|
|
AuthorizableAuthorizedAddressRemovedEventArgs,
|
|
AuthorizableEvents,
|
|
} from '@0x/contracts-utils';
|
|
import { AssetProxyId } from '@0x/types';
|
|
import { BigNumber } from '@0x/utils';
|
|
import { TxData } from 'ethereum-types';
|
|
|
|
// tslint:disable:no-unnecessary-type-assertion
|
|
blockchainTests('Deployment and Configuration End to End Tests', env => {
|
|
// Available Addresses
|
|
let owner: string;
|
|
|
|
// Contract Instances
|
|
let governor: ZeroExGovernorContract;
|
|
let erc20Proxy: ERC20ProxyContract;
|
|
let erc721Proxy: ERC721ProxyContract;
|
|
let erc1155Proxy: ERC1155ProxyContract;
|
|
let exchange: ExchangeContract;
|
|
let multiAssetProxy: MultiAssetProxyContract;
|
|
let readOnlyProxy: ReadOnlyProxyContract;
|
|
let staking: StakingContract;
|
|
let staticCallProxy: StaticCallProxyContract;
|
|
let stakingProxy: StakingProxyContract;
|
|
let stakingWrapper: StakingContract;
|
|
|
|
// TxDefaults
|
|
let txDefaults: Partial<TxData>;
|
|
|
|
// ChainId of the Exchange
|
|
let chainId: number;
|
|
|
|
// Protocol Fees
|
|
const protocolFeeMultiplier = new BigNumber(150000);
|
|
|
|
before(async () => {
|
|
// Get the chain ID.
|
|
chainId = await env.getChainIdAsync();
|
|
|
|
// Create accounts and tx defaults
|
|
[owner] = await env.getAccountAddressesAsync();
|
|
txDefaults = {
|
|
...env.txDefaults,
|
|
from: owner,
|
|
};
|
|
|
|
// Deploy ZeroExGovernor. For the purposes of this test, we will assume that
|
|
// the ZeroExGovernor does not know what destinations will be needed during
|
|
// construction.
|
|
governor = await ZeroExGovernorContract.deployFrom0xArtifactAsync(
|
|
multisigArtifacts.ZeroExGovernor,
|
|
env.provider,
|
|
txDefaults,
|
|
multisigArtifacts,
|
|
[],
|
|
[],
|
|
[],
|
|
[owner],
|
|
new BigNumber(1),
|
|
constants.ZERO_AMOUNT,
|
|
);
|
|
|
|
// Deploy Exchange.
|
|
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
exchangeArtifacts.Exchange,
|
|
env.provider,
|
|
txDefaults,
|
|
exchangeArtifacts,
|
|
new BigNumber(chainId),
|
|
);
|
|
|
|
// Deploy ReadOnlyProxy.
|
|
readOnlyProxy = await ReadOnlyProxyContract.deployFrom0xArtifactAsync(
|
|
stakingArtifacts.ReadOnlyProxy,
|
|
env.provider,
|
|
txDefaults,
|
|
stakingArtifacts,
|
|
);
|
|
|
|
// Deploy Staking.
|
|
staking = await StakingContract.deployFrom0xArtifactAsync(
|
|
stakingArtifacts.Staking,
|
|
env.provider,
|
|
txDefaults,
|
|
stakingArtifacts,
|
|
);
|
|
|
|
// Deploy the staking proxy.
|
|
stakingProxy = await StakingProxyContract.deployFrom0xArtifactAsync(
|
|
stakingArtifacts.StakingProxy,
|
|
env.provider,
|
|
txDefaults,
|
|
stakingArtifacts,
|
|
staking.address,
|
|
readOnlyProxy.address,
|
|
);
|
|
|
|
// Authorize owner in the staking proxy.
|
|
await stakingProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner);
|
|
|
|
// Deploy the asset proxy contracts.
|
|
erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
|
assetProxyArtifacts.ERC20Proxy,
|
|
env.provider,
|
|
txDefaults,
|
|
assetProxyArtifacts,
|
|
);
|
|
erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
|
assetProxyArtifacts.ERC721Proxy,
|
|
env.provider,
|
|
txDefaults,
|
|
assetProxyArtifacts,
|
|
);
|
|
erc1155Proxy = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
|
assetProxyArtifacts.ERC1155Proxy,
|
|
env.provider,
|
|
txDefaults,
|
|
assetProxyArtifacts,
|
|
);
|
|
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
|
|
assetProxyArtifacts.MultiAssetProxy,
|
|
env.provider,
|
|
txDefaults,
|
|
assetProxyArtifacts,
|
|
);
|
|
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
|
assetProxyArtifacts.StaticCallProxy,
|
|
env.provider,
|
|
txDefaults,
|
|
assetProxyArtifacts,
|
|
);
|
|
|
|
// Set up the staking wrapper so that the entire staking interface can be accessed
|
|
// easily through the proxy.
|
|
stakingWrapper = new StakingContract(stakingProxy.address, env.provider);
|
|
});
|
|
|
|
describe('deployment and configuration', () => {
|
|
describe('exchange specific', () => {
|
|
// Registers an asset proxy in the exchange contract and ensure that the correct state changes occurred.
|
|
async function registerAssetProxyAndAssertSuccessAsync(
|
|
registrationContract: AssetProxyDispatcher,
|
|
assetProxyAddress: string,
|
|
assetProxyId: string,
|
|
): Promise<void> {
|
|
// Register the asset proxy.
|
|
const receipt = await registrationContract.registerAssetProxy.awaitTransactionSuccessAsync(
|
|
assetProxyAddress,
|
|
{
|
|
from: owner,
|
|
},
|
|
);
|
|
|
|
// Ensure that the correct event was logged.
|
|
const logs = filterLogsToArguments<ExchangeAssetProxyRegisteredEventArgs>(
|
|
receipt.logs,
|
|
ExchangeEvents.AssetProxyRegistered,
|
|
);
|
|
expect(logs).to.be.deep.eq([{ id: assetProxyId, assetProxy: assetProxyAddress }]);
|
|
|
|
// Ensure that the asset proxy was actually registered.
|
|
const proxyAddress = await registrationContract.getAssetProxy.callAsync(assetProxyId);
|
|
expect(proxyAddress).to.be.eq(assetProxyAddress);
|
|
}
|
|
|
|
// Authorizes an address for a given asset proxy using the owner address.
|
|
async function authorizeAddressAndAssertSuccessAsync(
|
|
authorizable: Authorizable,
|
|
newAuthorityAddress: string,
|
|
): Promise<void> {
|
|
// Authorize the address.
|
|
const receipt = await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
newAuthorityAddress,
|
|
{ from: owner },
|
|
);
|
|
|
|
// Ensure that the correct log was emitted.
|
|
const logs = filterLogsToArguments<AuthorizableAuthorizedAddressAddedEventArgs>(
|
|
receipt.logs,
|
|
AuthorizableEvents.AuthorizedAddressAdded,
|
|
);
|
|
expect(logs).to.be.deep.eq([{ target: newAuthorityAddress, caller: owner }]);
|
|
|
|
// Ensure that the address was actually authorized.
|
|
const wasAuthorized = await authorizable.authorized.callAsync(newAuthorityAddress);
|
|
expect(wasAuthorized).to.be.true();
|
|
}
|
|
|
|
it('should successfully register the asset proxies in the exchange', async () => {
|
|
// Register the asset proxies in the exchange.
|
|
await registerAssetProxyAndAssertSuccessAsync(exchange, erc20Proxy.address, AssetProxyId.ERC20);
|
|
await registerAssetProxyAndAssertSuccessAsync(exchange, erc721Proxy.address, AssetProxyId.ERC721);
|
|
await registerAssetProxyAndAssertSuccessAsync(exchange, erc1155Proxy.address, AssetProxyId.ERC1155);
|
|
await registerAssetProxyAndAssertSuccessAsync(
|
|
exchange,
|
|
multiAssetProxy.address,
|
|
AssetProxyId.MultiAsset,
|
|
);
|
|
await registerAssetProxyAndAssertSuccessAsync(
|
|
exchange,
|
|
staticCallProxy.address,
|
|
AssetProxyId.StaticCall,
|
|
);
|
|
});
|
|
|
|
it('should successfully register the asset proxies in the multi-asset proxy', async () => {
|
|
// Register the asset proxies in the multi-asset proxy.
|
|
await registerAssetProxyAndAssertSuccessAsync(multiAssetProxy, erc20Proxy.address, AssetProxyId.ERC20);
|
|
await registerAssetProxyAndAssertSuccessAsync(
|
|
multiAssetProxy,
|
|
erc721Proxy.address,
|
|
AssetProxyId.ERC721,
|
|
);
|
|
await registerAssetProxyAndAssertSuccessAsync(
|
|
multiAssetProxy,
|
|
erc1155Proxy.address,
|
|
AssetProxyId.ERC1155,
|
|
);
|
|
await registerAssetProxyAndAssertSuccessAsync(
|
|
multiAssetProxy,
|
|
staticCallProxy.address,
|
|
AssetProxyId.StaticCall,
|
|
);
|
|
});
|
|
|
|
it('should successfully add the exchange as an authority in the appropriate asset proxies', async () => {
|
|
// Authorize the exchange in all of the asset proxies, except for the static call proxy.
|
|
await authorizeAddressAndAssertSuccessAsync(erc20Proxy, exchange.address);
|
|
await authorizeAddressAndAssertSuccessAsync(erc721Proxy, exchange.address);
|
|
await authorizeAddressAndAssertSuccessAsync(erc1155Proxy, exchange.address);
|
|
await authorizeAddressAndAssertSuccessAsync(multiAssetProxy, exchange.address);
|
|
});
|
|
|
|
it('should successfully add the multi asset proxy as an authority in the appropriate asset proxies', async () => {
|
|
// Authorize the multi-asset proxy in the token asset proxies.
|
|
await authorizeAddressAndAssertSuccessAsync(erc20Proxy, multiAssetProxy.address);
|
|
await authorizeAddressAndAssertSuccessAsync(erc721Proxy, multiAssetProxy.address);
|
|
await authorizeAddressAndAssertSuccessAsync(erc1155Proxy, multiAssetProxy.address);
|
|
});
|
|
});
|
|
|
|
describe('staking specific', () => {
|
|
it('should have properly configured the staking proxy with the logic contract and read-only proxy', async () => {
|
|
// Ensure that the registered read-only proxy is correct.
|
|
const readOnlyProxyAddress = await stakingProxy.readOnlyProxy.callAsync();
|
|
expect(readOnlyProxyAddress).to.be.eq(readOnlyProxy.address);
|
|
|
|
// Ensure that the registered read-only proxy callee is correct.
|
|
const readOnlyProxyCalleeAddress = await stakingProxy.readOnlyProxyCallee.callAsync();
|
|
expect(readOnlyProxyCalleeAddress).to.be.eq(staking.address);
|
|
|
|
// Ensure that the registered staking contract is correct.
|
|
const stakingAddress = await stakingProxy.stakingContract.callAsync();
|
|
expect(stakingAddress).to.be.eq(staking.address);
|
|
});
|
|
|
|
it('should have initialized the correct parameters in the staking proxy', async () => {
|
|
// Ensure that the correct parameters were set.
|
|
const params = await stakingWrapper.getParams.callAsync();
|
|
expect(params).to.be.deep.eq([
|
|
stakingConstants.DEFAULT_PARAMS.epochDurationInSeconds,
|
|
stakingConstants.DEFAULT_PARAMS.rewardDelegatedStakeWeight,
|
|
stakingConstants.DEFAULT_PARAMS.minimumPoolStake,
|
|
stakingConstants.DEFAULT_PARAMS.cobbDouglasAlphaNumerator,
|
|
stakingConstants.DEFAULT_PARAMS.cobbDouglasAlphaDenominator,
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('exchange and staking integration', () => {
|
|
it('should successfully register the exchange in the staking contract', async () => {
|
|
// Register the exchange.
|
|
const receipt = await stakingWrapper.addExchangeAddress.awaitTransactionSuccessAsync(exchange.address, {
|
|
from: owner,
|
|
});
|
|
|
|
// Ensure that the correct events were logged.
|
|
const logs = filterLogsToArguments<StakingExchangeAddedEventArgs>(
|
|
receipt.logs,
|
|
StakingEvents.ExchangeAdded,
|
|
);
|
|
expect(logs).to.be.deep.eq([{ exchangeAddress: exchange.address }]);
|
|
|
|
// Ensure that the exchange was registered.
|
|
const wasRegistered = await stakingWrapper.validExchanges.callAsync(exchange.address);
|
|
expect(wasRegistered).to.be.true();
|
|
});
|
|
|
|
it('should successfully register the staking contract in the exchange', async () => {
|
|
// Register the staking contract.
|
|
const receipt = await exchange.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(
|
|
stakingProxy.address,
|
|
{
|
|
from: owner,
|
|
},
|
|
);
|
|
|
|
// Ensure that the correct events were logged.
|
|
const logs = filterLogsToArguments<ExchangeProtocolFeeCollectorAddressEventArgs>(
|
|
receipt.logs,
|
|
ExchangeEvents.ProtocolFeeCollectorAddress,
|
|
);
|
|
expect(logs).to.be.deep.eq([
|
|
{
|
|
oldProtocolFeeCollector: constants.NULL_ADDRESS,
|
|
updatedProtocolFeeCollector: stakingProxy.address,
|
|
},
|
|
]);
|
|
|
|
// Ensure that the staking contract was registered.
|
|
const feeCollector = await exchange.protocolFeeCollector.callAsync();
|
|
expect(feeCollector).to.be.eq(stakingProxy.address);
|
|
});
|
|
|
|
it('should successfully update the protocol fee multiplier in the staking contract', async () => {
|
|
// Update the protocol fee multiplier.
|
|
const receipt = await exchange.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(
|
|
protocolFeeMultiplier,
|
|
);
|
|
|
|
// Ensure that the correct events were logged.
|
|
const logs = filterLogsToArguments<ExchangeProtocolFeeMultiplierEventArgs>(
|
|
receipt.logs,
|
|
ExchangeEvents.ProtocolFeeMultiplier,
|
|
);
|
|
expect(logs).to.be.deep.eq([
|
|
{
|
|
oldProtocolFeeMultiplier: constants.ZERO_AMOUNT,
|
|
updatedProtocolFeeMultiplier: protocolFeeMultiplier,
|
|
},
|
|
]);
|
|
|
|
// Ensure that the protocol fee multiplier was set correctly.
|
|
const multiplier = await exchange.protocolFeeMultiplier.callAsync();
|
|
expect(multiplier).bignumber.to.be.eq(protocolFeeMultiplier);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('transferring ownership', () => {
|
|
// Removes authorization of the "externally owned address" owner and transfers the authorization
|
|
// to the asset proxy owner.
|
|
async function transferAuthorizationAndAssertSuccessAsync(contract: Authorizable): Promise<void> {
|
|
// Remove authorization from the old owner.
|
|
let receipt = await contract.removeAuthorizedAddress.awaitTransactionSuccessAsync(owner, { from: owner });
|
|
|
|
// Ensure that the correct log was recorded.
|
|
let logs = filterLogsToArguments<AuthorizableAuthorizedAddressRemovedEventArgs>(
|
|
receipt.logs,
|
|
AuthorizableEvents.AuthorizedAddressRemoved,
|
|
);
|
|
expect(logs).to.be.deep.eq([{ target: owner, caller: owner }]);
|
|
|
|
// Ensure that the owner was actually removed.
|
|
let isAuthorized = await contract.authorized.callAsync(owner);
|
|
expect(isAuthorized).to.be.false();
|
|
|
|
// Authorize the asset-proxy owner.
|
|
receipt = await contract.addAuthorizedAddress.awaitTransactionSuccessAsync(governor.address, {
|
|
from: owner,
|
|
});
|
|
|
|
// Ensure that the correct log was recorded.
|
|
logs = filterLogsToArguments<AuthorizableAuthorizedAddressAddedEventArgs>(
|
|
receipt.logs,
|
|
AuthorizableEvents.AuthorizedAddressAdded,
|
|
);
|
|
expect(logs).to.be.deep.eq([{ target: governor.address, caller: owner }]);
|
|
|
|
// Ensure that the asset-proxy owner was actually authorized.
|
|
isAuthorized = await contract.authorized.callAsync(governor.address);
|
|
expect(isAuthorized).to.be.true();
|
|
}
|
|
|
|
// Transfers ownership of a contract to the asset-proxy owner, and ensures that the change was actually made.
|
|
async function transferOwnershipAndAssertSuccessAsync(contract: Ownable): Promise<void> {
|
|
// Transfer ownership to the new owner.
|
|
await contract.transferOwnership.awaitTransactionSuccessAsync(governor.address, { from: owner });
|
|
|
|
// Ensure that the owner address has been updated.
|
|
const ownerAddress = await contract.owner.callAsync();
|
|
expect(ownerAddress).to.be.eq(governor.address);
|
|
}
|
|
|
|
it('should transfer authorization of the owner to the asset-proxy owner in the staking contracts', async () => {
|
|
// Transfer authorization of the staking system. We intentionally neglect
|
|
// to add the asset-proxy owner as an authorized address in the asset proxies
|
|
// as a security precaution.
|
|
await transferAuthorizationAndAssertSuccessAsync(stakingProxy);
|
|
});
|
|
|
|
it('should transfer ownership of all appropriate contracts to the asset-proxy owner', async () => {
|
|
// Transfer ownership of most contracts (we exclude contracts that are not ownable).
|
|
await transferOwnershipAndAssertSuccessAsync(exchange);
|
|
await transferOwnershipAndAssertSuccessAsync(readOnlyProxy);
|
|
await transferOwnershipAndAssertSuccessAsync(staking);
|
|
await transferOwnershipAndAssertSuccessAsync(stakingProxy);
|
|
await transferOwnershipAndAssertSuccessAsync(erc20Proxy);
|
|
await transferOwnershipAndAssertSuccessAsync(erc721Proxy);
|
|
await transferOwnershipAndAssertSuccessAsync(erc1155Proxy);
|
|
await transferOwnershipAndAssertSuccessAsync(multiAssetProxy);
|
|
});
|
|
});
|
|
});
|
|
// tslint:enable:no-unnecessary-type-assertion
|