Merge pull request #2252 from 0xProject/feature/sandstorm/function-assertions
Initial Sandstorm Framework
This commit is contained in:
@@ -58,6 +58,16 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
test-integrations-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-integrations
|
||||
test-contracts-rest-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -392,6 +402,9 @@ workflows:
|
||||
- test-exchange-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-integrations-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-rest-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
|
@@ -87,13 +87,13 @@ contract ERC20Token is
|
||||
balances[_to] += _value;
|
||||
balances[_from] -= _value;
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
|
||||
|
||||
emit Transfer(
|
||||
_from,
|
||||
_to,
|
||||
_value
|
||||
);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
45
contracts/integrations/contracts/test/TestFramework.sol
Normal file
45
contracts/integrations/contracts/test/TestFramework.sol
Normal file
@@ -0,0 +1,45 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
|
||||
|
||||
// This contract is intended to be used in the unit tests that test the typescript
|
||||
// test framework found in `test/utils/`
|
||||
contract TestFramework {
|
||||
|
||||
event Event(string input);
|
||||
|
||||
// bytes4(keccak256("RichRevertErrorSelector(string)"))
|
||||
bytes4 internal constant RICH_REVERT_ERROR_SELECTOR = 0x49a7e246;
|
||||
|
||||
function emitEvent(string calldata input)
|
||||
external
|
||||
{
|
||||
emit Event(input);
|
||||
}
|
||||
|
||||
function emptyRevert()
|
||||
external
|
||||
{
|
||||
revert();
|
||||
}
|
||||
|
||||
function stringRevert(string calldata message)
|
||||
external
|
||||
{
|
||||
revert(message);
|
||||
}
|
||||
|
||||
function doNothing()
|
||||
external
|
||||
pure
|
||||
{} // solhint-disable-line no-empty-blocks
|
||||
|
||||
function returnInteger(uint256 integer)
|
||||
external
|
||||
pure
|
||||
returns (uint256)
|
||||
{
|
||||
return integer;
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-staking/contracts/test/TestStaking.sol";
|
||||
|
||||
|
||||
// TODO(jalextowle): This contract can be removed when the added to this package.
|
||||
contract TestStakingPlaceholder is
|
||||
TestStaking
|
||||
{
|
||||
constructor(address wethAddress, address zrxVaultAddress)
|
||||
public
|
||||
TestStaking(wethAddress, zrxVaultAddress)
|
||||
{} // solhint-disable-line no-empty-blocks
|
||||
}
|
@@ -35,7 +35,7 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(TestStakingPlaceholder).json",
|
||||
"abis": "./generated-artifacts/@(TestFramework).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
|
@@ -5,5 +5,5 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as TestStakingPlaceholder from '../generated-artifacts/TestStakingPlaceholder.json';
|
||||
export const artifacts = { TestStakingPlaceholder: TestStakingPlaceholder as ContractArtifact };
|
||||
import * as TestFramework from '../generated-artifacts/TestFramework.json';
|
||||
export const artifacts = { TestFramework: TestFramework as ContractArtifact };
|
||||
|
@@ -1,2 +1,5 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export * from '../test/utils/function_assertions';
|
||||
export * from '../test/utils/deployment_manager';
|
||||
export * from '../test/utils/address_manager';
|
||||
|
@@ -3,4 +3,4 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/test_staking_placeholder';
|
||||
export * from '../generated-wrappers/test_framework';
|
||||
|
@@ -2,7 +2,7 @@ import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||
import { constants } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { DeploymentManager } from '../deployment/deployment_mananger';
|
||||
import { DeploymentManager } from '../utils/deployment_manager';
|
||||
|
||||
export type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { Authorizable, Ownable } from '@0x/contracts-exchange';
|
||||
import { constants as stakingConstants } from '@0x/contracts-staking';
|
||||
import { blockchainTests, expect } from '@0x/contracts-test-utils';
|
||||
|
||||
import { DeploymentManager } from './deployment_mananger';
|
||||
import { DeploymentManager } from '../utils/deployment_manager';
|
||||
|
||||
blockchainTests('Deployment Manager', env => {
|
||||
let owner: string;
|
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
hexRandom,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BigNumber, StringRevertError } from '@0x/utils';
|
||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
|
||||
import { artifacts, TestFrameworkContract, TestFrameworkEventEventArgs, TestFrameworkEvents } from '../../src';
|
||||
import { FunctionAssertion, Result } from '../utils/function_assertions';
|
||||
|
||||
const { ZERO_AMOUNT, MAX_UINT256 } = constants;
|
||||
|
||||
blockchainTests.resets('FunctionAssertion Unit Tests', env => {
|
||||
let exampleContract: TestFrameworkContract;
|
||||
|
||||
before(async () => {
|
||||
exampleContract = await TestFrameworkContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestFramework,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
describe('runAsync', () => {
|
||||
it('should call the before function with the provided arguments', async () => {
|
||||
let sideEffectTarget = ZERO_AMOUNT;
|
||||
const assertion = new FunctionAssertion(exampleContract.returnInteger, {
|
||||
before: async (input: BigNumber) => {
|
||||
sideEffectTarget = randomInput;
|
||||
},
|
||||
after: async (beforeInfo: any, result: Result, input: BigNumber) => {},
|
||||
});
|
||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||
await assertion.runAsync(randomInput);
|
||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||
});
|
||||
|
||||
it('should call the after function with the provided arguments', async () => {
|
||||
let sideEffectTarget = ZERO_AMOUNT;
|
||||
const assertion = new FunctionAssertion(exampleContract.returnInteger, {
|
||||
before: async (input: BigNumber) => {},
|
||||
after: async (beforeInfo: any, result: Result, input: BigNumber) => {
|
||||
sideEffectTarget = input;
|
||||
},
|
||||
});
|
||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||
await assertion.runAsync(randomInput);
|
||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||
});
|
||||
|
||||
it('should not fail immediately if the wrapped function fails', async () => {
|
||||
const assertion = new FunctionAssertion(exampleContract.emptyRevert, {
|
||||
before: async () => {},
|
||||
after: async (beforeInfo: any, result: Result) => {},
|
||||
});
|
||||
await assertion.runAsync();
|
||||
});
|
||||
|
||||
it('should pass the return value of "before" to "after"', async () => {
|
||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||
let sideEffectTarget = ZERO_AMOUNT;
|
||||
const assertion = new FunctionAssertion(exampleContract.returnInteger, {
|
||||
before: async (input: BigNumber) => {
|
||||
return randomInput;
|
||||
},
|
||||
after: async (beforeInfo: any, result: Result, input: BigNumber) => {
|
||||
sideEffectTarget = beforeInfo;
|
||||
},
|
||||
});
|
||||
await assertion.runAsync(randomInput);
|
||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||
});
|
||||
|
||||
it('should pass the result from the function call to "after"', async () => {
|
||||
let sideEffectTarget = ZERO_AMOUNT;
|
||||
const assertion = new FunctionAssertion(exampleContract.returnInteger, {
|
||||
before: async (input: BigNumber) => {},
|
||||
after: async (beforeInfo: any, result: Result, input: BigNumber) => {
|
||||
sideEffectTarget = result.data;
|
||||
},
|
||||
});
|
||||
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
|
||||
await assertion.runAsync(randomInput);
|
||||
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
|
||||
});
|
||||
|
||||
it('should pass the receipt from the function call to "after"', async () => {
|
||||
let sideEffectTarget = {} as TransactionReceiptWithDecodedLogs;
|
||||
const assertion = new FunctionAssertion(exampleContract.emitEvent, {
|
||||
before: async (input: string) => {},
|
||||
after: async (beforeInfo: any, result: Result, input: string) => {
|
||||
if (result.receipt) {
|
||||
sideEffectTarget = result.receipt;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const input = 'emitted data';
|
||||
await assertion.runAsync(input);
|
||||
|
||||
// Ensure that the correct events were emitted.
|
||||
const [event] = filterLogsToArguments<TestFrameworkEventEventArgs>(
|
||||
sideEffectTarget.logs,
|
||||
TestFrameworkEvents.Event,
|
||||
);
|
||||
expect(event).to.be.deep.eq({ input });
|
||||
});
|
||||
|
||||
it('should pass the error to "after" if the function call fails', async () => {
|
||||
let sideEffectTarget: Error;
|
||||
const assertion = new FunctionAssertion(exampleContract.stringRevert, {
|
||||
before: async string => {},
|
||||
after: async (any, result: Result, string) => {
|
||||
sideEffectTarget = result.data;
|
||||
},
|
||||
});
|
||||
const message = 'error message';
|
||||
await assertion.runAsync(message);
|
||||
|
||||
const expectedError = new StringRevertError(message);
|
||||
return expect(
|
||||
new Promise((resolve, reject) => {
|
||||
reject(sideEffectTarget);
|
||||
}),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,135 @@
|
||||
import { blockchainTests, constants, expect, filterLogsToArguments, OrderFactory } from '@0x/contracts-test-utils';
|
||||
import { DummyERC20TokenContract, IERC20TokenEvents, IERC20TokenTransferEventArgs } from '@0x/contracts-erc20';
|
||||
import { IExchangeEvents, IExchangeFillEventArgs } from '@0x/contracts-exchange';
|
||||
import { IStakingEventsEvents, IStakingEventsStakingPoolActivatedEventArgs } from '@0x/contracts-staking';
|
||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { AddressManager } from '../utils/address_manager';
|
||||
import { DeploymentManager } from '../utils/deployment_manager';
|
||||
|
||||
blockchainTests('Exchange & Staking', env => {
|
||||
let accounts: string[];
|
||||
let makerAddress: string;
|
||||
let takers: string[] = [];
|
||||
let delegators: string[] = [];
|
||||
let feeRecipientAddress: string;
|
||||
let addressManager: AddressManager;
|
||||
let deploymentManager: DeploymentManager;
|
||||
let orderFactory: OrderFactory;
|
||||
let makerAsset: DummyERC20TokenContract;
|
||||
let takerAsset: DummyERC20TokenContract;
|
||||
let feeAsset: DummyERC20TokenContract;
|
||||
|
||||
const GAS_PRICE = 1e9;
|
||||
|
||||
before(async () => {
|
||||
const chainId = await env.getChainIdAsync();
|
||||
accounts = await env.getAccountAddressesAsync();
|
||||
[makerAddress, feeRecipientAddress, takers[0], takers[1], ...delegators] = accounts.slice(1);
|
||||
deploymentManager = await DeploymentManager.deployAsync(env);
|
||||
|
||||
// Create a staking pool with the operator as a maker address.
|
||||
await deploymentManager.staking.stakingWrapper.createStakingPool.awaitTransactionSuccessAsync(
|
||||
constants.ZERO_AMOUNT,
|
||||
true,
|
||||
{ from: makerAddress },
|
||||
);
|
||||
|
||||
// Set up an address for market making.
|
||||
addressManager = new AddressManager();
|
||||
await addressManager.addMakerAsync(
|
||||
deploymentManager,
|
||||
{
|
||||
address: makerAddress,
|
||||
mainToken: deploymentManager.tokens.erc20[0],
|
||||
feeToken: deploymentManager.tokens.erc20[2],
|
||||
},
|
||||
env,
|
||||
deploymentManager.tokens.erc20[1],
|
||||
feeRecipientAddress,
|
||||
chainId,
|
||||
);
|
||||
|
||||
// Set up two addresses for taking orders.
|
||||
await Promise.all(
|
||||
takers.map(taker =>
|
||||
addressManager.addTakerAsync(deploymentManager, {
|
||||
address: taker,
|
||||
mainToken: deploymentManager.tokens.erc20[1],
|
||||
feeToken: deploymentManager.tokens.erc20[2],
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
describe('fillOrder', () => {
|
||||
it('should be able to fill an order', async () => {
|
||||
const order = await addressManager.makers[0].orderFactory.newSignedOrderAsync({
|
||||
makerAddress,
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
takerAssetAmount: new BigNumber(1),
|
||||
makerFee: constants.ZERO_AMOUNT,
|
||||
takerFee: constants.ZERO_AMOUNT,
|
||||
feeRecipientAddress,
|
||||
});
|
||||
|
||||
const receipt = await deploymentManager.exchange.fillOrder.awaitTransactionSuccessAsync(
|
||||
order,
|
||||
new BigNumber(1),
|
||||
order.signature,
|
||||
{
|
||||
from: takers[0],
|
||||
gasPrice: GAS_PRICE,
|
||||
value: DeploymentManager.protocolFeeMultiplier.times(GAS_PRICE),
|
||||
},
|
||||
);
|
||||
|
||||
// Ensure that the number of emitted logs is equal to 3. There should have been a fill event
|
||||
// and two transfer events. A 'StakingPoolActivated' event should not be expected because
|
||||
// the only staking pool that was created does not have enough stake.
|
||||
expect(receipt.logs.length).to.be.eq(3);
|
||||
|
||||
// Ensure that the fill event was correct.
|
||||
const fillArgs = filterLogsToArguments<IExchangeFillEventArgs>(receipt.logs, IExchangeEvents.Fill);
|
||||
expect(fillArgs.length).to.be.eq(1);
|
||||
expect(fillArgs).to.be.deep.eq([
|
||||
{
|
||||
makerAddress,
|
||||
feeRecipientAddress,
|
||||
makerAssetData: order.makerAssetData,
|
||||
takerAssetData: order.takerAssetData,
|
||||
makerFeeAssetData: order.makerFeeAssetData,
|
||||
takerFeeAssetData: order.takerFeeAssetData,
|
||||
orderHash: orderHashUtils.getOrderHashHex(order),
|
||||
takerAddress: takers[0],
|
||||
senderAddress: takers[0],
|
||||
makerAssetFilledAmount: order.makerAssetAmount,
|
||||
takerAssetFilledAmount: order.takerAssetAmount,
|
||||
makerFeePaid: constants.ZERO_AMOUNT,
|
||||
takerFeePaid: constants.ZERO_AMOUNT,
|
||||
protocolFeePaid: DeploymentManager.protocolFeeMultiplier.times(GAS_PRICE),
|
||||
},
|
||||
]);
|
||||
|
||||
// Ensure that the transfer events were correctly emitted.
|
||||
const transferArgs = filterLogsToArguments<IERC20TokenTransferEventArgs>(
|
||||
receipt.logs,
|
||||
IERC20TokenEvents.Transfer,
|
||||
);
|
||||
expect(transferArgs.length).to.be.eq(2);
|
||||
expect(transferArgs).to.be.deep.eq([
|
||||
{
|
||||
_from: takers[0],
|
||||
_to: makerAddress,
|
||||
_value: order.takerAssetAmount,
|
||||
},
|
||||
{
|
||||
_from: makerAddress,
|
||||
_to: takers[0],
|
||||
_value: order.makerAssetAmount,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
97
contracts/integrations/test/utils/address_manager.ts
Normal file
97
contracts/integrations/test/utils/address_manager.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, OrderFactory, BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils, Order, SignatureType, SignedOrder } from '@0x/order-utils';
|
||||
|
||||
import { DeploymentManager } from '../../src';
|
||||
|
||||
interface MarketMaker {
|
||||
address: string;
|
||||
orderFactory: OrderFactory;
|
||||
}
|
||||
|
||||
interface ConfigurationArgs {
|
||||
address: string;
|
||||
mainToken: DummyERC20TokenContract;
|
||||
feeToken: DummyERC20TokenContract;
|
||||
}
|
||||
|
||||
export class AddressManager {
|
||||
// A set of addresses that have been configured for market making.
|
||||
public makers: MarketMaker[];
|
||||
|
||||
// A set of addresses that have been configured to take orders.
|
||||
public takers: string[];
|
||||
|
||||
/**
|
||||
* Sets up an address to take orders.
|
||||
*/
|
||||
public async addTakerAsync(deploymentManager: DeploymentManager, configArgs: ConfigurationArgs): Promise<void> {
|
||||
// Configure the taker address with the taker and fee tokens.
|
||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.mainToken);
|
||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.feeToken);
|
||||
|
||||
// Add the taker to the list of configured taker addresses.
|
||||
this.takers.push(configArgs.address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an address for market making.
|
||||
*/
|
||||
public async addMakerAsync(
|
||||
deploymentManager: DeploymentManager,
|
||||
configArgs: ConfigurationArgs,
|
||||
environment: BlockchainTestsEnvironment,
|
||||
takerToken: DummyERC20TokenContract,
|
||||
feeRecipientAddress: string,
|
||||
chainId: number,
|
||||
): Promise<void> {
|
||||
const accounts = await environment.getAccountAddressesAsync();
|
||||
|
||||
// Set up order signing for the maker address.
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
makerAddress: configArgs.address,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(configArgs.mainToken.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address),
|
||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(configArgs.feeToken.address),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(configArgs.feeToken.address),
|
||||
feeRecipientAddress,
|
||||
exchangeAddress: deploymentManager.exchange.address,
|
||||
chainId,
|
||||
};
|
||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(configArgs.address)];
|
||||
const orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||
|
||||
// Configure the maker address with the maker and fee tokens.
|
||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.mainToken);
|
||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.feeToken);
|
||||
|
||||
// Add the maker to the list of configured maker addresses.
|
||||
this.makers.push({
|
||||
address: configArgs.address,
|
||||
orderFactory,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up initial account balances for a token and approves the ERC20 asset proxy
|
||||
* to transfer the token.
|
||||
*/
|
||||
protected async _configureTokenForAddressAsync(
|
||||
deploymentManager: DeploymentManager,
|
||||
address: string,
|
||||
token: DummyERC20TokenContract,
|
||||
): Promise<void> {
|
||||
await token.setBalance.awaitTransactionSuccessAsync(address, constants.INITIAL_ERC20_BALANCE);
|
||||
await token.approve.awaitTransactionSuccessAsync(
|
||||
deploymentManager.assetProxies.erc20Proxy.address,
|
||||
constants.MAX_UINT256,
|
||||
{ from: address },
|
||||
);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.makers = [];
|
||||
this.takers = [];
|
||||
}
|
||||
}
|
@@ -149,7 +149,7 @@ export class DeploymentManager {
|
||||
exchangeArtifacts.Exchange,
|
||||
environment.provider,
|
||||
environment.txDefaults,
|
||||
exchangeArtifacts,
|
||||
{ ...ERC20Artifacts, ...exchangeArtifacts },
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
const governor = await ZeroExGovernorContract.deployFrom0xArtifactAsync(
|
68
contracts/integrations/test/utils/function_assertions.ts
Normal file
68
contracts/integrations/test/utils/function_assertions.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { PromiseWithTransactionHash } from '@0x/base-contract';
|
||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
|
||||
export interface ContractGetterFunction {
|
||||
callAsync: (...args: any[]) => Promise<any>;
|
||||
}
|
||||
|
||||
export interface ContractWrapperFunction extends ContractGetterFunction {
|
||||
awaitTransactionSuccessAsync?: (...args: any[]) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
|
||||
}
|
||||
|
||||
export interface Condition {
|
||||
before: (...args: any[]) => Promise<any>;
|
||||
after: (beforeInfo: any, result: Result, ...args: any[]) => Promise<any>;
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
data?: any;
|
||||
receipt?: TransactionReceiptWithDecodedLogs;
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export class FunctionAssertion {
|
||||
// A before and an after assertion that will be called around the wrapper function.
|
||||
public condition: Condition;
|
||||
|
||||
// The wrapper function that will be wrapped in assertions.
|
||||
public wrapperFunction: ContractWrapperFunction;
|
||||
|
||||
constructor(wrapperFunction: ContractWrapperFunction, condition: Condition) {
|
||||
this.condition = condition;
|
||||
this.wrapperFunction = wrapperFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the wrapped function and fails if the before or after assertions fail.
|
||||
* @param ...args The args to the contract wrapper function.
|
||||
*/
|
||||
public async runAsync(...args: any[]): Promise<{ beforeInfo: any; afterInfo: any }> {
|
||||
// Call the before condition.
|
||||
const beforeInfo = await this.condition.before(...args);
|
||||
|
||||
// Initialize the callResult so that the default success value is true.
|
||||
let callResult: Result = { success: true };
|
||||
|
||||
// Try to make the call to the function. If it is successful, pass the
|
||||
// result and receipt to the after condition.
|
||||
try {
|
||||
callResult.data = await this.wrapperFunction.callAsync(...args);
|
||||
callResult.receipt =
|
||||
this.wrapperFunction.awaitTransactionSuccessAsync !== undefined
|
||||
? await this.wrapperFunction.awaitTransactionSuccessAsync(...args)
|
||||
: undefined;
|
||||
} catch (error) {
|
||||
callResult.data = error;
|
||||
callResult.success = false;
|
||||
callResult.receipt = undefined;
|
||||
}
|
||||
|
||||
// Call the after condition.
|
||||
const afterInfo = await this.condition.after(beforeInfo, callResult, ...args);
|
||||
|
||||
return {
|
||||
beforeInfo,
|
||||
afterInfo,
|
||||
};
|
||||
}
|
||||
}
|
@@ -2,5 +2,5 @@
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": ["generated-artifacts/TestStakingPlaceholder.json"]
|
||||
"files": ["generated-artifacts/TestFramework.json"]
|
||||
}
|
||||
|
@@ -9,10 +9,12 @@ import { signingUtils } from './signing_utils';
|
||||
export class OrderFactory {
|
||||
private readonly _defaultOrderParams: Partial<Order>;
|
||||
private readonly _privateKey: Buffer;
|
||||
|
||||
constructor(privateKey: Buffer, defaultOrderParams: Partial<Order>) {
|
||||
this._defaultOrderParams = defaultOrderParams;
|
||||
this._privateKey = privateKey;
|
||||
}
|
||||
|
||||
public async newSignedOrderAsync(
|
||||
customOrderParams: Partial<Order> = {},
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
|
Reference in New Issue
Block a user