Compare commits
3 Commits
@0x/contra
...
feat/FQTv2
Author | SHA1 | Date | |
---|---|---|---|
|
b0b76024d5 | ||
|
16039a7193 | ||
|
b67ec9548a |
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -22,10 +22,6 @@ jobs:
|
||||
| jq .state)
|
||||
[[ "${REF_STATUS}" == '"${{ github.event.inputs.ci_status }}"' ]] || \
|
||||
(echo "::error ::${{ github.ref }} does not have a successful CI status" && false)
|
||||
- name: Add foundry
|
||||
uses: foundry-rs/foundry-toolchain@v1
|
||||
with:
|
||||
version: nightly
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -99,7 +99,6 @@ foundry-artifacts/
|
||||
|
||||
# foundry cache
|
||||
cache/
|
||||
foundry-cache/
|
||||
|
||||
#foundry output artifacts
|
||||
out/
|
||||
|
@@ -1,40 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681969282,
|
||||
"version": "4.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.0.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"timestamp": 1681697326,
|
||||
"version": "4.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681157139,
|
||||
"version": "4.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "4.0.2",
|
||||
|
@@ -5,22 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.0.6 - _April 20, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.5 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.4 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.3 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.2 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
Submodule contracts/erc20/lib/forge-std updated: fc560fa34f...a2edd39db9
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "4.0.6",
|
||||
"version": "4.0.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol",
|
||||
"devDependencies": {
|
||||
"@0x/contracts-utils": "^4.8.44",
|
||||
"@0x/contracts-utils": "^4.8.40",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"typedoc": "~0.16.11"
|
||||
},
|
||||
|
@@ -1,38 +0,0 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681756154,
|
||||
"version": "1.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681694690,
|
||||
"version": "1.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681687842,
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681157139,
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -1,22 +0,0 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.4 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.3 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.2 - _April 16, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
@@ -1,74 +0,0 @@
|
||||
## Governance
|
||||
|
||||
This package contains contracts for the ZeroEx governance of 0x Protocol and Treasury.
|
||||
|
||||
## Production deployment
|
||||
|
||||
`ZRXWrappedToken` 0xfcfaf7834f134f5146dbb3274bab9bed4bafa917
|
||||
`ZeroExVotesProxy` 0x9c766e51b46cbc1fa4f8b6718ed4a60ac9d591fb
|
||||
`ZeroExVotes` 0x8d208c5514b98c5b9ceed650b02df2aeb1c73e6f
|
||||
Protocol `ZeroExTimelock` 0xb6a1f58c5df9f13312639cddda0d128bf28cdd87
|
||||
`ZeroExProtocolGovernor` 0xc256035fe8533f9ce362012a6ae0aefed4df30f4
|
||||
Treasury `ZeroExTimelock` 0x0dcfb77a581bc8fe432e904643a5480cc183f38d
|
||||
`ZeroExTreasuryGovernor` 0x4822cfc1e7699bdb9551bdfd3a838ee414bc2008
|
||||
Security council 0x979BDb496e5f0A00af078b7a45F1E9E6bcff170F
|
||||
|
||||
## Design
|
||||
|
||||
This implementation fully decentralises governance of 0x Protocol and Treasury. This is enabled via a wrapped ZRX token and a Compound-like governors design. There are two separate governors for Protocol - `ZeroExProtocolGovernor` and Treasury - `ZeroExTreasuryGovernor` respectively working with two separate Timelock instances of the same contract implementation - `ZeroExTimelock`.
|
||||
|
||||
### Upgradability
|
||||
`ZRXWrappedToken` , `ZeroExProtocolGovernor` and `ZeroExTreasuryGovernor` governors are non-upgradable by design. However the voting implementation the governors use - `ZeroExVotes` is upgradable and using the OZ `ERC1967Proxy`.
|
||||
|
||||
### Wrapped ZRX
|
||||
wZRX will be issued 1-to-1 for ZRX. No locking/vesting mechanisms will exist between wZRX and ZRX and the two will be freely interchangeable. The ZRX token is non-upgradable and same will be valid for its wrapped equivalent.
|
||||
|
||||
The token supports delegation which allows a user to delegate their entire voting power to another account (which doesn't necessarily need to be a token holder). This is modelled on the standard OpenZeppelin `ERC20Votes` implementation. We have added logic for block number stamping delegators' balance changes stored in the `DelegateInfo.balanceLastUpdated` property. This block number information is sent in calls to `ZeroExVotes.moveVotingPower` in order to provide support for future upgrades to the vote power calculation.
|
||||
Note that for consistency `block.number` is used for the governor settings, voting checkpoints and this delegators' balance last updated property while timelock logic for the governor uses block.timestamp.
|
||||
|
||||
### Governors' settings
|
||||
Changing governors' settings for `votingDelay`, `votingPeriod` and `proposalThreshold` can be done via the normal proposal mechanism. Governors are deployed with the following initial settings:
|
||||
|
||||
| | voting delay | voting period | proposal threshold |
|
||||
|-------------------|--------------|---------------|--------------------|
|
||||
| Protocol governor | 2 days | 7 days | 1000000e18 |
|
||||
| Treasury governor | 2 days | 7 days | 250000e18 |
|
||||
|
||||
|
||||
This is using standard openzeppelin `GovernorSettings` implementation.
|
||||
|
||||
### Quorum
|
||||
Quorum for Protocol is fixed at 10m (10000000e18) while for Treasury this is calculated as 10% of voting power of the total supply (see voting strategies below for quadratic voting power implementation specifics). The quorum calculations for Treasury are using OpenZeppelin's `GovernorVotesQuorumFraction`.
|
||||
|
||||
Note that in-place updates to the quorum are not supported and will need to go through a governance upgrade. Reasoning behind this can be found in this discussion https://forum.openzeppelin.com/t/quorum-default-behaviour-on-governors/34560.
|
||||
|
||||
### Voting strategies
|
||||
The voting strategy will be linear 1-token-1-vote for Protocol and quadratic with threshold of 1000000e18 for Treasury (i.e. voting weight is linear up to 1m tokens and balance above that threshold is quadratic).
|
||||
|
||||
Worth noting is the `Checkpoint` struct design. For packing every `Checkpoint` into a single storage slot we are using the minimum uint type size for `votes` and `quadraticVotes` members, e.g.
|
||||
|
||||
```
|
||||
struct Checkpoint {
|
||||
uint32 fromBlock;
|
||||
uint96 votes;
|
||||
uint96 quadraticVotes;
|
||||
}
|
||||
```
|
||||
|
||||
since the maximum token supply is 1bn we can have maximum value for:
|
||||
`votes` : 1bn *10^18 => can be stored in 90 bits
|
||||
`quadraticVotes` : due to the likelihood of threshold changing and potentially bringing it closer to a linear vote, we are preemptively keeping this to the same size as linear votes slot.
|
||||
|
||||
### Time locks
|
||||
Governance proposals are subject to a 3 days delay for Protocol and 2 days for Treasury. This delay allows Security Council time to review passed proposals and take action where needed.
|
||||
|
||||
### Security Council
|
||||
The concept of a Security council is introduced which allows a multisig of security council members to cancel a proposal on the treasury or protocol governors and also rollback the protocol to an earlier version.
|
||||
|
||||
When no security council is assigned the following apply:
|
||||
|
||||
- ongoing proposals can still be voted on
|
||||
- new proposals cannot be created - except assignSecurityCouncil
|
||||
- expired proposals that are successful cannot be queued - excepted assignSecurityCouncil
|
||||
|
||||
There is a provision for the governors to have different security council set although initially these are the same.
|
12781
contracts/governance/ZrxTreasury.json
Normal file
12781
contracts/governance/ZrxTreasury.json
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/governance",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.0",
|
||||
"description": "Governance implementation for the 0x protocol and treasury",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
@@ -20,8 +20,6 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/protocol.git"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
@@ -14,20 +14,20 @@ import "../src/ZeroExProtocolGovernor.sol";
|
||||
import "../src/ZeroExTreasuryGovernor.sol";
|
||||
|
||||
contract Deploy is Script {
|
||||
address internal constant DEPLOYER = 0xEf37aD2BACD70119F141140f7B5E46Cd53a65fc4;
|
||||
address internal constant ZRX_TOKEN = 0xE41d2489571d322189246DaFA5ebDe1F4699F498;
|
||||
address internal constant TREASURY = 0x0bB1810061C2f5b2088054eE184E6C79e1591101;
|
||||
address internal constant EXCHANGE = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF;
|
||||
address payable internal constant SECURITY_COUNCIL = payable(0x979BDb496e5f0A00af078b7a45F1E9E6bcff170F);
|
||||
address payable internal constant SECURITY_COUNCIL = payable(DEPLOYER);
|
||||
uint256 internal constant QUADRATIC_THRESHOLD = 1000000e18;
|
||||
|
||||
function setUp() public {}
|
||||
|
||||
function run() external {
|
||||
address deployer = vm.envAddress("DEPLOYER");
|
||||
vm.startBroadcast(deployer);
|
||||
vm.startBroadcast(vm.envAddress("DEPLOYER"));
|
||||
|
||||
console2.log("Zrx Token", ZRX_TOKEN);
|
||||
address wTokenPrediction = predict(deployer, vm.getNonce(deployer) + 2);
|
||||
address wTokenPrediction = predict(DEPLOYER, vm.getNonce(DEPLOYER) + 2);
|
||||
ZeroExVotes votesImpl = new ZeroExVotes(wTokenPrediction, QUADRATIC_THRESHOLD);
|
||||
ERC1967Proxy votesProxy = new ERC1967Proxy(address(votesImpl), abi.encodeCall(votesImpl.initialize, ()));
|
||||
ZRXWrappedToken wToken = new ZRXWrappedToken(IERC20(ZRX_TOKEN), ZeroExVotes(address(votesProxy)));
|
||||
@@ -41,7 +41,7 @@ contract Deploy is Script {
|
||||
address[] memory proposers = new address[](0);
|
||||
address[] memory executors = new address[](0);
|
||||
|
||||
ZeroExTimelock protocolTimelock = new ZeroExTimelock(3 days, proposers, executors, deployer);
|
||||
ZeroExTimelock protocolTimelock = new ZeroExTimelock(3 days, proposers, executors, DEPLOYER);
|
||||
console2.log("Protocol timelock", address(protocolTimelock));
|
||||
|
||||
ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(
|
||||
@@ -52,10 +52,9 @@ contract Deploy is Script {
|
||||
protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor));
|
||||
protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor));
|
||||
protocolTimelock.grantRole(protocolTimelock.CANCELLER_ROLE(), address(protocolGovernor));
|
||||
protocolTimelock.renounceRole(protocolTimelock.TIMELOCK_ADMIN_ROLE(), deployer);
|
||||
console2.log("Protocol governor", address(protocolGovernor));
|
||||
|
||||
ZeroExTimelock treasuryTimelock = new ZeroExTimelock(2 days, proposers, executors, deployer);
|
||||
ZeroExTimelock treasuryTimelock = new ZeroExTimelock(2 days, proposers, executors, DEPLOYER);
|
||||
console2.log("Treasury timelock", address(treasuryTimelock));
|
||||
|
||||
ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(
|
||||
@@ -67,7 +66,6 @@ contract Deploy is Script {
|
||||
treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor));
|
||||
treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor));
|
||||
treasuryTimelock.grantRole(treasuryTimelock.CANCELLER_ROLE(), address(treasuryGovernor));
|
||||
treasuryTimelock.renounceRole(treasuryTimelock.TIMELOCK_ADMIN_ROLE(), deployer);
|
||||
console2.log("Treasury governor", address(treasuryGovernor));
|
||||
console2.log(unicode"0x governance deployed successfully 🎉");
|
||||
vm.stopBroadcast();
|
||||
|
@@ -23,7 +23,6 @@ import "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import "../mocks/IZeroExMock.sol";
|
||||
import "../mocks/IZrxTreasuryMock.sol";
|
||||
import "../mocks/IStakingMock.sol";
|
||||
import "../mocks/IZrxVaultMock.sol";
|
||||
import "../BaseTest.t.sol";
|
||||
import "../../src/ZRXWrappedToken.sol";
|
||||
import "../../src/ZeroExVotes.sol";
|
||||
@@ -35,11 +34,6 @@ contract GovernanceE2ETest is BaseTest {
|
||||
uint256 internal mainnetFork;
|
||||
string internal MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL");
|
||||
|
||||
struct DelegatorPool {
|
||||
address delegator;
|
||||
bytes32 pool;
|
||||
}
|
||||
|
||||
address internal constant ZRX_TOKEN = 0xE41d2489571d322189246DaFA5ebDe1F4699F498;
|
||||
address internal constant MATIC_TOKEN = 0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0;
|
||||
address internal constant WCELO_TOKEN = 0xE452E6Ea2dDeB012e20dB73bf5d3863A3Ac8d77a;
|
||||
@@ -48,25 +42,10 @@ contract GovernanceE2ETest is BaseTest {
|
||||
address internal constant EXCHANGE_PROXY = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF;
|
||||
address internal constant EXCHANGE_GOVERNOR = 0x618F9C67CE7Bf1a50afa1E7e0238422601b0ff6e;
|
||||
address internal constant TREASURY = 0x0bB1810061C2f5b2088054eE184E6C79e1591101;
|
||||
address internal constant STAKING = 0xa26e80e7Dea86279c6d778D702Cc413E6CFfA777; // Holds ~$262K in WETH for rewards
|
||||
address internal constant ZRX_VAULT = 0xBa7f8b5fB1b19c1211c5d49550fcD149177A5Eaf; // Holds ~$10m in staked ZRX
|
||||
address internal constant STAKING_AND_VAULT_OWNER = 0x7D3455421BbC5Ed534a83c88FD80387dc8271392;
|
||||
|
||||
address internal staker1 = 0x885c327cAD2aebb969dfaAb4c928B73CA17e3887;
|
||||
address internal staker2 = 0x03c823e96F6964076C118395F08a2D7edF0f8a8C;
|
||||
|
||||
address[] internal topStakers = [
|
||||
0x5775afA796818ADA27b09FaF5c90d101f04eF600,
|
||||
0xE1bdcd3B70e077D2d66ADcbe78be3941F0BF380B,
|
||||
0xcCa71809E8870AFEB72c4720d0fe50d5C3230e05,
|
||||
0x828FD91d3e3a9FFa6305e78B9aE2Cfbc5B5D9f6B,
|
||||
0x4A36C3DA5d367B148d17265e7d7feafcf8fb4a21,
|
||||
0xEeff6fd32DeaFe1a9d3258A51c7F952F9FF0B2Ce,
|
||||
0x1D0738b927dFCBFBD59A9F0944BbD1860d3B9248,
|
||||
0x0C073E7248C1b548a08b27dD3af5D0f39c774280,
|
||||
0xA178FF321335BB777A7E21A56376592F69556b9c,
|
||||
0xD06CfBb59d2e8918F84D99d981039d7706DCA288
|
||||
];
|
||||
address internal constant STAKING = 0xa26e80e7Dea86279c6d778D702Cc413E6CFfA777;
|
||||
address internal staker = 0x5265Bde27F57E738bE6c1F6AB3544e82cdc92a8f;
|
||||
bytes32 internal stakerPool = 0x0000000000000000000000000000000000000000000000000000000000000032;
|
||||
bytes32[] internal staker_operated_poolIds = [stakerPool];
|
||||
|
||||
// voting power 1500000e18
|
||||
address internal voter1 = 0x292c6DAE7417B3D31d8B6e1d2EeA0258d14C4C4b;
|
||||
@@ -110,9 +89,7 @@ contract GovernanceE2ETest is BaseTest {
|
||||
|
||||
IZeroExMock internal exchange;
|
||||
IZrxTreasuryMock internal treasury;
|
||||
IZrxVaultMock internal vault;
|
||||
IStakingMock internal staking;
|
||||
IERC20 internal weth;
|
||||
|
||||
ZRXWrappedToken internal wToken;
|
||||
ZeroExVotes internal votes;
|
||||
@@ -132,9 +109,7 @@ contract GovernanceE2ETest is BaseTest {
|
||||
|
||||
exchange = IZeroExMock(payable(EXCHANGE_PROXY));
|
||||
treasury = IZrxTreasuryMock(TREASURY);
|
||||
vault = IZrxVaultMock(ZRX_VAULT);
|
||||
staking = IStakingMock(STAKING);
|
||||
weth = IERC20(staking.getWethContract());
|
||||
|
||||
address protocolGovernorAddress;
|
||||
address treasuryGovernorAddress;
|
||||
@@ -167,7 +142,7 @@ contract GovernanceE2ETest is BaseTest {
|
||||
uint256 currentEpoch = staking.currentEpoch();
|
||||
uint256 executionEpoch = currentEpoch + 2;
|
||||
|
||||
vm.startPrank(voter3);
|
||||
vm.startPrank(staker);
|
||||
|
||||
IZrxTreasuryMock.ProposedAction[] memory actions = new IZrxTreasuryMock.ProposedAction[](4);
|
||||
|
||||
@@ -207,7 +182,7 @@ contract GovernanceE2ETest is BaseTest {
|
||||
actions,
|
||||
executionEpoch,
|
||||
"Z-5 Migrate to new treasury governor",
|
||||
voter3_operated_poolIds
|
||||
staker_operated_poolIds
|
||||
);
|
||||
|
||||
// Once a proposal is created, it becomes open for voting at the epoch after next (currentEpoch + 2)
|
||||
@@ -268,187 +243,4 @@ contract GovernanceE2ETest is BaseTest {
|
||||
uint256 wyvBalanceNewTreasury = wyvToken.balanceOf(address(treasuryGovernor));
|
||||
assertEq(wyvBalanceNewTreasury, wyvBalance);
|
||||
}
|
||||
|
||||
// Test entering catastrophic failure mode on the zrx vault to decomission v3 staking
|
||||
function testCatastrophicFailureModeOnStaking() public {
|
||||
DelegatorPool[5] memory delegatorPools = [
|
||||
DelegatorPool(
|
||||
0x0ee1F33A2EB0da738FdF035C48d62d75e996a3bd,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000016
|
||||
),
|
||||
DelegatorPool(
|
||||
0xcAb3d8cBBb3dA1bDabfB003B9C828B27a821717f,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000017
|
||||
),
|
||||
DelegatorPool(
|
||||
0x7f88b00Db27a500fBfA7EbC9c3CaA2Dea6F59d5b,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000014
|
||||
),
|
||||
DelegatorPool(
|
||||
0xcE266E6123B682f7A7388097e2155b5379D9AC78,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000014
|
||||
),
|
||||
DelegatorPool(
|
||||
0xBa4f44E774158408E2DC6c5cb65BC995F0a89180, // pool operator
|
||||
0x0000000000000000000000000000000000000000000000000000000000000017
|
||||
)
|
||||
];
|
||||
|
||||
// Enter catastrophic failure mode on the zrx vault
|
||||
vm.prank(STAKING_AND_VAULT_OWNER);
|
||||
vault.enterCatastrophicFailure();
|
||||
vm.stopPrank();
|
||||
|
||||
// Stakes can still be withdrawn
|
||||
// staker1 withdraws
|
||||
uint256 stake1 = vault.balanceOf(staker1);
|
||||
uint256 balance1 = token.balanceOf(staker1);
|
||||
assertGt(stake1, 0);
|
||||
|
||||
vm.prank(staker1);
|
||||
vault.withdrawAllFrom(staker1);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(vault.balanceOf(staker1), 0);
|
||||
assertEq(token.balanceOf(staker1), stake1 + balance1);
|
||||
|
||||
// staker2 withdraws
|
||||
uint256 stake2 = vault.balanceOf(staker2);
|
||||
uint256 balance2 = token.balanceOf(staker2);
|
||||
assertGt(stake2, 0);
|
||||
|
||||
vm.prank(staker2);
|
||||
vault.withdrawAllFrom(staker2);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(vault.balanceOf(staker2), 0);
|
||||
assertEq(token.balanceOf(staker2), stake2 + balance2);
|
||||
|
||||
// Test top stakers can withdraw
|
||||
for (uint256 i = 0; i < topStakers.length; i++) {
|
||||
address staker = topStakers[i];
|
||||
uint256 stake = vault.balanceOf(staker);
|
||||
uint256 balance = token.balanceOf(staker);
|
||||
assertGt(stake, 0);
|
||||
|
||||
vm.prank(staker);
|
||||
vault.withdrawAllFrom(staker);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(vault.balanceOf(staker), 0);
|
||||
assertEq(token.balanceOf(staker), stake + balance);
|
||||
}
|
||||
|
||||
// Delegator can withdraw rewards
|
||||
for (uint256 i = 0; i < delegatorPools.length; i++) {
|
||||
address delegator = delegatorPools[i].delegator;
|
||||
bytes32 pool = delegatorPools[i].pool;
|
||||
uint256 reward = staking.computeRewardBalanceOfDelegator(pool, delegator);
|
||||
assertGt(reward, 0);
|
||||
uint256 balanceBeforeReward = weth.balanceOf(delegator);
|
||||
|
||||
vm.prank(delegator);
|
||||
staking.withdrawDelegatorRewards(pool);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(weth.balanceOf(delegator), balanceBeforeReward + reward);
|
||||
}
|
||||
}
|
||||
|
||||
function testSwitchDelegationInCatastrophicMode() public {
|
||||
// Enter catastrophic failure mode on the zrx vault
|
||||
vm.prank(STAKING_AND_VAULT_OWNER);
|
||||
vault.enterCatastrophicFailure();
|
||||
|
||||
// 0x delegator
|
||||
address delegator = 0x5775afA796818ADA27b09FaF5c90d101f04eF600;
|
||||
|
||||
uint256 stake = vault.balanceOf(delegator);
|
||||
uint256 balance = token.balanceOf(delegator);
|
||||
assertGt(stake, 0);
|
||||
|
||||
// Withdraw stake all at once
|
||||
vm.startPrank(delegator);
|
||||
vault.withdrawAllFrom(delegator);
|
||||
|
||||
assertEq(vault.balanceOf(delegator), 0);
|
||||
assertEq(token.balanceOf(delegator), stake + balance);
|
||||
|
||||
// delegate 1M ZRX to 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08
|
||||
vm.roll(block.number + 1);
|
||||
address delegate = 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08;
|
||||
uint256 amountToDelegate = 1000000e18;
|
||||
|
||||
// Approve the wrapped token and deposit 1m ZRX
|
||||
token.approve(address(wToken), amountToDelegate);
|
||||
wToken.depositFor(delegator, amountToDelegate);
|
||||
|
||||
assertEq(wToken.balanceOf(delegator), amountToDelegate);
|
||||
|
||||
vm.roll(block.number + 1);
|
||||
wToken.delegate(delegate);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(votes.getVotes(delegate), amountToDelegate);
|
||||
assertEq(votes.getQuadraticVotes(delegate), amountToDelegate);
|
||||
}
|
||||
|
||||
function testSwitchDelegationInNormalOperationMode() public {
|
||||
// 0x delegator
|
||||
address delegator = 0x5775afA796818ADA27b09FaF5c90d101f04eF600;
|
||||
uint256 balance = token.balanceOf(delegator);
|
||||
|
||||
// Undelegate stake from pool 0x35
|
||||
vm.startPrank(delegator);
|
||||
staking.moveStake(
|
||||
IStructs.StakeInfo(
|
||||
IStructs.StakeStatus.DELEGATED,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000035
|
||||
),
|
||||
IStructs.StakeInfo(IStructs.StakeStatus.UNDELEGATED, bytes32(0)),
|
||||
3000000000000000000000000
|
||||
);
|
||||
|
||||
// Undelegate stake from pool 0x38
|
||||
IStructs.StoredBalance memory storedBalance38 = staking.getStakeDelegatedToPoolByOwner(
|
||||
delegator,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000038
|
||||
);
|
||||
staking.moveStake(
|
||||
IStructs.StakeInfo(
|
||||
IStructs.StakeStatus.DELEGATED,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000038
|
||||
),
|
||||
IStructs.StakeInfo(IStructs.StakeStatus.UNDELEGATED, bytes32(0)),
|
||||
storedBalance38.currentEpochBalance
|
||||
);
|
||||
|
||||
// Warp past an epochs and unstake
|
||||
uint256 epochEndTime = staking.getCurrentEpochEarliestEndTimeInSeconds();
|
||||
vm.warp(epochEndTime + 1);
|
||||
staking.endEpoch();
|
||||
|
||||
staking.unstake(6000000000000000000000000);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(token.balanceOf(delegator), balance + 2 * 3000000000000000000000000);
|
||||
|
||||
// delegate 1M ZRX to 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08
|
||||
vm.roll(block.number + 1);
|
||||
address delegate = 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08;
|
||||
uint256 amountToDelegate = 1000000e18;
|
||||
|
||||
// Approve the wrapped token and deposit 1m ZRX
|
||||
token.approve(address(wToken), amountToDelegate);
|
||||
wToken.depositFor(delegator, amountToDelegate);
|
||||
|
||||
assertEq(wToken.balanceOf(delegator), amountToDelegate);
|
||||
|
||||
vm.roll(block.number + 1);
|
||||
wToken.delegate(delegate);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(votes.getVotes(delegate), amountToDelegate);
|
||||
assertEq(votes.getQuadraticVotes(delegate), amountToDelegate);
|
||||
}
|
||||
}
|
||||
|
@@ -17,16 +17,36 @@
|
||||
|
||||
*/
|
||||
|
||||
import "./IZrxVaultMock.sol";
|
||||
import "./IStructs.sol";
|
||||
import "./IStorageMock.sol";
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
interface IStakingMock is IStorageMock {
|
||||
/// @dev Adds a new exchange address
|
||||
/// @param addr Address of exchange contract to add
|
||||
function addExchangeAddress(address addr) external;
|
||||
interface IStakingMock {
|
||||
/// @dev Statuses that stake can exist in.
|
||||
/// Any stake can be (re)delegated effective at the next epoch
|
||||
/// Undelegated stake can be withdrawn if it is available in both the current and next epoch
|
||||
enum StakeStatus {
|
||||
UNDELEGATED,
|
||||
DELEGATED
|
||||
}
|
||||
|
||||
/// @dev Encapsulates a balance for the current and next epochs.
|
||||
/// Note that these balances may be stale if the current epoch
|
||||
/// is greater than `currentEpoch`.
|
||||
/// @param currentEpoch The current epoch
|
||||
/// @param currentEpochBalance Balance in the current epoch.
|
||||
/// @param nextEpochBalance Balance in `currentEpoch+1`.
|
||||
struct StoredBalance {
|
||||
uint64 currentEpoch;
|
||||
uint96 currentEpochBalance;
|
||||
uint96 nextEpochBalance;
|
||||
}
|
||||
|
||||
/// @dev Holds the metadata for a staking pool.
|
||||
/// @param operator Operator of the pool.
|
||||
/// @param operatorShare Fraction of the total balance owned by the operator, in ppm.
|
||||
struct Pool {
|
||||
address operator;
|
||||
uint32 operatorShare;
|
||||
}
|
||||
|
||||
/// @dev Create a new staking pool. The sender will be the operator of this pool.
|
||||
/// Note that an operator must be payable.
|
||||
@@ -35,105 +55,26 @@ interface IStakingMock is IStorageMock {
|
||||
/// @return poolId The unique pool id generated for this pool.
|
||||
function createStakingPool(uint32 operatorShare, bool addOperatorAsMaker) external returns (bytes32 poolId);
|
||||
|
||||
/// @dev Decreases the operator share for the given pool (i.e. increases pool rewards for members).
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @param newOperatorShare The newly decreased percentage of any rewards owned by the operator.
|
||||
function decreaseStakingPoolOperatorShare(bytes32 poolId, uint32 newOperatorShare) external;
|
||||
/// @dev Returns the current staking epoch number.
|
||||
/// @return epoch The current epoch.
|
||||
function currentEpoch() external view returns (uint256 epoch);
|
||||
|
||||
/// @dev Begins a new epoch, preparing the prior one for finalization.
|
||||
/// Throws if not enough time has passed between epochs or if the
|
||||
/// previous epoch was not fully finalized.
|
||||
/// @return numPoolsToFinalize The number of unfinalized pools.
|
||||
function endEpoch() external returns (uint256);
|
||||
/// @dev Returns the time (in seconds) at which the current staking epoch started.
|
||||
/// @return startTime The start time of the current epoch, in seconds.
|
||||
function currentEpochStartTimeInSeconds() external view returns (uint256 startTime);
|
||||
|
||||
/// @dev Instantly finalizes a single pool that earned rewards in the previous
|
||||
/// epoch, crediting it rewards for members and withdrawing operator's
|
||||
/// rewards as WETH. This can be called by internal functions that need
|
||||
/// to finalize a pool immediately. Does nothing if the pool is already
|
||||
/// finalized or did not earn rewards in the previous epoch.
|
||||
/// @param poolId The pool ID to finalize.
|
||||
function finalizePool(bytes32 poolId) external;
|
||||
/// @dev Returns the duration of an epoch in seconds. This value can be updated.
|
||||
/// @return duration The duration of an epoch, in seconds.
|
||||
function epochDurationInSeconds() external view returns (uint256 duration);
|
||||
|
||||
/// @dev Initialize storage owned by this contract.
|
||||
/// This function should not be called directly.
|
||||
/// The StakingProxy contract will call it in `attachStakingContract()`.
|
||||
function init() external;
|
||||
|
||||
/// @dev Allows caller to join a staking pool as a maker.
|
||||
/// @dev Returns a staking pool
|
||||
/// @param poolId Unique id of pool.
|
||||
function joinStakingPoolAsMaker(bytes32 poolId) external;
|
||||
|
||||
/// @dev Moves stake between statuses: 'undelegated' or 'delegated'.
|
||||
/// Delegated stake can also be moved between pools.
|
||||
/// This change comes into effect next epoch.
|
||||
/// @param from status to move stake out of.
|
||||
/// @param to status to move stake into.
|
||||
/// @param amount of stake to move.
|
||||
function moveStake(IStructs.StakeInfo calldata from, IStructs.StakeInfo calldata to, uint256 amount) external;
|
||||
|
||||
/// @dev Pays a protocol fee in ETH.
|
||||
/// @param makerAddress The address of the order's maker.
|
||||
/// @param payerAddress The address that is responsible for paying the protocol fee.
|
||||
/// @param protocolFee The amount of protocol fees that should be paid.
|
||||
function payProtocolFee(address makerAddress, address payerAddress, uint256 protocolFee) external payable;
|
||||
|
||||
/// @dev Removes an existing exchange address
|
||||
/// @param addr Address of exchange contract to remove
|
||||
function removeExchangeAddress(address addr) external;
|
||||
|
||||
/// @dev Set all configurable parameters at once.
|
||||
/// @param _epochDurationInSeconds Minimum seconds between epochs.
|
||||
/// @param _rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
|
||||
/// @param _minimumPoolStake Minimum amount of stake required in a pool to collect rewards.
|
||||
/// @param _cobbDouglasAlphaNumerator Numerator for cobb douglas alpha factor.
|
||||
/// @param _cobbDouglasAlphaDenominator Denominator for cobb douglas alpha factor.
|
||||
function setParams(
|
||||
uint256 _epochDurationInSeconds,
|
||||
uint32 _rewardDelegatedStakeWeight,
|
||||
uint256 _minimumPoolStake,
|
||||
uint32 _cobbDouglasAlphaNumerator,
|
||||
uint32 _cobbDouglasAlphaDenominator
|
||||
) external;
|
||||
|
||||
/// @dev Stake ZRX tokens. Tokens are deposited into the ZRX Vault.
|
||||
/// Unstake to retrieve the ZRX. Stake is in the 'Active' status.
|
||||
/// @param amount of ZRX to stake.
|
||||
function stake(uint256 amount) external;
|
||||
|
||||
/// @dev Unstake. Tokens are withdrawn from the ZRX Vault and returned to
|
||||
/// the staker. Stake must be in the 'undelegated' status in both the
|
||||
/// current and next epoch in order to be unstaked.
|
||||
/// @param amount of ZRX to unstake.
|
||||
function unstake(uint256 amount) external;
|
||||
|
||||
/// @dev Withdraws the caller's WETH rewards that have accumulated
|
||||
/// until the last epoch.
|
||||
/// @param poolId Unique id of pool.
|
||||
function withdrawDelegatorRewards(bytes32 poolId) external;
|
||||
|
||||
/// @dev Computes the reward balance in ETH of a specific member of a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @param member The member of the pool.
|
||||
/// @return reward Balance in ETH.
|
||||
function computeRewardBalanceOfDelegator(bytes32 poolId, address member) external view returns (uint256 reward);
|
||||
|
||||
/// @dev Computes the reward balance in ETH of the operator of a pool.
|
||||
/// @param poolId Unique id of pool.
|
||||
/// @return reward Balance in ETH.
|
||||
function computeRewardBalanceOfOperator(bytes32 poolId) external view returns (uint256 reward);
|
||||
|
||||
/// @dev Returns the earliest end time in seconds of this epoch.
|
||||
/// The next epoch can begin once this time is reached.
|
||||
/// Epoch period = [startTimeInSeconds..endTimeInSeconds)
|
||||
/// @return Time in seconds.
|
||||
function getCurrentEpochEarliestEndTimeInSeconds() external view returns (uint256);
|
||||
function getStakingPool(bytes32 poolId) external view returns (Pool memory);
|
||||
|
||||
/// @dev Gets global stake for a given status.
|
||||
/// @param stakeStatus UNDELEGATED or DELEGATED
|
||||
/// @return balance Global stake for given status.
|
||||
function getGlobalStakeByStatus(
|
||||
IStructs.StakeStatus stakeStatus
|
||||
) external view returns (IStructs.StoredBalance memory balance);
|
||||
function getGlobalStakeByStatus(StakeStatus stakeStatus) external view returns (StoredBalance memory balance);
|
||||
|
||||
/// @dev Gets an owner's stake balances by status.
|
||||
/// @param staker Owner of stake.
|
||||
@@ -141,56 +82,25 @@ interface IStakingMock is IStorageMock {
|
||||
/// @return balance Owner's stake balances for given status.
|
||||
function getOwnerStakeByStatus(
|
||||
address staker,
|
||||
IStructs.StakeStatus stakeStatus
|
||||
) external view returns (IStructs.StoredBalance memory balance);
|
||||
StakeStatus stakeStatus
|
||||
) external view returns (StoredBalance memory balance);
|
||||
|
||||
/// @dev Retrieves all configurable parameter values.
|
||||
/// @return _epochDurationInSeconds Minimum seconds between epochs.
|
||||
/// @return _rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
|
||||
/// @return _minimumPoolStake Minimum amount of stake required in a pool to collect rewards.
|
||||
/// @return _cobbDouglasAlphaNumerator Numerator for cobb douglas alpha factor.
|
||||
/// @return _cobbDouglasAlphaDenominator Denominator for cobb douglas alpha factor.
|
||||
function getParams()
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256 _epochDurationInSeconds,
|
||||
uint32 _rewardDelegatedStakeWeight,
|
||||
uint256 _minimumPoolStake,
|
||||
uint32 _cobbDouglasAlphaNumerator,
|
||||
uint32 _cobbDouglasAlphaDenominator
|
||||
);
|
||||
/// @dev Returns the total stake delegated to a specific staking pool,
|
||||
/// across all members.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @return balance Total stake delegated to pool.
|
||||
function getTotalStakeDelegatedToPool(bytes32 poolId) external view returns (StoredBalance memory balance);
|
||||
|
||||
/// @dev Returns the stake delegated to a specific staking pool, by a given staker.
|
||||
/// @param staker of stake.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @return balance Stake delegated to pool by staker.
|
||||
function getStakeDelegatedToPoolByOwner(
|
||||
address staker,
|
||||
bytes32 poolId
|
||||
) external view returns (IStructs.StoredBalance memory balance);
|
||||
) external view returns (StoredBalance memory balance);
|
||||
|
||||
/// @dev Returns a staking pool
|
||||
/// @param poolId Unique id of pool.
|
||||
function getStakingPool(bytes32 poolId) external view returns (IStructs.Pool memory);
|
||||
function endEpoch() external returns (uint256);
|
||||
|
||||
/// @dev Get stats on a staking pool in this epoch.
|
||||
/// @param poolId Pool Id to query.
|
||||
/// @return PoolStats struct for pool id.
|
||||
function getStakingPoolStatsThisEpoch(bytes32 poolId) external view returns (IStructs.PoolStats memory);
|
||||
|
||||
/// @dev Returns the total stake delegated to a specific staking pool,
|
||||
/// across all members.
|
||||
/// @param poolId Unique Id of pool.
|
||||
/// @return balance Total stake delegated to pool.
|
||||
function getTotalStakeDelegatedToPool(bytes32 poolId) external view returns (IStructs.StoredBalance memory balance);
|
||||
|
||||
/// @dev An overridable way to access the deployed WETH contract.
|
||||
/// Must be view to allow overrides to access state.
|
||||
/// @return wethContract The WETH contract instance.
|
||||
function getWethContract() external view returns (address wethContract);
|
||||
|
||||
/// @dev An overridable way to access the deployed zrxVault.
|
||||
/// Must be view to allow overrides to access state.
|
||||
/// @return zrxVault The zrxVault contract.
|
||||
function getZrxVault() external view returns (IZrxVaultMock zrxVault);
|
||||
function finalizePool(bytes32 poolId) external;
|
||||
}
|
||||
|
@@ -1,49 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 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.8.19;
|
||||
|
||||
import "./IZrxVaultMock.sol";
|
||||
import "./IStructs.sol";
|
||||
|
||||
interface IStorageMock {
|
||||
function stakingContract() external view returns (address);
|
||||
|
||||
function lastPoolId() external view returns (bytes32);
|
||||
|
||||
function numMakersByPoolId(bytes32 poolId) external view returns (uint256);
|
||||
|
||||
function currentEpoch() external view returns (uint256);
|
||||
|
||||
function currentEpochStartTimeInSeconds() external view returns (uint256);
|
||||
|
||||
function protocolFeesThisEpochByPool(bytes32 poolId) external view returns (uint256);
|
||||
|
||||
function validExchanges(address exchangeAddress) external view returns (bool);
|
||||
|
||||
function epochDurationInSeconds() external view returns (uint256);
|
||||
|
||||
function rewardDelegatedStakeWeight() external view returns (uint32);
|
||||
|
||||
function minimumPoolStake() external view returns (uint256);
|
||||
|
||||
function cobbDouglasAlphaNumerator() external view returns (uint32);
|
||||
|
||||
function cobbDouglasAlphaDenominator() external view returns (uint32);
|
||||
}
|
@@ -1,92 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 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.8.19;
|
||||
|
||||
interface IStructs {
|
||||
/// @dev Stats for a pool that earned rewards.
|
||||
/// @param feesCollected Fees collected in ETH by this pool.
|
||||
/// @param weightedStake Amount of weighted stake in the pool.
|
||||
/// @param membersStake Amount of non-operator stake in the pool.
|
||||
struct PoolStats {
|
||||
uint256 feesCollected;
|
||||
uint256 weightedStake;
|
||||
uint256 membersStake;
|
||||
}
|
||||
|
||||
/// @dev Holds stats aggregated across a set of pools.
|
||||
/// @param rewardsAvailable Rewards (ETH) available to the epoch
|
||||
/// being finalized (the previous epoch). This is simply the balance
|
||||
/// of the contract at the end of the epoch.
|
||||
/// @param numPoolsToFinalize The number of pools that have yet to be finalized through `finalizePools()`.
|
||||
/// @param totalFeesCollected The total fees collected for the epoch being finalized.
|
||||
/// @param totalWeightedStake The total fees collected for the epoch being finalized.
|
||||
/// @param totalRewardsFinalized Amount of rewards that have been paid during finalization.
|
||||
struct AggregatedStats {
|
||||
uint256 rewardsAvailable;
|
||||
uint256 numPoolsToFinalize;
|
||||
uint256 totalFeesCollected;
|
||||
uint256 totalWeightedStake;
|
||||
uint256 totalRewardsFinalized;
|
||||
}
|
||||
|
||||
/// @dev Encapsulates a balance for the current and next epochs.
|
||||
/// Note that these balances may be stale if the current epoch
|
||||
/// is greater than `currentEpoch`.
|
||||
/// @param currentEpoch the current epoch
|
||||
/// @param currentEpochBalance balance in the current epoch.
|
||||
/// @param nextEpochBalance balance in `currentEpoch+1`.
|
||||
struct StoredBalance {
|
||||
uint64 currentEpoch;
|
||||
uint96 currentEpochBalance;
|
||||
uint96 nextEpochBalance;
|
||||
}
|
||||
|
||||
/// @dev Statuses that stake can exist in.
|
||||
/// Any stake can be (re)delegated effective at the next epoch
|
||||
/// Undelegated stake can be withdrawn if it is available in both the current and next epoch
|
||||
enum StakeStatus {
|
||||
UNDELEGATED,
|
||||
DELEGATED
|
||||
}
|
||||
|
||||
/// @dev Info used to describe a status.
|
||||
/// @param status of the stake.
|
||||
/// @param poolId Unique Id of pool. This is set when status=DELEGATED.
|
||||
struct StakeInfo {
|
||||
StakeStatus status;
|
||||
bytes32 poolId;
|
||||
}
|
||||
|
||||
/// @dev Struct to represent a fraction.
|
||||
/// @param numerator of fraction.
|
||||
/// @param denominator of fraction.
|
||||
struct Fraction {
|
||||
uint256 numerator;
|
||||
uint256 denominator;
|
||||
}
|
||||
|
||||
/// @dev Holds the metadata for a staking pool.
|
||||
/// @param operator of the pool.
|
||||
/// @param operatorShare Fraction of the total balance owned by the operator, in ppm.
|
||||
struct Pool {
|
||||
address operator;
|
||||
uint32 operatorShare;
|
||||
}
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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.8.19;
|
||||
|
||||
interface IZrxVaultMock {
|
||||
/// @dev Emmitted whenever a StakingProxy is set in a vault.
|
||||
event StakingProxySet(address stakingProxyAddress);
|
||||
|
||||
/// @dev Emitted when the Staking contract is put into Catastrophic Failure Mode
|
||||
/// @param sender Address of sender (`msg.sender`)
|
||||
event InCatastrophicFailureMode(address sender);
|
||||
|
||||
/// @dev Emitted when Zrx Tokens are deposited into the vault.
|
||||
/// @param staker of Zrx Tokens.
|
||||
/// @param amount of Zrx Tokens deposited.
|
||||
event Deposit(address indexed staker, uint256 amount);
|
||||
|
||||
/// @dev Emitted when Zrx Tokens are withdrawn from the vault.
|
||||
/// @param staker of Zrx Tokens.
|
||||
/// @param amount of Zrx Tokens withdrawn.
|
||||
event Withdraw(address indexed staker, uint256 amount);
|
||||
|
||||
/// @dev Emitted whenever the ZRX AssetProxy is set.
|
||||
event ZrxProxySet(address zrxProxyAddress);
|
||||
|
||||
/// @dev Sets the address of the StakingProxy contract.
|
||||
/// Note that only the contract staker can call this function.
|
||||
/// @param _stakingProxyAddress Address of Staking proxy contract.
|
||||
function setStakingProxy(address _stakingProxyAddress) external;
|
||||
|
||||
/// @dev Vault enters into Catastrophic Failure Mode.
|
||||
/// *** WARNING - ONCE IN CATOSTROPHIC FAILURE MODE, YOU CAN NEVER GO BACK! ***
|
||||
/// Note that only the contract staker can call this function.
|
||||
function enterCatastrophicFailure() external;
|
||||
|
||||
/// @dev Sets the Zrx proxy.
|
||||
/// Note that only the contract staker can call this.
|
||||
/// Note that this can only be called when *not* in Catastrophic Failure mode.
|
||||
/// @param zrxProxyAddress Address of the 0x Zrx Proxy.
|
||||
function setZrxProxy(address zrxProxyAddress) external;
|
||||
|
||||
/// @dev Deposit an `amount` of Zrx Tokens from `staker` into the vault.
|
||||
/// Note that only the Staking contract can call this.
|
||||
/// Note that this can only be called when *not* in Catastrophic Failure mode.
|
||||
/// @param staker of Zrx Tokens.
|
||||
/// @param amount of Zrx Tokens to deposit.
|
||||
function depositFrom(address staker, uint256 amount) external;
|
||||
|
||||
/// @dev Withdraw an `amount` of Zrx Tokens to `staker` from the vault.
|
||||
/// Note that only the Staking contract can call this.
|
||||
/// Note that this can only be called when *not* in Catastrophic Failure mode.
|
||||
/// @param staker of Zrx Tokens.
|
||||
/// @param amount of Zrx Tokens to withdraw.
|
||||
function withdrawFrom(address staker, uint256 amount) external;
|
||||
|
||||
/// @dev Withdraw ALL Zrx Tokens to `staker` from the vault.
|
||||
/// Note that this can only be called when *in* Catastrophic Failure mode.
|
||||
/// @param staker of Zrx Tokens.
|
||||
function withdrawAllFrom(address staker) external returns (uint256);
|
||||
|
||||
/// @dev Returns the balance in Zrx Tokens of the `staker`
|
||||
/// @return Balance in Zrx.
|
||||
function balanceOf(address staker) external view returns (uint256);
|
||||
|
||||
/// @dev Returns the entire balance of Zrx tokens in the vault.
|
||||
function balanceOfZrxVault() external view returns (uint256);
|
||||
}
|
@@ -1,31 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681969282,
|
||||
"version": "5.4.52",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681697326,
|
||||
"version": "5.4.51",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "5.4.50",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "5.4.49",
|
||||
|
@@ -5,18 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.52 - _April 20, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.51 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.50 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.49 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.52",
|
||||
"version": "5.4.49",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -39,19 +39,19 @@
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.36",
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/contract-addresses": "^8.5.0",
|
||||
"@0x/dev-utils": "^5.0.2",
|
||||
"@0x/contract-addresses": "^8.2.0",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-profiler": "^4.1.36",
|
||||
"@0x/sol-trace": "^3.0.46",
|
||||
"@0x/subproviders": "^8.0.1",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/js-combinatorics": "^0.5.29",
|
||||
"@types/lodash": "4.14.104",
|
||||
|
@@ -1,9 +1,12 @@
|
||||
import { devConstants, env, EnvVars, Web3Config, web3Factory } from '@0x/dev-utils';
|
||||
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { prependSubprovider, Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { logUtils } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from './constants';
|
||||
import { profiler } from './profiler';
|
||||
import { revertTrace } from './revert_trace';
|
||||
|
||||
export const txDefaults = {
|
||||
from: devConstants.TESTRPC_FIRST_ADDRESS,
|
||||
@@ -28,9 +31,26 @@ export const providerConfigs: Web3Config = {
|
||||
export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
|
||||
provider.stop();
|
||||
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
|
||||
const enabledSubproviderCount = _.filter([isCoverageEnabled], _.identity.bind(_)).length;
|
||||
const isProfilerEnabled = env.parseBoolean(EnvVars.SolidityProfiler);
|
||||
const isRevertTraceEnabled = env.parseBoolean(EnvVars.SolidityRevertTrace);
|
||||
const enabledSubproviderCount = _.filter(
|
||||
[isCoverageEnabled, isProfilerEnabled, isRevertTraceEnabled],
|
||||
_.identity.bind(_),
|
||||
).length;
|
||||
if (enabledSubproviderCount > 1) {
|
||||
throw new Error(`Only one of profiler or revert trace subproviders can be enabled at a time`);
|
||||
}
|
||||
if (isProfilerEnabled) {
|
||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||
logUtils.log(
|
||||
"By default profilerSubprovider is stopped so that you don't get noise from setup code. Don't forget to start it before the code you want to profile and stop it afterwards",
|
||||
);
|
||||
profilerSubprovider.stop();
|
||||
prependSubprovider(provider, profilerSubprovider);
|
||||
}
|
||||
if (isRevertTraceEnabled) {
|
||||
const revertTraceSubprovider = revertTrace.getRevertTraceSubproviderSingleton();
|
||||
prependSubprovider(provider, revertTraceSubprovider);
|
||||
}
|
||||
|
||||
export const web3Wrapper = new Web3Wrapper(provider);
|
||||
|
@@ -1,31 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681969282,
|
||||
"version": "1.4.45",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681697326,
|
||||
"version": "1.4.44",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.43",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "1.4.42",
|
||||
|
@@ -5,18 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.4.45 - _April 20, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.44 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.43 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.42 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.4.45",
|
||||
"version": "1.4.42",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -45,13 +45,13 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.5",
|
||||
"@0x/contract-addresses": "^8.5.0",
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contract-addresses": "^8.2.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "3.3.57",
|
||||
"@0x/contracts-gen": "^2.0.50",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.52",
|
||||
"@0x/contracts-test-utils": "^5.4.49",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@types/isomorphic-fetch": "^0.0.35",
|
||||
@@ -73,12 +73,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/protocol-utils": "^11.21.0",
|
||||
"@0x/subproviders": "^8.0.1",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/protocol-utils": "^11.18.1",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"ethereum-types": "^3.7.1",
|
||||
"ethereumjs-util": "^7.0.10"
|
||||
},
|
||||
|
@@ -1,40 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681969282,
|
||||
"version": "4.8.44",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.8.43",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"timestamp": 1681697326,
|
||||
"version": "4.8.42",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.8.41",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "4.8.40",
|
||||
|
@@ -5,22 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.8.44 - _April 20, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.43 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.42 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.41 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.40 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.8.44",
|
||||
"version": "4.8.40",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -15,6 +15,7 @@
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
@@ -43,14 +44,14 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.5",
|
||||
"@0x/contracts-gen": "^2.0.50",
|
||||
"@0x/contracts-test-utils": "^5.4.52",
|
||||
"@0x/dev-utils": "^5.0.2",
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-test-utils": "^5.4.49",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
|
@@ -1,40 +1,4 @@
|
||||
[
|
||||
{
|
||||
"version": "0.42.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Trader Joe V2 support on Avalanche"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681969282
|
||||
},
|
||||
{
|
||||
"version": "0.41.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support on Ethereum"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"version": "0.40.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support on Ethereum"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681697326
|
||||
},
|
||||
{
|
||||
"version": "0.39.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Uprgade dependencies"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"version": "0.39.1",
|
||||
"changes": [
|
||||
|
@@ -5,22 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.42.0 - _April 20, 2023_
|
||||
|
||||
* Add Trader Joe V2 support on Avalanche
|
||||
|
||||
## v0.41.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support on Ethereum
|
||||
|
||||
## v0.40.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support on Ethereum
|
||||
|
||||
## v0.39.2 - _April 10, 2023_
|
||||
|
||||
* Uprgade dependencies
|
||||
|
||||
## v0.39.1 - _March 10, 2023_
|
||||
|
||||
* Add KyberElastic mixin for Optimism and BSC
|
||||
|
@@ -31,7 +31,6 @@
|
||||
"./contracts/src/features/FundRecoveryFeature.sol",
|
||||
"./contracts/src/features/LiquidityProviderFeature.sol",
|
||||
"./contracts/src/features/MetaTransactionsFeature.sol",
|
||||
"./contracts/src/features/MetaTransactionsFeatureV2.sol",
|
||||
"./contracts/src/features/NativeOrdersFeature.sol",
|
||||
"./contracts/src/features/OtcOrdersFeature.sol",
|
||||
"./contracts/src/features/OwnableFeature.sol",
|
||||
@@ -49,7 +48,6 @@
|
||||
"./contracts/src/features/interfaces/IFundRecoveryFeature.sol",
|
||||
"./contracts/src/features/interfaces/ILiquidityProviderFeature.sol",
|
||||
"./contracts/src/features/interfaces/IMetaTransactionsFeature.sol",
|
||||
"./contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol",
|
||||
"./contracts/src/features/interfaces/IMultiplexFeature.sol",
|
||||
"./contracts/src/features/interfaces/INativeOrdersEvents.sol",
|
||||
"./contracts/src/features/interfaces/INativeOrdersFeature.sol",
|
||||
|
Submodule contracts/zero-ex/contracts/deps/forge-std updated: fc560fa34f...a2edd39db9
@@ -20,7 +20,6 @@ import "./features/interfaces/ISimpleFunctionRegistryFeature.sol";
|
||||
import "./features/interfaces/ITokenSpenderFeature.sol";
|
||||
import "./features/interfaces/ITransformERC20Feature.sol";
|
||||
import "./features/interfaces/IMetaTransactionsFeature.sol";
|
||||
import "./features/interfaces/IMetaTransactionsFeatureV2.sol";
|
||||
import "./features/interfaces/IUniswapFeature.sol";
|
||||
import "./features/interfaces/IUniswapV3Feature.sol";
|
||||
import "./features/interfaces/IPancakeSwapFeature.sol";
|
||||
@@ -40,7 +39,6 @@ interface IZeroEx is
|
||||
ISimpleFunctionRegistryFeature,
|
||||
ITransformERC20Feature,
|
||||
IMetaTransactionsFeature,
|
||||
IMetaTransactionsFeatureV2,
|
||||
IUniswapFeature,
|
||||
IUniswapV3Feature,
|
||||
IPancakeSwapFeature,
|
||||
|
@@ -1,616 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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/src/IEtherToken.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../errors/LibMetaTransactionsRichErrors.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "../fixins/FixinReentrancyGuard.sol";
|
||||
import "../fixins/FixinTokenSpender.sol";
|
||||
import "../fixins/FixinEIP712.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../storage/LibMetaTransactionsV2Storage.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/IMetaTransactionsFeatureV2.sol";
|
||||
import "./interfaces/IMultiplexFeature.sol";
|
||||
import "./interfaces/INativeOrdersFeature.sol";
|
||||
import "./interfaces/ITransformERC20Feature.sol";
|
||||
import "./libs/LibSignature.sol";
|
||||
|
||||
/// @dev MetaTransactions feature.
|
||||
contract MetaTransactionsFeatureV2 is
|
||||
IFeature,
|
||||
IMetaTransactionsFeatureV2,
|
||||
FixinCommon,
|
||||
FixinReentrancyGuard,
|
||||
FixinEIP712,
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibBytesV06 for bytes;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
/// @dev Describes the state of a meta transaction.
|
||||
struct ExecuteState {
|
||||
// Sender of the meta-transaction.
|
||||
address sender;
|
||||
// Hash of the meta-transaction data.
|
||||
bytes32 hash;
|
||||
// The meta-transaction data.
|
||||
MetaTransactionDataV2 mtx;
|
||||
// The meta-transaction signature (by `mtx.signer`).
|
||||
LibSignature.Signature signature;
|
||||
// The selector of the function being called.
|
||||
bytes4 selector;
|
||||
// The ETH balance of this contract before performing the call.
|
||||
uint256 selfBalance;
|
||||
// The block number at which the meta-transaction was executed.
|
||||
uint256 executedBlockNumber;
|
||||
}
|
||||
|
||||
/// @dev Arguments for a `TransformERC20.transformERC20()` call.
|
||||
struct ExternalTransformERC20Args {
|
||||
IERC20Token inputToken;
|
||||
IERC20Token outputToken;
|
||||
uint256 inputTokenAmount;
|
||||
uint256 minOutputTokenAmount;
|
||||
ITransformERC20Feature.Transformation[] transformations;
|
||||
}
|
||||
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MetaTransactionsV2";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
|
||||
bytes32 public immutable MTX_EIP712_TYPEHASH =
|
||||
keccak256(
|
||||
"MetaTransactionDataV2("
|
||||
"address signer,"
|
||||
"address sender,"
|
||||
"uint256 expirationTimeSeconds,"
|
||||
"uint256 salt,"
|
||||
"bytes callData,"
|
||||
"address feeToken,"
|
||||
"MetaTransactionFeeData[] fees"
|
||||
")"
|
||||
"MetaTransactionFeeData("
|
||||
"address recipient,"
|
||||
"uint256 amount"
|
||||
")"
|
||||
);
|
||||
bytes32 public immutable MTX_FEE_TYPEHASH =
|
||||
keccak256(
|
||||
"MetaTransactionFeeData("
|
||||
"address recipient,"
|
||||
"uint256 amount"
|
||||
")"
|
||||
);
|
||||
|
||||
/// @dev The WETH token contract.
|
||||
IEtherToken private immutable WETH;
|
||||
|
||||
/// @dev Ensures that the ETH balance of `this` does not go below the
|
||||
/// initial ETH balance before the call (excluding ETH attached to the call).
|
||||
modifier doesNotReduceEthBalance() {
|
||||
uint256 initialBalance = address(this).balance;
|
||||
_;
|
||||
require(initialBalance <= address(this).balance, "MetaTransactionsFeatureV2/ETH_LEAK");
|
||||
}
|
||||
|
||||
constructor(address zeroExAddress, IEtherToken weth) public FixinCommon() FixinEIP712(zeroExAddress) {
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||
/// @return success `LibMigrate.SUCCESS` on success.
|
||||
function migrate() external returns (bytes4 success) {
|
||||
_registerFeatureFunction(this.executeMetaTransactionV2.selector);
|
||||
_registerFeatureFunction(this.batchExecuteMetaTransactionsV2.selector);
|
||||
_registerFeatureFunction(this.getMetaTransactionV2ExecutedBlock.selector);
|
||||
_registerFeatureFunction(this.getMetaTransactionV2HashExecutedBlock.selector);
|
||||
_registerFeatureFunction(this.getMetaTransactionV2Hash.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Execute a single meta-transaction.
|
||||
/// @param mtx The meta-transaction.
|
||||
/// @param signature The signature by `mtx.signer`.
|
||||
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||
function executeMetaTransactionV2(
|
||||
MetaTransactionDataV2 memory mtx,
|
||||
LibSignature.Signature memory signature
|
||||
) public override nonReentrant(REENTRANCY_MTX) doesNotReduceEthBalance returns (bytes memory returnResult) {
|
||||
ExecuteState memory state;
|
||||
state.sender = msg.sender;
|
||||
state.mtx = mtx;
|
||||
state.hash = getMetaTransactionV2Hash(mtx);
|
||||
state.signature = signature;
|
||||
|
||||
returnResult = _executeMetaTransactionPrivate(state);
|
||||
}
|
||||
|
||||
/// @dev Execute multiple meta-transactions.
|
||||
/// @param mtxs The meta-transactions.
|
||||
/// @param signatures The signature by each respective `mtx.signer`.
|
||||
/// @return returnResults The ABI-encoded results of the underlying calls.
|
||||
function batchExecuteMetaTransactionsV2(
|
||||
MetaTransactionDataV2[] memory mtxs,
|
||||
LibSignature.Signature[] memory signatures
|
||||
) public override nonReentrant(REENTRANCY_MTX) doesNotReduceEthBalance returns (bytes[] memory returnResults) {
|
||||
if (mtxs.length != signatures.length) {
|
||||
LibMetaTransactionsRichErrors
|
||||
.InvalidMetaTransactionsArrayLengthsError(mtxs.length, signatures.length)
|
||||
.rrevert();
|
||||
}
|
||||
returnResults = new bytes[](mtxs.length);
|
||||
for (uint256 i = 0; i < mtxs.length; ++i) {
|
||||
ExecuteState memory state;
|
||||
state.sender = msg.sender;
|
||||
state.mtx = mtxs[i];
|
||||
state.hash = getMetaTransactionV2Hash(mtxs[i]);
|
||||
state.signature = signatures[i];
|
||||
|
||||
returnResults[i] = _executeMetaTransactionPrivate(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Get the block at which a meta-transaction has been executed.
|
||||
/// @param mtx The meta-transaction.
|
||||
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||
function getMetaTransactionV2ExecutedBlock(
|
||||
MetaTransactionDataV2 memory mtx
|
||||
) public view override returns (uint256 blockNumber) {
|
||||
return getMetaTransactionV2HashExecutedBlock(getMetaTransactionV2Hash(mtx));
|
||||
}
|
||||
|
||||
/// @dev Get the block at which a meta-transaction hash has been executed.
|
||||
/// @param mtxHash The meta-transaction hash.
|
||||
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||
function getMetaTransactionV2HashExecutedBlock(bytes32 mtxHash) public view override returns (uint256 blockNumber) {
|
||||
return LibMetaTransactionsV2Storage.getStorage().mtxHashToExecutedBlockNumber[mtxHash];
|
||||
}
|
||||
|
||||
/// @dev Get the EIP712 hash of a meta-transaction.
|
||||
/// @param mtx The meta-transaction.
|
||||
/// @return mtxHash The EIP712 hash of `mtx`.
|
||||
function getMetaTransactionV2Hash(MetaTransactionDataV2 memory mtx) public view override returns (bytes32 mtxHash) {
|
||||
bytes32[] memory feeHashes = new bytes32[](mtx.fees.length);
|
||||
for (uint256 i = 0; i < mtx.fees.length; ++i) {
|
||||
feeHashes[i] = keccak256(abi.encode(MTX_FEE_TYPEHASH, mtx.fees[i]));
|
||||
}
|
||||
|
||||
return
|
||||
_getEIP712Hash(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
MTX_EIP712_TYPEHASH,
|
||||
mtx.signer,
|
||||
mtx.sender,
|
||||
mtx.expirationTimeSeconds,
|
||||
mtx.salt,
|
||||
keccak256(mtx.callData),
|
||||
mtx.feeToken,
|
||||
keccak256(abi.encodePacked(feeHashes))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Execute a meta-transaction by `sender`. Low-level, hidden variant.
|
||||
/// @param state The `ExecuteState` for this metatransaction, with `sender`,
|
||||
/// `hash`, `mtx`, and `signature` fields filled.
|
||||
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||
function _executeMetaTransactionPrivate(ExecuteState memory state) private returns (bytes memory returnResult) {
|
||||
_validateMetaTransaction(state);
|
||||
|
||||
// Mark the transaction executed by storing the block at which it was executed.
|
||||
// Currently the block number just indicates that the mtx was executed and
|
||||
// serves no other purpose from within this contract.
|
||||
LibMetaTransactionsV2Storage.getStorage().mtxHashToExecutedBlockNumber[state.hash] = block.number;
|
||||
|
||||
// Pay the fees to the fee recipients.
|
||||
for (uint256 i = 0; i < state.mtx.fees.length; ++i) {
|
||||
_transferERC20TokensFrom(
|
||||
state.mtx.feeToken,
|
||||
state.mtx.signer,
|
||||
state.mtx.fees[i].recipient,
|
||||
state.mtx.fees[i].amount
|
||||
);
|
||||
}
|
||||
|
||||
// Execute the call based on the selector.
|
||||
state.selector = state.mtx.callData.readBytes4(0);
|
||||
if (state.selector == ITransformERC20Feature.transformERC20.selector) {
|
||||
returnResult = _executeTransformERC20Call(state);
|
||||
} else if (state.selector == INativeOrdersFeature.fillLimitOrder.selector) {
|
||||
returnResult = _executeFillLimitOrderCall(state);
|
||||
} else if (state.selector == INativeOrdersFeature.fillRfqOrder.selector) {
|
||||
returnResult = _executeFillRfqOrderCall(state);
|
||||
} else if (state.selector == IMultiplexFeature.multiplexBatchSellTokenForToken.selector) {
|
||||
returnResult = _executeMultiplexBatchSellTokenForTokenCall(state);
|
||||
} else if (state.selector == IMultiplexFeature.multiplexBatchSellTokenForEth.selector) {
|
||||
returnResult = _executeMultiplexBatchSellTokenForEthCall(state);
|
||||
} else if (state.selector == IMultiplexFeature.multiplexMultiHopSellTokenForToken.selector) {
|
||||
returnResult = _executeMultiplexMultiHopSellTokenForTokenCall(state);
|
||||
} else if (state.selector == IMultiplexFeature.multiplexMultiHopSellTokenForEth.selector) {
|
||||
returnResult = _executeMultiplexMultiHopSellTokenForEthCall(state);
|
||||
} else {
|
||||
LibMetaTransactionsRichErrors.MetaTransactionUnsupportedFunctionError(state.hash, state.selector).rrevert();
|
||||
}
|
||||
emit MetaTransactionExecuted(state.hash, state.selector, state.mtx.signer, state.mtx.sender);
|
||||
}
|
||||
|
||||
/// @dev Validate that a meta-transaction is executable.
|
||||
function _validateMetaTransaction(ExecuteState memory state) private view {
|
||||
// Must be from the required sender, if set.
|
||||
if (state.mtx.sender != address(0) && state.mtx.sender != state.sender) {
|
||||
LibMetaTransactionsRichErrors
|
||||
.MetaTransactionWrongSenderError(state.hash, state.sender, state.mtx.sender)
|
||||
.rrevert();
|
||||
}
|
||||
// Must not be expired.
|
||||
if (state.mtx.expirationTimeSeconds <= block.timestamp) {
|
||||
LibMetaTransactionsRichErrors
|
||||
.MetaTransactionExpiredError(state.hash, block.timestamp, state.mtx.expirationTimeSeconds)
|
||||
.rrevert();
|
||||
}
|
||||
|
||||
if (LibSignature.getSignerOfHash(state.hash, state.signature) != state.mtx.signer) {
|
||||
LibSignatureRichErrors
|
||||
.SignatureValidationError(
|
||||
LibSignatureRichErrors.SignatureValidationErrorCodes.WRONG_SIGNER,
|
||||
state.hash,
|
||||
state.mtx.signer,
|
||||
// TODO: Remove this field from SignatureValidationError
|
||||
// when rich reverts are part of the protocol repo.
|
||||
""
|
||||
)
|
||||
.rrevert();
|
||||
}
|
||||
// Transaction must not have been already executed.
|
||||
state.executedBlockNumber = LibMetaTransactionsV2Storage.getStorage().mtxHashToExecutedBlockNumber[state.hash];
|
||||
if (state.executedBlockNumber != 0) {
|
||||
LibMetaTransactionsRichErrors
|
||||
.MetaTransactionAlreadyExecutedError(state.hash, state.executedBlockNumber)
|
||||
.rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Execute a `ITransformERC20Feature.transformERC20()` meta-transaction call
|
||||
/// by decoding the call args and translating the call to the internal
|
||||
/// `ITransformERC20Feature._transformERC20()` variant, where we can override
|
||||
/// the taker address.
|
||||
function _executeTransformERC20Call(ExecuteState memory state) private returns (bytes memory returnResult) {
|
||||
// HACK(dorothy-zbornak): `abi.decode()` with the individual args
|
||||
// will cause a stack overflow. But we can prefix the call data with an
|
||||
// offset to transform it into the encoding for the equivalent single struct arg,
|
||||
// since decoding a single struct arg consumes far less stack space than
|
||||
// decoding multiple struct args.
|
||||
|
||||
// Where the encoding for multiple args (with the selector ommitted)
|
||||
// would typically look like:
|
||||
// | argument | offset |
|
||||
// |--------------------------|---------|
|
||||
// | inputToken | 0 |
|
||||
// | outputToken | 32 |
|
||||
// | inputTokenAmount | 64 |
|
||||
// | minOutputTokenAmount | 96 |
|
||||
// | transformations (offset) | 128 | = 32
|
||||
// | transformations (data) | 160 |
|
||||
|
||||
// We will ABI-decode a single struct arg copy with the layout:
|
||||
// | argument | offset |
|
||||
// |--------------------------|---------|
|
||||
// | (arg 1 offset) | 0 | = 32
|
||||
// | inputToken | 32 |
|
||||
// | outputToken | 64 |
|
||||
// | inputTokenAmount | 96 |
|
||||
// | minOutputTokenAmount | 128 |
|
||||
// | transformations (offset) | 160 | = 32
|
||||
// | transformations (data) | 192 |
|
||||
|
||||
ExternalTransformERC20Args memory args;
|
||||
{
|
||||
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
||||
// Copy the args data from the original, after the new struct offset prefix.
|
||||
bytes memory fromCallData = state.mtx.callData;
|
||||
assert(fromCallData.length >= 160);
|
||||
uint256 fromMem;
|
||||
uint256 toMem;
|
||||
assembly {
|
||||
// Prefix the calldata with a struct offset,
|
||||
// which points to just one word over.
|
||||
mstore(add(encodedStructArgs, 32), 32)
|
||||
// Copy everything after the selector.
|
||||
fromMem := add(fromCallData, 36)
|
||||
// Start copying after the struct offset.
|
||||
toMem := add(encodedStructArgs, 64)
|
||||
}
|
||||
LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
|
||||
// Decode call args for `ITransformERC20Feature.transformERC20()` as a struct.
|
||||
args = abi.decode(encodedStructArgs, (ExternalTransformERC20Args));
|
||||
}
|
||||
// Call `ITransformERC20Feature._transformERC20()` (internal variant).
|
||||
return
|
||||
_callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
ITransformERC20Feature._transformERC20.selector,
|
||||
ITransformERC20Feature.TransformERC20Args({
|
||||
taker: state.mtx.signer, // taker is mtx signer
|
||||
inputToken: args.inputToken,
|
||||
outputToken: args.outputToken,
|
||||
inputTokenAmount: args.inputTokenAmount,
|
||||
minOutputTokenAmount: args.minOutputTokenAmount,
|
||||
transformations: args.transformations,
|
||||
useSelfBalance: false,
|
||||
recipient: state.mtx.signer
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Extract arguments from call data by copying everything after the
|
||||
/// 4-byte selector into a new byte array.
|
||||
/// @param callData The call data from which arguments are to be extracted.
|
||||
/// @return args The extracted arguments as a byte array.
|
||||
function _extractArgumentsFromCallData(bytes memory callData) private pure returns (bytes memory args) {
|
||||
args = new bytes(callData.length - 4);
|
||||
uint256 fromMem;
|
||||
uint256 toMem;
|
||||
|
||||
assembly {
|
||||
fromMem := add(callData, 36) // skip length and 4-byte selector
|
||||
toMem := add(args, 32) // write after length prefix
|
||||
}
|
||||
|
||||
LibBytesV06.memCopy(toMem, fromMem, args.length);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// @dev Execute a `INativeOrdersFeature.fillLimitOrder()` meta-transaction call
|
||||
/// by decoding the call args and translating the call to the internal
|
||||
/// `INativeOrdersFeature._fillLimitOrder()` variant, where we can override
|
||||
/// the taker address.
|
||||
function _executeFillLimitOrderCall(ExecuteState memory state) private returns (bytes memory returnResult) {
|
||||
LibNativeOrder.LimitOrder memory order;
|
||||
LibSignature.Signature memory signature;
|
||||
uint128 takerTokenFillAmount;
|
||||
|
||||
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
|
||||
(order, signature, takerTokenFillAmount) = abi.decode(
|
||||
args,
|
||||
(LibNativeOrder.LimitOrder, LibSignature.Signature, uint128)
|
||||
);
|
||||
|
||||
return
|
||||
_callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
INativeOrdersFeature._fillLimitOrder.selector,
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
state.mtx.signer, // taker is mtx signer
|
||||
msg.sender
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Execute a `INativeOrdersFeature.fillRfqOrder()` meta-transaction call
|
||||
/// by decoding the call args and translating the call to the internal
|
||||
/// `INativeOrdersFeature._fillRfqOrder()` variant, where we can override
|
||||
/// the taker address.
|
||||
function _executeFillRfqOrderCall(ExecuteState memory state) private returns (bytes memory returnResult) {
|
||||
LibNativeOrder.RfqOrder memory order;
|
||||
LibSignature.Signature memory signature;
|
||||
uint128 takerTokenFillAmount;
|
||||
|
||||
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
|
||||
(order, signature, takerTokenFillAmount) = abi.decode(
|
||||
args,
|
||||
(LibNativeOrder.RfqOrder, LibSignature.Signature, uint128)
|
||||
);
|
||||
|
||||
return
|
||||
_callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
INativeOrdersFeature._fillRfqOrder.selector,
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
state.mtx.signer, // taker is mtx signer
|
||||
false,
|
||||
state.mtx.signer
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Execute a `IMultiplexFeature.multiplexBatchSellTokenForToken()` meta-transaction
|
||||
/// call by decoding the call args and translating the call to the internal
|
||||
/// `IMultiplexFeature._multiplexBatchSell()` variant, where we can override the
|
||||
/// payer address.
|
||||
function _executeMultiplexBatchSellTokenForTokenCall(
|
||||
ExecuteState memory state
|
||||
) private returns (bytes memory returnResult) {
|
||||
IERC20Token inputToken;
|
||||
IERC20Token outputToken;
|
||||
IMultiplexFeature.BatchSellSubcall[] memory calls;
|
||||
uint256 sellAmount;
|
||||
uint256 minBuyAmount;
|
||||
|
||||
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
|
||||
(inputToken, outputToken, calls, sellAmount, minBuyAmount) = abi.decode(
|
||||
args,
|
||||
(IERC20Token, IERC20Token, IMultiplexFeature.BatchSellSubcall[], uint256, uint256)
|
||||
);
|
||||
|
||||
return
|
||||
_callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
IMultiplexFeature._multiplexBatchSell.selector,
|
||||
IMultiplexFeature.BatchSellParams({
|
||||
inputToken: inputToken,
|
||||
outputToken: outputToken,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: state.mtx.signer,
|
||||
payer: state.mtx.signer
|
||||
}),
|
||||
minBuyAmount
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Execute a `IMultiplexFeature.multiplexBatchSellTokenForEth()` meta-transaction
|
||||
/// call by decoding the call args and translating the call to the internal
|
||||
/// `IMultiplexFeature._multiplexBatchSellTokenForEth()` variant, where we can override the
|
||||
/// payer address.
|
||||
function _executeMultiplexBatchSellTokenForEthCall(
|
||||
ExecuteState memory state
|
||||
) private returns (bytes memory returnResult) {
|
||||
IERC20Token inputToken;
|
||||
IMultiplexFeature.BatchSellSubcall[] memory calls;
|
||||
uint256 sellAmount;
|
||||
uint256 minBuyAmount;
|
||||
|
||||
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
|
||||
(inputToken, calls, sellAmount, minBuyAmount) = abi.decode(
|
||||
args,
|
||||
(IERC20Token, IMultiplexFeature.BatchSellSubcall[], uint256, uint256)
|
||||
);
|
||||
|
||||
returnResult = _callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
IMultiplexFeature._multiplexBatchSell.selector,
|
||||
IMultiplexFeature.BatchSellParams({
|
||||
inputToken: inputToken,
|
||||
outputToken: IERC20Token(WETH),
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: address(this),
|
||||
payer: state.mtx.signer
|
||||
}),
|
||||
minBuyAmount
|
||||
)
|
||||
);
|
||||
|
||||
// Unwrap and transfer WETH
|
||||
uint256 boughtAmount = abi.decode(returnResult, (uint256));
|
||||
WETH.withdraw(boughtAmount);
|
||||
_transferEth(state.mtx.signer, boughtAmount);
|
||||
}
|
||||
|
||||
/// @dev Execute a `IMultiplexFeature.multiplexMultiHopSellTokenForToken()` meta-transaction
|
||||
/// call by decoding the call args and translating the call to the internal
|
||||
/// `IMultiplexFeature._multiplexMultiHopSell()` variant, where we can override the
|
||||
/// payer address.
|
||||
function _executeMultiplexMultiHopSellTokenForTokenCall(
|
||||
ExecuteState memory state
|
||||
) private returns (bytes memory returnResult) {
|
||||
address[] memory tokens;
|
||||
IMultiplexFeature.MultiHopSellSubcall[] memory calls;
|
||||
uint256 sellAmount;
|
||||
uint256 minBuyAmount;
|
||||
|
||||
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
|
||||
(tokens, calls, sellAmount, minBuyAmount) = abi.decode(
|
||||
args,
|
||||
(address[], IMultiplexFeature.MultiHopSellSubcall[], uint256, uint256)
|
||||
);
|
||||
|
||||
return
|
||||
_callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
IMultiplexFeature._multiplexMultiHopSell.selector,
|
||||
IMultiplexFeature.MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: state.mtx.signer,
|
||||
payer: state.mtx.signer
|
||||
}),
|
||||
minBuyAmount
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Execute a `IMultiplexFeature.multiplexMultiHopSellTokenForEth()` meta-transaction
|
||||
/// call by decoding the call args and translating the call to the internal
|
||||
/// `IMultiplexFeature._multiplexMultiHopSellTokenForEth()` variant, where we can override the
|
||||
/// payer address.
|
||||
function _executeMultiplexMultiHopSellTokenForEthCall(
|
||||
ExecuteState memory state
|
||||
) private returns (bytes memory returnResult) {
|
||||
address[] memory tokens;
|
||||
IMultiplexFeature.MultiHopSellSubcall[] memory calls;
|
||||
uint256 sellAmount;
|
||||
uint256 minBuyAmount;
|
||||
|
||||
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
|
||||
(tokens, calls, sellAmount, minBuyAmount) = abi.decode(
|
||||
args,
|
||||
(address[], IMultiplexFeature.MultiHopSellSubcall[], uint256, uint256)
|
||||
);
|
||||
|
||||
require(
|
||||
tokens[tokens.length - 1] == address(WETH),
|
||||
"MetaTransactionsFeature::multiplexMultiHopSellTokenForEth/NOT_WETH"
|
||||
);
|
||||
|
||||
returnResult = _callSelf(
|
||||
state.hash,
|
||||
abi.encodeWithSelector(
|
||||
IMultiplexFeature._multiplexMultiHopSell.selector,
|
||||
IMultiplexFeature.MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: address(this),
|
||||
payer: state.mtx.signer
|
||||
}),
|
||||
minBuyAmount
|
||||
)
|
||||
);
|
||||
|
||||
// Unwrap and transfer WETH
|
||||
uint256 boughtAmount = abi.decode(returnResult, (uint256));
|
||||
WETH.withdraw(boughtAmount);
|
||||
_transferEth(state.mtx.signer, boughtAmount);
|
||||
}
|
||||
|
||||
/// @dev Make an arbitrary internal, meta-transaction call.
|
||||
/// Warning: Do not let unadulterated `callData` into this function.
|
||||
function _callSelf(bytes32 hash, bytes memory callData) private returns (bytes memory returnResult) {
|
||||
bool success;
|
||||
(success, returnResult) = address(this).call(callData);
|
||||
if (!success) {
|
||||
LibMetaTransactionsRichErrors.MetaTransactionCallFailedError(hash, callData, returnResult).rrevert();
|
||||
}
|
||||
}
|
||||
}
|
@@ -70,7 +70,6 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke
|
||||
_registerFeatureFunction(this.sellEthForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.sellTokenForEthToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.sellTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this._sellTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this._sellHeldTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.uniswapV3SwapCallback.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
@@ -140,23 +139,6 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke
|
||||
buyAmount = _swap(encodedPath, sellAmount, minBuyAmount, msg.sender, _normalizeRecipient(recipient));
|
||||
}
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3. Internal variant.
|
||||
/// @param encodedPath Uniswap-encoded path.
|
||||
/// @param sellAmount amount of the first token in the path to sell.
|
||||
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for payer.
|
||||
/// @param payer The address to pull the sold tokens from.
|
||||
/// @return buyAmount Amount of the last token in the path bought.
|
||||
function _sellTokenForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address recipient,
|
||||
address payer
|
||||
) public override onlySelf returns (uint256 buyAmount) {
|
||||
buyAmount = _swap(encodedPath, sellAmount, minBuyAmount, payer, _normalizeRecipient(recipient, payer));
|
||||
}
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3.
|
||||
/// Private variant, uses tokens held by `address(this)`.
|
||||
/// @param encodedPath Uniswap-encoded path.
|
||||
@@ -355,16 +337,8 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke
|
||||
}
|
||||
}
|
||||
|
||||
// Convert null address values to alternative address.
|
||||
function _normalizeRecipient(
|
||||
address recipient,
|
||||
address alternative
|
||||
) private pure returns (address payable normalizedRecipient) {
|
||||
return recipient == address(0) ? payable(alternative) : payable(recipient);
|
||||
}
|
||||
|
||||
// Convert null address values to msg.sender.
|
||||
function _normalizeRecipient(address recipient) private view returns (address payable normalizedRecipient) {
|
||||
return _normalizeRecipient(recipient, msg.sender);
|
||||
return recipient == address(0) ? msg.sender : payable(recipient);
|
||||
}
|
||||
}
|
||||
|
@@ -1,90 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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/src/IERC20Token.sol";
|
||||
import "../libs/LibSignature.sol";
|
||||
|
||||
/// @dev Meta-transactions feature.
|
||||
interface IMetaTransactionsFeatureV2 {
|
||||
/// @dev Describes an exchange proxy meta transaction.
|
||||
struct MetaTransactionFeeData {
|
||||
// ERC20 fee recipient
|
||||
address recipient;
|
||||
// ERC20 fee amount
|
||||
uint256 amount;
|
||||
}
|
||||
|
||||
struct MetaTransactionDataV2 {
|
||||
// Signer of meta-transaction. On whose behalf to execute the MTX.
|
||||
address payable signer;
|
||||
// Required sender, or NULL for anyone.
|
||||
address sender;
|
||||
// MTX is invalid after this time.
|
||||
uint256 expirationTimeSeconds;
|
||||
// Nonce to make this MTX unique.
|
||||
uint256 salt;
|
||||
// Encoded call data to a function on the exchange proxy.
|
||||
bytes callData;
|
||||
// ERC20 fee `signer` pays `sender`.
|
||||
IERC20Token feeToken;
|
||||
// ERC20 fees.
|
||||
MetaTransactionFeeData[] fees;
|
||||
}
|
||||
|
||||
/// @dev Emitted whenever a meta-transaction is executed via
|
||||
/// `executeMetaTransaction()` or `executeMetaTransactions()`.
|
||||
/// @param hash The EIP712 hash of the MetaTransactionDataV2 struct.
|
||||
/// @param selector The selector of the function being executed.
|
||||
/// @param signer Who to execute the meta-transaction on behalf of.
|
||||
/// @param sender Who executed the meta-transaction.
|
||||
event MetaTransactionExecuted(bytes32 hash, bytes4 indexed selector, address signer, address sender);
|
||||
|
||||
/// @dev Execute a single meta-transaction.
|
||||
/// @param mtx The meta-transaction.
|
||||
/// @param signature The signature by `mtx.signer`.
|
||||
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||
function executeMetaTransactionV2(
|
||||
MetaTransactionDataV2 calldata mtx,
|
||||
LibSignature.Signature calldata signature
|
||||
) external returns (bytes memory returnResult);
|
||||
|
||||
/// @dev Execute multiple meta-transactions.
|
||||
/// @param mtxs The meta-transactions.
|
||||
/// @param signatures The signature by each respective `mtx.signer`.
|
||||
/// @return returnResults The ABI-encoded results of the underlying calls.
|
||||
function batchExecuteMetaTransactionsV2(
|
||||
MetaTransactionDataV2[] calldata mtxs,
|
||||
LibSignature.Signature[] calldata signatures
|
||||
) external returns (bytes[] memory returnResults);
|
||||
|
||||
/// @dev Get the block at which a meta-transaction has been executed.
|
||||
/// @param mtx The meta-transaction.
|
||||
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||
function getMetaTransactionV2ExecutedBlock(
|
||||
MetaTransactionDataV2 calldata mtx
|
||||
) external view returns (uint256 blockNumber);
|
||||
|
||||
/// @dev Get the block at which a meta-transaction hash has been executed.
|
||||
/// @param mtxHash The EIP712 hash of the MetaTransactionDataV2 struct.
|
||||
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||
function getMetaTransactionV2HashExecutedBlock(bytes32 mtxHash) external view returns (uint256 blockNumber);
|
||||
|
||||
/// @dev Get the EIP712 hash of a meta-transaction.
|
||||
/// @param mtx The meta-transaction.
|
||||
/// @return mtxHash The EIP712 hash of `mtx`.
|
||||
function getMetaTransactionV2Hash(MetaTransactionDataV2 calldata mtx) external view returns (bytes32 mtxHash);
|
||||
}
|
@@ -46,8 +46,6 @@ interface IMultiplexFeature {
|
||||
bool useSelfBalance;
|
||||
// The recipient of the bought output tokens.
|
||||
address recipient;
|
||||
// The sender of the input tokens.
|
||||
address payer;
|
||||
}
|
||||
|
||||
// Represents a constituent call of a batch sell.
|
||||
@@ -77,8 +75,6 @@ interface IMultiplexFeature {
|
||||
bool useSelfBalance;
|
||||
// The recipient of the bought output tokens.
|
||||
address recipient;
|
||||
// The sender of the input tokens.
|
||||
address payer;
|
||||
}
|
||||
|
||||
// Represents a constituent call of a multi-hop sell.
|
||||
@@ -157,17 +153,6 @@ interface IMultiplexFeature {
|
||||
uint256 minBuyAmount
|
||||
) external returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Executes a multiplex BatchSell using the given
|
||||
/// parameters. Internal only.
|
||||
/// @param params The parameters for the BatchSell.
|
||||
/// @param minBuyAmount The minimum amount of `params.outputToken`
|
||||
/// that must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `params.outputToken` bought.
|
||||
function _multiplexBatchSell(
|
||||
BatchSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
) external returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Sells attached ETH via the given sequence of tokens
|
||||
/// and calls. `tokens[0]` must be WETH.
|
||||
/// The last token in `tokens` is the output token that
|
||||
@@ -219,15 +204,4 @@ interface IMultiplexFeature {
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
) external returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Executes a multiplex MultiHopSell using the given
|
||||
/// parameters. Internal only.
|
||||
/// @param params The parameters for the MultiHopSell.
|
||||
/// @param minBuyAmount The minimum amount of the output token
|
||||
/// that must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of the output token bought.
|
||||
function _multiplexMultiHopSell(
|
||||
MultiHopSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
) external returns (uint256 boughtAmount);
|
||||
}
|
||||
|
@@ -54,21 +54,6 @@ interface IUniswapV3Feature {
|
||||
address recipient
|
||||
) external returns (uint256 buyAmount);
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3. Internal variant.
|
||||
/// @param encodedPath Uniswap-encoded path.
|
||||
/// @param sellAmount amount of the first token in the path to sell.
|
||||
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for payer.
|
||||
/// @param payer The address to pull the sold tokens from.
|
||||
/// @return buyAmount Amount of the last token in the path bought.
|
||||
function _sellTokenForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address recipient,
|
||||
address payer
|
||||
) external returns (uint256 buyAmount);
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3.
|
||||
/// Private variant, uses tokens held by `address(this)`.
|
||||
/// @param encodedPath Uniswap-encoded path.
|
||||
|
@@ -80,11 +80,9 @@ contract MultiplexFeature is
|
||||
_registerFeatureFunction(this.multiplexBatchSellEthForToken.selector);
|
||||
_registerFeatureFunction(this.multiplexBatchSellTokenForEth.selector);
|
||||
_registerFeatureFunction(this.multiplexBatchSellTokenForToken.selector);
|
||||
_registerFeatureFunction(this._multiplexBatchSell.selector);
|
||||
_registerFeatureFunction(this.multiplexMultiHopSellEthForToken.selector);
|
||||
_registerFeatureFunction(this.multiplexMultiHopSellTokenForEth.selector);
|
||||
_registerFeatureFunction(this.multiplexMultiHopSellTokenForToken.selector);
|
||||
_registerFeatureFunction(this._multiplexMultiHopSell.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -105,15 +103,14 @@ contract MultiplexFeature is
|
||||
// WETH is now held by this contract,
|
||||
// so `useSelfBalance` is true.
|
||||
return
|
||||
_multiplexBatchSellPrivate(
|
||||
_multiplexBatchSell(
|
||||
BatchSellParams({
|
||||
inputToken: WETH,
|
||||
outputToken: outputToken,
|
||||
sellAmount: msg.value,
|
||||
calls: calls,
|
||||
useSelfBalance: true,
|
||||
recipient: msg.sender,
|
||||
payer: msg.sender
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
@@ -136,15 +133,14 @@ contract MultiplexFeature is
|
||||
// The outputToken is implicitly WETH. The `recipient`
|
||||
// of the WETH is set to this contract, since we
|
||||
// must unwrap the WETH and transfer the resulting ETH.
|
||||
boughtAmount = _multiplexBatchSellPrivate(
|
||||
boughtAmount = _multiplexBatchSell(
|
||||
BatchSellParams({
|
||||
inputToken: inputToken,
|
||||
outputToken: WETH,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: address(this),
|
||||
payer: msg.sender
|
||||
recipient: address(this)
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
@@ -171,40 +167,26 @@ contract MultiplexFeature is
|
||||
uint256 minBuyAmount
|
||||
) public override returns (uint256 boughtAmount) {
|
||||
return
|
||||
_multiplexBatchSellPrivate(
|
||||
_multiplexBatchSell(
|
||||
BatchSellParams({
|
||||
inputToken: inputToken,
|
||||
outputToken: outputToken,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender,
|
||||
payer: msg.sender
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Executes a batch sell and checks that at least
|
||||
/// `minBuyAmount` of `outputToken` was bought. Internal variant.
|
||||
/// @param params Batch sell parameters.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken` that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function _multiplexBatchSell(
|
||||
BatchSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
) public override onlySelf returns (uint256 boughtAmount) {
|
||||
return _multiplexBatchSellPrivate(params, minBuyAmount);
|
||||
}
|
||||
|
||||
/// @dev Executes a batch sell and checks that at least
|
||||
/// `minBuyAmount` of `outputToken` was bought.
|
||||
/// @param params Batch sell parameters.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken` that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function _multiplexBatchSellPrivate(
|
||||
function _multiplexBatchSell(
|
||||
BatchSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
) private returns (uint256 boughtAmount) {
|
||||
@@ -244,14 +226,13 @@ contract MultiplexFeature is
|
||||
// WETH is now held by this contract,
|
||||
// so `useSelfBalance` is true.
|
||||
return
|
||||
_multiplexMultiHopSellPrivate(
|
||||
_multiplexMultiHopSell(
|
||||
MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: msg.value,
|
||||
calls: calls,
|
||||
useSelfBalance: true,
|
||||
recipient: msg.sender,
|
||||
payer: msg.sender
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
@@ -281,14 +262,13 @@ contract MultiplexFeature is
|
||||
);
|
||||
// The `recipient of the WETH is set to this contract, since
|
||||
// we must unwrap the WETH and transfer the resulting ETH.
|
||||
boughtAmount = _multiplexMultiHopSellPrivate(
|
||||
boughtAmount = _multiplexMultiHopSell(
|
||||
MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: address(this),
|
||||
payer: msg.sender
|
||||
recipient: address(this)
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
@@ -317,38 +297,25 @@ contract MultiplexFeature is
|
||||
uint256 minBuyAmount
|
||||
) public override returns (uint256 boughtAmount) {
|
||||
return
|
||||
_multiplexMultiHopSellPrivate(
|
||||
_multiplexMultiHopSell(
|
||||
MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender,
|
||||
payer: msg.sender
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Executes a multi-hop sell. Internal variant.
|
||||
/// @param params Multi-hop sell parameters.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function _multiplexMultiHopSell(
|
||||
MultiHopSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
) public override onlySelf returns (uint256 boughtAmount) {
|
||||
return _multiplexMultiHopSellPrivate(params, minBuyAmount);
|
||||
}
|
||||
|
||||
/// @dev Executes a multi-hop sell and checks that at least
|
||||
/// `minBuyAmount` of output tokens were bought.
|
||||
/// @param params Multi-hop sell parameters.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function _multiplexMultiHopSellPrivate(
|
||||
function _multiplexMultiHopSell(
|
||||
MultiHopSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
) private returns (uint256 boughtAmount) {
|
||||
@@ -420,14 +387,14 @@ contract MultiplexFeature is
|
||||
// amount of the multi-hop fill.
|
||||
state.outputTokenAmount = params.sellAmount;
|
||||
// The first call may expect the input tokens to be held by
|
||||
// `payer`, `address(this)`, or some other address.
|
||||
// `msg.sender`, `address(this)`, or some other address.
|
||||
// Compute the expected address and transfer the input tokens
|
||||
// there if necessary.
|
||||
state.from = _computeHopTarget(params, 0);
|
||||
// If the input tokens are currently held by `payer` but
|
||||
// If the input tokens are currently held by `msg.sender` but
|
||||
// the first hop expects them elsewhere, perform a `transferFrom`.
|
||||
if (!params.useSelfBalance && state.from != params.payer) {
|
||||
_transferERC20TokensFrom(IERC20Token(params.tokens[0]), params.payer, state.from, params.sellAmount);
|
||||
if (!params.useSelfBalance && state.from != msg.sender) {
|
||||
_transferERC20TokensFrom(IERC20Token(params.tokens[0]), msg.sender, state.from, params.sellAmount);
|
||||
}
|
||||
// If the input tokens are currently held by `address(this)` but
|
||||
// the first hop expects them elsewhere, perform a `transfer`.
|
||||
@@ -444,13 +411,11 @@ contract MultiplexFeature is
|
||||
if (subcall.id == MultiplexSubcall.UniswapV2) {
|
||||
_multiHopSellUniswapV2(state, params, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.UniswapV3) {
|
||||
_multiHopSellUniswapV3(state, params, subcall.data);
|
||||
_multiHopSellUniswapV3(state, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.LiquidityProvider) {
|
||||
_multiHopSellLiquidityProvider(state, params, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.BatchSell) {
|
||||
_nestedBatchSell(state, params, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.OTC) {
|
||||
_multiHopSellOtcOrder(state, params, subcall.data);
|
||||
} else {
|
||||
revert("MultiplexFeature::_executeMultiHopSell/INVALID_SUBCALL");
|
||||
}
|
||||
@@ -478,8 +443,6 @@ contract MultiplexFeature is
|
||||
// Likewise, the recipient of the multi-hop sell is
|
||||
// equal to the recipient of its containing batch sell.
|
||||
multiHopParams.recipient = params.recipient;
|
||||
// The payer is the same too.
|
||||
multiHopParams.payer = params.payer;
|
||||
// Execute the nested multi-hop sell.
|
||||
uint256 outputTokenAmount = _executeMultiHopSell(multiHopParams).outputTokenAmount;
|
||||
// Increment the sold and bought amounts.
|
||||
@@ -506,7 +469,7 @@ contract MultiplexFeature is
|
||||
// If the nested batch sell is the first hop
|
||||
// and `useSelfBalance` for the containing multi-
|
||||
// hop sell is false, the nested batch sell should
|
||||
// pull tokens from `payer` (so `batchSellParams.useSelfBalance`
|
||||
// pull tokens from `msg.sender` (so `batchSellParams.useSelfBalance`
|
||||
// should be false). Otherwise `batchSellParams.useSelfBalance`
|
||||
// should be true.
|
||||
batchSellParams.useSelfBalance = state.hopIndex > 0 || params.useSelfBalance;
|
||||
@@ -514,8 +477,6 @@ contract MultiplexFeature is
|
||||
// that should receive the output tokens of the
|
||||
// batch sell.
|
||||
batchSellParams.recipient = state.to;
|
||||
// payer shound be the same too.
|
||||
batchSellParams.payer = params.payer;
|
||||
// Execute the nested batch sell.
|
||||
state.outputTokenAmount = _executeBatchSell(batchSellParams).boughtAmount;
|
||||
}
|
||||
@@ -544,33 +505,29 @@ contract MultiplexFeature is
|
||||
// is executed, so we the target is the address encoded
|
||||
// in the subcall data.
|
||||
(target, ) = abi.decode(subcall.data, (address, bytes));
|
||||
} else if (
|
||||
subcall.id == MultiplexSubcall.UniswapV3 ||
|
||||
subcall.id == MultiplexSubcall.BatchSell ||
|
||||
subcall.id == MultiplexSubcall.OTC
|
||||
) {
|
||||
} else if (subcall.id == MultiplexSubcall.UniswapV3 || subcall.id == MultiplexSubcall.BatchSell) {
|
||||
// UniswapV3 uses a callback to pull in the tokens being
|
||||
// sold to it. The callback implemented in `UniswapV3Feature`
|
||||
// can either:
|
||||
// - call `transferFrom` to move tokens from `payer` to the
|
||||
// - call `transferFrom` to move tokens from `msg.sender` to the
|
||||
// UniswapV3 pool, or
|
||||
// - call `transfer` to move tokens from `address(this)` to the
|
||||
// UniswapV3 pool.
|
||||
// A nested batch sell is similar, in that it can either:
|
||||
// - use tokens from `payer`, or
|
||||
// - use tokens from `msg.sender`, or
|
||||
// - use tokens held by `address(this)`.
|
||||
|
||||
// Suppose UniswapV3/BatchSell is the first call in the multi-hop
|
||||
// path. The input tokens are either held by `payer`,
|
||||
// path. The input tokens are either held by `msg.sender`,
|
||||
// or in the case of `multiplexMultiHopSellEthForToken` WETH is
|
||||
// held by `address(this)`. The target is set accordingly.
|
||||
|
||||
// If this is _not_ the first call in the multi-hop path, we
|
||||
// are dealing with an "intermediate" token in the multi-hop path,
|
||||
// which `payer` may not have an allowance set for. Thus
|
||||
// which `msg.sender` may not have an allowance set for. Thus
|
||||
// target must be set to `address(this)` for `i > 0`.
|
||||
if (i == 0 && !params.useSelfBalance) {
|
||||
target = params.payer;
|
||||
target = msg.sender;
|
||||
} else {
|
||||
target = address(this);
|
||||
}
|
||||
|
@@ -67,7 +67,7 @@ abstract contract MultiplexLiquidityProvider is FixinCommon, FixinTokenSpender {
|
||||
_transferERC20Tokens(params.inputToken, provider, sellAmount);
|
||||
} else {
|
||||
// Otherwise, transfer the input tokens from `msg.sender`.
|
||||
_transferERC20TokensFrom(params.inputToken, params.payer, provider, sellAmount);
|
||||
_transferERC20TokensFrom(params.inputToken, msg.sender, provider, sellAmount);
|
||||
}
|
||||
// Cache the recipient's balance of the output token.
|
||||
uint256 balanceBefore = params.outputToken.balanceOf(params.recipient);
|
||||
|
@@ -55,7 +55,7 @@ abstract contract MultiplexOtc is FixinEIP712 {
|
||||
order,
|
||||
signature,
|
||||
sellAmount.safeDowncastToUint128(),
|
||||
params.payer,
|
||||
msg.sender,
|
||||
params.useSelfBalance,
|
||||
params.recipient
|
||||
)
|
||||
@@ -65,34 +65,4 @@ abstract contract MultiplexOtc is FixinEIP712 {
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(makerTokenFilledAmount);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function _multiHopSellOtcOrder(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
IMultiplexFeature.MultiHopSellParams memory params,
|
||||
bytes memory wrappedCallData
|
||||
) internal {
|
||||
// Decode the Otc order, and signature.
|
||||
(LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) = abi.decode(
|
||||
wrappedCallData,
|
||||
(LibNativeOrder.OtcOrder, LibSignature.Signature)
|
||||
);
|
||||
//Make sure that the otc orders maker and taker tokens match the fill sequence in params.tokens[]
|
||||
require(
|
||||
address(order.takerToken) == params.tokens[state.hopIndex] &&
|
||||
address(order.makerToken) == params.tokens[state.hopIndex + 1],
|
||||
"MultiplexOtcOrder::_multiHopSellOtcOrder/INVALID_TOKENS"
|
||||
);
|
||||
// Try filling the Otc order. Bubble up reverts.
|
||||
(uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) = IOtcOrdersFeature(address(this))
|
||||
._fillOtcOrder(
|
||||
order,
|
||||
signature,
|
||||
state.outputTokenAmount.safeDowncastToUint128(),
|
||||
state.from,
|
||||
params.useSelfBalance,
|
||||
state.to
|
||||
);
|
||||
//store the bought amount for the next hop
|
||||
state.outputTokenAmount = makerTokenFilledAmount;
|
||||
}
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ abstract contract MultiplexRfq is FixinEIP712 {
|
||||
order,
|
||||
signature,
|
||||
sellAmount.safeDowncastToUint128(),
|
||||
params.payer,
|
||||
msg.sender,
|
||||
params.useSelfBalance,
|
||||
params.recipient
|
||||
)
|
||||
|
@@ -30,8 +30,8 @@ abstract contract MultiplexTransformERC20 {
|
||||
) internal {
|
||||
ITransformERC20Feature.TransformERC20Args memory args;
|
||||
// We want the TransformedERC20 event to have
|
||||
// `payer` as the taker.
|
||||
args.taker = payable(params.payer);
|
||||
// `msg.sender` as the taker.
|
||||
args.taker = msg.sender;
|
||||
args.inputToken = params.inputToken;
|
||||
args.outputToken = params.outputToken;
|
||||
args.inputTokenAmount = sellAmount;
|
||||
|
@@ -77,7 +77,7 @@ abstract contract MultiplexUniswapV2 is FixinCommon, FixinTokenSpender {
|
||||
if (params.useSelfBalance) {
|
||||
_transferERC20Tokens(IERC20Token(tokens[0]), firstPairAddress, sellAmount);
|
||||
} else {
|
||||
_transferERC20TokensFrom(IERC20Token(tokens[0]), params.payer, firstPairAddress, sellAmount);
|
||||
_transferERC20TokensFrom(IERC20Token(tokens[0]), msg.sender, firstPairAddress, sellAmount);
|
||||
}
|
||||
// Execute the Uniswap/Sushiswap trade.
|
||||
return _sellToUniswapV2(tokens, sellAmount, isSushi, firstPairAddress, params.recipient);
|
||||
|
@@ -45,16 +45,16 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we self-call `_sellTokenForTokenToUniswapV3`,
|
||||
// which pulls the input token from a specified `payer`.
|
||||
(success, resultData) = address(this).call(
|
||||
// Otherwise, we self-delegatecall the normal variant
|
||||
// `sellTokenForTokenToUniswapV3`, which pulls the input token
|
||||
// from `msg.sender`.
|
||||
(success, resultData) = address(this).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature._sellTokenForTokenToUniswapV3.selector,
|
||||
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
|
||||
wrappedCallData,
|
||||
sellAmount,
|
||||
0,
|
||||
params.recipient,
|
||||
params.payer
|
||||
params.recipient
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -69,7 +69,6 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender {
|
||||
|
||||
function _multiHopSellUniswapV3(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
IMultiplexFeature.MultiHopSellParams memory params,
|
||||
bytes memory wrappedCallData
|
||||
) internal {
|
||||
bool success;
|
||||
@@ -88,16 +87,16 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we self-call `_sellTokenForTokenToUniswapV3`,
|
||||
// which pulls the input token from `payer`.
|
||||
(success, resultData) = address(this).call(
|
||||
// Otherwise, we self-delegatecall the normal variant
|
||||
// `sellTokenForTokenToUniswapV3`, which pulls the input token
|
||||
// from `msg.sender`.
|
||||
(success, resultData) = address(this).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature._sellTokenForTokenToUniswapV3.selector,
|
||||
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
|
||||
wrappedCallData,
|
||||
state.outputTokenAmount,
|
||||
0,
|
||||
state.to,
|
||||
params.payer
|
||||
state.to
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -1,38 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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 "./LibStorage.sol";
|
||||
|
||||
/// @dev Storage helpers for the `MetaTransactions` feature.
|
||||
library LibMetaTransactionsV2Storage {
|
||||
/// @dev Storage bucket for this feature.
|
||||
struct Storage {
|
||||
// The block number when a hash was executed.
|
||||
mapping(bytes32 => uint256) mtxHashToExecutedBlockNumber;
|
||||
}
|
||||
|
||||
/// @dev Get the storage bucket for this contract.
|
||||
function getStorage() internal pure returns (Storage storage stor) {
|
||||
uint256 storageSlot = LibStorage.getStorageSlot(LibStorage.StorageId.MetaTransactionsV2);
|
||||
// Dip into assembly to change the slot pointed to by the local variable `stor`.
|
||||
// solhint-disable-next-line max-line-length
|
||||
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
|
||||
assembly {
|
||||
stor_slot := storageSlot
|
||||
}
|
||||
}
|
||||
}
|
@@ -34,8 +34,7 @@ library LibStorage {
|
||||
NativeOrders,
|
||||
OtcOrders,
|
||||
ERC721Orders,
|
||||
ERC1155Orders,
|
||||
MetaTransactionsV2
|
||||
ERC1155Orders
|
||||
}
|
||||
|
||||
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced slots to storage bucket variables
|
||||
|
@@ -119,7 +119,9 @@ contract FillQuoteTransformer is Transformer {
|
||||
/// @dev Intermediate state variables to get around stack limits.
|
||||
struct FillState {
|
||||
uint256 ethRemaining;
|
||||
/// deprecated
|
||||
uint256 boughtAmount;
|
||||
/// deprecated
|
||||
uint256 soldAmount;
|
||||
uint256 protocolFee;
|
||||
uint256 takerTokenBalanceRemaining;
|
||||
@@ -208,19 +210,6 @@ contract FillQuoteTransformer is Transformer {
|
||||
|
||||
// Fill the orders.
|
||||
for (uint256 i = 0; i < data.fillSequence.length; ++i) {
|
||||
// Check if we've hit our targets.
|
||||
if (data.side == Side.Sell) {
|
||||
// Market sell check.
|
||||
if (state.soldAmount >= data.fillAmount) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Market buy check.
|
||||
if (state.boughtAmount >= data.fillAmount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.currentOrderType = OrderType(data.fillSequence[i]);
|
||||
uint256 orderIndex = state.currentIndices[uint256(state.currentOrderType)];
|
||||
// Fill the order.
|
||||
@@ -238,30 +227,10 @@ contract FillQuoteTransformer is Transformer {
|
||||
}
|
||||
|
||||
// Accumulate totals.
|
||||
state.soldAmount = state.soldAmount.safeAdd(results.takerTokenSoldAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(results.makerTokenBoughtAmount);
|
||||
state.ethRemaining = state.ethRemaining.safeSub(results.protocolFeePaid);
|
||||
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining.safeSub(results.takerTokenSoldAmount);
|
||||
state.currentIndices[uint256(state.currentOrderType)]++;
|
||||
}
|
||||
|
||||
// Ensure we hit our targets.
|
||||
if (data.side == Side.Sell) {
|
||||
// Market sell check.
|
||||
if (state.soldAmount < data.fillAmount) {
|
||||
LibTransformERC20RichErrors
|
||||
.IncompleteFillSellQuoteError(address(data.sellToken), state.soldAmount, data.fillAmount)
|
||||
.rrevert();
|
||||
}
|
||||
} else {
|
||||
// Market buy check.
|
||||
if (state.boughtAmount < data.fillAmount) {
|
||||
LibTransformERC20RichErrors
|
||||
.IncompleteFillBuyQuoteError(address(data.buyToken), state.boughtAmount, data.fillAmount)
|
||||
.rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
// Refund unspent protocol fees.
|
||||
if (state.ethRemaining > 0 && data.refundReceiver != address(0)) {
|
||||
bool transferSuccess;
|
||||
|
@@ -26,7 +26,6 @@ import "./mixins/MixinKyberElastic.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinPlatypus.sol";
|
||||
import "./mixins/MixinTraderJoeV2.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinWOOFi.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
@@ -42,7 +41,6 @@ contract AvalancheBridgeAdapter is
|
||||
MixinAaveV2,
|
||||
MixinNerve,
|
||||
MixinPlatypus,
|
||||
MixinTraderJoeV2,
|
||||
MixinUniswapV2,
|
||||
MixinWOOFi,
|
||||
MixinZeroExBridge
|
||||
@@ -102,11 +100,6 @@ contract AvalancheBridgeAdapter is
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradePlatypus(buyToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.TRADERJOEV2) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeTraderJoeV2(buyToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.WOOFI) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@@ -55,6 +55,4 @@ library BridgeProtocols {
|
||||
uint128 internal constant WOOFI = 31;
|
||||
uint128 internal constant AAVEV3 = 32;
|
||||
uint128 internal constant KYBERELASTIC = 33;
|
||||
uint128 internal constant BARTER = 34;
|
||||
uint128 internal constant TRADERJOEV2 = 35;
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@ import "./mixins/MixinBalancer.sol";
|
||||
import "./mixins/MixinBalancerV2Batch.sol";
|
||||
import "./mixins/MixinBancor.sol";
|
||||
import "./mixins/MixinBancorV3.sol";
|
||||
import "./mixins/MixinBarter.sol";
|
||||
import "./mixins/MixinCompound.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
@@ -49,7 +48,6 @@ contract EthereumBridgeAdapter is
|
||||
MixinBalancerV2Batch,
|
||||
MixinBancor,
|
||||
MixinBancorV3,
|
||||
MixinBarter,
|
||||
MixinCompound,
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
@@ -199,11 +197,6 @@ contract EthereumBridgeAdapter is
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeSynthetix(sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.BARTER) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeBarter(sellToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@@ -1,41 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/src/IERC20Token.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
|
||||
contract MixinBarter {
|
||||
using LibERC20TokenV06 for IERC20Token;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
function _tradeBarter(
|
||||
IERC20Token sellToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
) internal returns (uint256 boughtAmount) {
|
||||
(address barterRouter, bytes memory data) = abi.decode(bridgeData, (address, bytes));
|
||||
sellToken.approveIfBelow(barterRouter, sellAmount);
|
||||
|
||||
(bool success, bytes memory resultData) = barterRouter.call(data);
|
||||
if (!success) {
|
||||
resultData.rrevert();
|
||||
}
|
||||
|
||||
return abi.decode(resultData, (uint256));
|
||||
}
|
||||
}
|
@@ -1,82 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/src/IERC20Token.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
interface ILBRouter {
|
||||
/// @notice Swaps exact tokens for tokens while performing safety checks
|
||||
/// @param amountIn The amount of token to send
|
||||
/// @param amountOutMin The min amount of token to receive
|
||||
/// @param pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
|
||||
/// @param tokenPath The swap path using the binSteps following `_pairBinSteps`
|
||||
/// @param to The address of the recipient
|
||||
/// @param deadline The deadline of the tx
|
||||
/// @return amountOut Output amount of the swap
|
||||
function swapExactTokensForTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
uint256[] memory pairBinSteps,
|
||||
IERC20Token[] memory tokenPath,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountOut);
|
||||
}
|
||||
|
||||
contract MixinTraderJoeV2 {
|
||||
using LibERC20TokenV06 for IERC20Token;
|
||||
|
||||
function _tradeTraderJoeV2(
|
||||
IERC20Token buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
) internal returns (uint256 boughtAmount) {
|
||||
ILBRouter router;
|
||||
IERC20Token[] memory tokenPath;
|
||||
uint256[] memory pairBinSteps;
|
||||
{
|
||||
address[] memory _path;
|
||||
(router, _path, pairBinSteps) = abi.decode(bridgeData, (ILBRouter, address[], uint256[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly {
|
||||
tokenPath := _path
|
||||
}
|
||||
}
|
||||
|
||||
require(tokenPath.length >= 2, "MixinTraderJoeV2/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
tokenPath.length == pairBinSteps.length + 1,
|
||||
"MixinTraderJoeV2/PAIR_BIN_STEPS_LENGTH_MUST_BE_ONE_LESS_THAN_TOKEN_PATH_LENGTH"
|
||||
);
|
||||
require(
|
||||
tokenPath[tokenPath.length - 1] == buyToken,
|
||||
"MixinTraderJoeV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
// Grant the Trader Joe V2 router an allowance to sell the first token.
|
||||
tokenPath[0].approveIfBelow(address(router), sellAmount);
|
||||
|
||||
boughtAmount = router.swapExactTokensForTokens(
|
||||
sellAmount,
|
||||
1,
|
||||
pairBinSteps,
|
||||
tokenPath,
|
||||
address(this),
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-zero-ex",
|
||||
"version": "0.42.0",
|
||||
"version": "0.39.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -36,9 +36,9 @@
|
||||
"typechain": "typechain --target=ethers-v5 --out-dir='typechain-wrappers' './foundry-artifacts/**/*.json'"
|
||||
},
|
||||
"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,MetaTransactionsFeatureV2",
|
||||
"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": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|ArbitrumBridgeAdapter|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|IMetaTransactionsFeatureV2|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|MetaTransactionsFeatureV2|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|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
"abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|ArbitrumBridgeAdapter|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|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -50,12 +50,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.5",
|
||||
"@0x/contract-addresses": "^8.5.0",
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contract-addresses": "^8.2.0",
|
||||
"@0x/contracts-erc20": "^3.3.57",
|
||||
"@0x/contracts-gen": "^2.0.50",
|
||||
"@0x/contracts-test-utils": "^5.4.52",
|
||||
"@0x/dev-utils": "^5.0.2",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-test-utils": "^5.4.49",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
@@ -80,12 +80,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/protocol-utils": "^11.21.0",
|
||||
"@0x/subproviders": "^8.0.1",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/protocol-utils": "^11.18.1",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"ethereum-types": "^3.7.1",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
"ethers": "~4.0.4"
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { constants } from '@0x/contracts-test-utils';
|
||||
import { RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { RPCSubprovider, SupportedProvider, Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { AbiEncoder, BigNumber, logUtils, providerUtils } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { MethodAbi, SupportedProvider } from 'ethereum-types';
|
||||
import { MethodAbi } from 'ethereum-types';
|
||||
import * as fetch from 'isomorphic-fetch';
|
||||
import * as _ from 'lodash';
|
||||
import * as prompts from 'prompts';
|
||||
|
@@ -32,7 +32,6 @@ import * as IZeroEx from '../generated-artifacts/IZeroEx.json';
|
||||
import * as LiquidityProviderFeature from '../generated-artifacts/LiquidityProviderFeature.json';
|
||||
import * as LogMetadataTransformer from '../generated-artifacts/LogMetadataTransformer.json';
|
||||
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
|
||||
import * as MetaTransactionsFeatureV2 from '../generated-artifacts/MetaTransactionsFeatureV2.json';
|
||||
import * as MultiplexFeature from '../generated-artifacts/MultiplexFeature.json';
|
||||
import * as NativeOrdersFeature from '../generated-artifacts/NativeOrdersFeature.json';
|
||||
import * as OptimismBridgeAdapter from '../generated-artifacts/OptimismBridgeAdapter.json';
|
||||
@@ -85,5 +84,4 @@ export const artifacts = {
|
||||
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
|
||||
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
|
||||
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
|
||||
MetaTransactionsFeatureV2: MetaTransactionsFeatureV2 as ContractArtifact,
|
||||
};
|
||||
|
@@ -26,7 +26,6 @@ export {
|
||||
RevertErrorAbi,
|
||||
StandardContractOutput,
|
||||
StateMutability,
|
||||
SupportedProvider,
|
||||
TupleDataItem,
|
||||
} from 'ethereum-types';
|
||||
export { artifacts } from './artifacts';
|
||||
@@ -59,3 +58,4 @@ export {
|
||||
ZeroExContract,
|
||||
} from './wrappers';
|
||||
export { EIP712TypedData } from '@0x/types';
|
||||
export { SupportedProvider } from '@0x/subproviders';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { SimpleContractArtifact } from '@0x/types';
|
||||
import { NULL_ADDRESS } from '@0x/utils';
|
||||
import { TxData, SupportedProvider } from 'ethereum-types';
|
||||
import { TxData } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
@@ -30,7 +30,6 @@ export * from '../generated-wrappers/initial_migration';
|
||||
export * from '../generated-wrappers/liquidity_provider_feature';
|
||||
export * from '../generated-wrappers/log_metadata_transformer';
|
||||
export * from '../generated-wrappers/meta_transactions_feature';
|
||||
export * from '../generated-wrappers/meta_transactions_feature_v2';
|
||||
export * from '../generated-wrappers/multiplex_feature';
|
||||
export * from '../generated-wrappers/native_orders_feature';
|
||||
export * from '../generated-wrappers/optimism_bridge_adapter';
|
||||
|
@@ -51,7 +51,6 @@ import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvi
|
||||
import * as ILiquidityProviderFeature from '../test/generated-artifacts/ILiquidityProviderFeature.json';
|
||||
import * as ILiquidityProviderSandbox from '../test/generated-artifacts/ILiquidityProviderSandbox.json';
|
||||
import * as IMetaTransactionsFeature from '../test/generated-artifacts/IMetaTransactionsFeature.json';
|
||||
import * as IMetaTransactionsFeatureV2 from '../test/generated-artifacts/IMetaTransactionsFeatureV2.json';
|
||||
import * as IMooniswapPool from '../test/generated-artifacts/IMooniswapPool.json';
|
||||
import * as IMultiplexFeature from '../test/generated-artifacts/IMultiplexFeature.json';
|
||||
import * as INativeOrdersEvents from '../test/generated-artifacts/INativeOrdersEvents.json';
|
||||
@@ -105,7 +104,6 @@ import * as LiquidityProviderFeature from '../test/generated-artifacts/Liquidity
|
||||
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
|
||||
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
|
||||
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
|
||||
import * as MetaTransactionsFeatureV2 from '../test/generated-artifacts/MetaTransactionsFeatureV2.json';
|
||||
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
|
||||
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
||||
import * as MixinBalancerV2Batch from '../test/generated-artifacts/MixinBalancerV2Batch.json';
|
||||
@@ -238,7 +236,6 @@ export const artifacts = {
|
||||
FundRecoveryFeature: FundRecoveryFeature as ContractArtifact,
|
||||
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
|
||||
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||
MetaTransactionsFeatureV2: MetaTransactionsFeatureV2 as ContractArtifact,
|
||||
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
|
||||
OtcOrdersFeature: OtcOrdersFeature as ContractArtifact,
|
||||
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||
@@ -256,7 +253,6 @@ export const artifacts = {
|
||||
IFundRecoveryFeature: IFundRecoveryFeature as ContractArtifact,
|
||||
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
|
||||
IMetaTransactionsFeature: IMetaTransactionsFeature as ContractArtifact,
|
||||
IMetaTransactionsFeatureV2: IMetaTransactionsFeatureV2 as ContractArtifact,
|
||||
IMultiplexFeature: IMultiplexFeature as ContractArtifact,
|
||||
INativeOrdersEvents: INativeOrdersEvents as ContractArtifact,
|
||||
INativeOrdersFeature: INativeOrdersFeature as ContractArtifact,
|
||||
|
@@ -49,7 +49,6 @@ export * from '../test/generated-wrappers/i_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider_feature';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider_sandbox';
|
||||
export * from '../test/generated-wrappers/i_meta_transactions_feature';
|
||||
export * from '../test/generated-wrappers/i_meta_transactions_feature_v2';
|
||||
export * from '../test/generated-wrappers/i_mooniswap_pool';
|
||||
export * from '../test/generated-wrappers/i_multiplex_feature';
|
||||
export * from '../test/generated-wrappers/i_native_orders_events';
|
||||
@@ -103,7 +102,6 @@ export * from '../test/generated-wrappers/liquidity_provider_feature';
|
||||
export * from '../test/generated-wrappers/liquidity_provider_sandbox';
|
||||
export * from '../test/generated-wrappers/log_metadata_transformer';
|
||||
export * from '../test/generated-wrappers/meta_transactions_feature';
|
||||
export * from '../test/generated-wrappers/meta_transactions_feature_v2';
|
||||
export * from '../test/generated-wrappers/mixin_aave_v2';
|
||||
export * from '../test/generated-wrappers/mixin_balancer';
|
||||
export * from '../test/generated-wrappers/mixin_balancer_v2_batch';
|
||||
|
@@ -1,501 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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 "./utils/BaseTest.sol";
|
||||
import "forge-std/Test.sol";
|
||||
import "./utils/LocalTest.sol";
|
||||
import "../contracts/src/features/MetaTransactionsFeatureV2.sol";
|
||||
import "../contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol";
|
||||
import "../contracts/src/features/interfaces/IMetaTransactionsFeature.sol";
|
||||
import "../contracts/test/TestMintTokenERC20Transformer.sol";
|
||||
import "../contracts/src/features/libs/LibSignature.sol";
|
||||
import "src/features/libs/LibNativeOrder.sol";
|
||||
import "../contracts/test/tokens/TestMintableERC20Token.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-erc20/src/IEtherToken.sol";
|
||||
|
||||
contract MetaTransactionTest is LocalTest {
|
||||
address private constant USER_ADDRESS = 0x6dc3a54FeAE57B65d185A7B159c5d3FA7fD7FD0F;
|
||||
uint256 private constant USER_KEY = 0x1fc1630343b31e60b7a197a53149ca571ed9d9791e2833337bbd8110c30710ec;
|
||||
|
||||
event MetaTransactionExecuted(bytes32 hash, bytes4 indexed selector, address signer, address sender);
|
||||
|
||||
function _mtxSignature(
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx
|
||||
) private returns (LibSignature.Signature memory) {
|
||||
return _mtxSignatureWithSignerKey(mtx, USER_KEY);
|
||||
}
|
||||
|
||||
function _mtxSignatureWithSignerKey(
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx,
|
||||
uint256 key
|
||||
) private returns (LibSignature.Signature memory) {
|
||||
// Mint fee to signer and approve
|
||||
for (uint256 i = 0; i < mtx.fees.length; ++i) {
|
||||
_mintTo(address(weth), mtx.signer, mtx.fees[i].amount);
|
||||
}
|
||||
vm.prank(mtx.signer);
|
||||
mtx.feeToken.approve(address(zeroExDeployed.zeroEx), 1e18);
|
||||
|
||||
bytes32 mtxHash = zeroExDeployed.features.metaTransactionsFeatureV2.getMetaTransactionV2Hash(mtx);
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(key, mtxHash);
|
||||
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
function _getMetaTransaction(
|
||||
bytes memory callData
|
||||
) private view returns (IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory) {
|
||||
IMetaTransactionsFeatureV2.MetaTransactionFeeData[]
|
||||
memory fees = new IMetaTransactionsFeatureV2.MetaTransactionFeeData[](1);
|
||||
fees[0] = IMetaTransactionsFeatureV2.MetaTransactionFeeData({recipient: address(this), amount: 1});
|
||||
return _getMetaTransactionWithFees(callData, fees);
|
||||
}
|
||||
|
||||
function _getMetaTransactionWithFees(
|
||||
bytes memory callData,
|
||||
IMetaTransactionsFeatureV2.MetaTransactionFeeData[] memory fees
|
||||
) private view returns (IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory) {
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx = IMetaTransactionsFeatureV2.MetaTransactionDataV2({
|
||||
signer: payable(USER_ADDRESS),
|
||||
sender: address(this),
|
||||
expirationTimeSeconds: block.timestamp + 60,
|
||||
salt: 123,
|
||||
callData: callData,
|
||||
feeToken: weth,
|
||||
fees: fees
|
||||
});
|
||||
return mtx;
|
||||
}
|
||||
|
||||
function _badSelectorTransformERC20Call() private pure returns (bytes memory) {
|
||||
return abi.encodeWithSelector(ITransformERC20Feature.createTransformWallet.selector);
|
||||
}
|
||||
|
||||
function _badTokenTransformERC20Call() private returns (bytes memory) {
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](1);
|
||||
transformations[0] = ITransformERC20Feature.Transformation(
|
||||
uint32(transformerNonce),
|
||||
abi.encode(address(dai), address(weth), 0, 1e18, 0)
|
||||
);
|
||||
|
||||
_mintTo(address(dai), USER_ADDRESS, 1e18);
|
||||
vm.prank(USER_ADDRESS);
|
||||
dai.approve(address(zeroExDeployed.zeroEx), 1e18);
|
||||
|
||||
return
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.transformERC20.selector, // 0x415565b0
|
||||
dai,
|
||||
weth,
|
||||
1e18,
|
||||
1e18,
|
||||
transformations
|
||||
);
|
||||
}
|
||||
|
||||
function _makeTestRfqOrder(
|
||||
IERC20Token makerToken,
|
||||
IERC20Token takerToken,
|
||||
address makerAddress,
|
||||
address takerAddress,
|
||||
uint256 makerKey
|
||||
) internal returns (bytes memory callData) {
|
||||
LibNativeOrder.RfqOrder memory order = LibNativeOrder.RfqOrder({
|
||||
makerToken: makerToken,
|
||||
takerToken: takerToken,
|
||||
makerAmount: 1e18,
|
||||
takerAmount: 1e18,
|
||||
maker: makerAddress,
|
||||
taker: address(0),
|
||||
txOrigin: tx.origin,
|
||||
pool: 0x0000000000000000000000000000000000000000000000000000000000000000,
|
||||
expiry: uint64(block.timestamp + 60),
|
||||
salt: 123
|
||||
});
|
||||
_mintTo(address(order.makerToken), order.maker, order.makerAmount);
|
||||
vm.prank(order.maker);
|
||||
order.makerToken.approve(address(zeroExDeployed.zeroEx), order.makerAmount);
|
||||
_mintTo(address(order.takerToken), takerAddress, order.takerAmount);
|
||||
vm.prank(takerAddress);
|
||||
order.takerToken.approve(address(zeroExDeployed.zeroEx), order.takerAmount);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
makerKey,
|
||||
zeroExDeployed.features.nativeOrdersFeature.getRfqOrderHash(order)
|
||||
);
|
||||
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
|
||||
|
||||
return
|
||||
abi.encodeWithSelector(
|
||||
INativeOrdersFeature.fillRfqOrder.selector, // 0xaa77476c
|
||||
order, // RFQOrder
|
||||
sig, // Order Signature
|
||||
1e18 // Fill Amount
|
||||
);
|
||||
}
|
||||
|
||||
function _makeTestLimitOrder(
|
||||
IERC20Token makerToken,
|
||||
IERC20Token takerToken,
|
||||
address makerAddress,
|
||||
address takerAddress,
|
||||
uint256 makerKey
|
||||
) internal returns (bytes memory callData) {
|
||||
LibNativeOrder.LimitOrder memory order = LibNativeOrder.LimitOrder({
|
||||
makerToken: makerToken,
|
||||
takerToken: takerToken,
|
||||
makerAmount: 1e18,
|
||||
takerAmount: 1e18,
|
||||
maker: makerAddress,
|
||||
taker: address(0),
|
||||
sender: address(0),
|
||||
takerTokenFeeAmount: 0,
|
||||
feeRecipient: address(0),
|
||||
pool: 0x0000000000000000000000000000000000000000000000000000000000000000,
|
||||
expiry: uint64(block.timestamp + 60),
|
||||
salt: 123
|
||||
});
|
||||
_mintTo(address(order.makerToken), order.maker, order.makerAmount);
|
||||
vm.prank(order.maker);
|
||||
order.makerToken.approve(address(zeroExDeployed.zeroEx), order.makerAmount);
|
||||
_mintTo(address(order.takerToken), takerAddress, order.takerAmount);
|
||||
vm.prank(takerAddress);
|
||||
order.takerToken.approve(address(zeroExDeployed.zeroEx), order.takerAmount);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
makerKey,
|
||||
zeroExDeployed.features.nativeOrdersFeature.getLimitOrderHash(order)
|
||||
);
|
||||
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
|
||||
|
||||
return
|
||||
abi.encodeWithSelector(
|
||||
INativeOrdersFeature.fillLimitOrder.selector, // 0xf6274f66
|
||||
order, // LimitOrder
|
||||
sig, // Order Signature
|
||||
1e18 // Fill Amount
|
||||
);
|
||||
}
|
||||
|
||||
function _transformERC20Call(
|
||||
IERC20Token makerToken,
|
||||
IERC20Token takerToken,
|
||||
address takerAddress
|
||||
) internal returns (bytes memory) {
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](1);
|
||||
transformations[0] = ITransformERC20Feature.Transformation(
|
||||
uint32(transformerNonce),
|
||||
abi.encode(address(takerToken), address(makerToken), 0, 1e18, 0)
|
||||
);
|
||||
|
||||
_mintTo(address(takerToken), takerAddress, 1e18);
|
||||
vm.prank(takerAddress);
|
||||
takerToken.approve(address(zeroExDeployed.zeroEx), 1e18);
|
||||
|
||||
return
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.transformERC20.selector, // 0x415565b0
|
||||
takerToken,
|
||||
makerToken,
|
||||
1e18,
|
||||
1e18,
|
||||
transformations
|
||||
);
|
||||
}
|
||||
|
||||
function test_createHash() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
|
||||
bytes32 mtxHash = zeroExDeployed.features.metaTransactionsFeatureV2.getMetaTransactionV2Hash(mtxData);
|
||||
assertTrue(mtxHash != bytes32(0));
|
||||
}
|
||||
|
||||
function test_EIP_712_signature() external {
|
||||
// metamask wallet signed data
|
||||
bytes32 r_mm = 0xcd6c09d558e23803afae870ca53a8e7bfaf5564c64ee29f23dc4a19e7dd9e9b5;
|
||||
bytes32 s_mm = 0x1ae68e89fadab4a7f4d01fd5543e5e0efd5697e87c993f045f671aba3e1f55ac;
|
||||
uint8 v_mm = 0x1b;
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionFeeData[]
|
||||
memory fees = new IMetaTransactionsFeatureV2.MetaTransactionFeeData[](2);
|
||||
fees[0] = IMetaTransactionsFeatureV2.MetaTransactionFeeData({recipient: address(0), amount: 1000000});
|
||||
fees[1] = IMetaTransactionsFeatureV2.MetaTransactionFeeData({recipient: address(0), amount: 1000});
|
||||
IERC20Token usdcToken = IERC20Token(address(0x2e234DAe75C793f67A35089C9d99245E1C58470b));
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx = IMetaTransactionsFeatureV2.MetaTransactionDataV2({
|
||||
signer: address(0),
|
||||
sender: address(0),
|
||||
expirationTimeSeconds: 99999999,
|
||||
salt: 1234,
|
||||
callData: new bytes(0),
|
||||
feeToken: usdcToken,
|
||||
fees: fees
|
||||
});
|
||||
|
||||
bytes32 mtxHash = zeroExDeployed.features.metaTransactionsFeatureV2.getMetaTransactionV2Hash(mtx);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(USER_KEY, mtxHash);
|
||||
//emit log_bytes(abi.encodePacked(r, s, bytes1(v)));
|
||||
|
||||
// Verify signature matches from what we generated using metamask
|
||||
assertTrue(v == v_mm);
|
||||
assertTrue(r == r_mm);
|
||||
assertTrue(s == s_mm);
|
||||
}
|
||||
|
||||
function test_transformERC20() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 1e18);
|
||||
vm.expectEmit(true, false, false, true);
|
||||
emit MetaTransactionExecuted(
|
||||
zeroExDeployed.features.metaTransactionsFeatureV2.getMetaTransactionV2Hash(mtxData),
|
||||
zeroExDeployed.zeroEx.transformERC20.selector, // 0x415565b0
|
||||
USER_ADDRESS,
|
||||
address(this)
|
||||
);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(
|
||||
mtxData,
|
||||
_mtxSignature(mtxData)
|
||||
);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 1e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(weth.balanceOf(address(this)), 1);
|
||||
}
|
||||
|
||||
function test_rfqOrder() external {
|
||||
bytes memory callData = _makeTestRfqOrder(zrx, dai, signerAddress, USER_ADDRESS, signerKey);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(callData);
|
||||
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 1e18);
|
||||
vm.expectEmit(true, false, false, true);
|
||||
emit MetaTransactionExecuted(
|
||||
zeroExDeployed.features.metaTransactionsFeatureV2.getMetaTransactionV2Hash(mtxData),
|
||||
INativeOrdersFeature.fillRfqOrder.selector, // 0xaa77476c
|
||||
USER_ADDRESS,
|
||||
address(this)
|
||||
);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(
|
||||
mtxData,
|
||||
_mtxSignature(mtxData)
|
||||
);
|
||||
|
||||
assertEq(zrx.balanceOf(signerAddress), 0);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 1e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(dai.balanceOf(signerAddress), 1e18);
|
||||
assertEq(weth.balanceOf(address(this)), 1);
|
||||
}
|
||||
|
||||
function test_fillLimitOrder() external {
|
||||
bytes memory callData = _makeTestLimitOrder(zrx, dai, signerAddress, USER_ADDRESS, signerKey);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(callData);
|
||||
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 1e18);
|
||||
vm.expectEmit(true, false, false, true);
|
||||
emit MetaTransactionExecuted(
|
||||
zeroExDeployed.features.metaTransactionsFeatureV2.getMetaTransactionV2Hash(mtxData),
|
||||
INativeOrdersFeature.fillLimitOrder.selector, // 0xf6274f66
|
||||
USER_ADDRESS,
|
||||
address(this)
|
||||
);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(
|
||||
mtxData,
|
||||
_mtxSignature(mtxData)
|
||||
);
|
||||
|
||||
assertEq(zrx.balanceOf(signerAddress), 0);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 1e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(dai.balanceOf(signerAddress), 1e18);
|
||||
assertEq(weth.balanceOf(address(this)), 1);
|
||||
}
|
||||
|
||||
function test_transformERC20WithAnySender() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
mtxData.sender = address(0);
|
||||
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 1e18);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(
|
||||
mtxData,
|
||||
_mtxSignature(mtxData)
|
||||
);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 1e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(weth.balanceOf(address(this)), 1);
|
||||
}
|
||||
|
||||
function test_transformERC20WithoutFee() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionFeeData[] memory fees;
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransactionWithFees(
|
||||
transformCallData,
|
||||
fees
|
||||
);
|
||||
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 1e18);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(
|
||||
mtxData,
|
||||
_mtxSignature(mtxData)
|
||||
);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 1e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(weth.balanceOf(address(this)), 0); // no fee paid out
|
||||
}
|
||||
|
||||
function test_transformERC20MultipleFees() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionFeeData[]
|
||||
memory fees = new IMetaTransactionsFeatureV2.MetaTransactionFeeData[](2);
|
||||
fees[0] = IMetaTransactionsFeatureV2.MetaTransactionFeeData({recipient: address(this), amount: 10});
|
||||
fees[1] = IMetaTransactionsFeatureV2.MetaTransactionFeeData({recipient: signerAddress, amount: 20});
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransactionWithFees(
|
||||
transformCallData,
|
||||
fees
|
||||
);
|
||||
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 1e18);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(
|
||||
mtxData,
|
||||
_mtxSignature(mtxData)
|
||||
);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 1e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(weth.balanceOf(address(this)), 10);
|
||||
assertEq(weth.balanceOf(address(signerAddress)), 20);
|
||||
}
|
||||
|
||||
function test_transformERC20TranslatedCallFail() external {
|
||||
bytes memory transformCallData = _badTokenTransformERC20Call();
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
LibSignature.Signature memory sig = _mtxSignature(mtxData);
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
}
|
||||
|
||||
function test_transformERC20UnsupportedFunction() external {
|
||||
bytes memory transformCallData = _badSelectorTransformERC20Call();
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
LibSignature.Signature memory sig = _mtxSignature(mtxData);
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
}
|
||||
|
||||
function test_transformERC20CantExecuteTwice() external {
|
||||
bytes memory callData = _makeTestRfqOrder(zrx, dai, signerAddress, USER_ADDRESS, signerKey);
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(callData);
|
||||
LibSignature.Signature memory sig = _mtxSignature(mtxData);
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
}
|
||||
|
||||
function test_metaTxnFailsIfExpired() external {
|
||||
bytes memory callData = _makeTestRfqOrder(zrx, dai, signerAddress, USER_ADDRESS, signerKey);
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(callData);
|
||||
mtxData.expirationTimeSeconds = block.timestamp - 1;
|
||||
|
||||
LibSignature.Signature memory sig = _mtxSignature(mtxData);
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
}
|
||||
|
||||
function test_metaTxnFailsIfWrongSender() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
mtxData.sender = USER_ADDRESS;
|
||||
|
||||
LibSignature.Signature memory sig = _mtxSignature(mtxData);
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
}
|
||||
|
||||
function test_metaTxnFailsWrongSignature() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtxData = _getMetaTransaction(transformCallData);
|
||||
|
||||
LibSignature.Signature memory sig = _mtxSignatureWithSignerKey(mtxData, signerKey);
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).executeMetaTransactionV2(mtxData, sig);
|
||||
}
|
||||
|
||||
function test_batchExecuteMetaTransactionsMultipleTransactions() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
bytes memory rfqCallData = _makeTestRfqOrder(zrx, shib, signerAddress, USER_ADDRESS, signerKey);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2[]
|
||||
memory mtxns = new IMetaTransactionsFeatureV2.MetaTransactionDataV2[](2);
|
||||
LibSignature.Signature[] memory sigs = new LibSignature.Signature[](2);
|
||||
|
||||
mtxns[0] = _getMetaTransaction(transformCallData);
|
||||
sigs[0] = _mtxSignature(mtxns[0]);
|
||||
mtxns[1] = _getMetaTransaction(rfqCallData);
|
||||
sigs[1] = _mtxSignature(mtxns[1]);
|
||||
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).batchExecuteMetaTransactionsV2(mtxns, sigs);
|
||||
assertEq(zrx.balanceOf(USER_ADDRESS), 2e18);
|
||||
assertEq(dai.balanceOf(USER_ADDRESS), 0);
|
||||
assertEq(shib.balanceOf(USER_ADDRESS), 0);
|
||||
}
|
||||
|
||||
function test_batchExecuteMetaTransactionsCantExecuteSameTxnTwice() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2[]
|
||||
memory mtxns = new IMetaTransactionsFeatureV2.MetaTransactionDataV2[](2);
|
||||
LibSignature.Signature[] memory sigs = new LibSignature.Signature[](2);
|
||||
|
||||
mtxns[0] = _getMetaTransaction(transformCallData);
|
||||
sigs[0] = _mtxSignature(mtxns[0]);
|
||||
mtxns[1] = mtxns[0];
|
||||
sigs[1] = _mtxSignature(mtxns[1]);
|
||||
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).batchExecuteMetaTransactionsV2(mtxns, sigs);
|
||||
}
|
||||
|
||||
function test_batchExecuteMetaTransactionsFailsIfTransactionFails() external {
|
||||
bytes memory transformCallData = _transformERC20Call(zrx, dai, USER_ADDRESS);
|
||||
bytes memory badTransformCallData = _badTokenTransformERC20Call();
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2[]
|
||||
memory mtxns = new IMetaTransactionsFeatureV2.MetaTransactionDataV2[](2);
|
||||
LibSignature.Signature[] memory sigs = new LibSignature.Signature[](2);
|
||||
|
||||
mtxns[0] = _getMetaTransaction(transformCallData);
|
||||
sigs[0] = _mtxSignature(mtxns[0]);
|
||||
mtxns[1] = _getMetaTransaction(badTransformCallData);
|
||||
sigs[1] = _mtxSignature(mtxns[1]);
|
||||
|
||||
vm.expectRevert();
|
||||
IMetaTransactionsFeatureV2(address(zeroExDeployed.zeroEx)).batchExecuteMetaTransactionsV2(mtxns, sigs);
|
||||
}
|
||||
}
|
@@ -1,407 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {LocalTest} from "utils/LocalTest.sol";
|
||||
import {MultiplexUtils} from "utils/MultiplexUtils.sol";
|
||||
import {LibSignature} from "src/features/libs/LibSignature.sol";
|
||||
import {LibNativeOrder} from "src/features/libs/LibNativeOrder.sol";
|
||||
import {IMetaTransactionsFeatureV2} from "src/features/interfaces/IMetaTransactionsFeatureV2.sol";
|
||||
|
||||
contract MultiplexMetaTransactionsV2 is LocalTest, MultiplexUtils {
|
||||
function _makeMetaTransactionV2(
|
||||
bytes memory callData
|
||||
) private view returns (IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory, LibSignature.Signature memory) {
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx = IMetaTransactionsFeatureV2.MetaTransactionDataV2({
|
||||
signer: payable(otherSignerAddress),
|
||||
sender: address(0),
|
||||
expirationTimeSeconds: block.timestamp + 600,
|
||||
salt: 123,
|
||||
callData: callData,
|
||||
feeToken: dai,
|
||||
fees: new IMetaTransactionsFeatureV2.MetaTransactionFeeData[](0)
|
||||
});
|
||||
|
||||
bytes32 mtxHash = zeroExDeployed.zeroEx.getMetaTransactionV2Hash(mtx);
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(otherSignerKey, mtxHash);
|
||||
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
|
||||
|
||||
return (mtx, sig);
|
||||
}
|
||||
|
||||
function _executeMetaTransaction(bytes memory callData) private {
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx;
|
||||
LibSignature.Signature memory sig;
|
||||
(mtx, sig) = _makeMetaTransactionV2(callData);
|
||||
zeroExDeployed.zeroEx.executeMetaTransactionV2(mtx, sig);
|
||||
}
|
||||
|
||||
// batch
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_rfqOrder() external {
|
||||
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
|
||||
rfqOrder.taker = otherSignerAddress;
|
||||
_mintTo(address(rfqOrder.takerToken), otherSignerAddress, rfqOrder.takerAmount);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeRfqSubcall(rfqOrder)),
|
||||
rfqOrder.takerAmount,
|
||||
rfqOrder.makerAmount
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_otcOrder() external {
|
||||
LibNativeOrder.OtcOrder memory otcOrder = _makeTestOtcOrder();
|
||||
otcOrder.taker = otherSignerAddress;
|
||||
_mintTo(address(otcOrder.takerToken), otherSignerAddress, otcOrder.takerAmount);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeOtcSubcall(otcOrder)),
|
||||
otcOrder.takerAmount,
|
||||
otcOrder.makerAmount
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_uniswapV2() external {
|
||||
_createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeUniswapV2BatchSubcall(_makeArray(address(dai), address(zrx)), 1e18, false)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_uniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(zrx)), 1e18)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_liquidityProvider() external {
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
_mintTo(address(zrx), address(liquidityProvider), 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeMockLiquidityProviderBatchSubcall(1e18)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_transformErc20() external {
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeMockTransformERC20Subcall(dai, zrx, 1e18, 1e18)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_rfqOrderUniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
|
||||
rfqOrder.taker = otherSignerAddress;
|
||||
_mintTo(address(dai), otherSignerAddress, 2e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(
|
||||
_makeRfqSubcall(rfqOrder),
|
||||
_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(zrx)), 1e18)
|
||||
),
|
||||
2e18,
|
||||
11e18
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_rfqOrderFallbackUniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 2e18);
|
||||
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
|
||||
rfqOrder.taker = otherSignerAddress;
|
||||
rfqOrder.expiry = 1;
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(
|
||||
_makeRfqSubcall(rfqOrder),
|
||||
_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(zrx)), 1e18)
|
||||
),
|
||||
1e18,
|
||||
10e18
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_uniswapV3_revertsIfIncorrectAmount() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx;
|
||||
LibSignature.Signature memory sig;
|
||||
(mtx, sig) = _makeMetaTransactionV2(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(zrx)), 5e17)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
|
||||
vm.expectRevert();
|
||||
zeroExDeployed.zeroEx.executeMetaTransactionV2(mtx, sig);
|
||||
}
|
||||
|
||||
// multi hop
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForToken_uniswapV2() external {
|
||||
_createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
|
||||
address[] memory tokens = _makeArray(address(dai), address(zrx));
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
|
||||
tokens,
|
||||
_makeArray(_makeUniswapV2MultiHopSubcall(tokens, false)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForToken_uniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
address[] memory tokens = _makeArray(address(dai), address(zrx));
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
|
||||
tokens,
|
||||
_makeArray(_makeUniswapV3MultiHopSubcall(tokens)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForToken_liquidityProvider() external {
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
_mintTo(address(zrx), address(liquidityProvider), 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
|
||||
_makeArray(address(dai), address(zrx)),
|
||||
_makeArray(_makeMockLiquidityProviderMultiHopSubcall()),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForToken_uniswapV2UniswapV3() external {
|
||||
_createUniswapV2Pool(uniV2Factory, dai, shib, 10e18, 10e18);
|
||||
_createUniswapV3Pool(uniV3Factory, shib, zrx, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
|
||||
_makeArray(address(dai), address(shib), address(zrx)),
|
||||
_makeArray(
|
||||
_makeUniswapV2MultiHopSubcall(_makeArray(address(dai), address(shib)), false),
|
||||
_makeUniswapV3MultiHopSubcall(_makeArray(address(shib), address(zrx)))
|
||||
),
|
||||
1e18,
|
||||
10e18
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// batch for eth
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForEth_uniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, weth, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForEth.selector,
|
||||
dai,
|
||||
_makeArray(_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(weth)), 1e18)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForEth_rfqOrderUniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, weth, 10e18, 10e18);
|
||||
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
|
||||
rfqOrder.taker = otherSignerAddress;
|
||||
rfqOrder.makerToken = weth;
|
||||
_mintTo(address(weth), rfqOrder.maker, rfqOrder.makerAmount);
|
||||
_mintTo(address(dai), otherSignerAddress, 2e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForEth.selector,
|
||||
dai,
|
||||
_makeArray(
|
||||
_makeRfqSubcall(rfqOrder),
|
||||
_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(weth)), 1e18)
|
||||
),
|
||||
2e18,
|
||||
11e18
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// nested
|
||||
|
||||
function test_metaTransaction_multiplexBatchSellTokenForToken_nestedUniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
address[] memory tokens = _makeArray(address(dai), address(zrx));
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken.selector,
|
||||
dai,
|
||||
zrx,
|
||||
_makeArray(
|
||||
_makeNestedMultiHopSellSubcall(tokens, _makeArray(_makeUniswapV3MultiHopSubcall(tokens)), 1e18)
|
||||
),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForToken_nestedUniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
|
||||
_makeArray(address(dai), address(zrx)),
|
||||
_makeArray(
|
||||
_makeNestedBatchSellSubcall(
|
||||
_makeArray(_makeUniswapV3BatchSubcall(_makeArray(address(dai), address(zrx)), 1e18))
|
||||
)
|
||||
),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// multi hop for eth
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForEth_uniswapV3() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, weth, 10e18, 10e18);
|
||||
address[] memory tokens = _makeArray(address(dai), address(weth));
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForEth.selector,
|
||||
tokens,
|
||||
_makeArray(_makeUniswapV3MultiHopSubcall(tokens)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForEth_uniswapV3_revertsNotWeth() external {
|
||||
_createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
|
||||
address[] memory tokens = _makeArray(address(dai), address(zrx));
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
IMetaTransactionsFeatureV2.MetaTransactionDataV2 memory mtx;
|
||||
LibSignature.Signature memory sig;
|
||||
(mtx, sig) = _makeMetaTransactionV2(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForEth.selector,
|
||||
tokens,
|
||||
_makeArray(_makeUniswapV3MultiHopSubcall(tokens)),
|
||||
1e18,
|
||||
1
|
||||
)
|
||||
);
|
||||
|
||||
vm.expectRevert("MetaTransactionsFeature::multiplexMultiHopSellTokenForEth/NOT_WETH");
|
||||
zeroExDeployed.zeroEx.executeMetaTransactionV2(mtx, sig);
|
||||
}
|
||||
|
||||
function test_metaTransaction_multiplexMultiHopSellTokenForEth_uniswapV2UniswapV3() external {
|
||||
_createUniswapV2Pool(uniV2Factory, dai, shib, 10e18, 10e18);
|
||||
_createUniswapV3Pool(uniV3Factory, shib, weth, 10e18, 10e18);
|
||||
_mintTo(address(dai), otherSignerAddress, 1e18);
|
||||
|
||||
_executeMetaTransaction(
|
||||
abi.encodeWithSelector(
|
||||
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
|
||||
_makeArray(address(dai), address(shib), address(weth)),
|
||||
_makeArray(
|
||||
_makeUniswapV2MultiHopSubcall(_makeArray(address(dai), address(shib)), false),
|
||||
_makeUniswapV3MultiHopSubcall(_makeArray(address(shib), address(weth)))
|
||||
),
|
||||
1e18,
|
||||
10e18
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -4,62 +4,48 @@
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec",
|
||||
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
|
||||
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
|
||||
"KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec"
|
||||
},
|
||||
"56": {
|
||||
"UniswapV2Router": "0x10ed43c718714eb63d5aa57b78b54704e256024e",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c",
|
||||
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
|
||||
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
|
||||
"KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c"
|
||||
},
|
||||
"137": {
|
||||
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2",
|
||||
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
|
||||
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
|
||||
"KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2"
|
||||
},
|
||||
"43114": {
|
||||
"UniswapV2Router": "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf",
|
||||
"TraderJoeV2Pool": "0x1D7A1a79e2b4Ef88D2323f3845246D24a3c20F1d",
|
||||
"TraderJoeV2Router": "0xE3Ffc583dC176575eEA7FD9dF2A7c65F7E23f4C3"
|
||||
"KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf"
|
||||
},
|
||||
"250": {
|
||||
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01",
|
||||
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
|
||||
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
|
||||
"KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01"
|
||||
},
|
||||
"10": {
|
||||
"UniswapV2Router": "0x0000000000000000000000000000000000000000",
|
||||
"UniswapV3Router": "0x61ffe014ba17989e743c5f6cb21bf9697530b21e",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5",
|
||||
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
|
||||
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
|
||||
"KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5"
|
||||
},
|
||||
"42161": {
|
||||
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea",
|
||||
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
|
||||
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
|
||||
"KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea"
|
||||
}
|
||||
}
|
||||
|
@@ -20,8 +20,8 @@
|
||||
"43114": {
|
||||
"WrappedNativeToken": "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
|
||||
"DAI": "0xd586e7f844cea2f87f50152665bcbc2c279d8d70",
|
||||
"USDC": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
||||
"USDT": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"
|
||||
"USDC": "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664",
|
||||
"USDT": "0xc7198437980c041c805A1EDcbA50c1Ce5db95118"
|
||||
},
|
||||
"250": {
|
||||
"WrappedNativeToken": "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83",
|
||||
|
@@ -1,219 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../utils/ForkUtils.sol";
|
||||
import "../utils/TestUtils.sol";
|
||||
import "src/IZeroEx.sol";
|
||||
import "@0x/contracts-erc20/src/IEtherToken.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/features/multiplex/MultiplexFeature.sol";
|
||||
import "src/external/TransformerDeployer.sol";
|
||||
import "src/transformers/WethTransformer.sol";
|
||||
import "src/transformers/FillQuoteTransformer.sol";
|
||||
import "src/transformers/bridges/BridgeProtocols.sol";
|
||||
import "src/features/OtcOrdersFeature.sol";
|
||||
|
||||
contract MultiplexRfqtTest is Test, ForkUtils, TestUtils {
|
||||
using LibERC20TokenV06 for IERC20Token;
|
||||
using LibERC20TokenV06 for IEtherToken;
|
||||
|
||||
function setUp() public {
|
||||
_setup();
|
||||
}
|
||||
|
||||
function test_swapEthForUSDTThroughFqtOtcs() public {
|
||||
log_string("SwapEthForUSDTThroughFqtOtc");
|
||||
/* */
|
||||
for (uint256 i = 0; i < 1; i++) {
|
||||
//skip fantom/avax failing test
|
||||
if (i == 3 || i == 4) {
|
||||
continue;
|
||||
}
|
||||
vm.selectFork(forkIds[chains[i]]);
|
||||
log_named_string(" Selecting Fork On", chains[i]);
|
||||
vm.deal(address(this), 1e18);
|
||||
labelAddresses(
|
||||
chains[i],
|
||||
indexChainsByChain[chains[i]],
|
||||
getTokens(i),
|
||||
getContractAddresses(i),
|
||||
getLiquiditySourceAddresses(i)
|
||||
);
|
||||
|
||||
//redeploy and migrate multiplexFeature and OtcOrders for logging
|
||||
|
||||
MultiplexFeature multiplexFeature = new MultiplexFeature(
|
||||
address(IZERO_EX),
|
||||
IEtherToken(tokens.WrappedNativeToken),
|
||||
ILiquidityProviderSandbox(addresses.exchangeProxyLiquidityProviderSandbox),
|
||||
address(0), // uniswapFactory
|
||||
address(0), // sushiswapFactory
|
||||
bytes32(0), // uniswapPairInitCodeHash
|
||||
bytes32(0) // sushiswapPairInitCodeHash
|
||||
);
|
||||
|
||||
OtcOrdersFeature otcOrdersFeature = new OtcOrdersFeature(
|
||||
address(addresses.exchangeProxy),
|
||||
tokens.WrappedNativeToken
|
||||
);
|
||||
|
||||
vm.label(address(multiplexFeature), "zeroEx/NewMultiplexFeature");
|
||||
vm.label(address(otcOrdersFeature), "zeroEx/NewOtcOrdersFeature");
|
||||
vm.prank(IZeroEx(addresses.exchangeProxy).owner());
|
||||
|
||||
IZeroEx(addresses.exchangeProxy).migrate(
|
||||
address(otcOrdersFeature),
|
||||
abi.encodeWithSelector(OtcOrdersFeature.migrate.selector),
|
||||
address(addresses.exchangeProxy)
|
||||
);
|
||||
vm.prank(IZeroEx(addresses.exchangeProxy).owner());
|
||||
IZeroEx(addresses.exchangeProxy).migrate(
|
||||
address(multiplexFeature),
|
||||
abi.encodeWithSelector(MultiplexFeature.migrate.selector),
|
||||
address(addresses.exchangeProxy)
|
||||
);
|
||||
swapMultihopOtc(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* solhint-disable function-max-lines */
|
||||
|
||||
function swapMultihopOtc(
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses,
|
||||
LiquiditySources memory sources
|
||||
) public onlyForked {
|
||||
IZERO_EX = IZeroEx(addresses.exchangeProxy);
|
||||
|
||||
address[] memory tradeTokens = new address[](3);
|
||||
tradeTokens[0] = address(tokens.WrappedNativeToken);
|
||||
tradeTokens[1] = address(tokens.USDC);
|
||||
tradeTokens[2] = address(tokens.DAI);
|
||||
|
||||
deal(tradeTokens[0], address(this), 1e18);
|
||||
|
||||
tokens.WrappedNativeToken.approveIfBelow(addresses.exchangeProxy, uint(-1));
|
||||
uint inputAmount = 1e18;
|
||||
uint outputAmount = 5e17;
|
||||
|
||||
IMultiplexFeature.MultiHopSellSubcall[] memory subcalls = new IMultiplexFeature.MultiHopSellSubcall[](2);
|
||||
|
||||
IMultiplexFeature.MultiHopSellSubcall memory subcall1;
|
||||
subcall1.id = IMultiplexFeature.MultiplexSubcall.OTC;
|
||||
|
||||
//subcall.data = abi.encode(address[], LibNativeOrder.OtcOrder, LibSignature.Signature);
|
||||
(LibNativeOrder.OtcOrder memory order1, LibSignature.Signature memory signature1) = createOtcOrder(
|
||||
tokens.WrappedNativeToken,
|
||||
tokens.USDC,
|
||||
1e18,
|
||||
5e17,
|
||||
0
|
||||
);
|
||||
subcall1.data = abi.encode(order1, signature1);
|
||||
|
||||
IMultiplexFeature.MultiHopSellSubcall memory subcall2;
|
||||
subcall2.id = IMultiplexFeature.MultiplexSubcall.OTC;
|
||||
(LibNativeOrder.OtcOrder memory order2, LibSignature.Signature memory signature2) = createOtcOrder(
|
||||
tokens.USDC,
|
||||
tokens.DAI,
|
||||
5e17,
|
||||
5e17,
|
||||
1
|
||||
);
|
||||
subcall2.data = abi.encode(order2, signature2);
|
||||
|
||||
subcalls[0] = subcall1;
|
||||
subcalls[1] = subcall2;
|
||||
|
||||
uint balanceBefore = tokens.DAI.balanceOf(address(this));
|
||||
emit log_named_uint("DAI Balance Before", balanceBefore);
|
||||
emit log_string("Multihop Rfqt: WETH->USDC->DAI");
|
||||
|
||||
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
|
||||
/// via the given sequence of tokens and calls.
|
||||
/// The last token in `tokens` is the output token that
|
||||
/// will ultimately be sent to `msg.sender`
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
IZERO_EX.multiplexMultiHopSellTokenForToken(
|
||||
// input token[] [input, intermediate, output]
|
||||
tradeTokens,
|
||||
//array of subcalls [{},{}]
|
||||
subcalls,
|
||||
// input token amount
|
||||
inputAmount,
|
||||
// min output token amount
|
||||
outputAmount
|
||||
);
|
||||
uint balanceAfter = tokens.DAI.balanceOf(address(this));
|
||||
emit log_named_uint("DAI Balance After", balanceAfter - balanceBefore);
|
||||
require(balanceAfter >= 5e17, "Failed: UNDERBOUGHT");
|
||||
}
|
||||
|
||||
function createOtcOrder(
|
||||
IERC20Token inputToken,
|
||||
IERC20Token ouputToken,
|
||||
uint128 takerAmount,
|
||||
uint128 makerAmount,
|
||||
uint bump
|
||||
) public returns (LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) {
|
||||
LibNativeOrder.OtcOrder memory order;
|
||||
LibSignature.Signature memory signature;
|
||||
order.makerToken = ouputToken;
|
||||
order.takerToken = inputToken;
|
||||
order.takerAmount = takerAmount;
|
||||
order.makerAmount = makerAmount;
|
||||
uint privateKey;
|
||||
|
||||
(order.maker, privateKey) = _getSigner();
|
||||
|
||||
deal(address(order.makerToken), order.maker, 2e20);
|
||||
deal(address(order.takerToken), order.maker, 2e20);
|
||||
|
||||
vm.startPrank(order.maker);
|
||||
IERC20Token(order.makerToken).approveIfBelow(addresses.exchangeProxy, 2e20);
|
||||
|
||||
order.taker = address(0);
|
||||
order.txOrigin = address(tx.origin);
|
||||
order.expiryAndNonce = encodeExpiryAndNonce(order.maker, bump);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, IZERO_EX.getOtcOrderHash(order));
|
||||
|
||||
vm.stopPrank();
|
||||
signature.signatureType = LibSignature.SignatureType.EIP712;
|
||||
signature.v = v;
|
||||
signature.r = r;
|
||||
signature.s = s;
|
||||
|
||||
return (order, signature);
|
||||
}
|
||||
|
||||
/* solhint-enable function-max-lines */
|
||||
function encodeExpiryAndNonce(address maker, uint bump) public returns (uint256) {
|
||||
uint256 expiry = (block.timestamp + 120) << 192;
|
||||
uint256 bucket = (0 + bump) << 128;
|
||||
uint256 nonce = vm.getNonce(maker);
|
||||
return expiry | bucket | nonce;
|
||||
}
|
||||
}
|
@@ -54,75 +54,6 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
function test_swapERC20ForERC20OnTraderJoeV2() public {
|
||||
for (uint256 i = 0; i < chains.length; i++) {
|
||||
// TraderJoeV2 mixin only enabled on Avalanche
|
||||
if (i != 3) {
|
||||
continue;
|
||||
}
|
||||
vm.selectFork(forkIds[chains[i]]);
|
||||
labelAddresses(
|
||||
chains[i],
|
||||
indexChainsByChain[chains[i]],
|
||||
getTokens(i),
|
||||
getContractAddresses(i),
|
||||
getLiquiditySourceAddresses(i)
|
||||
);
|
||||
swapOnTraderJoeV2(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i));
|
||||
}
|
||||
}
|
||||
|
||||
function swapOnTraderJoeV2(
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses,
|
||||
LiquiditySources memory sources
|
||||
) public onlyForked {
|
||||
if (sources.TraderJoeV2Router == address(0)) {
|
||||
emit log_string("TraderJoeV2Router not available on this chain");
|
||||
return;
|
||||
}
|
||||
if (sources.TraderJoeV2Pool == address(0)) {
|
||||
emit log_string("TraderJoeV2Pool not available on this chain");
|
||||
return;
|
||||
}
|
||||
|
||||
FillQuoteTransformer.TransformData memory fqtData;
|
||||
fqtData.side = FillQuoteTransformer.Side.Sell;
|
||||
fqtData.sellToken = IERC20Token(address(tokens.USDC));
|
||||
fqtData.buyToken = IERC20Token(address(tokens.USDT));
|
||||
fqtData.fillSequence = new FillQuoteTransformer.OrderType[](1);
|
||||
fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge;
|
||||
fqtData.fillAmount = 1e6;
|
||||
|
||||
(uint256 amountOut, uint256 binStep) = sampleTraderJoeV2(
|
||||
fqtData.fillAmount,
|
||||
address(fqtData.sellToken),
|
||||
address(fqtData.buyToken),
|
||||
sources.TraderJoeV2Router,
|
||||
sources.TraderJoeV2Pool
|
||||
);
|
||||
log_named_uint("amountOut", amountOut);
|
||||
|
||||
IBridgeAdapter.BridgeOrder memory order;
|
||||
{
|
||||
address[] memory tokenPath = new address[](2);
|
||||
tokenPath[0] = address(fqtData.sellToken);
|
||||
tokenPath[1] = address(fqtData.buyToken);
|
||||
uint256[] memory binSteps = new uint256[](1);
|
||||
binSteps[0] = binStep;
|
||||
order.bridgeData = abi.encode(address(sources.TraderJoeV2Router), tokenPath, binSteps);
|
||||
}
|
||||
|
||||
order.source = bytes32(uint256(BridgeProtocols.TRADERJOEV2) << 128);
|
||||
order.takerTokenAmount = fqtData.fillAmount;
|
||||
order.makerTokenAmount = amountOut;
|
||||
|
||||
fqtData.bridgeOrders = new IBridgeAdapter.BridgeOrder[](1);
|
||||
fqtData.bridgeOrders[0] = order;
|
||||
|
||||
settleAndLogBalances(fqtData, tokens, addresses);
|
||||
}
|
||||
|
||||
function swapOnKyberElastic(
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses,
|
||||
@@ -140,6 +71,20 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
|
||||
emit log_string("KyberElasticPool not available on this chain");
|
||||
return;
|
||||
}
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](2);
|
||||
|
||||
transformations[0].deploymentNonce = _findTransformerNonce(
|
||||
address(addresses.transformers.wethTransformer),
|
||||
address(addresses.exchangeProxyTransformerDeployer)
|
||||
);
|
||||
emit log_named_uint("WethTransformer nonce", transformations[0].deploymentNonce);
|
||||
createNewFQT(tokens.WrappedNativeToken, addresses.exchangeProxy, addresses.exchangeProxyTransformerDeployer);
|
||||
transformations[0].data = abi.encode(LibERC20Transformer.ETH_TOKEN_ADDRESS, 1e18);
|
||||
transformations[1].deploymentNonce = _findTransformerNonce(
|
||||
address(fillQuoteTransformer),
|
||||
address(addresses.exchangeProxyTransformerDeployer)
|
||||
);
|
||||
emit log_named_uint("FillQuoteTransformer nonce", transformations[1].deploymentNonce);
|
||||
|
||||
FillQuoteTransformer.TransformData memory fqtData;
|
||||
fqtData.side = FillQuoteTransformer.Side.Sell;
|
||||
@@ -166,8 +111,37 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
|
||||
order.makerTokenAmount = amountOut;
|
||||
order.bridgeData = abi.encode(address(sources.KyberElasticRouter), path);
|
||||
fqtData.bridgeOrders[0] = order;
|
||||
transformations[1].data = abi.encode(fqtData);
|
||||
|
||||
settleAndLogBalances(fqtData, tokens, addresses);
|
||||
vm.deal(address(this), 1e18);
|
||||
uint256 balanceETHBefore = address(this).balance;
|
||||
uint256 balanceERC20Before = IERC20Token(tokens.USDT).balanceOf(address(this));
|
||||
|
||||
writeTokenBalance(address(this), address(tokens.USDC), 1e16);
|
||||
uint256 balanceUSDCbefore = IERC20Token(tokens.USDC).balanceOf(address(this));
|
||||
|
||||
IERC20Token(address(tokens.USDC)).approve(addresses.exchangeProxy, 1e16);
|
||||
|
||||
IZeroEx(payable(addresses.exchangeProxy)).transformERC20{value: 1e18}(
|
||||
// input token
|
||||
IERC20Token(address(tokens.USDC)),
|
||||
// output token
|
||||
IERC20Token(address(tokens.USDT)),
|
||||
// input token amount
|
||||
1e6,
|
||||
// min output token amount
|
||||
order.makerTokenAmount,
|
||||
// list of transform
|
||||
transformations
|
||||
);
|
||||
|
||||
log_named_uint("NativeAsset balance before", balanceETHBefore);
|
||||
log_named_uint("ERC-20 balance before", balanceERC20Before);
|
||||
log_named_uint("NativeAsset balance after", balanceETHBefore - address(this).balance);
|
||||
log_named_uint("ERC-20 balance after", IERC20Token(tokens.USDT).balanceOf(address(this)) - balanceERC20Before);
|
||||
log_named_uint("USDC balance before", balanceUSDCbefore);
|
||||
log_named_uint("USDC balance after", IERC20Token(tokens.USDT).balanceOf(address(tokens.USDC)));
|
||||
assert(IERC20Token(tokens.USDT).balanceOf(address(this)) > 0);
|
||||
}
|
||||
|
||||
function sampleKyberElastic(
|
||||
@@ -176,7 +150,7 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
|
||||
address makerToken,
|
||||
address quoter,
|
||||
address pool
|
||||
) private returns (uint256 makerTokenAmount, bytes memory path) {
|
||||
) public returns (uint256 makerTokenAmount, bytes memory path) {
|
||||
log_string(" Sampling KyberElastic for tokens");
|
||||
log_named_address(" ", takerToken);
|
||||
log_string(" -> ");
|
||||
@@ -193,64 +167,4 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
|
||||
(uint256 amountOut, , , ) = kyberQuoter.quoteExactInput(path, amount);
|
||||
return (amountOut, path);
|
||||
}
|
||||
|
||||
function sampleTraderJoeV2(
|
||||
uint256 amount,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
address router,
|
||||
address pool
|
||||
) private returns (uint256 makerTokenAmount, uint256 binStep) {
|
||||
log_string("Sampling TraderJoeV2");
|
||||
log_named_address("takerToken", takerToken);
|
||||
log_named_address("makerToken", makerToken);
|
||||
log_named_address("router", router);
|
||||
log_named_address("pool", pool);
|
||||
|
||||
bool swapForY = ITraderJoeV2Pool(pool).tokenY() == makerToken;
|
||||
|
||||
(makerTokenAmount, ) = ITraderJoeV2Router(router).getSwapOut(pool, amount, swapForY);
|
||||
|
||||
binStep = ITraderJoeV2Pool(pool).feeParameters().binStep;
|
||||
}
|
||||
|
||||
function deployFQTAndGetDeploymentNonce(
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses
|
||||
) private returns (uint32) {
|
||||
createNewFQT(tokens.WrappedNativeToken, addresses.exchangeProxy, addresses.exchangeProxyTransformerDeployer);
|
||||
return
|
||||
_findTransformerNonce(address(fillQuoteTransformer), address(addresses.exchangeProxyTransformerDeployer));
|
||||
}
|
||||
|
||||
function settleAndLogBalances(
|
||||
FillQuoteTransformer.TransformData memory fqtData,
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses
|
||||
) private {
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](1);
|
||||
transformations[0].deploymentNonce = deployFQTAndGetDeploymentNonce(tokens, addresses);
|
||||
transformations[0].data = abi.encode(fqtData);
|
||||
|
||||
address sellToken = address(fqtData.sellToken);
|
||||
address buyToken = address(fqtData.buyToken);
|
||||
|
||||
writeTokenBalance(address(this), sellToken, 1e16);
|
||||
uint256 sellTokenBalanceBefore = IERC20Token(sellToken).balanceOf(address(this));
|
||||
uint256 buyTokenBalanceBefore = IERC20Token(buyToken).balanceOf(address(this));
|
||||
|
||||
IERC20Token(sellToken).approve(addresses.exchangeProxy, 1e16);
|
||||
IZeroEx(payable(addresses.exchangeProxy)).transformERC20(
|
||||
IERC20Token(sellToken),
|
||||
IERC20Token(buyToken),
|
||||
fqtData.fillAmount,
|
||||
fqtData.bridgeOrders[0].makerTokenAmount,
|
||||
transformations
|
||||
);
|
||||
|
||||
log_named_uint("sellToken balance before", sellTokenBalanceBefore);
|
||||
log_named_uint("sellToken balance after", IERC20Token(sellToken).balanceOf(address(this)));
|
||||
log_named_uint("buyToken balance before", buyTokenBalanceBefore);
|
||||
log_named_uint("buyToken balance after", IERC20Token(buyToken).balanceOf(address(this)));
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,6 @@ import "src/features/FundRecoveryFeature.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/features/OtcOrdersFeature.sol";
|
||||
import "src/features/MetaTransactionsFeature.sol";
|
||||
import "src/features/MetaTransactionsFeatureV2.sol";
|
||||
import "src/features/nft_orders/ERC1155OrdersFeature.sol";
|
||||
import "src/features/nft_orders/ERC721OrdersFeature.sol";
|
||||
import "src/features/UniswapFeature.sol";
|
||||
@@ -69,7 +68,6 @@ contract DeployZeroEx is Test {
|
||||
FundRecoveryFeature fundRecoveryFeature;
|
||||
TransformERC20Feature transformERC20Feature;
|
||||
MetaTransactionsFeature metaTransactionsFeature;
|
||||
MetaTransactionsFeatureV2 metaTransactionsFeatureV2;
|
||||
ERC1155OrdersFeature erc1155OrdersFeature;
|
||||
ERC721OrdersFeature erc721OrdersFeature;
|
||||
MultiplexFeature multiplexFeature;
|
||||
@@ -135,10 +133,6 @@ contract DeployZeroEx is Test {
|
||||
emit log_named_address("UniswapV3Feature", address(ZERO_EX_DEPLOYED.features.uniswapV3Feature));
|
||||
emit log_named_address("FundRecoveryFeature", address(ZERO_EX_DEPLOYED.features.fundRecoveryFeature));
|
||||
emit log_named_address("MetaTransactionsFeature", address(ZERO_EX_DEPLOYED.features.metaTransactionsFeature));
|
||||
emit log_named_address(
|
||||
"MetaTransactionsFeatureV2",
|
||||
address(ZERO_EX_DEPLOYED.features.metaTransactionsFeatureV2)
|
||||
);
|
||||
emit log_named_address("ERC1155OrdersFeature", address(ZERO_EX_DEPLOYED.features.erc1155OrdersFeature));
|
||||
emit log_named_address("ERC721OrdersFeature", address(ZERO_EX_DEPLOYED.features.erc721OrdersFeature));
|
||||
emit log_named_address("TransformERC20Feature", address(ZERO_EX_DEPLOYED.features.transformERC20Feature));
|
||||
@@ -207,10 +201,6 @@ contract DeployZeroEx is Test {
|
||||
);
|
||||
ZERO_EX_DEPLOYED.features.fundRecoveryFeature = new FundRecoveryFeature();
|
||||
ZERO_EX_DEPLOYED.features.metaTransactionsFeature = new MetaTransactionsFeature(address(ZERO_EX));
|
||||
ZERO_EX_DEPLOYED.features.metaTransactionsFeatureV2 = new MetaTransactionsFeatureV2(
|
||||
address(ZERO_EX),
|
||||
ZERO_EX_DEPLOYED.weth
|
||||
);
|
||||
ZERO_EX_DEPLOYED.features.erc1155OrdersFeature = new ERC1155OrdersFeature(
|
||||
address(ZERO_EX),
|
||||
ZERO_EX_DEPLOYED.weth
|
||||
@@ -280,11 +270,6 @@ contract DeployZeroEx is Test {
|
||||
abi.encodeWithSelector(MetaTransactionsFeature.migrate.selector),
|
||||
address(this)
|
||||
);
|
||||
IZERO_EX.migrate(
|
||||
address(ZERO_EX_DEPLOYED.features.metaTransactionsFeatureV2),
|
||||
abi.encodeWithSelector(MetaTransactionsFeatureV2.migrate.selector),
|
||||
address(this)
|
||||
);
|
||||
IZERO_EX.migrate(
|
||||
address(ZERO_EX_DEPLOYED.features.erc1155OrdersFeature),
|
||||
abi.encodeWithSelector(ERC1155OrdersFeature.migrate.selector),
|
||||
|
@@ -88,13 +88,10 @@ struct TokenAddresses {
|
||||
IEtherToken WrappedNativeToken;
|
||||
}
|
||||
|
||||
// keep the names of the struct members in alphabetical order for correct json unparsing
|
||||
struct LiquiditySources {
|
||||
address KyberElasticPool;
|
||||
address KyberElasticQuoter;
|
||||
address KyberElasticRouter;
|
||||
address TraderJoeV2Pool;
|
||||
address TraderJoeV2Router;
|
||||
address UniswapV2Router;
|
||||
address UniswapV3Router;
|
||||
}
|
||||
@@ -103,37 +100,6 @@ interface IFQT {
|
||||
function bridgeAdapter() external returns (address);
|
||||
}
|
||||
|
||||
interface ITraderJoeV2Pool {
|
||||
struct FeeParameters {
|
||||
// 144 lowest bits in slot
|
||||
uint16 binStep;
|
||||
uint16 baseFactor;
|
||||
uint16 filterPeriod;
|
||||
uint16 decayPeriod;
|
||||
uint16 reductionFactor;
|
||||
uint24 variableFeeControl;
|
||||
uint16 protocolShare;
|
||||
uint24 maxVolatilityAccumulated;
|
||||
// 112 highest bits in slot
|
||||
uint24 volatilityAccumulated;
|
||||
uint24 volatilityReference;
|
||||
uint24 indexRef;
|
||||
uint40 time;
|
||||
}
|
||||
|
||||
function feeParameters() external view returns (FeeParameters memory);
|
||||
|
||||
function tokenY() external view returns (address);
|
||||
}
|
||||
|
||||
interface ITraderJoeV2Router {
|
||||
function getSwapOut(
|
||||
address pool,
|
||||
uint256 amountIn,
|
||||
bool swapForY
|
||||
) external view returns (uint256 amountOut, uint256 feesIn);
|
||||
}
|
||||
|
||||
interface IKyberElasticQuoter {
|
||||
function quoteExactInput(
|
||||
bytes memory path,
|
||||
|
@@ -54,9 +54,6 @@ contract LocalTest is Test, TestUtils {
|
||||
address internal signerAddress;
|
||||
uint256 internal signerKey;
|
||||
|
||||
address internal otherSignerAddress;
|
||||
uint256 internal otherSignerKey;
|
||||
|
||||
function _infiniteApprovals() private {
|
||||
shib.approve(address(zeroExDeployed.zeroEx), type(uint256).max);
|
||||
dai.approve(address(zeroExDeployed.zeroEx), type(uint256).max);
|
||||
@@ -95,23 +92,12 @@ contract LocalTest is Test, TestUtils {
|
||||
zrx = IERC20Token(address(new TestMintableERC20Token()));
|
||||
weth = zeroExDeployed.weth;
|
||||
|
||||
// TODO this should be somewhere else
|
||||
string memory mnemonic = "conduct into noodle wreck before satisfy alarm vendor dose lunch vapor party";
|
||||
otherSignerKey = vm.deriveKey(mnemonic, 0);
|
||||
otherSignerAddress = vm.addr(otherSignerKey);
|
||||
vm.label(otherSignerAddress, "zeroEx/OtherGuy");
|
||||
|
||||
_infiniteApprovals();
|
||||
|
||||
vm.startPrank(signerAddress);
|
||||
_infiniteApprovals();
|
||||
vm.stopPrank();
|
||||
|
||||
vm.startPrank(otherSignerAddress);
|
||||
_infiniteApprovals();
|
||||
vm.stopPrank();
|
||||
|
||||
vm.deal(address(this), 20e18);
|
||||
vm.deal(address(this), 10e18);
|
||||
}
|
||||
|
||||
function _mintTo(address token, address recipient, uint256 amount) internal {
|
||||
|
@@ -20,8 +20,6 @@ import "forge-std/Test.sol";
|
||||
import "src/transformers/LibERC20Transformer.sol";
|
||||
|
||||
contract TestUtils is Test {
|
||||
address private constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000;
|
||||
|
||||
function _findTransformerNonce(address transformer, address deployer) internal pure returns (uint32) {
|
||||
address current;
|
||||
for (uint32 i = 0; i < 1024; i++) {
|
||||
|
@@ -30,7 +30,6 @@
|
||||
"generated-artifacts/LiquidityProviderFeature.json",
|
||||
"generated-artifacts/LogMetadataTransformer.json",
|
||||
"generated-artifacts/MetaTransactionsFeature.json",
|
||||
"generated-artifacts/MetaTransactionsFeatureV2.json",
|
||||
"generated-artifacts/MultiplexFeature.json",
|
||||
"generated-artifacts/NativeOrdersFeature.json",
|
||||
"generated-artifacts/OptimismBridgeAdapter.json",
|
||||
@@ -89,7 +88,6 @@
|
||||
"test/generated-artifacts/ILiquidityProviderFeature.json",
|
||||
"test/generated-artifacts/ILiquidityProviderSandbox.json",
|
||||
"test/generated-artifacts/IMetaTransactionsFeature.json",
|
||||
"test/generated-artifacts/IMetaTransactionsFeatureV2.json",
|
||||
"test/generated-artifacts/IMooniswapPool.json",
|
||||
"test/generated-artifacts/IMultiplexFeature.json",
|
||||
"test/generated-artifacts/INativeOrdersEvents.json",
|
||||
@@ -143,7 +141,6 @@
|
||||
"test/generated-artifacts/LiquidityProviderSandbox.json",
|
||||
"test/generated-artifacts/LogMetadataTransformer.json",
|
||||
"test/generated-artifacts/MetaTransactionsFeature.json",
|
||||
"test/generated-artifacts/MetaTransactionsFeatureV2.json",
|
||||
"test/generated-artifacts/MixinAaveV2.json",
|
||||
"test/generated-artifacts/MixinBalancer.json",
|
||||
"test/generated-artifacts/MixinBalancerV2Batch.json",
|
||||
|
BIN
docs/_static/img/logo.png
vendored
BIN
docs/_static/img/logo.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
15
docs/_static/img/logo.svg
generated
vendored
Normal file
15
docs/_static/img/logo.svg
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="1028" height="500" viewBox="0 0 1028 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M656.054 246.637C656.054 163.677 690.359 122.422 743.722 122.422C796.861 122.422 831.839 164.35 831.839 246.637C831.839 328.924 796.861 370.628 743.722 370.628C690.583 370.628 656.054 328.924 656.054 246.637ZM743.498 149.327C704.933 149.327 685.874 185.426 685.874 246.861C685.874 275.785 690.135 298.206 698.206 314.574L773.991 159.641C765.247 152.691 755.157 149.327 743.498 149.327ZM713.677 334.305C722.197 340.807 732.063 344.171 743.722 344.171C782.287 344.171 801.794 307.848 801.794 246.861C801.794 218.61 797.758 196.413 789.462 179.597L713.677 334.305Z" fill="black"/>
|
||||
<path d="M1022.87 194.17L973.991 277.13L1027.35 366.368H994.619L951.794 294.17H939.686L895.964 366.368H864.126L917.713 278.251L869.507 194.17H900.449L939.91 262.556H952.242L992.601 194.17H1022.87Z" fill="black"/>
|
||||
<path d="M105.022 316.009L143.834 275.852L95.583 210.74L34.148 123.812C12.4439 160.852 0 203.969 0 250C0 326.256 34.148 394.529 88.0045 440.381L165.987 385.269C139.462 368.744 118.094 344.686 105.022 316.009Z" fill="black"/>
|
||||
<path d="M183.991 105.022L224.148 143.834L289.26 95.583L376.188 34.148C339.148 12.4439 296.031 0 250 0C173.744 0 105.471 34.148 59.6188 88.0045L114.731 165.987C131.256 139.462 155.314 118.094 183.991 105.022Z" fill="black"/>
|
||||
<path d="M356.166 224.148L404.417 289.26L465.852 376.188C487.556 339.148 500 296.031 500 250C500 173.744 465.852 105.471 411.996 59.6188L334.013 114.731C360.538 131.256 381.906 155.314 394.978 183.991L356.166 224.148Z" fill="black"/>
|
||||
<path d="M440.381 411.996L385.269 334.014C368.744 360.538 344.686 381.906 316.009 394.978L275.852 356.166L210.74 404.417L123.812 465.852C160.852 487.556 203.969 500 250 500C326.256 500 394.529 465.852 440.381 411.996Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="1027.35" height="500" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
BIN
docs/_static/img/smart_nonce_diagram.webp
vendored
BIN
docs/_static/img/smart_nonce_diagram.webp
vendored
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB |
@@ -62,16 +62,16 @@ Function timelocks are represented in days, where one day is equivalent to 86,40
|
||||
.. csv-table::
|
||||
:header: "Contract", "Function", "Selector", "Timelock"
|
||||
|
||||
AllowanceTarget, ``addAuthorizedAddress``, ``42f1181e``, 2 day
|
||||
AllowanceTarget, ``addAuthorizedAddress``, ``42f1181e``, 2 days
|
||||
AllowanceTarget, ``removeAuthorizedAddress``, ``70712939``, 0 days
|
||||
AllowanceTarget, ``removeAuthorizedAddressAtIndex``, ``9ad26744``, 0 days
|
||||
Governor, ``registerFunctionCall``, ``751ad560``, 2 day
|
||||
ExchangeProxy, ``extend``, ``6eb224cb``, 2 day
|
||||
ExchangeProxy, ``migrate``, ``261fe679``, 2 day
|
||||
Governor, ``registerFunctionCall``, ``751ad560``, 2 days
|
||||
ExchangeProxy, ``extend``, ``6eb224cb``, 2 days
|
||||
ExchangeProxy, ``migrate``, ``261fe679``, 2 days
|
||||
ExchangeProxy, ``rollback``, ``9db64a40``, 0 days
|
||||
ExchangeProxy, ``setQuoteSigner``, ``<deprecation in progress>``, 2 day
|
||||
ExchangeProxy, ``setTransformerDeployer``, ``87c96419``, 2 day
|
||||
ExchangeProxy, ``transferOwnership``, ``f2fde38b``, 2 day
|
||||
ExchangeProxy, ``setQuoteSigner``, ``<deprecation in progress>``, 2 days
|
||||
ExchangeProxy, ``setTransformerDeployer``, ``87c96419``, 2 days
|
||||
ExchangeProxy, ``transferOwnership``, ``f2fde38b``, 2 days
|
||||
StakingProxy, ``addExchangeAddress``, ``8a2e271a``, 14 days
|
||||
StakingProxy, ``removeExchangeAddress``, ``01e28d84``, 14 days
|
||||
StakingProxy, ``attachStakingContract``, ``66615d56``, 14 days
|
||||
|
@@ -2,7 +2,7 @@
|
||||
Overview
|
||||
###############################
|
||||
|
||||
The `ZeroEx` (Exchange Proxy) contract implements a delegate-call proxy pattern to create a system of composable smart contracts. This architecture enables 0x Protocol to innovate with minimal friction alongside the growing DeFi ecosystem.
|
||||
The 0x Exchange implements a delegate-call proxy pattern to create a system of composable smart contracts. This architecture enables 0x to innovate with minimal friction alongside the growing DeFi ecosystem.
|
||||
|
||||
The diagram below illustrates our system (click to enlarge).
|
||||
|
||||
@@ -24,7 +24,13 @@ The table below defines our smart contract nomenclature.
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| `Flash Wallet <./flash_wallet.html>`_ | The Flash Wallet is a sandboxed escrow contract that holds funds for Transformers to operate on. For example, the ``WETHtransformer`` wraps any Ether in the Flash Wallet. |
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| `Governor <./governor.html>`_ | A smart contract that governs trusted contracts in the system: Proxy, Features, Flash Wallet. |
|
||||
| `Allowance Target <../basics/allowances.html>`_ | Users set their allowances on this contract. It is scheduled to be deprecated after the official V4 release in January, 2021. After which point allowances will be set directly on the Proxy. |
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| `Governor <./governor.html>`_ | A MultiSig that governs trusted contracts in the system: Proxy, Features, Flash Wallet. |
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| `Transformer Deployer <./transformer_deployer.html>`_ | Deploys Transformers. A transformer is authenticated using a nonce of the Transformer Deployer. |
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| `Fee Collectors <./fee_collectors.html>`_ | `Protocol fees <../basics/protocol_fees.html>`_ are paid into these contracts at time-of-fill. |
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| `PLP Sandbox <./plp_sandbox.html>`_ | `PLP <../advanced/plp.html>`_ liquidity providers are called from this sandbox. |
|
||||
+-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
@@ -2,22 +2,37 @@
|
||||
Proxy
|
||||
###############################
|
||||
|
||||
The `ZeroEx <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/ZeroEx.sol>`_ contract (also called Exchange Proxy or EP) is the main contract that manages the process and performs the exchange of assets. It implements a per-function proxy pattern where each function can have a distinct implementation contract, also known as "features". The `ZeroEx` contract's sole responsibility is to maintain a mapping of "features" to implementation contracts and route (delegate) calls to per-function implementation contracts through its fallback mechanism.
|
||||
The `ZeroEx <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/ZeroEx.sol>`_ contract implements a per-function proxy pattern. Every function registered to the proxy contract can have a distinct implementation contract. Implementation contracts are called “features” and can expose multiple, related functions. Since features can be upgraded independently, there will no longer be a collective “version” of the API, defaulting to a rolling release model. The ZeroEx contract’s only responsibility is to route (delegate) calls to per-function implementation contracts through its fallback.
|
||||
|
||||
.. image:: ../_static/img/proxy.png
|
||||
:align: center
|
||||
:scale: 100%
|
||||
|
||||
View the code for the Proxy `here <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/ZeroEx.sol>`_. The deployed contract address for each network can be found in `here <https://github.com/0xProject/protocol/blob/development/packages/contract-addresses/addresses.json>`_.
|
||||
View the code for the Proxy `here <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/ZeroEx.sol>`_. There is also a `gas-optimized implementation <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/ZeroExOptimized.sol>`_ that may be put into production in the future (there is a lot of overhead for our integrators when redeploying this contract).
|
||||
|
||||
Deployment
|
||||
==========
|
||||
At deployment, the ``ZeroEx`` contract goes through an ``InitialMigration`` that bootstraps two core features to the proxy pattern: Function Registry and Ownership.
|
||||
Bootstrapping
|
||||
=============
|
||||
The ZeroEx contract comes pre-loaded with only one Feature: `Bootstrap <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/features/BootstrapFeature.sol>`_. This exposes a ``bootstrap()`` function that can only be called by the deployer. This function does a few things:
|
||||
|
||||
1. De-register the bootstrap() function, which prevents it being called again.
|
||||
2. Self-destruct.
|
||||
3. Delegatecall the bootstrapper target contract and call data.
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// Execute a bootstrapper in the context of the proxy.
|
||||
function bootstrap(address target, bytes callData) external
|
||||
|
||||
Below is the bootstrap workflow (click to enlarge).
|
||||
|
||||
.. image:: ../_static/img/bootstrap.png
|
||||
:align: center
|
||||
:scale: 70%
|
||||
|
||||
Function Registry
|
||||
=================
|
||||
|
||||
``SimpleFunctionRegistry`` is one of the initial and core features of the `ZeroEx` contract boostrapped in during the ``InitialMigration``. This feature exposes the following function registry management features: ``extend()`` and ``rollback()``.
|
||||
One of the initial features InitialMigration bootstraps into the ZeroEx contract is the function registry feature, SimpleFunctionRegistry. This feature exposes the following function registry management features: ``extend()`` and ``rollback()``.
|
||||
|
||||
Call ``extend()`` to register a new function (selector) and implementation (address). This also maintains a history of past implementations so we can roll back to one, if needed.
|
||||
|
||||
@@ -39,7 +54,7 @@ Call ``rollback()`` to revert a function implementation to a prior version in it
|
||||
|
||||
Ownership
|
||||
=========
|
||||
``Ownable`` is another initial and core feature of the `ZeroEx` contract that is bootstrapped into the proxy during the ``InitialMigration``. This exposes ownership management functions: ``transferOwnership()`` and ``getOwner()``. This feature also enables ubiquitous modifiers such as onlyOwner, so it is an implicit dependency of nearly every other feature.
|
||||
Another Feature, ``InitialMigration``, bootstraps into the proxy is the Ownable feature. This exposes ownership management functions: ``transferOwnership()`` and ``owner()``. This feature also enables ubiquitous modifiers such as onlyOwner, so it is an implicit dependency of nearly every other feature.
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
@@ -241,24 +256,3 @@ Functions can be re-entered by default; those secured by the ``nonReentrant`` mo
|
||||
**Colliding Function Selectors**
|
||||
|
||||
We manually ensure that function selectors do not collide during PR's. See the `Feature Checklist <./features.html#best-practices>`_ for a complete list of our best practices on Feature Development.
|
||||
|
||||
Initial Bootstrapping
|
||||
=====================
|
||||
|
||||
The way that the initial bootstrapping is accomplished is through the ``bootstrap()`` function that can only be called by the deployer. Check `here <https://github.com/0xProject/protocol/blob/development/contracts/zero-ex/contracts/src/features/BootstrapFeature.sol>`_ to see the full boostrapping feature.
|
||||
|
||||
This function does a few things:
|
||||
1. De-register the bootstrap() function, which prevents it being called again.
|
||||
2. Self-destruct.
|
||||
3. Delegatecall the bootstrapper target contract and call data.
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// Execute a bootstrapper in the context of the proxy.
|
||||
function bootstrap(address target, bytes callData) external
|
||||
|
||||
Below is the bootstrap workflow (click to enlarge).
|
||||
|
||||
.. image:: ../_static/img/bootstrap.png
|
||||
:align: center
|
||||
:scale: 70%
|
||||
|
@@ -295,9 +295,9 @@ In both cases, the ``@0x/protocol-utils`` package simplifies generating these si
|
||||
|
||||
The Orderbook
|
||||
=======================
|
||||
Orders can be hosted by any server and are usually represented as a JSON object off-chain. For example, one off-chain way to post and discover orders is through `0x API <https://0x.org/api>`_.
|
||||
Orders are shared through a decentralized and permissionless network, called `0x Mesh <https://0x.org/mesh>`_. The simplest way to post and discover orders is through `0x API <https://0x.org/api>`_. See `this guide <https://0x.org/docs/guides/market-making-on-0x>`_ tailored for Market Makers.
|
||||
|
||||
Below is a table represention and example of how orders should be formatted off-chain.
|
||||
Orders are usually represented as a JSON object off-chain. Below is a table represention and example of how orders should be formatted off-chain.
|
||||
|
||||
JSON representation of RFQ Orders
|
||||
*********************************
|
||||
|
@@ -1,75 +0,0 @@
|
||||
Signatures
|
||||
==========
|
||||
|
||||
Signatures are used in several places in 0x Protocol to prove an actor
|
||||
has agreed to the behavior in some off-chain message.
|
||||
|
||||
Signatures are represented by the following struct:
|
||||
|
||||
.. code:: solidity
|
||||
|
||||
struct Signature {
|
||||
// How to validate the signature.
|
||||
SignatureType signatureType;
|
||||
// EC Signature data.
|
||||
uint8 v;
|
||||
// EC Signature data.
|
||||
bytes32 r;
|
||||
// EC Signature data.
|
||||
bytes32 s;
|
||||
}
|
||||
|
||||
Where ``SignatureType`` is:
|
||||
|
||||
.. code:: solidity
|
||||
|
||||
enum SignatureType {
|
||||
ILLEGAL,
|
||||
INVALID,
|
||||
EIP712,
|
||||
ETHSIGN,
|
||||
PRESIGNED
|
||||
}
|
||||
|
||||
Descriptions of the valid signature types follow.
|
||||
|
||||
**EIP712 (``2``)**
|
||||
|
||||
This is the signature type typically used when an order is signed
|
||||
through a UI, such as Metamask. This is commonly achieved by calling
|
||||
some variant of the ``eth_signTypedData`` (which fully utilizes EIP712)
|
||||
JSONRPC command on the Ethereum provider.
|
||||
|
||||
It can also be generated in a headless manner using a standard
|
||||
``ecsign()`` implementation
|
||||
(`example <https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/modules/_signature_.md#const-ecsign>`__)
|
||||
by re-hashing the `canonical order
|
||||
hash <signatures.md#limit-order-hashes>`__ with a prefix as follows and
|
||||
signing the result:
|
||||
|
||||
.. code:: solidity
|
||||
|
||||
eip712HashToSign = keccak256(abi.encodePacked(
|
||||
"\x19Ethereum Signed Message:\n32",
|
||||
orderHash
|
||||
));
|
||||
|
||||
ETHSIGN (``3``)
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This is the signature type typically used when an order is signed in a
|
||||
headless environment (e.g., script or backend). This commonly achieved
|
||||
by calling the ``eth_sign`` JSONRPC command on the Ethereum provider or,
|
||||
perhaps more straight-forwardly, using a standard ``ecsign()``
|
||||
implementation
|
||||
(`example <https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/modules/_signature_.md#const-ecsign>`__).
|
||||
Unlike the ``EIP712`` signature type, the hash to sign is simply the
|
||||
`canonical order hash <signatures.md#limit-order-hashes>`__ (no prefix).
|
||||
|
||||
PRESIGNED (``4``)
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This signature type is used exclusively with NFT orders (721 and 1155)
|
||||
for now. This value indicates that the order maker has previously marked
|
||||
the order as fillable on-chain. The remaining fields in the
|
||||
``Signature`` struct will be ignored.
|
@@ -1,454 +0,0 @@
|
||||
###############################
|
||||
NFT Guides - Creating Orders
|
||||
###############################
|
||||
|
||||
The easiest way of creating a 0x NFT order is to use the npm packages `@0x/protocol-utils` and `@0x/utils`
|
||||
|
||||
>>> yarn add @0x/protocol-utils @0x/utils
|
||||
or
|
||||
npm install @0x/protocol-utils @0x/utils
|
||||
|
||||
|
||||
Create an ERC721Order
|
||||
=====================
|
||||
|
||||
The following code snippet shows how to construct a basic ERC721 sell order in JavaScript. In the following example, the seller indicates that they would like to receive ether by providing the sentinel value `0xeee...`` as the `erc20Token`.
|
||||
|
||||
.. code-block:: javascript
|
||||
const { ERC721Order, NFTOrder } = require("@0x/protocol-utils");
|
||||
const utils = require("@0x/utils");
|
||||
|
||||
// Construct sell order
|
||||
const sellOrder = new ERC721Order({
|
||||
// The EVM blockchain that this order is for, in this case Ethereum mainnet.
|
||||
chainId: 1,
|
||||
// The address of the 0x v4 ExchangeProxy contract on Ethereum.
|
||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
||||
// Whether to sell or buy the given NFT
|
||||
direction: NFTOrder.TradeDirection.SellNFT,
|
||||
// This indicates that the seller would like to receive ETH.
|
||||
erc20Token: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
// The price, in this case 1 ETH.
|
||||
erc20TokenAmount: utils.BigNumber('1e18'),
|
||||
// Address of the ERC721 contract.
|
||||
erc721Token: '0x5180db8f5c931aae63c74266b211f580155ecac8',
|
||||
// Token ID of the NFT to sell.
|
||||
erc721TokenId: 123,
|
||||
// Address of the seller. This is also the address that will be
|
||||
// signing this order.
|
||||
maker: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
// A null taker address allows anyone to fill this order
|
||||
taker: '0x0000000000000000000000000000000000000000',
|
||||
// A unique order nonce
|
||||
nonce: 420,
|
||||
// Order expires in one hour
|
||||
expiry: new utils.BigNumber(Math.floor(Date.now() / 1000 + 3600)),
|
||||
});
|
||||
|
||||
An ERC721 sell order can be created similarly. Note that buy orders must use WETH instead of ether, because the ERC20 `transferFrom` functionality is needed to execute a buy order.
|
||||
|
||||
.. code-block:: javascript
|
||||
const { ERC721Order, NFTOrder } = require("@0x/protocol-utils");
|
||||
const utils = require("@0x/utils");
|
||||
|
||||
// Construct buy order
|
||||
const buyOrder = new ERC721Order({
|
||||
// The EVM blockchain that this order is for, in this case Ethereum mainnet.
|
||||
chainId: 1,
|
||||
// The address of the 0x v4 ExchangeProxy contract on Ethereum.
|
||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
||||
// Whether to sell or buy the given NFT
|
||||
direction: NFTOrder.TradeDirection.BuyNFT,
|
||||
// Address of the ERC20 token to buy with, in this case WETH.
|
||||
erc20Token: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
// The price, in this case 1 WETH.
|
||||
erc20TokenAmount: utils.BigNumber('1e18'),
|
||||
// Address of the ERC721 contract.
|
||||
erc721Token: '0x5180db8f5c931aae63c74266b211f580155ecac8',
|
||||
// Token ID of the NFT to buy.
|
||||
erc721TokenId: 234,
|
||||
// Address of the buyer. This is also the address that will be
|
||||
// signing this order.
|
||||
maker: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
// A null taker address allows anyone to fill this order
|
||||
taker: '0x0000000000000000000000000000000000000000',
|
||||
// A unique order nonce
|
||||
nonce: 421,
|
||||
// Order expires in one hour
|
||||
expiry: new utils.BigNumber(Math.floor(Date.now() / 1000 + 3600)),
|
||||
});
|
||||
|
||||
**Choosing a nonce**
|
||||
|
||||
If two orders signed by the same maker have the same nonce, filling or cancelling one can result in the other becoming unfillable.
|
||||
|
||||
::
|
||||
|
||||
Two ERC721 orders with the same nonce cannot both be filled, but two ERC1155 orders with the same nonce can both be filled (as long as the orders are not identical).
|
||||
|
||||
You can use a pseudorandom value or the current timestamp as the order nonce, but nonces can be chosen in a specific way to enable more gas-efficient fills and cancellations. See `Smart Nonces <./smart_nonce.rst>`_ for explanation.
|
||||
|
||||
We recommend using the most significant 128 bits of the nonce as an application/marketplace identifier. The least significant 128 bits of the nonce can be incremented from 0 for each order created by a particular maker.
|
||||
|
||||
**Royalties and Fees**
|
||||
|
||||
0x V4 has flexible support for creator royalties and platform fees. Marketplaces can pay out royalties to creators in real-time, and even have the option to send fees to their own custom fee disbursement contract.
|
||||
Fees are paid by the **buyer**, denominated in the asset paid by the buyer, and are paid **in addition** to the `erc20TokenAmount` specified in the order.
|
||||
The following code snippet shows how to create an ERC721 order with a single fee. Multiple fees can be specified by providing multiple fee objects in the order fees field.
|
||||
|
||||
.. code-block:: javascript
|
||||
const { ERC721Order, NFTOrder } = require("@0x/protocol-utils");
|
||||
const utils = require("@0x/utils");
|
||||
|
||||
const fee = {
|
||||
// Address to receive the fee. Can be a smart contract.
|
||||
recipient: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
|
||||
amount: utils.BigNumber('1e17'), // 0.1 ETH
|
||||
// If the fee recipient is a contract, this field can be used
|
||||
// to invoke a callback. In this case, there is no callback.
|
||||
feeData: '0x',
|
||||
};
|
||||
|
||||
// Construct sell order
|
||||
const order = new ERC721Order({
|
||||
chainId: 1,
|
||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
||||
direction: NFTOrder.TradeDirection.SellNFT,
|
||||
erc20Token: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
erc20TokenAmount: utils.BigNumber('1e18'),
|
||||
erc721Token: '0x5180db8f5c931aae63c74266b211f580155ecac8',
|
||||
erc721TokenId: 123,
|
||||
maker: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
taker: '0x0000000000000000000000000000000000000000',
|
||||
fees: [fee],
|
||||
nonce: 420,
|
||||
expiry: new utils.BigNumber(Math.floor(Date.now() / 1000 + 3600)),
|
||||
});
|
||||
|
||||
**Collection Offers**
|
||||
|
||||
In 0x V4, it is possible to create a bid for any NFT in a particular collection. The following code snippet shows how to create an order to buy any CryptoCoven $WITCH.
|
||||
|
||||
.. code-block:: javascript
|
||||
const { ERC721Order, NFTOrder } = require("@0x/protocol-utils");
|
||||
const utils = require("@0x/utils");
|
||||
|
||||
const property = {
|
||||
// Providing `address(0)` and `0x` serves as the sentinel
|
||||
// values for a "null property", i.e. any token ID from the
|
||||
// given collection can be used to fill the order.
|
||||
propertyValidator: '0x0000000000000000000000000000000000000000',
|
||||
propertyData: '0x',
|
||||
};
|
||||
|
||||
// Construct sell order
|
||||
const order = new ERC721Order({
|
||||
chainId: 1,
|
||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
||||
direction: NFTOrder.TradeDirection.SellNFT,
|
||||
erc20Token: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
erc20TokenAmount: utils.BigNumber('1e18'),
|
||||
erc721Token: '0x5180db8f5c931aae63c74266b211f580155ecac8',
|
||||
// If one or more properties are specified in the order, the
|
||||
// `erc721TokenId` must be 0.
|
||||
erc721TokenId: 0,
|
||||
maker: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
taker: '0x0000000000000000000000000000000000000000',
|
||||
erc721TokenProperties: [property],
|
||||
nonce: 420,
|
||||
expiry: new utils.BigNumber(Math.floor(Date.now() / 1000 + 3600)),
|
||||
}
|
||||
|
||||
Sign an ERC721 Order
|
||||
====================
|
||||
|
||||
Off-chain orders must be signed by the order maker to be filled. For on-chain orders, refer to the next section.
|
||||
|
||||
**Signing with a private key**
|
||||
|
||||
Signing an order with a private key is easy: the `ERC721Order` and `ERC1155Order` classes from `@0x/protocol-utils` expose a `getSignatureWithKey` function that take a 0x-prefixed private key string.
|
||||
|
||||
.. code-block:: javascript
|
||||
const { ERC721Order, NFTOrder, SignatureType } = require("@0x/protocol-utils");
|
||||
const utils = require("@0x/utils");
|
||||
|
||||
// Construct order
|
||||
const order = new ERC721Order({
|
||||
chainId: 1,
|
||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
||||
direction: NFTOrder.TradeDirection.SellNFT,
|
||||
erc20Token: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
erc20TokenAmount: utils.BigNumber('1e18'),
|
||||
erc721Token: '0x5180db8f5c931aae63c74266b211f580155ecac8',
|
||||
erc721TokenId: 123,
|
||||
maker: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
taker: '0x0000000000000000000000000000000000000000',
|
||||
nonce: 420,
|
||||
expiry: new utils.BigNumber(Math.floor(Date.now() / 1000 + 3600)),
|
||||
});
|
||||
|
||||
// Sign order with private key
|
||||
const signature = await order.getSignatureWithKey(
|
||||
PRIVATE_KEY, // '0x123456789...'
|
||||
SignatureType.EIP712
|
||||
);
|
||||
|
||||
**Signing with ethers**
|
||||
|
||||
.. code-block:: javascript
|
||||
const { ERC721Order, NFTOrder, SignatureType } = require("@0x/protocol-utils");
|
||||
const utils = require("@0x/utils");
|
||||
const { ethers } = require("ethers");
|
||||
|
||||
// Construct order
|
||||
const order = new ERC721Order({
|
||||
chainId: 1,
|
||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
||||
direction: NFTOrder.TradeDirection.SellNFT,
|
||||
erc20Token: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
erc20TokenAmount: utils.BigNumber('1e18'),
|
||||
erc721Token: '0x5180db8f5c931aae63c74266b211f580155ecac8',
|
||||
erc721TokenId: 123,
|
||||
maker: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
taker: '0x0000000000000000000000000000000000000000',
|
||||
nonce: 420,
|
||||
expiry: new utils.BigNumber(Math.floor(Date.now() / 1000 + 3600)),
|
||||
});
|
||||
|
||||
// Get ethers Signer
|
||||
const provider = new ethers.providers.JsonRpcProvider(/* constructor params */);
|
||||
const signer = provider.getSigner(/* address */);
|
||||
|
||||
const { domain, message } = order.getEIP712TypedData();
|
||||
const types = {
|
||||
[ERC721Order.STRUCT_NAME]: ERC721Order.STRUCT_ABI,
|
||||
['Fee']: NFTOrder.FEE_ABI,
|
||||
['Property']: NFTOrder.PROPERTY_ABI,
|
||||
};
|
||||
const rawSignature = await signer._signTypedData(
|
||||
domain,
|
||||
types,
|
||||
message
|
||||
);
|
||||
const { v, r, s } = ethers.utils.splitSignature(rawSignature);
|
||||
const signature = {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
signatureType: 2
|
||||
};
|
||||
|
||||
**On-chain Orders**
|
||||
|
||||
Orders can be simultaneously "signed" and listed on-chain using the `preSignERC721Order` or `preSignERC1155Order` functions. Orders can only be signed by the maker address specified in the order.
|
||||
|
||||
.. code-block:: solidity
|
||||
/// @dev Approves an ERC721 order on-chain. After pre-signing
|
||||
/// the order, the `PRESIGNED` signature type will become
|
||||
/// valid for that order and signer.
|
||||
/// @param order An ERC721 order.
|
||||
function preSignERC721Order(LibNFTOrder.ERC721Order calldata order)
|
||||
external;
|
||||
|
||||
/// @dev Approves an ERC1155 order on-chain. After pre-signing
|
||||
/// the order, the `PRESIGNED` signature type will become
|
||||
/// valid for that order and signer.
|
||||
/// @param order An ERC1155 order.
|
||||
function preSignERC1155Order(LibNFTOrder.ERC1155Order calldata order)
|
||||
external;
|
||||
|
||||
If an order has been pre-signed, it can be filled by providing a “null” signature with the PRESIGNED signature type (see `LibSignature.sol <https://github.com/0xProject/protocol/blob/refactor/nft-orders/contracts/zero-ex/contracts/src/features/libs/LibSignature.sol#L42-L61>`_):
|
||||
|
||||
.. code-block:: solidity
|
||||
LibSignature.Signature({
|
||||
signatureType: LibSignature.SignatureType.PRESIGNED,
|
||||
v: uint8(0),
|
||||
r: bytes32(0),
|
||||
s: bytes32(0)
|
||||
});
|
||||
|
||||
The pre-sign functions emit the entire order as an event, so that the order is easily indexable by subgraphs and thus easily indexable by subgraphs and thus easily discoverable without the need for an off-chain database.
|
||||
|
||||
.. code-block:: solidity
|
||||
/// @dev Emitted when an `ERC721Order` is pre-signed.
|
||||
/// Contains all the fields of the order.
|
||||
event ERC721OrderPreSigned(
|
||||
LibNFTOrder.TradeDirection direction,
|
||||
address maker,
|
||||
address taker,
|
||||
uint256 expiry,
|
||||
uint256 nonce,
|
||||
IERC20TokenV06 erc20Token,
|
||||
uint256 erc20TokenAmount,
|
||||
LibNFTOrder.Fee[] fees,
|
||||
IERC721Token erc721Token,
|
||||
uint256 erc721TokenId,
|
||||
LibNFTOrder.Property[] erc721TokenProperties
|
||||
);
|
||||
|
||||
/// @dev Emitted when an `ERC1155Order` is pre-signed.
|
||||
/// Contains all the fields of the order.
|
||||
event ERC1155OrderPreSigned(
|
||||
LibNFTOrder.TradeDirection direction,
|
||||
address maker,
|
||||
address taker,
|
||||
uint256 expiry,
|
||||
uint256 nonce,
|
||||
IERC20TokenV06 erc20Token,
|
||||
uint256 erc20TokenAmount,
|
||||
LibNFTOrder.Fee[] fees,
|
||||
IERC1155Token erc1155Token,
|
||||
uint256 erc1155TokenId,
|
||||
LibNFTOrder.Property[] erc1155TokenProperties,
|
||||
uint128 erc1155TokenAmount
|
||||
);
|
||||
|
||||
The pre-sign functions also enable smart contracts to create and "sign" NFT orders, opening the door for potential integrations with e.g. multisig wallets.
|
||||
|
||||
Filling an ERC721 Order
|
||||
=======================
|
||||
|
||||
The basic functions used for filling NFT orders are the following:
|
||||
|
||||
.. code-block:: solidity
|
||||
/// @dev Sells an ERC721 asset to fill the given order.
|
||||
/// @param buyOrder The ERC721 buy order.
|
||||
/// @param signature The order signature from the maker.
|
||||
/// @param erc721TokenId The ID of the ERC721 asset being
|
||||
/// sold. If the given order specifies properties,
|
||||
/// the asset must satisfy those properties. Otherwise,
|
||||
/// it must equal the tokenId in the order.
|
||||
/// @param unwrapNativeToken If this parameter is true and the
|
||||
/// ERC20 token of the order is e.g. WETH, unwraps the
|
||||
/// token before transferring it to the taker.
|
||||
/// @param callbackData If this parameter is non-zero, invokes
|
||||
/// `zeroExERC721OrderCallback` on `msg.sender` after
|
||||
/// the ERC20 tokens have been transferred to `msg.sender`
|
||||
/// but before transferring the ERC721 asset to the buyer.
|
||||
function sellERC721(
|
||||
LibNFTOrder.ERC721Order calldata buyOrder,
|
||||
LibSignature.Signature calldata signature,
|
||||
uint256 erc721TokenId,
|
||||
bool unwrapNativeToken,
|
||||
bytes calldata callbackData
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Buys an ERC721 asset by filling the given order.
|
||||
/// @param sellOrder The ERC721 sell order.
|
||||
/// @param signature The order signature.
|
||||
/// @param callbackData If this parameter is non-zero, invokes
|
||||
/// `zeroExERC721OrderCallback` on `msg.sender` after
|
||||
/// the ERC721 asset has been transferred to `msg.sender`
|
||||
/// but before transferring the ERC20 tokens to the seller.
|
||||
/// Native tokens acquired during the callback can be used
|
||||
/// to fill the order.
|
||||
function buyERC721(
|
||||
LibNFTOrder.ERC721Order calldata sellOrder,
|
||||
LibSignature.Signature calldata signature,
|
||||
bytes calldata callbackData
|
||||
)
|
||||
external
|
||||
payable;
|
||||
|
||||
/// @dev Sells an ERC1155 asset to fill the given order.
|
||||
/// @param buyOrder The ERC1155 buy order.
|
||||
/// @param signature The order signature from the maker.
|
||||
/// @param erc1155TokenId The ID of the ERC1155 asset being
|
||||
/// sold. If the given order specifies properties,
|
||||
/// the asset must satisfy those properties. Otherwise,
|
||||
/// it must equal the tokenId in the order.
|
||||
/// @param erc1155SellAmount The amount of the ERC1155 asset
|
||||
/// to sell.
|
||||
/// @param unwrapNativeToken If this parameter is true and the
|
||||
/// ERC20 token of the order is e.g. WETH, unwraps the
|
||||
/// token before transferring it to the taker.
|
||||
/// @param callbackData If this parameter is non-zero, invokes
|
||||
/// `zeroExERC1155OrderCallback` on `msg.sender` after
|
||||
/// the ERC20 tokens have been transferred to `msg.sender`
|
||||
/// but before transferring the ERC1155 asset to the buyer.
|
||||
function sellERC1155(
|
||||
LibNFTOrder.ERC1155Order calldata buyOrder,
|
||||
LibSignature.Signature calldata signature,
|
||||
uint256 erc1155TokenId,
|
||||
uint128 erc1155SellAmount,
|
||||
bool unwrapNativeToken,
|
||||
bytes calldata callbackData
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Buys an ERC1155 asset by filling the given order.
|
||||
/// @param sellOrder The ERC1155 sell order.
|
||||
/// @param signature The order signature.
|
||||
/// @param erc1155BuyAmount The amount of the ERC1155 asset
|
||||
/// to buy.
|
||||
/// @param callbackData If this parameter is non-zero, invokes
|
||||
/// `zeroExERC1155OrderCallback` on `msg.sender` after
|
||||
/// the ERC1155 asset has been transferred to `msg.sender`
|
||||
/// but before transferring the ERC20 tokens to the seller.
|
||||
/// Native tokens acquired during the callback can be used
|
||||
/// to fill the order.
|
||||
function buyERC1155(
|
||||
LibNFTOrder.ERC1155Order calldata sellOrder,
|
||||
LibSignature.Signature calldata signature,
|
||||
uint128 erc1155BuyAmount,
|
||||
bytes calldata callbackData
|
||||
)
|
||||
external
|
||||
payable;
|
||||
|
||||
`sellERC721` and `sellERC1155` are used when the caller is **selling** an NFT, so the order being filled is a **buy** order.
|
||||
`buyERC721` and `buyERC1155` are used when the caller is **buying** an NFT, so the order being filled is a **sell** order.
|
||||
Note that the only difference in parameters between the ERC721 and ERC1155 functions is `erc1155BuyAmount`. This value specifies the amount of the ERC1155 asset to sell/buy from the given order, which may be greater than one in the case of semi-fungible ERC1155 assets.
|
||||
|
||||
Cancelling an ERC721 Order
|
||||
==========================
|
||||
|
||||
All orders, whether off-chain or on-chain, can only be cancelled on-chain. The following contract functions are used to cancel individual ERC721 and ERC1155 orders.
|
||||
|
||||
.. code-block:: solidity
|
||||
/// @dev Cancel a single ERC721 order by its nonce. The caller
|
||||
/// should be the maker of the order. Silently succeeds if
|
||||
/// an order with the same nonce has already been filled or
|
||||
/// cancelled.
|
||||
/// @param orderNonce The order nonce.
|
||||
function cancelERC721Order(uint256 orderNonce)
|
||||
external;
|
||||
|
||||
/// @dev Cancel a single ERC1155 order by its nonce. The caller
|
||||
/// should be the maker of the order. Silently succeeds if
|
||||
/// an order with the same nonce has already been filled or
|
||||
/// cancelled.
|
||||
/// @param orderNonce The order nonce.
|
||||
function cancelERC1155Order(uint256 orderNonce)
|
||||
external;
|
||||
|
||||
Note that if there are multiple outstanding orders with the same nonce, calling `cancelERC721Order` or `cancelERC1155Order` would cancel all those orders.
|
||||
The following functions can be used to cancel multiple orders.
|
||||
|
||||
.. code-block:: solidity
|
||||
/// @dev Cancel multiple ERC721 orders by their nonces. The caller
|
||||
/// should be the maker of the orders. Silently succeeds if
|
||||
/// an order with the same nonce has already been filled or
|
||||
/// cancelled.
|
||||
/// @param orderNonces The order nonces.
|
||||
function batchCancelERC721Orders(uint256[] calldata orderNonces)
|
||||
external;
|
||||
|
||||
/// @dev Cancel multiple ERC1155 orders by their nonces. The caller
|
||||
/// should be the maker of the orders. Silently succeeds if
|
||||
/// an order with the same nonce has already been filled or
|
||||
/// cancelled.
|
||||
/// @param orderNonces The order nonces.
|
||||
function batchCancelERC1155Orders(uint256[] calldata orderNonces)
|
||||
external;
|
||||
|
||||
Fetching NFT Order Data
|
||||
=======================
|
||||
|
||||
To fetch collection-level NFT stats from 0x orders (e.g. floor price, total volume), checkout the following tools:
|
||||
|
||||
- `https://module.readme.io/reference/retrieve-collection-floor <https://module.readme.io/reference/retrieve-collection-floor>`_
|
||||
- `https://api.reservoir.tools/#/2.%20Aggregator/getEventsCollectionsFlooraskV1 <https://api.reservoir.tools/#/2.%20Aggregator/getEventsCollectionsFlooraskV1>`_
|
||||
|
||||
These tools are not 0x specific but NFT data is somewhat universal so these tools should do the trick.
|
@@ -1,17 +0,0 @@
|
||||
############
|
||||
Smart Nonces
|
||||
############
|
||||
|
||||
The `nonce` field in the order is a number the maker chooses to ensure uniqueness of the order, but it also has some other functions:
|
||||
- To identify the order (in addition to the caller/maker) in cancellation functions.
|
||||
- To identify the order when checking/updating its filled state.
|
||||
- To potentially reuse EVM storage slots that mark an order as filled, which can provide significant gas savings (~15k) when filling or cancelling the order.
|
||||
|
||||
**Gas Optimized Nonces**
|
||||
|
||||
The protocol marks ERC721 orders as filled when they either get cancelled or filled. Instead of always using a completely new storage slot for each order from a maker, the upper 248 bits of the nonce will identify the storage slot/bucket/status vector to be used and the lower 8 bits of the nonce will identify the bit offset in that slot (each slot is also 256 bits) which will be flipped from 0 to 1 when the order is cancelled or filled.
|
||||
|
||||
.. image:: ./_static/img/smart_nonce_diagram.webp
|
||||
:width: 600
|
||||
|
||||
To take advantage of this gas optimization, makers should reuse the upper 248 bits of their nonce across up to 256 different orders, varying the value of the lower 8 bits between them.
|
@@ -1,28 +1,39 @@
|
||||
0x Protocol
|
||||
===========
|
||||
|
||||
0x Protocol is the trusted open source settlement layer for the permissionless global exchange of value.
|
||||
|
||||
This documentation is aimed to serve as the canonical technical documentation for 0x Protocol and the system of smart contracts that make up its full functionality. The structure of this documentation is as follows:
|
||||
|
||||
- Architectural Overview of the whole system of smart contracts that 0x Protocol is comprised of.
|
||||
- Subsections will provide a deep dive into each contracts
|
||||
- Tutorial and tools to get started building with/on top of 0x Protocol
|
||||
|
||||
To learn more about why 0x Protocol was created, read the whitepaper `here <https://github.com/0xProject/whitepaper/blob/master/0x_white_paper.pdf>`_ and at `0xProtocol.org <httpsL//0xProtocol.org>`_.
|
||||
|
||||
All code is open sourced on `GitHub <https://github.com/0xProject/protocol>`_.
|
||||
|
||||
Connect with the community on our `Forum <https://forum.0xProtocol.org/>`_, `Discord <https://discord.com/invite/official0x>`_, `Reddit <https://www.reddit.com/r/0xProject/>`_, and `Twitter <https://twitter.com/zeroexprotocol>`_.
|
||||
0x is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets.
|
||||
Learn more about 0x Labs at `0x.org <https://0x.org>`_. Check out our code on `GitHub <https://github.com/0xProject/protocol>`_.
|
||||
Connect with the community on our `Forum <https://forum.0x.org/>`_ and `Reddit <https://www.reddit.com/r/0xProject/>`_.
|
||||
Chat with our team privately on `Discord <https://discord.com/invite/d3FTX3M>`_ or publicly on `Twitter <https://twitter.com/0xproject>`_.
|
||||
|
||||
|
||||
.. image:: ./_static/img/logo.png
|
||||
:width: 60%
|
||||
.. image:: ./_static/img/logo.svg
|
||||
:width: 20%
|
||||
:alt: 0x Protocol logo
|
||||
:align: center
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 2
|
||||
:caption: Basics
|
||||
|
||||
basics/orders.rst
|
||||
basics/functions.rst
|
||||
basics/events.rst
|
||||
basics/allowances.rst
|
||||
basics/protocol_fees.rst
|
||||
basics/addresses.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Advanced
|
||||
|
||||
advanced/mtx.rst
|
||||
advanced/erc20_transformations.rst
|
||||
advanced/plp.rst
|
||||
advanced/uniswap.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Architecture
|
||||
|
||||
architecture/overview.rst
|
||||
@@ -34,36 +45,19 @@ Connect with the community on our `Forum <https://forum.0xProtocol.org/>`_, `Dis
|
||||
architecture/transformer_deployer.rst
|
||||
architecture/fee_collectors.rst
|
||||
architecture/plp_sandbox.rst
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Basics
|
||||
:maxdepth: 2
|
||||
:caption: ZRX Tokenomics
|
||||
|
||||
basics/orders.rst
|
||||
basics/functions.rst
|
||||
basics/events.rst
|
||||
basics/allowances.rst
|
||||
basics/protocol_fees.rst
|
||||
basics/addresses.rst
|
||||
tokenomics/research.rst
|
||||
tokenomics/staking.md
|
||||
tokenomics/staking_reward_formula.rst
|
||||
tokenomics/governance.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Advanced
|
||||
|
||||
advanced/mtx.rst
|
||||
advanced/erc20_transformations.rst
|
||||
advanced/plp.rst
|
||||
advanced/uniswap.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Guide
|
||||
|
||||
guides/nft_guide.rst
|
||||
guides/smart_nonce.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 2
|
||||
:caption: Additional Material
|
||||
|
||||
additional/audits.rst
|
||||
@@ -77,9 +71,9 @@ Connect with the community on our `Forum <https://forum.0xProtocol.org/>`_, `Dis
|
||||
:maxdepth: 1
|
||||
:caption: Connect
|
||||
|
||||
0x Protocol <https://0xProtocol.org>
|
||||
0x Labs <https://0x.org>
|
||||
GitHub <https://github.com/0xProject/protocol>
|
||||
Forum <https://forum.0xProtocol.org/>
|
||||
Forum <https://forum.0x.org/>
|
||||
Reddit <https://www.reddit.com/r/0xProject/>
|
||||
Discord <https://discord.com/invite/official0x>
|
||||
Twitter <https://twitter.com/zeroexprotocol>
|
||||
Discord <https://discord.com/invite/d3FTX3M>
|
||||
Twitter <https://twitter.com/0xproject>
|
@@ -1,4 +1,3 @@
|
||||
six
|
||||
sphinx==3.5.4
|
||||
sphinx-markdown-tables
|
||||
jinja2<3.1
|
5
docs/tokenomics/governance.rst
Normal file
5
docs/tokenomics/governance.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
###############################
|
||||
Governance
|
||||
###############################
|
||||
|
||||
The 0x Community uses their ZRX (and staked ZRX) tokens to govern the protocol, by voting on proposals called `ZEIPs <https://github.com/0xProject/ZEIPs>`_. Anyone can propose a change to the system by creating a ZEIP. Visit `https://0x.org/zrx/vote <https://0x.org/zrx/vote>`_ to participate!
|
9
docs/tokenomics/research.rst
Normal file
9
docs/tokenomics/research.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
###############################
|
||||
Research
|
||||
###############################
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<embed>
|
||||
<iframe class="researchPdf" src="../_static/protocol-fees.pdf" width="100%" />
|
||||
</embed>
|
1486
docs/tokenomics/staking.md
Normal file
1486
docs/tokenomics/staking.md
Normal file
File diff suppressed because it is too large
Load Diff
9
docs/tokenomics/staking_reward_formula.rst
Normal file
9
docs/tokenomics/staking_reward_formula.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
###############################
|
||||
Staking Reward Formula
|
||||
###############################
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<embed>
|
||||
<iframe class="researchPdf" src="../_static/adjusted-staking-percentage-formula.pdf" width="100%" />
|
||||
</embed>
|
@@ -1,31 +1,4 @@
|
||||
[
|
||||
{
|
||||
"version": "8.5.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Trader Joe V2 support on Avalanche"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681969282
|
||||
},
|
||||
{
|
||||
"version": "8.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support in Ethereum FillQuoteTransformer"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"version": "8.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support in Ethereum FillQuoteTransformer"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681697326
|
||||
},
|
||||
{
|
||||
"version": "8.2.0",
|
||||
"changes": [
|
||||
|
@@ -6,15 +6,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v8.5.0 - _April 20, 2023_
|
||||
|
||||
* Add Trader Joe V2 support on Avalanche
|
||||
## v8.4.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support in Ethereum FillQuoteTransformer
|
||||
## v8.3.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support in Ethereum FillQuoteTransformer
|
||||
## v8.2.0 - _March 10, 2023_
|
||||
|
||||
* Upgrade Polygon, Avalanche, Arbitrum, and Optimism FillQuoteTransformers to remove Aave V3 L2 Encoding and support KyberElastic (#678)
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
|
||||
"payTakerTransformer": "0xea500d073652336a58846ada15c25f2c6d2d241f",
|
||||
"affiliateFeeTransformer": "0x8146cbbe327364b13d0699f2ced39c637f92501a",
|
||||
"fillQuoteTransformer": "0x21c3bee93fad436dedd29f971dc4fdf82f3e3a3a",
|
||||
"fillQuoteTransformer": "0xd7a6ced9e0eaa8e5ad6e7335d25d46b6f456d4fd",
|
||||
"positiveSlippageFeeTransformer": "0x818a4a855bfeb16c305cb65e8d4fb239a308bc48"
|
||||
}
|
||||
},
|
||||
@@ -156,7 +156,7 @@
|
||||
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
|
||||
"payTakerTransformer": "0xb9a4c32547bc3cdc2ee2fb13cc1a0717dac9888f",
|
||||
"affiliateFeeTransformer": "0x105679f99d668001370b4621ad8648ac570c860f",
|
||||
"fillQuoteTransformer": "0x540079df6023d39b2686fd9f6c06f1f8f66aca4a",
|
||||
"fillQuoteTransformer": "0x7991f2c35ab19472dbfb6f27593f7f6f38fb3eab",
|
||||
"positiveSlippageFeeTransformer": "0xadbfdc58a24b6dbc16f21541800f43dd6e282250"
|
||||
}
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-addresses",
|
||||
"version": "8.5.0",
|
||||
"version": "8.2.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
|
@@ -1,31 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681969282,
|
||||
"version": "13.22.21",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1681697326,
|
||||
"version": "13.22.20",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "13.22.19",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "13.22.18",
|
||||
|
@@ -6,15 +6,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v13.22.21 - _April 20, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v13.22.20 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v13.22.19 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v13.22.18 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-wrappers",
|
||||
"version": "13.22.21",
|
||||
"version": "13.22.18",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -57,13 +57,13 @@
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.36",
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/contract-addresses": "^8.5.0",
|
||||
"@0x/contract-addresses": "^8.2.0",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"ethereum-types": "^3.7.1",
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
|
@@ -1,40 +1,4 @@
|
||||
[
|
||||
{
|
||||
"version": "11.21.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Trader Joe V2 support on Avalanche"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681969282
|
||||
},
|
||||
{
|
||||
"version": "11.20.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"version": "11.19.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681697326
|
||||
},
|
||||
{
|
||||
"version": "11.18.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "11.18.1",
|
||||
|
@@ -6,18 +6,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v11.21.0 - _April 20, 2023_
|
||||
|
||||
* Add Trader Joe V2 support on Avalanche
|
||||
## v11.20.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support
|
||||
## v11.19.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support
|
||||
## v11.18.2 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v11.18.1 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/protocol-utils",
|
||||
"version": "11.21.0",
|
||||
"version": "11.18.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -38,9 +38,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/packages/protocol-utils",
|
||||
"devDependencies": {
|
||||
"@0x/dev-utils": "^5.0.2",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -61,13 +61,13 @@
|
||||
"web3-provider-engine": "14.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.36",
|
||||
"@0x/contract-addresses": "^8.5.0",
|
||||
"@0x/contract-wrappers": "^13.22.21",
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/contract-addresses": "^8.2.0",
|
||||
"@0x/contract-wrappers": "^13.22.18",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/subproviders": "^8.0.1",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"chai": "^4.0.1",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { EIP712TypedData } from '@0x/types';
|
||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { EIP712TypedData } from '@0x/types';
|
||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user