Added mainnet DydxBridge integration tests with dYdX SoloMargin contract

This commit is contained in:
Greg Hysen
2019-12-18 01:26:57 -08:00
parent 930b95a548
commit 1248868169
3 changed files with 167 additions and 2 deletions

View File

@@ -0,0 +1,159 @@
import {
artifacts as assetProxyArtifacts,
DydxBridgeActionType,
DydxBridgeContract,
DydxBridgeData,
dydxBridgeDataEncoder,
} from '@0x/contracts-asset-proxy';
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
import { contractAddresses, dydxAccountOwner } from '../mainnet_fork_utils';
import { dydxEvents } from './abi/dydxEvents';
blockchainTests.resets.fork('Mainnet dydx bridge tests', env => {
let testContract: DydxBridgeContract;
// random account to receive tokens from dydx
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
const defaultAccountNumber = new BigNumber(0);
const daiMarketId = new BigNumber(3);
const defaultAmount = toBaseUnitAmount(1);
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit as number,
accountId: constants.ZERO_AMOUNT,
marketId: daiMarketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw as number,
accountId: constants.ZERO_AMOUNT,
marketId: daiMarketId,
// This ratio must be less than the `1` to account
// for interest in dydx balances because the test
// account has an initial dydx balance of zero.
conversionRateNumerator: new BigNumber(1),
conversionRateDenominator: new BigNumber(2),
};
before(async () => {
testContract = new DydxBridgeContract(contractAddresses.dydxBridge, env.provider, env.txDefaults, {
DydxBridge: assetProxyArtifacts.DydxBridge.compilerOutput.abi,
ERC20: erc20Artifacts.ERC20Token.compilerOutput.abi,
Dydx: dydxEvents.abi,
});
});
describe('bridgeTransferFrom()', () => {
const callAndVerifyDydxEvents = async (bridgeData: DydxBridgeData): Promise<void> => {
const txReceipt = await testContract
.bridgeTransferFrom(
constants.NULL_ADDRESS,
dydxAccountOwner,
receiver,
defaultAmount,
dydxBridgeDataEncoder.encode({ bridgeData }),
)
.awaitTransactionSuccessAsync({ from: contractAddresses.erc20BridgeProxy, gasPrice: 0 });
// Construct expected events
const expectedDepositEvents = [];
const expectedWithdrawEvents = [];
for (const action of bridgeData.actions) {
const scaledAmount = action.conversionRateDenominator.gt(0)
? defaultAmount
.times(action.conversionRateNumerator)
.dividedToIntegerBy(action.conversionRateDenominator)
: defaultAmount;
switch (action.actionType) {
case DydxBridgeActionType.Deposit:
expectedDepositEvents.push({
accountOwner: dydxAccountOwner,
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
market: action.marketId,
update: [[true, scaledAmount]],
from: dydxAccountOwner,
});
break;
case DydxBridgeActionType.Withdraw:
expectedWithdrawEvents.push({
accountOwner: dydxAccountOwner,
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
market: action.marketId,
update: [[false, scaledAmount]],
to: receiver,
});
break;
default:
throw new Error(`Unrecognized Action: ${action.actionType}`);
}
}
// Verify events
let nextExpectedDepositEventIdx = 0;
let nextExpectedWithdrawEventIdx = 0;
for (const rawLog of txReceipt.logs) {
// tslint:disable-next-line no-unnecessary-type-assertion
const log = rawLog as LogWithDecodedArgs<DecodedLogArgs>;
if (log.event !== 'LogDeposit' && log.event !== 'LogWithdraw') {
continue;
}
const expectedEvent =
log.event === 'LogDeposit'
? expectedDepositEvents[nextExpectedDepositEventIdx++]
: expectedWithdrawEvents[nextExpectedWithdrawEventIdx++];
expect(expectedEvent.accountOwner, 'accountOwner').to.equal(log.args.accountOwner);
expect(expectedEvent.accountNumber, 'accountNumber').to.bignumber.equal(log.args.accountNumber);
expect(expectedEvent.market, 'market').to.bignumber.equal(log.args.market);
expect(expectedEvent.from, 'from').to.equal(log.args.from);
// We only check the first update field because it's the delta balance (amount deposited).
// The next field is the new total, which depends on interest rates at the time of execution.
expect(expectedEvent.update[0][0], 'update sign').to.equal(log.args.update[0][0]);
const updateValueHex = log.args.update[0][1]._hex;
const updateValueBn = new BigNumber(updateValueHex, 16);
expect(expectedEvent.update[0][1], 'update value').to.bignumber.equal(updateValueBn);
}
};
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
});
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultDepositAction],
});
});
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction],
});
});
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction],
});
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction, defaultDepositAction],
});
});
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction, defaultDepositAction],
});
});
});
});

View File

@@ -5,5 +5,6 @@ import { provider } from '@0x/contracts-test-utils';
const chainId = 1;
const contractAddresses = getContractAddressesForChainOrThrow(chainId);
const contractWrappers = new ContractWrappers(provider, { chainId, contractAddresses });
const dydxAccountOwner = '0xbd67dce6348dc5949a8af5888d6a2bd5dc3cb86d';
export { contractAddresses, contractWrappers };
export { contractAddresses, contractWrappers, dydxAccountOwner };

View File

@@ -1,3 +1,4 @@
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { devConstants, env, EnvVars, Web3Config, web3Factory } from '@0x/dev-utils';
import { prependSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { logUtils } from '@0x/utils';
@@ -26,11 +27,15 @@ if (process.env.FORK_RPC_URL !== undefined) {
...providerConfigs,
fork: process.env.FORK_RPC_URL,
blockTime: 0,
// ZeroExGovernor signer addresses
unlocked_accounts: [
// ZeroExGovernor signer addresses
'0x257619b7155d247e43c8b6d90c8c17278ae481f0',
'0x5ee2a00f8f01d099451844af7f894f26a57fcbf2',
'0x894d623e0e0e8ed12c4a73dada999e275684a37d',
// Test dYdX user account (can trade DAI on dydx bridge)
'0xbd67dce6348dc5949a8af5888d6a2bd5dc3cb86d',
// ERC20BridgeProxy
getContractAddressesForChainOrThrow(1).erc20BridgeProxy,
],
};
}