Migrate FlashWallet tests to foundry (#637)

* Remove contracts/zero-ex/contracts/test/TestBridge.sol

* Move foundry tests to their default location

* Wire up base foundry test and start moving
FlashWallet tests and mocks to foundry

* Move remaining FlashWallet contract tests to foundry

* Switch to foundry default cache config
This commit is contained in:
Elena 2023-01-10 15:47:25 +02:00 committed by GitHub
parent dcb17768cc
commit b33fc27220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 164 additions and 285 deletions

4
.gitignore vendored
View File

@ -176,8 +176,8 @@ contracts/treasury/test/generated-wrappers/
# foundry artifacts # foundry artifacts
contracts/zero-ex/foundry-artifacts/ contracts/zero-ex/foundry-artifacts/
# foundry cache # foundry cache
contracts/zero-ex/foundry-cache/ contracts/zero-ex/cache/
# typechain wrappers # typechain wrappers
contracts/zero-ex/typechain-wrappers/ contracts/zero-ex/typechain-wrappers/

View File

@ -157,8 +157,6 @@
"./contracts/src/vendor/v3/IERC20Bridge.sol", "./contracts/src/vendor/v3/IERC20Bridge.sol",
"./contracts/src/vendor/v3/IStaking.sol", "./contracts/src/vendor/v3/IStaking.sol",
"./contracts/test/ITestSimpleFunctionRegistryFeature.sol", "./contracts/test/ITestSimpleFunctionRegistryFeature.sol",
"./contracts/test/TestBridge.sol",
"./contracts/test/TestCallTarget.sol",
"./contracts/test/TestDelegateCaller.sol", "./contracts/test/TestDelegateCaller.sol",
"./contracts/test/TestFeeCollectorController.sol", "./contracts/test/TestFeeCollectorController.sol",
"./contracts/test/TestFeeRecipient.sol", "./contracts/test/TestFeeRecipient.sol",

View File

@ -1,49 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
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-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../src/vendor/v3/IERC20Bridge.sol";
contract TestBridge is IERC20Bridge {
IERC20TokenV06 public immutable xAsset;
IERC20TokenV06 public immutable yAsset;
constructor(IERC20TokenV06 xAsset_, IERC20TokenV06 yAsset_) public {
xAsset = xAsset_;
yAsset = yAsset_;
}
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
/// @param tokenAddress The address of the ERC20 token to transfer.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @return success The magic bytes `0xdc1600f3` if successful.
function bridgeTransferFrom(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata /* bridgeData */
) external override returns (bytes4 success) {
IERC20TokenV06 takerToken = tokenAddress == address(xAsset) ? yAsset : xAsset;
uint256 takerTokenBalance = takerToken.balanceOf(address(this));
emit ERC20BridgeTransfer(address(takerToken), tokenAddress, takerTokenBalance, amount, from, to);
return 0xdecaf000;
}
}

View File

@ -1,8 +1,7 @@
[profile.default] [profile.default]
src = 'contracts/src' src = 'contracts/src'
out = 'foundry-artifacts' out = 'foundry-artifacts'
test = 'contracts/test/foundry' test = 'tests'
libs = ["contracts/deps/", "../utils/contracts/src/"] libs = ["contracts/deps/", "../utils/contracts/src/"]
remappings = ['@0x/contracts-utils/=../utils/', '@0x/contracts-erc20/=../erc20/', 'src/=./contracts/src'] remappings = ['@0x/contracts-utils/=../utils/', '@0x/contracts-erc20/=../erc20/', 'src/=./contracts/src']
cache_path = 'foundry-cache'
optimizer_runs = 1000000 optimizer_runs = 1000000

View File

@ -43,7 +43,7 @@
"config": { "config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter", "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2Batch|MixinBancor|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinSolidly|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json" "abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2Batch|MixinBancor|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinSolidly|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -152,8 +152,6 @@ import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/
import * as PolygonBridgeAdapter from '../test/generated-artifacts/PolygonBridgeAdapter.json'; import * as PolygonBridgeAdapter from '../test/generated-artifacts/PolygonBridgeAdapter.json';
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json'; import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json'; import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
import * as TestCurve from '../test/generated-artifacts/TestCurve.json'; import * as TestCurve from '../test/generated-artifacts/TestCurve.json';
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json'; import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
import * as TestFeeCollectorController from '../test/generated-artifacts/TestFeeCollectorController.json'; import * as TestFeeCollectorController from '../test/generated-artifacts/TestFeeCollectorController.json';
@ -364,8 +362,6 @@ export const artifacts = {
IERC20Bridge: IERC20Bridge as ContractArtifact, IERC20Bridge: IERC20Bridge as ContractArtifact,
IStaking: IStaking as ContractArtifact, IStaking: IStaking as ContractArtifact,
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact, ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
TestBridge: TestBridge as ContractArtifact,
TestCallTarget: TestCallTarget as ContractArtifact,
TestDelegateCaller: TestDelegateCaller as ContractArtifact, TestDelegateCaller: TestDelegateCaller as ContractArtifact,
TestFeeCollectorController: TestFeeCollectorController as ContractArtifact, TestFeeCollectorController: TestFeeCollectorController as ContractArtifact,
TestFeeRecipient: TestFeeRecipient as ContractArtifact, TestFeeRecipient: TestFeeRecipient as ContractArtifact,

View File

@ -1,211 +0,0 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { hexUtils, OwnableRevertErrors, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
import { artifacts } from './artifacts';
import { FlashWalletContract, TestCallTargetContract, TestCallTargetEvents } from './wrappers';
blockchainTests.resets('FlashWallet', env => {
let owner: string;
let wallet: FlashWalletContract;
let callTarget: TestCallTargetContract;
before(async () => {
[owner] = await env.getAccountAddressesAsync();
wallet = await FlashWalletContract.deployFrom0xArtifactAsync(
artifacts.FlashWallet,
env.provider,
{
...env.txDefaults,
from: owner,
},
artifacts,
);
callTarget = await TestCallTargetContract.deployFrom0xArtifactAsync(
artifacts.TestCallTarget,
env.provider,
env.txDefaults,
artifacts,
);
});
const TARGET_RETURN_VALUE = hexUtils.rightPad('0x12345678');
const REVERTING_DATA = '0x1337';
it('owned by deployer', () => {
return expect(wallet.owner().callAsync()).to.eventually.eq(owner);
});
describe('executeCall()', () => {
it('non-owner cannot call executeCall()', async () => {
const notOwner = randomAddress();
const tx = wallet
.executeCall(randomAddress(), hexUtils.random(), getRandomInteger(0, '100e18'))
.callAsync({ from: notOwner });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner));
});
it('owner can call executeCall()', async () => {
const targetData = hexUtils.random(128);
const receipt = await wallet
.executeCall(callTarget.address, targetData, constants.ZERO_AMOUNT)
.awaitTransactionSuccessAsync({ from: owner });
verifyEventsFromLogs(
receipt.logs,
[
{
context: callTarget.address,
sender: wallet.address,
data: targetData,
value: constants.ZERO_AMOUNT,
},
],
TestCallTargetEvents.CallTargetCalled,
);
});
it('owner can call executeCall() with attached ETH', async () => {
const targetData = hexUtils.random(128);
const callValue = getRandomInteger(1, '1e18');
const receipt = await wallet
.executeCall(callTarget.address, targetData, callValue)
.awaitTransactionSuccessAsync({ from: owner, value: callValue });
verifyEventsFromLogs(
receipt.logs,
[
{
context: callTarget.address,
sender: wallet.address,
data: targetData,
value: callValue,
},
],
TestCallTargetEvents.CallTargetCalled,
);
});
it('owner can call executeCall() can transfer less ETH than attached', async () => {
const targetData = hexUtils.random(128);
const callValue = getRandomInteger(1, '1e18');
const receipt = await wallet
.executeCall(callTarget.address, targetData, callValue.minus(1))
.awaitTransactionSuccessAsync({ from: owner, value: callValue });
verifyEventsFromLogs(
receipt.logs,
[
{
context: callTarget.address,
sender: wallet.address,
data: targetData,
value: callValue.minus(1),
},
],
TestCallTargetEvents.CallTargetCalled,
);
});
it('wallet returns call result', async () => {
const result = await wallet
.executeCall(callTarget.address, hexUtils.random(128), constants.ZERO_AMOUNT)
.callAsync({ from: owner });
expect(result).to.eq(TARGET_RETURN_VALUE);
});
it('wallet wraps call revert', async () => {
const tx = wallet
.executeCall(callTarget.address, REVERTING_DATA, constants.ZERO_AMOUNT)
.callAsync({ from: owner });
return expect(tx).to.revertWith(
new ZeroExRevertErrors.Wallet.WalletExecuteCallFailedError(
wallet.address,
callTarget.address,
REVERTING_DATA,
constants.ZERO_AMOUNT,
new StringRevertError('TestCallTarget/REVERT'),
),
);
});
it('wallet can receive ETH', async () => {
await env.web3Wrapper.sendTransactionAsync({
to: wallet.address,
from: owner,
value: 1,
});
const bal = await env.web3Wrapper.getBalanceInWeiAsync(wallet.address);
expect(bal).to.bignumber.eq(1);
});
});
describe('executeDelegateCall()', () => {
it('non-owner cannot call executeDelegateCall()', async () => {
const notOwner = randomAddress();
const tx = wallet.executeDelegateCall(randomAddress(), hexUtils.random()).callAsync({ from: notOwner });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner));
});
it('owner can call executeDelegateCall()', async () => {
const targetData = hexUtils.random(128);
const receipt = await wallet
.executeDelegateCall(callTarget.address, targetData)
.awaitTransactionSuccessAsync({ from: owner });
verifyEventsFromLogs(
receipt.logs,
[
{
context: wallet.address,
sender: owner,
data: targetData,
value: constants.ZERO_AMOUNT,
},
],
TestCallTargetEvents.CallTargetCalled,
);
});
it('executeDelegateCall() is payable', async () => {
const targetData = hexUtils.random(128);
const callValue = getRandomInteger(1, '1e18');
const receipt = await wallet
.executeDelegateCall(callTarget.address, targetData)
.awaitTransactionSuccessAsync({ from: owner, value: callValue });
verifyEventsFromLogs(
receipt.logs,
[
{
context: wallet.address,
sender: owner,
data: targetData,
value: callValue,
},
],
TestCallTargetEvents.CallTargetCalled,
);
});
it('wallet returns call result', async () => {
const result = await wallet
.executeDelegateCall(callTarget.address, hexUtils.random(128))
.callAsync({ from: owner });
expect(result).to.eq(TARGET_RETURN_VALUE);
});
it('wallet wraps call revert', async () => {
const tx = wallet.executeDelegateCall(callTarget.address, REVERTING_DATA).callAsync({ from: owner });
return expect(tx).to.revertWith(
new ZeroExRevertErrors.Wallet.WalletExecuteDelegateCallFailedError(
wallet.address,
callTarget.address,
REVERTING_DATA,
new StringRevertError('TestCallTarget/REVERT'),
),
);
});
});
});

View File

@ -150,8 +150,6 @@ export * from '../test/generated-wrappers/permissionless_transformer_deployer';
export * from '../test/generated-wrappers/polygon_bridge_adapter'; export * from '../test/generated-wrappers/polygon_bridge_adapter';
export * from '../test/generated-wrappers/positive_slippage_fee_transformer'; export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
export * from '../test/generated-wrappers/simple_function_registry_feature'; export * from '../test/generated-wrappers/simple_function_registry_feature';
export * from '../test/generated-wrappers/test_bridge';
export * from '../test/generated-wrappers/test_call_target';
export * from '../test/generated-wrappers/test_curve'; export * from '../test/generated-wrappers/test_curve';
export * from '../test/generated-wrappers/test_delegate_caller'; export * from '../test/generated-wrappers/test_delegate_caller';
export * from '../test/generated-wrappers/test_fee_collector_controller'; export * from '../test/generated-wrappers/test_fee_collector_controller';

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
/* /*
Copyright 2022 ZeroEx Intl. Copyright 2023 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,14 +17,18 @@
*/ */
pragma solidity ^0.6; pragma solidity ^0.6.5;
import "forge-std/Test.sol"; import "forge-std/Test.sol";
contract ContractTest is Test { contract BaseTest is Test {
function setUp() public {} address payable internal account1 = payable(vm.addr(1));
address payable internal account2 = payable(vm.addr(2));
address payable internal account3 = payable(vm.addr(3));
function testExample() public { constructor() public {
assertTrue(true); vm.deal(account1, 1e20);
vm.deal(account2, 1e20);
vm.deal(account3, 1e20);
} }
} }

View File

@ -0,0 +1,146 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2022 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;
import "./BaseTest.sol";
import "../contracts/src/external/FlashWallet.sol";
import "./mocks/TestCallTarget.sol";
contract FlashWalletTest is BaseTest {
address public owner = account1;
FlashWallet public wallet;
TestCallTarget public callTarget;
bytes public constant MAGIC_BYTES = hex"1234567800000000000000000000000000000000000000000000000000000000";
bytes public constant REVERTING_DATA = hex"1337";
event CallTargetCalled(address sender, bytes data, uint256 value);
function setUp() public {
vm.startPrank(owner);
wallet = new FlashWallet();
callTarget = new TestCallTarget();
vm.stopPrank();
}
function test_OwnedByDeployer() public {
assertEq(wallet.owner(), account1);
}
function test_executeCall_nonOwnerCannotExecute() public {
vm.expectRevert(LibOwnableRichErrorsV06.OnlyOwnerError(account2, owner));
vm.startPrank(account2);
wallet.executeCall(address(callTarget), "0x1", 123);
}
function test_executeCall_ownerCanCallWithZeroValue() public {
vm.expectEmit(true, true, true, true);
emit CallTargetCalled(address(wallet), "0x1", 0);
vm.startPrank(owner);
wallet.executeCall(address(callTarget), "0x1", 0);
}
function test_executeCall_ownerCanCallWithNonZeroValue() public {
vm.expectEmit(true, true, true, true);
emit CallTargetCalled(address(wallet), "0x1", 1);
vm.startPrank(owner);
wallet.executeCall{value: 1}(address(callTarget), "0x1", 1);
}
function test_executeCall_ownerCanTransferLessETHThanAttached() public {
vm.expectEmit(true, true, true, true);
emit CallTargetCalled(address(wallet), "0x1", 1);
vm.startPrank(owner);
// Send value 2 but execute call with 1
wallet.executeCall{value: 2}(address(callTarget), "0x1", 1);
}
function test_executeCall_walletReturnsCallResult() public {
vm.startPrank(owner);
bytes memory result = wallet.executeCall(address(callTarget), "0x1", 0);
assertEq0(result, MAGIC_BYTES);
}
function test_executeCall_walletWrapsCallRevert() public {
(, bytes memory resultData) = address(callTarget).call(REVERTING_DATA);
bytes memory error = LibWalletRichErrors.WalletExecuteCallFailedError(
address(wallet),
address(callTarget),
REVERTING_DATA,
0,
resultData
);
vm.expectRevert(error);
vm.startPrank(owner);
wallet.executeCall(address(callTarget), REVERTING_DATA, 0);
}
function test_executeCall_walletCanReceiveETH() public {
vm.startPrank(owner);
(bool sent, ) = address(wallet).call{value: 1}("");
assertTrue(sent, "Failed to send ETH to wallet");
assertEq(address(wallet).balance, 1);
}
function test_executeDelegateCall_nonOwnerCannotExecute() public {
vm.expectRevert(LibOwnableRichErrorsV06.OnlyOwnerError(account2, owner));
vm.startPrank(account2);
wallet.executeDelegateCall(address(callTarget), "0x1");
}
function test_executeDelegateCall_ownerCanExecute() public {
vm.expectEmit(true, true, true, true);
emit CallTargetCalled(owner, "0x1", 0);
vm.startPrank(owner);
wallet.executeDelegateCall(address(callTarget), "0x1");
}
function test_executeDelegateCall_ownerCanExecuteWithValue() public {
vm.expectEmit(true, true, true, true);
emit CallTargetCalled(owner, "0x1", 1);
vm.startPrank(owner);
wallet.executeDelegateCall{value: 1}(address(callTarget), "0x1");
}
function test_executeDelegateCall_walletReturnsCallResult() public {
vm.startPrank(owner);
bytes memory result = wallet.executeDelegateCall(address(callTarget), "0x1");
assertEq0(result, MAGIC_BYTES);
}
function test_executeDelegateCall_walletWrapsCallRevert() public {
(, bytes memory resultData) = address(callTarget).call(REVERTING_DATA);
bytes memory error = LibWalletRichErrors.WalletExecuteDelegateCallFailedError(
address(wallet),
address(callTarget),
REVERTING_DATA,
resultData
);
vm.expectRevert(error);
vm.startPrank(owner);
wallet.executeDelegateCall(address(callTarget), REVERTING_DATA);
}
}

View File

@ -21,16 +21,16 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
contract TestCallTarget { contract TestCallTarget {
event CallTargetCalled(address context, address sender, bytes data, uint256 value); event CallTargetCalled(address sender, bytes data, uint256 value);
bytes4 private constant MAGIC_BYTES = 0x12345678; bytes4 public constant MAGIC_BYTES = 0x12345678;
bytes private constant REVERTING_DATA = hex"1337"; bytes public constant REVERTING_DATA = hex"1337";
fallback() external payable { fallback() external payable {
if (keccak256(msg.data) == keccak256(REVERTING_DATA)) { if (keccak256(msg.data) == keccak256(REVERTING_DATA)) {
revert("TestCallTarget/REVERT"); revert("TestCallTarget/REVERT");
} }
emit CallTargetCalled(address(this), msg.sender, msg.data, msg.value); emit CallTargetCalled(msg.sender, msg.data, msg.value);
bytes4 rval = MAGIC_BYTES; bytes4 rval = MAGIC_BYTES;
assembly { assembly {
mstore(0, rval) mstore(0, rval)

View File

@ -189,8 +189,6 @@
"test/generated-artifacts/PolygonBridgeAdapter.json", "test/generated-artifacts/PolygonBridgeAdapter.json",
"test/generated-artifacts/PositiveSlippageFeeTransformer.json", "test/generated-artifacts/PositiveSlippageFeeTransformer.json",
"test/generated-artifacts/SimpleFunctionRegistryFeature.json", "test/generated-artifacts/SimpleFunctionRegistryFeature.json",
"test/generated-artifacts/TestBridge.json",
"test/generated-artifacts/TestCallTarget.json",
"test/generated-artifacts/TestCurve.json", "test/generated-artifacts/TestCurve.json",
"test/generated-artifacts/TestDelegateCaller.json", "test/generated-artifacts/TestDelegateCaller.json",
"test/generated-artifacts/TestFeeCollectorController.json", "test/generated-artifacts/TestFeeCollectorController.json",