Compare commits
	
		
			33 Commits
		
	
	
		
			@0x/contra
			...
			protocol@a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ae281c33ca | ||
|  | 5d034dd106 | ||
|  | c1f8df0eca | ||
|  | 76dda9eeda | ||
|  | a9b84a92ac | ||
|  | 0f7e881899 | ||
|  | 6045f777ab | ||
|  | 5a15044ead | ||
|  | a69d76e487 | ||
|  | 3adfcdffa8 | ||
|  | 164a5d44d9 | ||
|  | 70ddab0231 | ||
|  | 7bf009fbf6 | ||
|  | 525bc8197b | ||
|  | 24397c51a8 | ||
|  | 06b3464756 | ||
|  | bbaa90bd9a | ||
|  | 5c683cbc0f | ||
|  | 95345f18bc | ||
|  | 4c3fbe83ac | ||
|  | 7caa43d02c | ||
|  | 3d4c03c9df | ||
|  | 22e1ed35d3 | ||
|  | dabe6fd793 | ||
|  | 3cc639c8d0 | ||
|  | 22c8e0b6db | ||
|  | f3ca4293bc | ||
|  | db3e076d03 | ||
|  | 1a6759820a | ||
|  | 61c5e7b948 | ||
|  | 5fd78ef32f | ||
|  | 14ff9b827c | ||
|  | 598dc2cd71 | 
							
								
								
									
										7
									
								
								.github/workflows/publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/publish.yml
									
									
									
									
										vendored
									
									
								
							| @@ -7,6 +7,9 @@ on: | ||||
|               description: 'required CI status' | ||||
|               default: 'success' | ||||
|               required: true | ||||
|           prerelease: | ||||
|               description: 'prerelease name' | ||||
|               required: false | ||||
|  | ||||
| jobs: | ||||
|     publish: | ||||
| @@ -21,7 +24,7 @@ jobs: | ||||
|                   (echo "::error ::${{ github.ref }} does not have a successful CI status" && false) | ||||
|             - uses: actions/checkout@v2 | ||||
|               with: | ||||
|                 ref: 'development' | ||||
|                 ref: ${{ github.ref }} | ||||
|                 fetch-depth: 0 | ||||
|             - uses: actions/setup-node@v1 | ||||
|               with: | ||||
| @@ -41,7 +44,9 @@ jobs: | ||||
|               env: | ||||
|                   NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} | ||||
|                   GITHUB_TOKEN: ${{ github.token }} | ||||
|                   PUBLISH_PRERELEASE: ${{ github.event.inputs.prerelease }} | ||||
|             - name: 'merge into main branch' | ||||
|               if: github.event.inputs.prerelease == '' # unless it's a prerelease | ||||
|               run: | | ||||
|                   git checkout main && \ | ||||
|                   git merge ${{ github.ref }} && \ | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "3.7.9", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "3.7.8", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "3.7.7", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v3.7.9 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.7.8 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.7.7 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-asset-proxy", | ||||
|     "version": "3.7.7", | ||||
|     "version": "3.7.9", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -51,13 +51,13 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contract-wrappers": "^13.13.0", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contract-wrappers": "^13.15.0", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@types/lodash": "4.14.104", | ||||
| @@ -76,15 +76,15 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-erc1155": "^2.1.25", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-erc721": "^3.1.25", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/contracts-erc1155": "^2.1.27", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-erc721": "^3.1.27", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|   | ||||
| @@ -1,354 +0,0 @@ | ||||
| import { ContractTxFunctionObj } from '@0x/contract-wrappers'; | ||||
| import { | ||||
|     blockchainTests, | ||||
|     constants, | ||||
|     expect, | ||||
|     filterLogsToArguments, | ||||
|     getRandomInteger, | ||||
|     randomAddress, | ||||
|     shortZip, | ||||
| } from '@0x/contracts-test-utils'; | ||||
| import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils'; | ||||
| import { DecodedLogs } from 'ethereum-types'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| import { DexForwarderBridgeCall, dexForwarderBridgeDataEncoder } from '../src/dex_forwarder_bridge'; | ||||
|  | ||||
| import { artifacts } from './artifacts'; | ||||
| import { | ||||
|     TestDexForwarderBridgeBridgeTransferFromCalledEventArgs as BtfCalledEventArgs, | ||||
|     TestDexForwarderBridgeContract, | ||||
|     TestDexForwarderBridgeEvents as TestEvents, | ||||
| } from './wrappers'; | ||||
|  | ||||
| const { ZERO_AMOUNT } = constants; | ||||
|  | ||||
| blockchainTests.resets('DexForwarderBridge unit tests', env => { | ||||
|     let testContract: TestDexForwarderBridgeContract; | ||||
|     let inputToken: string; | ||||
|     let outputToken: string; | ||||
|     const BRIDGE_SUCCESS = '0xdc1600f3'; | ||||
|     const BRIDGE_FAILURE = '0xffffffff'; | ||||
|     const BRIDGE_REVERT_ERROR = 'oopsie'; | ||||
|     const NOT_AUTHORIZED_REVERT = 'DexForwarderBridge/SENDER_NOT_AUTHORIZED'; | ||||
|     const DEFAULTS = { | ||||
|         toAddress: randomAddress(), | ||||
|     }; | ||||
|  | ||||
|     before(async () => { | ||||
|         testContract = await TestDexForwarderBridgeContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.TestDexForwarderBridge, | ||||
|             env.provider, | ||||
|             env.txDefaults, | ||||
|             artifacts, | ||||
|         ); | ||||
|         // Create test tokens. | ||||
|         [inputToken, outputToken] = [ | ||||
|             await callAndTransactAsync(testContract.createToken()), | ||||
|             await callAndTransactAsync(testContract.createToken()), | ||||
|         ]; | ||||
|         await callAndTransactAsync(testContract.setAuthorized(env.txDefaults.from as string)); | ||||
|     }); | ||||
|  | ||||
|     async function callAndTransactAsync<TResult>(fnCall: ContractTxFunctionObj<TResult>): Promise<TResult> { | ||||
|         const result = await fnCall.callAsync(); | ||||
|         await fnCall.awaitTransactionSuccessAsync({}, { shouldValidate: false }); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     function getRandomBridgeCall( | ||||
|         bridgeAddress: string, | ||||
|         fields: Partial<DexForwarderBridgeCall> = {}, | ||||
|     ): DexForwarderBridgeCall { | ||||
|         return { | ||||
|             target: bridgeAddress, | ||||
|             inputTokenAmount: getRandomInteger(1, '100e18'), | ||||
|             outputTokenAmount: getRandomInteger(1, '100e18'), | ||||
|             bridgeData: hexUtils.leftPad(inputToken), | ||||
|             ...fields, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     describe('bridgeTransferFrom()', () => { | ||||
|         let goodBridgeCalls: DexForwarderBridgeCall[]; | ||||
|         let revertingBridgeCall: DexForwarderBridgeCall; | ||||
|         let failingBridgeCall: DexForwarderBridgeCall; | ||||
|         let allBridgeCalls: DexForwarderBridgeCall[]; | ||||
|         let totalFillableOutputAmount: BigNumber; | ||||
|         let totalFillableInputAmount: BigNumber; | ||||
|         let recipientOutputBalance: BigNumber; | ||||
|  | ||||
|         beforeEach(async () => { | ||||
|             goodBridgeCalls = []; | ||||
|             for (let i = 0; i < 4; ++i) { | ||||
|                 goodBridgeCalls.push(await createBridgeCallAsync({ returnCode: BRIDGE_SUCCESS })); | ||||
|             } | ||||
|             revertingBridgeCall = await createBridgeCallAsync({ revertError: BRIDGE_REVERT_ERROR }); | ||||
|             failingBridgeCall = await createBridgeCallAsync({ returnCode: BRIDGE_FAILURE }); | ||||
|             allBridgeCalls = _.shuffle([failingBridgeCall, revertingBridgeCall, ...goodBridgeCalls]); | ||||
|  | ||||
|             totalFillableInputAmount = BigNumber.sum(...goodBridgeCalls.map(c => c.inputTokenAmount)); | ||||
|             totalFillableOutputAmount = BigNumber.sum(...goodBridgeCalls.map(c => c.outputTokenAmount)); | ||||
|  | ||||
|             // Grant the taker some output tokens. | ||||
|             await testContract.setTokenBalance( | ||||
|                 outputToken, | ||||
|                 DEFAULTS.toAddress, | ||||
|                 (recipientOutputBalance = getRandomInteger(1, '100e18')), | ||||
|             ); | ||||
|         }); | ||||
|  | ||||
|         async function setForwarderInputBalanceAsync(amount: BigNumber): Promise<void> { | ||||
|             await testContract | ||||
|                 .setTokenBalance(inputToken, testContract.address, amount) | ||||
|                 .awaitTransactionSuccessAsync({}, { shouldValidate: false }); | ||||
|         } | ||||
|  | ||||
|         async function createBridgeCallAsync( | ||||
|             opts: Partial<{ | ||||
|                 returnCode: string; | ||||
|                 revertError: string; | ||||
|                 callFields: Partial<DexForwarderBridgeCall>; | ||||
|                 outputFillAmount: BigNumber; | ||||
|             }>, | ||||
|         ): Promise<DexForwarderBridgeCall> { | ||||
|             const { returnCode, revertError, callFields, outputFillAmount } = { | ||||
|                 returnCode: BRIDGE_SUCCESS, | ||||
|                 revertError: '', | ||||
|                 ...opts, | ||||
|             }; | ||||
|             const bridge = await callAndTransactAsync(testContract.createBridge(returnCode, revertError)); | ||||
|             const call = getRandomBridgeCall(bridge, callFields); | ||||
|             await testContract | ||||
|                 .setBridgeTransferAmount(call.target, outputFillAmount || call.outputTokenAmount) | ||||
|                 .awaitTransactionSuccessAsync({}, { shouldValidate: false }); | ||||
|             return call; | ||||
|         } | ||||
|  | ||||
|         async function callBridgeTransferFromAsync(opts: { | ||||
|             bridgeData: string; | ||||
|             sellAmount?: BigNumber; | ||||
|             buyAmount?: BigNumber; | ||||
|         }): Promise<DecodedLogs> { | ||||
|             // Fund the forwarder with input tokens to sell. | ||||
|             await setForwarderInputBalanceAsync(opts.sellAmount || totalFillableInputAmount); | ||||
|             const call = testContract.bridgeTransferFrom( | ||||
|                 outputToken, | ||||
|                 testContract.address, | ||||
|                 DEFAULTS.toAddress, | ||||
|                 opts.buyAmount || totalFillableOutputAmount, | ||||
|                 opts.bridgeData, | ||||
|             ); | ||||
|             const returnCode = await call.callAsync(); | ||||
|             if (returnCode !== BRIDGE_SUCCESS) { | ||||
|                 throw new Error('Expected BRIDGE_SUCCESS'); | ||||
|             } | ||||
|             const receipt = await call.awaitTransactionSuccessAsync({}, { shouldValidate: false }); | ||||
|             // tslint:disable-next-line: no-unnecessary-type-assertion | ||||
|             return receipt.logs as DecodedLogs; | ||||
|         } | ||||
|  | ||||
|         it('succeeds with no bridge calls and no input balance', async () => { | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls: [], | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ bridgeData, sellAmount: ZERO_AMOUNT }); | ||||
|         }); | ||||
|  | ||||
|         it('succeeds with bridge calls and no input balance', async () => { | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls: allBridgeCalls, | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ bridgeData, sellAmount: ZERO_AMOUNT }); | ||||
|         }); | ||||
|  | ||||
|         it('succeeds with no bridge calls and an input balance', async () => { | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls: [], | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ | ||||
|                 bridgeData, | ||||
|                 sellAmount: new BigNumber(1), | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('succeeds if entire input token balance is not consumed', async () => { | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls: allBridgeCalls, | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ | ||||
|                 bridgeData, | ||||
|                 sellAmount: totalFillableInputAmount.plus(1), | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('fails if not authorized', async () => { | ||||
|             const calls = goodBridgeCalls.slice(0, 1); | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             await callAndTransactAsync(testContract.setAuthorized(NULL_ADDRESS)); | ||||
|             return expect(callBridgeTransferFromAsync({ bridgeData, sellAmount: new BigNumber(1) })).to.revertWith( | ||||
|                 NOT_AUTHORIZED_REVERT, | ||||
|             ); | ||||
|         }); | ||||
|  | ||||
|         it('succeeds with one bridge call', async () => { | ||||
|             const calls = goodBridgeCalls.slice(0, 1); | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ bridgeData, sellAmount: calls[0].inputTokenAmount }); | ||||
|         }); | ||||
|  | ||||
|         it('succeeds with many bridge calls', async () => { | ||||
|             const calls = goodBridgeCalls; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ bridgeData }); | ||||
|         }); | ||||
|  | ||||
|         it('swallows a failing bridge call', async () => { | ||||
|             const calls = _.shuffle([...goodBridgeCalls, failingBridgeCall]); | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ bridgeData }); | ||||
|         }); | ||||
|  | ||||
|         it('consumes input tokens for output tokens', async () => { | ||||
|             const calls = allBridgeCalls; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             await callBridgeTransferFromAsync({ bridgeData }); | ||||
|             const currentBridgeInputBalance = await testContract | ||||
|                 .balanceOf(inputToken, testContract.address) | ||||
|                 .callAsync(); | ||||
|             expect(currentBridgeInputBalance).to.bignumber.eq(0); | ||||
|             const currentRecipientOutputBalance = await testContract | ||||
|                 .balanceOf(outputToken, DEFAULTS.toAddress) | ||||
|                 .callAsync(); | ||||
|             expect(currentRecipientOutputBalance).to.bignumber.eq(totalFillableOutputAmount); | ||||
|         }); | ||||
|  | ||||
|         it("transfers only up to each call's input amount to each bridge", async () => { | ||||
|             const calls = goodBridgeCalls; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); | ||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); | ||||
|             for (const [call, btf] of shortZip(goodBridgeCalls, btfs)) { | ||||
|                 expect(btf.inputTokenBalance).to.bignumber.eq(call.inputTokenAmount); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         it('transfers only up to outstanding sell amount to each bridge', async () => { | ||||
|             // Prepend an extra bridge call. | ||||
|             const calls = [ | ||||
|                 await createBridgeCallAsync({ | ||||
|                     callFields: { | ||||
|                         inputTokenAmount: new BigNumber(1), | ||||
|                         outputTokenAmount: new BigNumber(1), | ||||
|                     }, | ||||
|                 }), | ||||
|                 ...goodBridgeCalls, | ||||
|             ]; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); | ||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); | ||||
|             expect(btfs).to.be.length(goodBridgeCalls.length + 1); | ||||
|             // The last call will receive 1 less token. | ||||
|             const lastCall = calls.slice(-1)[0]; | ||||
|             const lastBtf = btfs.slice(-1)[0]; | ||||
|             expect(lastBtf.inputTokenBalance).to.bignumber.eq(lastCall.inputTokenAmount.minus(1)); | ||||
|         }); | ||||
|  | ||||
|         it('recoups funds from a bridge that fails', async () => { | ||||
|             // Prepend a call that will take the whole input amount but will | ||||
|             // fail. | ||||
|             const badCall = await createBridgeCallAsync({ | ||||
|                 callFields: { inputTokenAmount: totalFillableInputAmount }, | ||||
|                 returnCode: BRIDGE_FAILURE, | ||||
|             }); | ||||
|             const calls = [badCall, ...goodBridgeCalls]; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); | ||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); | ||||
|             expect(btfs).to.be.length(goodBridgeCalls.length); | ||||
|         }); | ||||
|  | ||||
|         it('recoups funds from a bridge that reverts', async () => { | ||||
|             // Prepend a call that will take the whole input amount but will | ||||
|             // revert. | ||||
|             const badCall = await createBridgeCallAsync({ | ||||
|                 callFields: { inputTokenAmount: totalFillableInputAmount }, | ||||
|                 revertError: BRIDGE_REVERT_ERROR, | ||||
|             }); | ||||
|             const calls = [badCall, ...goodBridgeCalls]; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); | ||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); | ||||
|             expect(btfs).to.be.length(goodBridgeCalls.length); | ||||
|         }); | ||||
|  | ||||
|         it('recoups funds from a bridge that under-pays', async () => { | ||||
|             // Prepend a call that will take the whole input amount but will | ||||
|             // underpay the output amount.. | ||||
|             const badCall = await createBridgeCallAsync({ | ||||
|                 callFields: { | ||||
|                     inputTokenAmount: totalFillableInputAmount, | ||||
|                     outputTokenAmount: new BigNumber(2), | ||||
|                 }, | ||||
|                 outputFillAmount: new BigNumber(1), | ||||
|             }); | ||||
|             const calls = [badCall, ...goodBridgeCalls]; | ||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ | ||||
|                 inputToken, | ||||
|                 calls, | ||||
|             }); | ||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); | ||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); | ||||
|             expect(btfs).to.be.length(goodBridgeCalls.length); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe('executeBridgeCall()', () => { | ||||
|         it('cannot be called externally', async () => { | ||||
|             return expect( | ||||
|                 testContract | ||||
|                     .executeBridgeCall( | ||||
|                         randomAddress(), | ||||
|                         randomAddress(), | ||||
|                         randomAddress(), | ||||
|                         randomAddress(), | ||||
|                         new BigNumber(1), | ||||
|                         new BigNumber(1), | ||||
|                         constants.NULL_BYTES, | ||||
|                     ) | ||||
|                     .callAsync(), | ||||
|             ).to.revertWith('DexForwarderBridge/ONLY_SELF'); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "1.1.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "1.1.26", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "1.1.25", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v1.1.27 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.1.26 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.1.25 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-broker", | ||||
|     "version": "1.1.25", | ||||
|     "version": "1.1.27", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -51,16 +51,16 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-erc721": "^3.1.25", | ||||
|         "@0x/contracts-exchange": "^3.2.26", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-erc721": "^3.1.27", | ||||
|         "@0x/contracts-exchange": "^3.2.28", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -81,11 +81,11 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|         "ethereum-types": "^3.4.0" | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "3.1.28", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "3.1.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "3.1.26", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v3.1.28 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.1.27 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.1.26 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-coordinator", | ||||
|     "version": "3.1.26", | ||||
|     "version": "3.1.28", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,14 +52,14 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-dev-utils": "^1.3.24", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-dev-utils": "^1.3.26", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/web3-wrapper": "^7.4.1", | ||||
| @@ -79,15 +79,15 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/assert": "^3.0.21", | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contract-addresses": "^5.11.0", | ||||
|         "@0x/contracts-exchange": "^3.2.26", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/contract-addresses": "^6.0.0", | ||||
|         "@0x/contracts-exchange": "^3.2.28", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/json-schemas": "^5.4.1", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "1.3.26", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "1.3.25", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "1.3.24", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v1.3.26 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.3.25 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.3.24 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-dev-utils", | ||||
|     "version": "1.3.24", | ||||
|     "version": "1.3.26", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -41,13 +41,13 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/dev-utils", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/assert": "^3.0.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -60,7 +60,7 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "2.1.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "2.1.26", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "2.1.25", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v2.1.27 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v2.1.26 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v2.1.25 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-erc1155", | ||||
|     "version": "2.1.25", | ||||
|     "version": "2.1.27", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,11 +52,11 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -77,11 +77,11 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|         "@0x/web3-wrapper": "^7.4.1", | ||||
|         "lodash": "^4.17.11" | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "3.3.6", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "3.3.5", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "3.3.4", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v3.3.6 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.3.5 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.3.4 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-erc20", | ||||
|     "version": "3.3.4", | ||||
|     "version": "3.3.6", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -51,12 +51,12 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -79,7 +79,7 @@ | ||||
|         "solhint": "^1.4.1", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18" | ||||
|   | ||||
| @@ -6,6 +6,7 @@ export { | ||||
|     WETH9Events, | ||||
|     WETH9DepositEventArgs, | ||||
|     WETH9TransferEventArgs, | ||||
|     WETH9WithdrawalEventArgs, | ||||
|     ZRXTokenContract, | ||||
|     DummyERC20TokenTransferEventArgs, | ||||
|     ERC20TokenEventArgs, | ||||
|   | ||||
| @@ -39,8 +39,8 @@ describe('EtherToken', () => { | ||||
|             artifacts.WETH9, | ||||
|             provider, | ||||
|             { | ||||
|                 gasPrice, | ||||
|                 ...txDefaults, | ||||
|                 gasPrice, | ||||
|             }, | ||||
|             artifacts, | ||||
|         ); | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "3.1.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "3.1.26", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "3.1.25", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v3.1.27 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.1.26 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.1.25 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-erc721", | ||||
|     "version": "3.1.25", | ||||
|     "version": "3.1.27", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,12 +52,12 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -81,7 +81,7 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18" | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "4.2.28", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "4.2.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "4.2.26", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v4.2.28 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.2.27 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.2.26 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-exchange-forwarder", | ||||
|     "version": "4.2.26", | ||||
|     "version": "4.2.28", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,20 +52,20 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-dev-utils": "^1.3.24", | ||||
|         "@0x/contracts-erc1155": "^2.1.25", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-erc721": "^3.1.25", | ||||
|         "@0x/contracts-exchange": "^3.2.26", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-dev-utils": "^1.3.26", | ||||
|         "@0x/contracts-erc1155": "^2.1.27", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-erc721": "^3.1.27", | ||||
|         "@0x/contracts-exchange": "^3.2.28", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -87,7 +87,7 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "4.3.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "4.3.26", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "4.3.25", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v4.3.27 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.3.26 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.3.25 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-exchange-libs", | ||||
|     "version": "4.3.25", | ||||
|     "version": "4.3.27", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,10 +52,10 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/libs", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/subproviders": "^6.4.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
| @@ -77,13 +77,13 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "3.2.28", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "3.2.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "3.2.26", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v3.2.28 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.2.27 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v3.2.26 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-exchange", | ||||
|     "version": "3.2.26", | ||||
|     "version": "3.2.28", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,16 +52,16 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-multisig": "^4.1.26", | ||||
|         "@0x/contracts-staking": "^2.0.33", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-multisig": "^4.1.28", | ||||
|         "@0x/contracts-staking": "^2.0.35", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -85,15 +85,15 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-dev-utils": "^1.3.24", | ||||
|         "@0x/contracts-erc1155": "^2.1.25", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-erc721": "^3.1.25", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/contracts-dev-utils": "^1.3.26", | ||||
|         "@0x/contracts-erc1155": "^2.1.27", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-erc721": "^3.1.27", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|         "lodash": "^4.17.11" | ||||
|     }, | ||||
|   | ||||
| @@ -12,12 +12,12 @@ export abstract class AbstractBalanceAndProxyAllowanceFetcher { | ||||
|      * @param userAddress Ethereum address for which to fetch the balance | ||||
|      * @return Balance amount in base units | ||||
|      */ | ||||
|     public abstract async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     public abstract getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     /** | ||||
|      * Get the 0x asset proxy allowance of assetData for userAddress | ||||
|      * @param assetData AssetData for which to fetch the allowance | ||||
|      * @param userAddress Ethereum address for which to fetch the allowance | ||||
|      * @return Allowance amount in base units | ||||
|      */ | ||||
|     public abstract async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     public abstract getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { BigNumber } from '@0x/utils'; | ||||
|  | ||||
| export abstract class AbstractBalanceAndProxyAllowanceLazyStore { | ||||
|     public abstract async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     public abstract async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     public abstract getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     public abstract getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber>; | ||||
|     public abstract setBalance(assetData: string, userAddress: string, balance: BigNumber): void; | ||||
|     public abstract deleteBalance(assetData: string, userAddress: string): void; | ||||
|     public abstract setProxyAllowance(assetData: string, userAddress: string, proxyAllowance: BigNumber): void; | ||||
|   | ||||
| @@ -11,5 +11,5 @@ export abstract class AbstractOrderFilledCancelledFetcher { | ||||
|      * @param orderHash OrderHash of order we are interested in | ||||
|      * @return FilledTakerAmount | ||||
|      */ | ||||
|     public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>; | ||||
|     public abstract getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>; | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { BigNumber } from '@0x/utils'; | ||||
|  | ||||
| export abstract class AbstractOrderFilledCancelledLazyStore { | ||||
|     public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>; | ||||
|     public abstract getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>; | ||||
|     public abstract setFilledTakerAmount(orderHash: string, balance: BigNumber): void; | ||||
|     public abstract deleteFilledTakerAmount(orderHash: string): void; | ||||
|     public abstract setIsCancelled(orderHash: string, isCancelled: boolean): void; | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import { | ||||
|     IsolatedExchangeFillEventArgs as FillEventArgs, | ||||
| } from '../wrappers'; | ||||
|  | ||||
| export { Order } from '@0x/types'; | ||||
| export interface AssetBalances { | ||||
|     [assetData: string]: { [address: string]: BigNumber }; | ||||
| } | ||||
| @@ -27,7 +28,6 @@ export interface IsolatedExchangeEvents { | ||||
|     transferFromCalls: DispatchTransferFromCallArgs[]; | ||||
| } | ||||
|  | ||||
| export type Order = Order; | ||||
| export type Numberish = string | number | BigNumber; | ||||
|  | ||||
| export const DEFAULT_GOOD_SIGNATURE = createGoodSignature(); | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "6.2.22", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "6.2.21", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "6.2.20", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v6.2.22 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v6.2.21 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v6.2.20 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-extensions", | ||||
|     "version": "6.2.20", | ||||
|     "version": "6.2.22", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -52,18 +52,18 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-dev-utils": "^1.3.24", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-erc721": "^3.1.25", | ||||
|         "@0x/contracts-exchange": "^3.2.26", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-dev-utils": "^1.3.26", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-erc721": "^3.1.27", | ||||
|         "@0x/contracts-exchange": "^3.2.28", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -87,11 +87,11 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|         "ethereum-types": "^3.4.0" | ||||
|     }, | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-integrations", | ||||
|     "version": "2.7.27", | ||||
|     "version": "2.7.32", | ||||
|     "private": true, | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
| @@ -52,23 +52,23 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contract-addresses": "^5.11.0", | ||||
|         "@0x/contract-wrappers": "^13.13.0", | ||||
|         "@0x/contracts-broker": "^1.1.25", | ||||
|         "@0x/contracts-coordinator": "^3.1.26", | ||||
|         "@0x/contracts-dev-utils": "^1.3.24", | ||||
|         "@0x/contracts-exchange-forwarder": "^4.2.26", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/contracts-extensions": "^6.2.20", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contract-addresses": "^6.0.0", | ||||
|         "@0x/contract-wrappers": "^13.15.0", | ||||
|         "@0x/contracts-broker": "^1.1.27", | ||||
|         "@0x/contracts-coordinator": "^3.1.28", | ||||
|         "@0x/contracts-dev-utils": "^1.3.26", | ||||
|         "@0x/contracts-exchange-forwarder": "^4.2.28", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/contracts-extensions": "^6.2.22", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/coordinator-server": "^1.0.5", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/migrations": "^7.0.0", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/protocol-utils": "^1.3.0", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/migrations": "^8.0.1", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/protocol-utils": "^1.4.1", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/web3-wrapper": "^7.4.1", | ||||
|         "@azure/core-asynciterator-polyfill": "^1.0.0", | ||||
| @@ -90,20 +90,20 @@ | ||||
|         "solhint": "^1.4.1", | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/asset-swapper": "^6.1.0", | ||||
|         "@0x/asset-swapper": "^6.5.1", | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-erc1155": "^2.1.25", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-erc721": "^3.1.25", | ||||
|         "@0x/contracts-exchange": "^3.2.26", | ||||
|         "@0x/contracts-multisig": "^4.1.26", | ||||
|         "@0x/contracts-staking": "^2.0.33", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-zero-ex": "^0.19.0", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-erc1155": "^2.1.27", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-erc721": "^3.1.27", | ||||
|         "@0x/contracts-exchange": "^3.2.28", | ||||
|         "@0x/contracts-multisig": "^4.1.28", | ||||
|         "@0x/contracts-staking": "^2.0.35", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-zero-ex": "^0.21.1", | ||||
|         "@0x/subproviders": "^6.4.1", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|   | ||||
| @@ -63,11 +63,9 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { | ||||
|     let dai: ERC20TokenContract; | ||||
|     let usdc: ERC20TokenContract; | ||||
|     let devUtils: DevUtilsContract; | ||||
|     let accountOwner: string; | ||||
|     let minMarginRatio: number; | ||||
|  | ||||
|     before(async () => { | ||||
|         [accountOwner] = await env.getAccountAddressesAsync(); | ||||
|         dydx = new IDydxContract(DYDX_ADDRESS, env.provider, env.txDefaults); | ||||
|         dai = new ERC20TokenContract(DAI_ADDRESS, env.provider, env.txDefaults); | ||||
|         usdc = new ERC20TokenContract(USDC_ADDRESS, env.provider, env.txDefaults); | ||||
|   | ||||
| @@ -19,5 +19,5 @@ export function filterActorsByRole<TClass extends Constructor>( | ||||
|     actors: Actor[], | ||||
|     role: TClass, | ||||
| ): Array<InstanceType<typeof role>> { | ||||
|     return actors.filter(actor => actor.mixins.includes(role.name)) as InstanceType<typeof role>; | ||||
|     return actors.filter(actor => actor.mixins.includes(role.name)) as Array<InstanceType<typeof role>>; | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "4.1.28", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "4.1.27", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "4.1.26", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v4.1.28 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.1.27 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.1.26 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-multisig", | ||||
|     "version": "4.1.26", | ||||
|     "version": "4.1.28", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -49,14 +49,14 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/utils": "^6.2.0", | ||||
| @@ -75,7 +75,7 @@ | ||||
|         "shx": "^0.2.2", | ||||
|         "solhint": "^1.4.1", | ||||
|         "tslint": "5.11.0", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "2.0.35", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "2.0.34", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "2.0.33", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v2.0.35 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v2.0.34 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v2.0.33 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-staking", | ||||
|     "version": "2.0.33", | ||||
|     "version": "2.0.35", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -53,16 +53,16 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-dev-utils": "^1.3.24", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.25", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-utils": "^4.7.4", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-dev-utils": "^1.3.26", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-exchange-libs": "^4.3.27", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-utils": "^4.7.6", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
| @@ -84,11 +84,11 @@ | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|         "ethereum-types": "^3.4.0", | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types'; | ||||
|  | ||||
| import { constants as stakingConstants } from './constants'; | ||||
|  | ||||
| export { Numberish } from '@0x/contracts-test-utils'; | ||||
| // tslint:disable:max-classes-per-file | ||||
|  | ||||
| export interface StakingParams { | ||||
| @@ -259,5 +260,3 @@ export class AggregatedStats { | ||||
| export interface AggregatedStatsByEpoch { | ||||
|     [epoch: string]: AggregatedStats; | ||||
| } | ||||
|  | ||||
| export type Numberish = Numberish; | ||||
|   | ||||
| @@ -12,7 +12,6 @@ import { | ||||
|  | ||||
| blockchainTests.resets('Exchange Unit Tests', env => { | ||||
|     // Addresses | ||||
|     let nonOwner: string; | ||||
|     let owner: string; | ||||
|     let nonExchange: string; | ||||
|     let exchange: string; | ||||
| @@ -24,7 +23,7 @@ blockchainTests.resets('Exchange Unit Tests', env => { | ||||
|  | ||||
|     before(async () => { | ||||
|         // Set up addresses for testing. | ||||
|         [nonOwner, owner, nonExchange, exchange, nonAuthority, authority] = await env.getAccountAddressesAsync(); | ||||
|         [, owner, nonExchange, exchange, nonAuthority, authority] = await env.getAccountAddressesAsync(); | ||||
|  | ||||
|         // Deploy the Exchange Manager contract. | ||||
|         exchangeManager = await TestExchangeManagerContract.deployFrom0xArtifactAsync( | ||||
|   | ||||
| @@ -543,7 +543,7 @@ blockchainTests.resets('Finalizer unit tests', env => { | ||||
|             const expectedPoolRewards = await calculatePoolRewardsAsync(INITIAL_BALANCE, pools); | ||||
|             const [pool, reward] = _.sampleSize(shortZip(pools, expectedPoolRewards), 1)[0]; | ||||
|             return assertUnfinalizedPoolRewardsAsync(pool.poolId, { | ||||
|                 totalReward: (reward as any) as BigNumber, | ||||
|                 totalReward: reward, | ||||
|                 membersStake: pool.membersStake, | ||||
|             }); | ||||
|         }); | ||||
|   | ||||
| @@ -12,17 +12,13 @@ import * as _ from 'lodash'; | ||||
| import { artifacts } from '../artifacts'; | ||||
| import { TestCobbDouglasContract } from '../wrappers'; | ||||
|  | ||||
| // tslint:disable: no-unnecessary-type-assertion | ||||
| blockchainTests('LibCobbDouglas unit tests', env => { | ||||
|     const FUZZ_COUNT = 1024; | ||||
|     const PRECISION = 15; | ||||
|  | ||||
|     let testContract: TestCobbDouglasContract; | ||||
|     let ownerAddress: string; | ||||
|     let notOwnerAddress: string; | ||||
|  | ||||
|     before(async () => { | ||||
|         [ownerAddress, notOwnerAddress] = await env.getAccountAddressesAsync(); | ||||
|         testContract = await TestCobbDouglasContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.TestCobbDouglas, | ||||
|             env.provider, | ||||
| @@ -211,4 +207,3 @@ blockchainTests('LibCobbDouglas unit tests', env => { | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| // tslint:enable:no-unnecessary-type-assertion | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "5.3.24", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "5.3.23", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "5.3.22", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v5.3.24 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v5.3.23 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v5.3.22 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-test-utils", | ||||
|     "version": "5.3.22", | ||||
|     "version": "5.3.24", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -34,23 +34,23 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils", | ||||
|     "devDependencies": { | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "npm-run-all": "^4.1.2", | ||||
|         "shx": "^0.2.2", | ||||
|         "tslint": "5.11.0", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/assert": "^3.0.21", | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/contract-addresses": "^5.11.0", | ||||
|         "@0x/contract-addresses": "^6.0.0", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/json-schemas": "^5.4.1", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/sol-coverage": "^4.0.29", | ||||
|         "@0x/sol-profiler": "^4.1.19", | ||||
|         "@0x/sol-trace": "^3.0.29", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/sol-coverage": "^4.0.31", | ||||
|         "@0x/sol-profiler": "^4.1.21", | ||||
|         "@0x/sol-trace": "^3.0.31", | ||||
|         "@0x/subproviders": "^6.4.1", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|   | ||||
| @@ -14,6 +14,6 @@ export function shortZip<T1, T2>(a: T1[], b: T2[]): Array<[T1, T2]> { | ||||
| export function replaceKeysDeep(obj: {}, mapKeys: (key: string) => string | void): _.Dictionary<{}> { | ||||
|     return _.transform(obj, (result, value, key) => { | ||||
|         const currentKey = mapKeys(key) || key; | ||||
|         result[currentKey] = _.isObject(value) ? replaceKeysDeep(value, mapKeys) : value; | ||||
|         result[currentKey] = _.isObject(value) ? replaceKeysDeep(value as {}, mapKeys) : (value as {}); | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -22,14 +22,14 @@ export class OrderFactory { | ||||
|     ): Promise<SignedOrder> { | ||||
|         const fifteenMinutesInSeconds = 15 * 60; | ||||
|         const currentBlockTimestamp = await getLatestBlockTimestampAsync(); | ||||
|         const order = ({ | ||||
|         const order = { | ||||
|             takerAddress: constants.NULL_ADDRESS, | ||||
|             senderAddress: constants.NULL_ADDRESS, | ||||
|             expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(fifteenMinutesInSeconds), | ||||
|             salt: generatePseudoRandomSalt(), | ||||
|             ...this._defaultOrderParams, | ||||
|             ...customOrderParams, | ||||
|         } as any) as Order; | ||||
|         } as Order; // tslint:disable-line:no-object-literal-type-assertion | ||||
|         const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); | ||||
|         const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType); | ||||
|         const signedOrder = { | ||||
|   | ||||
| @@ -30,7 +30,8 @@ export const orderUtils = { | ||||
|         return cancel; | ||||
|     }, | ||||
|     createOrderWithoutSignature(signedOrder: SignedOrder): Order { | ||||
|         return _.omit(signedOrder, ['signature']) as Order; | ||||
|         const { signature, ...order } = signedOrder; | ||||
|         return order; | ||||
|     }, | ||||
|     createBatchMatchOrders(signedOrdersLeft: SignedOrder[], signedOrdersRight: SignedOrder[]): BatchMatchOrder { | ||||
|         return { | ||||
|   | ||||
| @@ -1,4 +1,32 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1618259868, | ||||
|         "version": "1.1.2", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "1.1.1", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.1.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Make the proposal/quorum thresholds updatable", | ||||
|                 "pr": 165 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1616005394 | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "1.0.2", | ||||
|   | ||||
| @@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v1.1.2 - _April 12, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.1.1 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.1.0 - _March 17, 2021_ | ||||
|  | ||||
|     * Make the proposal/quorum thresholds updatable (#165) | ||||
|  | ||||
| ## v1.0.2 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -96,6 +96,18 @@ interface IZrxTreasury { | ||||
|         view | ||||
|         returns (uint256); | ||||
|  | ||||
|     /// @dev Updates the proposal and quorum thresholds to the given | ||||
|     ///      values. Note that this function is only callable by the | ||||
|     ///      treasury contract itself, so the threshold can only be | ||||
|     ///      updated via a successful treasury proposal. | ||||
|     /// @param newProposalThreshold The new value for the proposal threshold. | ||||
|     /// @param newQuorumThreshold The new value for the quorum threshold. | ||||
|     function updateThresholds( | ||||
|         uint256 newProposalThreshold, | ||||
|         uint256 newQuorumThreshold | ||||
|     ) | ||||
|         external; | ||||
|  | ||||
|     /// @dev Creates a proposal to send ZRX from this treasury on the | ||||
|     ///      the given actions. Must have at least `proposalThreshold` | ||||
|     ///      of voting power to call this function. See `getVotingPower` | ||||
|   | ||||
| @@ -42,8 +42,8 @@ contract ZrxTreasury is | ||||
|     DefaultPoolOperator public immutable override defaultPoolOperator; | ||||
|     bytes32 public immutable override defaultPoolId; | ||||
|     uint256 public immutable override votingPeriod; | ||||
|     uint256 public immutable override proposalThreshold; | ||||
|     uint256 public immutable override quorumThreshold; | ||||
|     uint256 public override proposalThreshold; | ||||
|     uint256 public override quorumThreshold; | ||||
|  | ||||
|     // Storage | ||||
|     Proposal[] public proposals; | ||||
| @@ -82,6 +82,24 @@ contract ZrxTreasury is | ||||
|     receive() external payable {} | ||||
|     // solhint-enable | ||||
|  | ||||
|     /// @dev Updates the proposal and quorum thresholds to the given | ||||
|     ///      values. Note that this function is only callable by the | ||||
|     ///      treasury contract itself, so the threshold can only be | ||||
|     ///      updated via a successful treasury proposal. | ||||
|     /// @param newProposalThreshold The new value for the proposal threshold. | ||||
|     /// @param newQuorumThreshold The new value for the quorum threshold. | ||||
|     function updateThresholds( | ||||
|         uint256 newProposalThreshold, | ||||
|         uint256 newQuorumThreshold | ||||
|     ) | ||||
|         external | ||||
|         override | ||||
|     { | ||||
|         require(msg.sender == address(this), "updateThresholds/ONLY_SELF"); | ||||
|         proposalThreshold = newProposalThreshold; | ||||
|         quorumThreshold = newQuorumThreshold; | ||||
|     } | ||||
|  | ||||
|     /// @dev Creates a proposal to send ZRX from this treasury on the | ||||
|     ///      the given actions. Must have at least `proposalThreshold` | ||||
|     ///      of voting power to call this function. See `getVotingPower` | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-treasury", | ||||
|     "version": "1.0.2", | ||||
|     "version": "1.1.2", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -46,14 +46,14 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.13", | ||||
|         "@0x/contract-addresses": "^5.11.0", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.7", | ||||
|         "@0x/contracts-erc20": "^3.3.4", | ||||
|         "@0x/contracts-gen": "^2.0.24", | ||||
|         "@0x/contracts-staking": "^2.0.33", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/sol-compiler": "^4.4.1", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contract-addresses": "^6.0.0", | ||||
|         "@0x/contracts-asset-proxy": "^3.7.9", | ||||
|         "@0x/contracts-erc20": "^3.3.6", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-staking": "^2.0.35", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/ts-doc-gen": "^0.0.28", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@types/isomorphic-fetch": "^0.0.35", | ||||
| @@ -69,16 +69,16 @@ | ||||
|         "solhint": "^1.4.1", | ||||
|         "tslint": "5.11.0", | ||||
|         "typedoc": "~0.16.11", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.14", | ||||
|         "@0x/protocol-utils": "^1.3.0", | ||||
|         "@0x/subproviders": "^6.2.3", | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|         "@0x/protocol-utils": "^1.4.1", | ||||
|         "@0x/subproviders": "^6.4.1", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/typescript-typings": "^5.1.6", | ||||
|         "@0x/utils": "^6.1.1", | ||||
|         "@0x/web3-wrapper": "^7.3.0", | ||||
|         "@0x/utils": "^6.2.0", | ||||
|         "@0x/web3-wrapper": "^7.4.1", | ||||
|         "ethereum-types": "^3.4.0", | ||||
|         "ethereumjs-util": "^5.1.1" | ||||
|     }, | ||||
|   | ||||
| @@ -580,4 +580,47 @@ blockchainTests.resets('Treasury governance', env => { | ||||
|             expect(await weth.balanceOf(staking.address).callAsync()).to.bignumber.equal(wethAmount); | ||||
|         }); | ||||
|     }); | ||||
|     describe('Can update thresholds via proposal', () => { | ||||
|         it('Updates proposal and quorum thresholds', async () => { | ||||
|             // Delegator has enough ZRX to create and pass a proposal | ||||
|             await staking.stake(TREASURY_PARAMS.quorumThreshold).awaitTransactionSuccessAsync({ from: delegator }); | ||||
|             await staking | ||||
|                 .moveStake( | ||||
|                     new StakeInfo(StakeStatus.Undelegated), | ||||
|                     new StakeInfo(StakeStatus.Delegated, defaultPoolId), | ||||
|                     TREASURY_PARAMS.quorumThreshold, | ||||
|                 ) | ||||
|                 .awaitTransactionSuccessAsync({ from: delegator }); | ||||
|             await fastForwardToNextEpochAsync(); | ||||
|             const currentEpoch = await staking.currentEpoch().callAsync(); | ||||
|             const newProposalThreshold = new BigNumber(420); | ||||
|             const newQuorumThreshold = new BigNumber(1337); | ||||
|             const updateThresholdsAction = { | ||||
|                 target: treasury.address, | ||||
|                 data: treasury | ||||
|                     .updateThresholds(newProposalThreshold, newQuorumThreshold) | ||||
|                     .getABIEncodedTransactionData(), | ||||
|                 value: constants.ZERO_AMOUNT, | ||||
|             }; | ||||
|             const tx = treasury.propose( | ||||
|                 [updateThresholdsAction], | ||||
|                 currentEpoch.plus(3), | ||||
|                 `Updates proposal threshold to ${newProposalThreshold} and quorum threshold to ${newQuorumThreshold}`, | ||||
|                 [], | ||||
|             ); | ||||
|             const proposalId = await tx.callAsync({ from: delegator }); | ||||
|             await tx.awaitTransactionSuccessAsync({ from: delegator }); | ||||
|             await fastForwardToNextEpochAsync(); | ||||
|             await fastForwardToNextEpochAsync(); | ||||
|             await treasury.castVote(proposalId, true, []).awaitTransactionSuccessAsync({ from: delegator }); | ||||
|             await fastForwardToNextEpochAsync(); | ||||
|             await treasury | ||||
|                 .execute(proposalId, [updateThresholdsAction]) | ||||
|                 .awaitTransactionSuccessAsync({ from: delegator }); | ||||
|             const proposalThreshold = await treasury.proposalThreshold().callAsync(); | ||||
|             const quorumThreshold = await treasury.quorumThreshold().callAsync(); | ||||
|             expect(proposalThreshold).to.bignumber.equal(newProposalThreshold); | ||||
|             expect(quorumThreshold).to.bignumber.equal(newQuorumThreshold); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1617311315, | ||||
|         "version": "4.7.6", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1616005394, | ||||
|         "version": "4.7.5", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1614141718, | ||||
|         "version": "4.7.4", | ||||
|   | ||||
| @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v4.7.6 - _April 1, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.7.5 - _March 17, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v4.7.4 - _February 24, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-utils", | ||||
|     "version": "4.7.4", | ||||
|     "version": "4.7.6", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -50,12 +50,12 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^5.4.19", | ||||
|         "@0x/contracts-gen": "^2.0.30", | ||||
|         "@0x/contracts-test-utils": "^5.3.22", | ||||
|         "@0x/abi-gen": "^5.4.21", | ||||
|         "@0x/contracts-gen": "^2.0.32", | ||||
|         "@0x/contracts-test-utils": "^5.3.24", | ||||
|         "@0x/dev-utils": "^4.2.1", | ||||
|         "@0x/order-utils": "^10.4.17", | ||||
|         "@0x/sol-compiler": "^4.5.2", | ||||
|         "@0x/order-utils": "^10.4.19", | ||||
|         "@0x/sol-compiler": "^4.6.1", | ||||
|         "@0x/tslint-config": "^4.1.3", | ||||
|         "@0x/types": "^3.3.1", | ||||
|         "@0x/web3-wrapper": "^7.4.1", | ||||
| @@ -76,7 +76,7 @@ | ||||
|         "solhint": "^1.4.1", | ||||
|         "truffle": "^5.0.32", | ||||
|         "tslint": "5.11.0", | ||||
|         "typescript": "3.0.1" | ||||
|         "typescript": "4.2.2" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^6.2.18", | ||||
|   | ||||
| @@ -1,4 +1,65 @@ | ||||
| [ | ||||
|     { | ||||
|         "timestamp": 1618259868, | ||||
|         "version": "0.21.1", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "version": "0.21.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Encoding protocol ID and source name in bridge source ID", | ||||
|                 "pr": 162 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Add PancakeSwapFeature", | ||||
|                 "pr": 164 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Remove TokenSpender/AllowanceTarget/greedy tokens stuff", | ||||
|                 "pr": 164 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Added Nerve in BridgeAdapter", | ||||
|                 "pr": 181 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Delete TokenSpenderFeature", | ||||
|                 "pr": 189 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Fix PancakeSwapFeature BakerySwap swap selector", | ||||
|                 "pr": 190 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1617311315 | ||||
|     }, | ||||
|     { | ||||
|         "version": "0.20.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Add `MooniswapLiquidityProvider`", | ||||
|                 "pr": 143 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Emit `LiquidityProviderFill` event in `CurveLiquidityProvider`", | ||||
|                 "pr": 143 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Add BatchFillNativeOrdersFeature and MultiplexFeature", | ||||
|                 "pr": 140 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Export MultiplexFeatureContract", | ||||
|                 "pr": 168 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1616005394 | ||||
|     }, | ||||
|     { | ||||
|         "version": "0.19.0", | ||||
|         "changes": [ | ||||
|   | ||||
| @@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v0.21.1 - _April 12, 2021_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v0.21.0 - _April 1, 2021_ | ||||
|  | ||||
|     * Encoding protocol ID and source name in bridge source ID (#162) | ||||
|     * Add PancakeSwapFeature (#164) | ||||
|     * Remove TokenSpender/AllowanceTarget/greedy tokens stuff (#164) | ||||
|     * Added Nerve in BridgeAdapter (#181) | ||||
|     * Delete TokenSpenderFeature (#189) | ||||
|     * Fix PancakeSwapFeature BakerySwap swap selector (#190) | ||||
|  | ||||
| ## v0.20.0 - _March 17, 2021_ | ||||
|  | ||||
|     * Add `MooniswapLiquidityProvider` (#143) | ||||
|     * Emit `LiquidityProviderFill` event in `CurveLiquidityProvider` (#143) | ||||
|     * Add BatchFillNativeOrdersFeature and MultiplexFeature (#140) | ||||
|     * Export MultiplexFeatureContract (#168) | ||||
|  | ||||
| ## v0.19.0 - _February 24, 2021_ | ||||
|  | ||||
|     * Add `CurveLiquidityProvider` and misc refactors (#127) | ||||
|   | ||||
| @@ -20,26 +20,31 @@ | ||||
| pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "./features/IOwnableFeature.sol"; | ||||
| import "./features/ISimpleFunctionRegistryFeature.sol"; | ||||
| import "./features/ITokenSpenderFeature.sol"; | ||||
| import "./features/ITransformERC20Feature.sol"; | ||||
| import "./features/IMetaTransactionsFeature.sol"; | ||||
| import "./features/IUniswapFeature.sol"; | ||||
| import "./features/ILiquidityProviderFeature.sol"; | ||||
| import "./features/INativeOrdersFeature.sol"; | ||||
| import "./features/interfaces/IOwnableFeature.sol"; | ||||
| import "./features/interfaces/ISimpleFunctionRegistryFeature.sol"; | ||||
| import "./features/interfaces/ITokenSpenderFeature.sol"; | ||||
| import "./features/interfaces/ITransformERC20Feature.sol"; | ||||
| import "./features/interfaces/IMetaTransactionsFeature.sol"; | ||||
| import "./features/interfaces/IUniswapFeature.sol"; | ||||
| import "./features/interfaces/IPancakeSwapFeature.sol"; | ||||
| import "./features/interfaces/ILiquidityProviderFeature.sol"; | ||||
| import "./features/interfaces/INativeOrdersFeature.sol"; | ||||
| import "./features/interfaces/IBatchFillNativeOrdersFeature.sol"; | ||||
| import "./features/interfaces/IMultiplexFeature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Interface for a fully featured Exchange Proxy. | ||||
| interface IZeroEx is | ||||
|     IOwnableFeature, | ||||
|     ISimpleFunctionRegistryFeature, | ||||
|     ITokenSpenderFeature, | ||||
|     ITransformERC20Feature, | ||||
|     IMetaTransactionsFeature, | ||||
|     IUniswapFeature, | ||||
|     IPancakeSwapFeature, | ||||
|     ILiquidityProviderFeature, | ||||
|     INativeOrdersFeature | ||||
|     INativeOrdersFeature, | ||||
|     IBatchFillNativeOrdersFeature, | ||||
|     IMultiplexFeature | ||||
| { | ||||
|     // solhint-disable state-visibility | ||||
|  | ||||
|   | ||||
| @@ -170,4 +170,21 @@ library LibNativeOrdersRichErrors { | ||||
|             maker | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     function BatchFillIncompleteError( | ||||
|         bytes32 orderHash, | ||||
|         uint256 takerTokenFilledAmount, | ||||
|         uint256 takerTokenFillAmount | ||||
|     ) | ||||
|         internal | ||||
|         pure | ||||
|         returns (bytes memory) | ||||
|     { | ||||
|         return abi.encodeWithSelector( | ||||
|             bytes4(keccak256("BatchFillIncompleteError(bytes32,uint256,uint256)")), | ||||
|             orderHash, | ||||
|             takerTokenFilledAmount, | ||||
|             takerTokenFillAmount | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,56 +0,0 @@ | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
| /* | ||||
|  | ||||
|   Copyright 2020 ZeroEx Intl. | ||||
|  | ||||
|   Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   you may not use this file except in compliance with the License. | ||||
|   You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|   Unless required by applicable law or agreed to in writing, software | ||||
|   distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   See the License for the specific language governing permissions and | ||||
|   limitations under the License. | ||||
|  | ||||
| */ | ||||
|  | ||||
| pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol"; | ||||
| import "../errors/LibSpenderRichErrors.sol"; | ||||
| import "./IAllowanceTarget.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev The allowance target for the TokenSpender feature. | ||||
| contract AllowanceTarget is | ||||
|     IAllowanceTarget, | ||||
|     AuthorizableV06 | ||||
| { | ||||
|     // solhint-disable no-unused-vars,indent,no-empty-blocks | ||||
|     using LibRichErrorsV06 for bytes; | ||||
|  | ||||
|     /// @dev Execute an arbitrary call. Only an authority can call this. | ||||
|     /// @param target The call target. | ||||
|     /// @param callData The call data. | ||||
|     /// @return resultData The data returned by the call. | ||||
|     function executeCall( | ||||
|         address payable target, | ||||
|         bytes calldata callData | ||||
|     ) | ||||
|         external | ||||
|         override | ||||
|         onlyAuthorized | ||||
|         returns (bytes memory resultData) | ||||
|     { | ||||
|         bool success; | ||||
|         (success, resultData) = target.call(callData); | ||||
|         if (!success) { | ||||
|             resultData.rrevert(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,198 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol"; | ||||
| import "../errors/LibNativeOrdersRichErrors.sol"; | ||||
| import "../fixins/FixinCommon.sol"; | ||||
| import "../fixins/FixinEIP712.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/IBatchFillNativeOrdersFeature.sol"; | ||||
| import "./interfaces/INativeOrdersFeature.sol"; | ||||
| import "./libs/LibNativeOrder.sol"; | ||||
| import "./libs/LibSignature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Feature for batch/market filling limit and RFQ orders. | ||||
| contract BatchFillNativeOrdersFeature is | ||||
|     IFeature, | ||||
|     IBatchFillNativeOrdersFeature, | ||||
|     FixinCommon, | ||||
|     FixinEIP712 | ||||
| { | ||||
|     using LibSafeMathV06 for uint128; | ||||
|     using LibSafeMathV06 for uint256; | ||||
|     using LibRichErrorsV06 for bytes; | ||||
|  | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "BatchFill"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); | ||||
|  | ||||
|     constructor(address zeroExAddress) | ||||
|         public | ||||
|         FixinEIP712(zeroExAddress) | ||||
|     { | ||||
|         // solhint-disable no-empty-blocks | ||||
|     } | ||||
|  | ||||
|     /// @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.batchFillLimitOrders.selector); | ||||
|         _registerFeatureFunction(this.batchFillRfqOrders.selector); | ||||
|         return LibMigrate.MIGRATE_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     /// @dev Fills multiple limit orders. | ||||
|     /// @param orders Array of limit orders. | ||||
|     /// @param signatures Array of signatures corresponding to each order. | ||||
|     /// @param takerTokenFillAmounts Array of desired amounts to fill each order. | ||||
|     /// @param revertIfIncomplete If true, reverts if this function fails to | ||||
|     ///        fill the full fill amount for any individual order. | ||||
|     /// @return takerTokenFilledAmounts Array of amounts filled, in taker token. | ||||
|     /// @return makerTokenFilledAmounts Array of amounts filled, in maker token. | ||||
|     function batchFillLimitOrders( | ||||
|         LibNativeOrder.LimitOrder[] calldata orders, | ||||
|         LibSignature.Signature[] calldata signatures, | ||||
|         uint128[] calldata takerTokenFillAmounts, | ||||
|         bool revertIfIncomplete | ||||
|     ) | ||||
|         external | ||||
|         payable | ||||
|         override | ||||
|         returns ( | ||||
|             uint128[] memory takerTokenFilledAmounts, | ||||
|             uint128[] memory makerTokenFilledAmounts | ||||
|         ) | ||||
|     { | ||||
|         require( | ||||
|             orders.length == signatures.length && orders.length == takerTokenFillAmounts.length, | ||||
|             'BatchFillNativeOrdersFeature::batchFillLimitOrders/MISMATCHED_ARRAY_LENGTHS' | ||||
|         ); | ||||
|         takerTokenFilledAmounts = new uint128[](orders.length); | ||||
|         makerTokenFilledAmounts = new uint128[](orders.length); | ||||
|         uint256 protocolFee = uint256(INativeOrdersFeature(address(this)).getProtocolFeeMultiplier()) | ||||
|             .safeMul(tx.gasprice); | ||||
|         uint256 ethProtocolFeePaid; | ||||
|         for (uint256 i = 0; i != orders.length; i++) { | ||||
|             try | ||||
|                 INativeOrdersFeature(address(this))._fillLimitOrder | ||||
|                     ( | ||||
|                         orders[i], | ||||
|                         signatures[i], | ||||
|                         takerTokenFillAmounts[i], | ||||
|                         msg.sender, | ||||
|                         msg.sender | ||||
|                     ) | ||||
|                 returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) | ||||
|             { | ||||
|                 // Update amounts filled. | ||||
|                 (takerTokenFilledAmounts[i], makerTokenFilledAmounts[i]) = | ||||
|                     (takerTokenFilledAmount, makerTokenFilledAmount); | ||||
|                 ethProtocolFeePaid = ethProtocolFeePaid.safeAdd(protocolFee); | ||||
|             } catch {} | ||||
|  | ||||
|             if ( | ||||
|                 revertIfIncomplete && | ||||
|                 takerTokenFilledAmounts[i] < takerTokenFillAmounts[i] | ||||
|             ) { | ||||
|                 bytes32 orderHash = _getEIP712Hash( | ||||
|                     LibNativeOrder.getLimitOrderStructHash(orders[i]) | ||||
|                 ); | ||||
|                 // Did not fill the amount requested. | ||||
|                 LibNativeOrdersRichErrors.BatchFillIncompleteError( | ||||
|                     orderHash, | ||||
|                     takerTokenFilledAmounts[i], | ||||
|                     takerTokenFillAmounts[i] | ||||
|                 ).rrevert(); | ||||
|             } | ||||
|         } | ||||
|         LibNativeOrder.refundExcessProtocolFeeToSender(ethProtocolFeePaid); | ||||
|     } | ||||
|  | ||||
|     /// @dev Fills multiple RFQ orders. | ||||
|     /// @param orders Array of RFQ orders. | ||||
|     /// @param signatures Array of signatures corresponding to each order. | ||||
|     /// @param takerTokenFillAmounts Array of desired amounts to fill each order. | ||||
|     /// @param revertIfIncomplete If true, reverts if this function fails to | ||||
|     ///        fill the full fill amount for any individual order. | ||||
|     /// @return takerTokenFilledAmounts Array of amounts filled, in taker token. | ||||
|     /// @return makerTokenFilledAmounts Array of amounts filled, in maker token. | ||||
|     function batchFillRfqOrders( | ||||
|         LibNativeOrder.RfqOrder[] calldata orders, | ||||
|         LibSignature.Signature[] calldata signatures, | ||||
|         uint128[] calldata takerTokenFillAmounts, | ||||
|         bool revertIfIncomplete | ||||
|     ) | ||||
|         external | ||||
|         override | ||||
|         returns ( | ||||
|             uint128[] memory takerTokenFilledAmounts, | ||||
|             uint128[] memory makerTokenFilledAmounts | ||||
|         ) | ||||
|     { | ||||
|         require( | ||||
|             orders.length == signatures.length && orders.length == takerTokenFillAmounts.length, | ||||
|             'BatchFillNativeOrdersFeature::batchFillRfqOrders/MISMATCHED_ARRAY_LENGTHS' | ||||
|         ); | ||||
|         takerTokenFilledAmounts = new uint128[](orders.length); | ||||
|         makerTokenFilledAmounts = new uint128[](orders.length); | ||||
|         for (uint256 i = 0; i != orders.length; i++) { | ||||
|             try | ||||
|                 INativeOrdersFeature(address(this))._fillRfqOrder | ||||
|                     ( | ||||
|                         orders[i], | ||||
|                         signatures[i], | ||||
|                         takerTokenFillAmounts[i], | ||||
|                         msg.sender | ||||
|                     ) | ||||
|                 returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) | ||||
|             { | ||||
|                 // Update amounts filled. | ||||
|                 (takerTokenFilledAmounts[i], makerTokenFilledAmounts[i]) = | ||||
|                     (takerTokenFilledAmount, makerTokenFilledAmount); | ||||
|             } catch {} | ||||
|  | ||||
|             if ( | ||||
|                 revertIfIncomplete && | ||||
|                 takerTokenFilledAmounts[i] < takerTokenFillAmounts[i] | ||||
|             ) { | ||||
|                 // Did not fill the amount requested. | ||||
|                 bytes32 orderHash = _getEIP712Hash( | ||||
|                     LibNativeOrder.getRfqOrderStructHash(orders[i]) | ||||
|                 ); | ||||
|                 LibNativeOrdersRichErrors.BatchFillIncompleteError( | ||||
|                     orderHash, | ||||
|                     takerTokenFilledAmounts[i], | ||||
|                     takerTokenFillAmounts[i] | ||||
|                 ).rrevert(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2; | ||||
| import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; | ||||
| import "../migrations/LibBootstrap.sol"; | ||||
| import "../storage/LibProxyStorage.sol"; | ||||
| import "./IBootstrapFeature.sol"; | ||||
| import "./interfaces/IBootstrapFeature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Detachable `bootstrap()` feature. | ||||
|   | ||||
| @@ -31,8 +31,8 @@ import "../fixins/FixinCommon.sol"; | ||||
| import "../fixins/FixinTokenSpender.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "../transformers/LibERC20Transformer.sol"; | ||||
| import "./IFeature.sol"; | ||||
| import "./ILiquidityProviderFeature.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/ILiquidityProviderFeature.sol"; | ||||
|  | ||||
|  | ||||
| contract LiquidityProviderFeature is | ||||
| @@ -47,15 +47,14 @@ contract LiquidityProviderFeature is | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "LiquidityProviderFeature"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 3); | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 4); | ||||
|  | ||||
|     /// @dev The sandbox contract address. | ||||
|     ILiquidityProviderSandbox public immutable sandbox; | ||||
|  | ||||
|     constructor(LiquidityProviderSandbox sandbox_, bytes32 greedyTokensBloomFilter) | ||||
|     constructor(LiquidityProviderSandbox sandbox_) | ||||
|         public | ||||
|         FixinCommon() | ||||
|         FixinTokenSpender(greedyTokensBloomFilter) | ||||
|     { | ||||
|         sandbox = sandbox_; | ||||
|     } | ||||
|   | ||||
| @@ -30,11 +30,11 @@ import "../fixins/FixinTokenSpender.sol"; | ||||
| import "../fixins/FixinEIP712.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "../storage/LibMetaTransactionsStorage.sol"; | ||||
| import "./IMetaTransactionsFeature.sol"; | ||||
| import "./ITransformERC20Feature.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/IMetaTransactionsFeature.sol"; | ||||
| import "./interfaces/INativeOrdersFeature.sol"; | ||||
| import "./interfaces/ITransformERC20Feature.sol"; | ||||
| import "./libs/LibSignature.sol"; | ||||
| import "./IFeature.sol"; | ||||
| import "./INativeOrdersFeature.sol"; | ||||
|  | ||||
| /// @dev MetaTransactions feature. | ||||
| contract MetaTransactionsFeature is | ||||
| @@ -78,7 +78,7 @@ contract MetaTransactionsFeature is | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "MetaTransactions"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0); | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1); | ||||
|     /// @dev EIP712 typehash of the `MetaTransactionData` struct. | ||||
|     bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256( | ||||
|         "MetaTransactionData(" | ||||
| @@ -105,11 +105,10 @@ contract MetaTransactionsFeature is | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     constructor(address zeroExAddress, bytes32 greedyTokensBloomFilter) | ||||
|     constructor(address zeroExAddress) | ||||
|         public | ||||
|         FixinCommon() | ||||
|         FixinEIP712(zeroExAddress) | ||||
|         FixinTokenSpender(greedyTokensBloomFilter) | ||||
|     { | ||||
|         // solhint-disable-next-line no-empty-blocks | ||||
|     } | ||||
|   | ||||
							
								
								
									
										803
									
								
								contracts/zero-ex/contracts/src/features/MultiplexFeature.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										803
									
								
								contracts/zero-ex/contracts/src/features/MultiplexFeature.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,803 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; | ||||
| import "../external/ILiquidityProviderSandbox.sol"; | ||||
| import "../fixins/FixinCommon.sol"; | ||||
| import "../fixins/FixinEIP712.sol"; | ||||
| import "../fixins/FixinTokenSpender.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "../transformers/LibERC20Transformer.sol"; | ||||
| import "../vendor/ILiquidityProvider.sol"; | ||||
| import "../vendor/IUniswapV2Pair.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/IMultiplexFeature.sol"; | ||||
| import "./interfaces/INativeOrdersFeature.sol"; | ||||
| import "./interfaces/ITransformERC20Feature.sol"; | ||||
| import "./libs/LibNativeOrder.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev This feature enables efficient batch and multi-hop trades | ||||
| ///      using different liquidity sources. | ||||
| contract MultiplexFeature is | ||||
|     IFeature, | ||||
|     IMultiplexFeature, | ||||
|     FixinCommon, | ||||
|     FixinEIP712, | ||||
|     FixinTokenSpender | ||||
| { | ||||
|     using LibERC20Transformer for IERC20TokenV06; | ||||
|     using LibSafeMathV06 for uint128; | ||||
|     using LibSafeMathV06 for uint256; | ||||
|  | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "MultiplexFeature"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1); | ||||
|  | ||||
|     /// @dev The WETH token contract. | ||||
|     IEtherTokenV06 private immutable weth; | ||||
|     /// @dev The sandbox contract address. | ||||
|     ILiquidityProviderSandbox public immutable sandbox; | ||||
|     // address of the UniswapV2Factory contract. | ||||
|     address private constant UNISWAP_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; | ||||
|     // address of the (Sushiswap) UniswapV2Factory contract. | ||||
|     address private constant SUSHISWAP_FACTORY = 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac; | ||||
|     // Init code hash of the UniswapV2Pair contract. | ||||
|     uint256 private constant UNISWAP_PAIR_INIT_CODE_HASH = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f; | ||||
|     // Init code hash of the (Sushiswap) UniswapV2Pair contract. | ||||
|     uint256 private constant SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303; | ||||
|  | ||||
|     constructor( | ||||
|         address zeroExAddress, | ||||
|         IEtherTokenV06 weth_, | ||||
|         ILiquidityProviderSandbox sandbox_ | ||||
|     ) | ||||
|         public | ||||
|         FixinEIP712(zeroExAddress) | ||||
|     { | ||||
|         weth = weth_; | ||||
|         sandbox = sandbox_; | ||||
|     } | ||||
|  | ||||
|     /// @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.batchFill.selector); | ||||
|         _registerFeatureFunction(this.multiHopFill.selector); | ||||
|         return LibMigrate.MIGRATE_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     /// @dev Executes a batch of fills selling `fillData.inputToken` | ||||
|     ///      for `fillData.outputToken` in sequence. Refer to the | ||||
|     ///      internal variant `_batchFill` for the allowed nested | ||||
|     ///      operations. | ||||
|     /// @param fillData Encodes the input/output tokens, the sell | ||||
|     ///        amount, and the nested operations for this batch fill. | ||||
|     /// @param minBuyAmount The minimum amount of `fillData.outputToken` | ||||
|     ///        to buy. Reverts if this amount is not met. | ||||
|     /// @return outputTokenAmount The amount of the output token bought. | ||||
|     function batchFill( | ||||
|         BatchFillData memory fillData, | ||||
|         uint256 minBuyAmount | ||||
|     ) | ||||
|         public | ||||
|         payable | ||||
|         override | ||||
|         returns (uint256 outputTokenAmount) | ||||
|     { | ||||
|         // Cache the sender's balance of the output token. | ||||
|         outputTokenAmount = fillData.outputToken.getTokenBalanceOf(msg.sender); | ||||
|         // Cache the contract's ETH balance prior to this call. | ||||
|         uint256 ethBalanceBefore = address(this).balance.safeSub(msg.value); | ||||
|  | ||||
|         // Perform the batch fill. | ||||
|         _batchFill(fillData); | ||||
|  | ||||
|         // The `outputTokenAmount` returned by `_batchFill` may not | ||||
|         // be fully accurate (e.g. due to some janky token). | ||||
|         outputTokenAmount = fillData.outputToken.getTokenBalanceOf(msg.sender) | ||||
|             .safeSub(outputTokenAmount); | ||||
|         require( | ||||
|             outputTokenAmount >= minBuyAmount, | ||||
|             "MultiplexFeature::batchFill/UNDERBOUGHT" | ||||
|         ); | ||||
|  | ||||
|         uint256 ethBalanceAfter = address(this).balance; | ||||
|         require( | ||||
|             ethBalanceAfter >= ethBalanceBefore, | ||||
|             "MultiplexFeature::batchFill/OVERSPENT_ETH" | ||||
|         ); | ||||
|         // Refund ETH | ||||
|         if (ethBalanceAfter > ethBalanceBefore) { | ||||
|             _transferEth(msg.sender, ethBalanceAfter - ethBalanceBefore); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @dev Executes a sequence of fills "hopping" through the | ||||
|     ///      path of tokens given by `fillData.tokens`. Refer to the | ||||
|     ///      internal variant `_multiHopFill` for the allowed nested | ||||
|     ///      operations. | ||||
|     /// @param fillData Encodes the path of tokens, the sell amount, | ||||
|     ///        and the nested operations for this multi-hop fill. | ||||
|     /// @param minBuyAmount The minimum amount of the output token | ||||
|     ///        to buy. Reverts if this amount is not met. | ||||
|     /// @return outputTokenAmount The amount of the output token bought. | ||||
|     function multiHopFill( | ||||
|         MultiHopFillData memory fillData, | ||||
|         uint256 minBuyAmount | ||||
|     ) | ||||
|         public | ||||
|         payable | ||||
|         override | ||||
|         returns (uint256 outputTokenAmount) | ||||
|     { | ||||
|         IERC20TokenV06 outputToken = IERC20TokenV06(fillData.tokens[fillData.tokens.length - 1]); | ||||
|         // Cache the sender's balance of the output token. | ||||
|         outputTokenAmount = outputToken.getTokenBalanceOf(msg.sender); | ||||
|         // Cache the contract's ETH balance prior to this call. | ||||
|         uint256 ethBalanceBefore = address(this).balance.safeSub(msg.value); | ||||
|  | ||||
|         // Perform the multi-hop fill. Pass in `msg.value` as the maximum | ||||
|         // allowable amount of ETH for the wrapped calls to consume. | ||||
|         _multiHopFill(fillData, msg.value); | ||||
|  | ||||
|         // The `outputTokenAmount` returned by `_multiHopFill` may not | ||||
|         // be fully accurate (e.g. due to some janky token). | ||||
|         outputTokenAmount = outputToken.getTokenBalanceOf(msg.sender) | ||||
|             .safeSub(outputTokenAmount); | ||||
|         require( | ||||
|             outputTokenAmount >= minBuyAmount, | ||||
|             "MultiplexFeature::multiHopFill/UNDERBOUGHT" | ||||
|         ); | ||||
|  | ||||
|         uint256 ethBalanceAfter = address(this).balance; | ||||
|         require( | ||||
|             ethBalanceAfter >= ethBalanceBefore, | ||||
|             "MultiplexFeature::multiHopFill/OVERSPENT_ETH" | ||||
|         ); | ||||
|         // Refund ETH | ||||
|         if (ethBalanceAfter > ethBalanceBefore) { | ||||
|             _transferEth(msg.sender, ethBalanceAfter - ethBalanceBefore); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Similar to FQT. If `fillData.sellAmount` is set to `type(uint256).max`, | ||||
|     // this is effectively a batch fill. Otherwise it can be set to perform a | ||||
|     // market sell of some amount. Note that the `outputTokenAmount` returned | ||||
|     // by this function could theoretically be inaccurate if `msg.sender` has | ||||
|     // set a token allowance on an external contract that gets called during | ||||
|     // the execution of this function. | ||||
|     function _batchFill(BatchFillData memory fillData) | ||||
|         internal | ||||
|         returns (uint256 outputTokenAmount, uint256 remainingEth) | ||||
|     { | ||||
|         // Track the remaining ETH allocated to this call. | ||||
|         remainingEth = msg.value; | ||||
|         // Track the amount of input token sold. | ||||
|         uint256 soldAmount; | ||||
|         for (uint256 i = 0; i != fillData.calls.length; i++) { | ||||
|             // Check if we've hit our target. | ||||
|             if (soldAmount >= fillData.sellAmount) { break; } | ||||
|             WrappedBatchCall memory wrappedCall = fillData.calls[i]; | ||||
|             // Compute the fill amount. | ||||
|             uint256 inputTokenAmount = LibSafeMathV06.min256( | ||||
|                 wrappedCall.sellAmount, | ||||
|                 fillData.sellAmount.safeSub(soldAmount) | ||||
|             ); | ||||
|             if (wrappedCall.selector == INativeOrdersFeature._fillRfqOrder.selector) { | ||||
|                 // Decode the RFQ order and signature. | ||||
|                 ( | ||||
|                     LibNativeOrder.RfqOrder memory order, | ||||
|                     LibSignature.Signature memory signature | ||||
|                 ) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (LibNativeOrder.RfqOrder, LibSignature.Signature) | ||||
|                 ); | ||||
|                 if (order.expiry <= uint64(block.timestamp)) { | ||||
|                     bytes32 orderHash = _getEIP712Hash( | ||||
|                         LibNativeOrder.getRfqOrderStructHash(order) | ||||
|                     ); | ||||
|                     emit ExpiredRfqOrder( | ||||
|                         orderHash, | ||||
|                         order.maker, | ||||
|                         order.expiry | ||||
|                     ); | ||||
|                     continue; | ||||
|                 } | ||||
|                 require( | ||||
|                     order.takerToken == fillData.inputToken && | ||||
|                     order.makerToken == fillData.outputToken, | ||||
|                     "MultiplexFeature::_batchFill/RFQ_ORDER_INVALID_TOKENS" | ||||
|                 ); | ||||
|                 // Try filling the RFQ order. Swallows reverts. | ||||
|                 try | ||||
|                     INativeOrdersFeature(address(this))._fillRfqOrder | ||||
|                         ( | ||||
|                             order, | ||||
|                             signature, | ||||
|                             inputTokenAmount.safeDowncastToUint128(), | ||||
|                             msg.sender | ||||
|                         ) | ||||
|                     returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) | ||||
|                 { | ||||
|                     // Increment the sold and bought amounts. | ||||
|                     soldAmount = soldAmount.safeAdd(takerTokenFilledAmount); | ||||
|                     outputTokenAmount = outputTokenAmount.safeAdd(makerTokenFilledAmount); | ||||
|                 } catch {} | ||||
|             } else if (wrappedCall.selector == this._sellToUniswap.selector) { | ||||
|                 (address[] memory tokens, bool isSushi) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (address[], bool) | ||||
|                 ); | ||||
|                 require( | ||||
|                     tokens.length >= 2 && | ||||
|                     tokens[0] == address(fillData.inputToken) && | ||||
|                     tokens[tokens.length - 1] == address(fillData.outputToken), | ||||
|                     "MultiplexFeature::_batchFill/UNISWAP_INVALID_TOKENS" | ||||
|                 ); | ||||
|                 // Perform the Uniswap/Sushiswap trade. | ||||
|                 uint256 outputTokenAmount_  = _sellToUniswap( | ||||
|                     tokens, | ||||
|                     inputTokenAmount, | ||||
|                     isSushi, | ||||
|                     address(0), | ||||
|                     msg.sender | ||||
|                 ); | ||||
|                 // Increment the sold and bought amounts. | ||||
|                 soldAmount = soldAmount.safeAdd(inputTokenAmount); | ||||
|                 outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_); | ||||
|             } else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) { | ||||
|                 (address provider, bytes memory auxiliaryData) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (address, bytes) | ||||
|                 ); | ||||
|                 if (fillData.inputToken.isTokenETH()) { | ||||
|                     inputTokenAmount = LibSafeMathV06.min256( | ||||
|                         inputTokenAmount, | ||||
|                         remainingEth | ||||
|                     ); | ||||
|                     // Transfer the input ETH to the provider. | ||||
|                     _transferEth(payable(provider), inputTokenAmount); | ||||
|                     // Count that ETH as spent. | ||||
|                     remainingEth -= inputTokenAmount; | ||||
|                 } else { | ||||
|                     // Transfer input ERC20 tokens to the provider. | ||||
|                     _transferERC20Tokens( | ||||
|                         fillData.inputToken, | ||||
|                         msg.sender, | ||||
|                         provider, | ||||
|                         inputTokenAmount | ||||
|                     ); | ||||
|                 } | ||||
|                 // Perform the PLP trade. | ||||
|                 uint256 outputTokenAmount_ = _sellToLiquidityProvider( | ||||
|                     fillData.inputToken, | ||||
|                     fillData.outputToken, | ||||
|                     inputTokenAmount, | ||||
|                     ILiquidityProvider(provider), | ||||
|                     msg.sender, | ||||
|                     auxiliaryData | ||||
|                 ); | ||||
|                 // Increment the sold and bought amounts. | ||||
|                 soldAmount = soldAmount.safeAdd(inputTokenAmount); | ||||
|                 outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_); | ||||
|             } else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) { | ||||
|                 ITransformERC20Feature.TransformERC20Args memory args; | ||||
|                 args.taker = msg.sender; | ||||
|                 args.inputToken = fillData.inputToken; | ||||
|                 args.outputToken = fillData.outputToken; | ||||
|                 args.inputTokenAmount = inputTokenAmount; | ||||
|                 args.minOutputTokenAmount = 0; | ||||
|                 uint256 ethValue; | ||||
|                 (args.transformations, ethValue) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (ITransformERC20Feature.Transformation[], uint256) | ||||
|                 ); | ||||
|                 // Do not spend more than the remaining ETH. | ||||
|                 ethValue = LibSafeMathV06.min256( | ||||
|                     ethValue, | ||||
|                     remainingEth | ||||
|                 ); | ||||
|                 if (ethValue > 0) { | ||||
|                     require( | ||||
|                         args.inputToken.isTokenETH(), | ||||
|                         "MultiplexFeature::_batchFill/ETH_TRANSFORM_ONLY" | ||||
|                     ); | ||||
|                 } | ||||
|                 try ITransformERC20Feature(address(this))._transformERC20 | ||||
|                     {value: ethValue} | ||||
|                     (args) | ||||
|                     returns (uint256 outputTokenAmount_) | ||||
|                 { | ||||
|                     remainingEth -= ethValue; | ||||
|                     soldAmount = soldAmount.safeAdd(inputTokenAmount); | ||||
|                     outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_); | ||||
|                 } catch {} | ||||
|             } else if (wrappedCall.selector == this._multiHopFill.selector) { | ||||
|                 MultiHopFillData memory multiHopFillData; | ||||
|                 uint256 ethValue; | ||||
|                 ( | ||||
|                     multiHopFillData.tokens, | ||||
|                     multiHopFillData.calls, | ||||
|                     ethValue | ||||
|                 ) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (address[], WrappedMultiHopCall[], uint256) | ||||
|                 ); | ||||
|                 multiHopFillData.sellAmount = inputTokenAmount; | ||||
|                 // Do not spend more than the remaining ETH. | ||||
|                 ethValue = LibSafeMathV06.min256( | ||||
|                     ethValue, | ||||
|                     remainingEth | ||||
|                 ); | ||||
|                 // Subtract the ethValue allocated to the nested multi-hop fill. | ||||
|                 remainingEth -= ethValue; | ||||
|                 (uint256 outputTokenAmount_, uint256 leftoverEth) = | ||||
|                     _multiHopFill(multiHopFillData, ethValue); | ||||
|                 // Increment the sold and bought amounts. | ||||
|                 soldAmount = soldAmount.safeAdd(inputTokenAmount); | ||||
|                 outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_); | ||||
|                 // Add back any ETH that wasn't used by the nested multi-hop fill. | ||||
|                 remainingEth += leftoverEth; | ||||
|             } else { | ||||
|                 revert("MultiplexFeature::_batchFill/UNRECOGNIZED_SELECTOR"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Internal variant of `multiHopFill`. This function can be nested within | ||||
|     // a `_batchFill`. | ||||
|     // This function executes a sequence of fills "hopping" through the | ||||
|     // path of tokens given by `fillData.tokens`. The nested operations that | ||||
|     // can be used as "hops" are: | ||||
|     // - WETH.deposit (wraps ETH) | ||||
|     // - WETH.withdraw (unwraps WETH) | ||||
|     // - _sellToUniswap (executes a Uniswap/Sushiswap swap) | ||||
|     // - _sellToLiquidityProvider (executes a PLP swap) | ||||
|     // - _transformERC20 (executes arbitrary ERC20 Transformations) | ||||
|     // This function optimizes the number of ERC20 transfers performed | ||||
|     // by having each hop transfer its output tokens directly to the | ||||
|     // target address of the next hop. Note that the `outputTokenAmount` returned | ||||
|     // by this function could theoretically be inaccurate if `msg.sender` has | ||||
|     // set a token allowance on an external contract that gets called during | ||||
|     // the execution of this function. | ||||
|     function _multiHopFill(MultiHopFillData memory fillData, uint256 totalEth) | ||||
|         public | ||||
|         returns (uint256 outputTokenAmount, uint256 remainingEth) | ||||
|     { | ||||
|         // There should be one call/hop between every two tokens | ||||
|         // in the path. | ||||
|         // tokens[0]––calls[0]––>tokens[1]––...––calls[n-1]––>tokens[n] | ||||
|         require( | ||||
|             fillData.tokens.length == fillData.calls.length + 1, | ||||
|             "MultiplexFeature::_multiHopFill/MISMATCHED_ARRAY_LENGTHS" | ||||
|         ); | ||||
|         // Track the remaining ETH allocated to this call. | ||||
|         remainingEth = totalEth; | ||||
|         // This variable is used as the input and output amounts of | ||||
|         // each hop. After the final hop, this will contain the output | ||||
|         // amount of the multi-hop fill. | ||||
|         outputTokenAmount = fillData.sellAmount; | ||||
|         // This variable is used to cache the address to target in the | ||||
|         // next hop. See `_computeHopRecipient` for details. | ||||
|         address nextTarget; | ||||
|         for (uint256 i = 0; i != fillData.calls.length; i++) { | ||||
|             WrappedMultiHopCall memory wrappedCall = fillData.calls[i]; | ||||
|             if (wrappedCall.selector == this._sellToUniswap.selector) { | ||||
|                 // If the next hop supports a "transfer then execute" pattern, | ||||
|                 // the recipient will not be `msg.sender`. See `_computeHopRecipient` | ||||
|                 // for details. | ||||
|                 address recipient = _computeHopRecipient(fillData.calls, i); | ||||
|                 (address[] memory tokens, bool isSushi) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (address[], bool) | ||||
|                 ); | ||||
|                 // Perform the Uniswap/Sushiswap trade. | ||||
|                 outputTokenAmount = _sellToUniswap( | ||||
|                     tokens, | ||||
|                     outputTokenAmount, | ||||
|                     isSushi, | ||||
|                     nextTarget, | ||||
|                     recipient | ||||
|                 ); | ||||
|                 // If the recipient was not `msg.sender`, it must be the target | ||||
|                 // contract for the next hop. | ||||
|                 nextTarget = recipient == msg.sender ? address(0) : recipient; | ||||
|             } else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) { | ||||
|                 // If the next hop supports a "transfer then execute" pattern, | ||||
|                 // the recipient will not be `msg.sender`. See `_computeHopRecipient` | ||||
|                 // for details. | ||||
|                 address recipient = _computeHopRecipient(fillData.calls, i); | ||||
|                 // If `nextTarget` was not set in the previous hop, then we | ||||
|                 // need to send in the input ETH/tokens to the liquidity provider | ||||
|                 // contract before executing the trade. | ||||
|                 if (nextTarget == address(0)) { | ||||
|                     (address provider, bytes memory auxiliaryData) = abi.decode( | ||||
|                         wrappedCall.data, | ||||
|                         (address, bytes) | ||||
|                     ); | ||||
|                     // Transfer input ETH or ERC20 tokens to the liquidity | ||||
|                     // provider contract. | ||||
|                     if (IERC20TokenV06(fillData.tokens[i]).isTokenETH()) { | ||||
|                         outputTokenAmount = LibSafeMathV06.min256( | ||||
|                             outputTokenAmount, | ||||
|                             remainingEth | ||||
|                         ); | ||||
|                         _transferEth(payable(provider), outputTokenAmount); | ||||
|                         remainingEth -= outputTokenAmount; | ||||
|                     } else { | ||||
|                         _transferERC20Tokens( | ||||
|                             IERC20TokenV06(fillData.tokens[i]), | ||||
|                             msg.sender, | ||||
|                             provider, | ||||
|                             outputTokenAmount | ||||
|                         ); | ||||
|                     } | ||||
|                     outputTokenAmount = _sellToLiquidityProvider( | ||||
|                         IERC20TokenV06(fillData.tokens[i]), | ||||
|                         IERC20TokenV06(fillData.tokens[i + 1]), | ||||
|                         outputTokenAmount, | ||||
|                         ILiquidityProvider(provider), | ||||
|                         recipient, | ||||
|                         auxiliaryData | ||||
|                     ); | ||||
|                 } else { | ||||
|                     (, bytes memory auxiliaryData) = abi.decode( | ||||
|                         wrappedCall.data, | ||||
|                         (address, bytes) | ||||
|                     ); | ||||
|                     // Tokens and ETH have already been transferred to | ||||
|                     // the liquidity provider contract in the previous hop. | ||||
|                     outputTokenAmount = _sellToLiquidityProvider( | ||||
|                         IERC20TokenV06(fillData.tokens[i]), | ||||
|                         IERC20TokenV06(fillData.tokens[i + 1]), | ||||
|                         outputTokenAmount, | ||||
|                         ILiquidityProvider(nextTarget), | ||||
|                         recipient, | ||||
|                         auxiliaryData | ||||
|                     ); | ||||
|                 } | ||||
|                 // If the recipient was not `msg.sender`, it must be the target | ||||
|                 // contract for the next hop. | ||||
|                 nextTarget = recipient == msg.sender ? address(0) : recipient; | ||||
|             } else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) { | ||||
|                 ITransformERC20Feature.TransformERC20Args memory args; | ||||
|                 args.inputToken = IERC20TokenV06(fillData.tokens[i]); | ||||
|                 args.outputToken = IERC20TokenV06(fillData.tokens[i + 1]); | ||||
|                 args.minOutputTokenAmount = 0; | ||||
|                 args.taker = payable(_computeHopRecipient(fillData.calls, i)); | ||||
|                 if (nextTarget != address(0)) { | ||||
|                     // If `nextTarget` was set in the previous hop, then the input | ||||
|                     // token was already sent to the FlashWallet. Setting | ||||
|                     // `inputTokenAmount` to 0 indicates that no tokens need to | ||||
|                     // be pulled into the FlashWallet before executing the | ||||
|                     // transformations. | ||||
|                     args.inputTokenAmount = 0; | ||||
|                 } else if ( | ||||
|                     args.taker != msg.sender && | ||||
|                     !args.inputToken.isTokenETH() | ||||
|                 ) { | ||||
|                     address flashWallet = address( | ||||
|                         ITransformERC20Feature(address(this)).getTransformWallet() | ||||
|                     ); | ||||
|                     // The input token has _not_ already been sent to the | ||||
|                     // FlashWallet. We also want PayTakerTransformer to | ||||
|                     // send the output token to some address other than | ||||
|                     // msg.sender, so we must transfer the input token | ||||
|                     // to the FlashWallet here. | ||||
|                     _transferERC20Tokens( | ||||
|                         args.inputToken, | ||||
|                         msg.sender, | ||||
|                         flashWallet, | ||||
|                         outputTokenAmount | ||||
|                     ); | ||||
|                     args.inputTokenAmount = 0; | ||||
|                 } else { | ||||
|                     // Otherwise, either: | ||||
|                     // (1) args.taker == msg.sender, in which case | ||||
|                     //     `_transformERC20` will pull the input token | ||||
|                     //     into the FlashWallet, or | ||||
|                     // (2) args.inputToken == ETH_TOKEN_ADDRESS, in which | ||||
|                     //     case ETH is attached to the call and no token | ||||
|                     //     transfer occurs. | ||||
|                     args.inputTokenAmount = outputTokenAmount; | ||||
|                 } | ||||
|                 uint256 ethValue; | ||||
|                 (args.transformations, ethValue) = abi.decode( | ||||
|                     wrappedCall.data, | ||||
|                     (ITransformERC20Feature.Transformation[], uint256) | ||||
|                 ); | ||||
|                 // Do not spend more than the remaining ETH. | ||||
|                 ethValue = LibSafeMathV06.min256(ethValue, remainingEth); | ||||
|                 if (ethValue > 0) { | ||||
|                     require( | ||||
|                         args.inputToken.isTokenETH(), | ||||
|                         "MultiplexFeature::_multiHopFill/ETH_TRANSFORM_ONLY" | ||||
|                     ); | ||||
|                 } | ||||
|                 // Call `_transformERC20`. | ||||
|                 outputTokenAmount = ITransformERC20Feature(address(this)) | ||||
|                     ._transformERC20{value: ethValue}(args); | ||||
|                 // Decrement the remaining ETH. | ||||
|                 remainingEth -= ethValue; | ||||
|                 // If the recipient was not `msg.sender`, it must be the target | ||||
|                 // contract for the next hop. | ||||
|                 nextTarget = args.taker == msg.sender ? address(0) : args.taker; | ||||
|             } else if (wrappedCall.selector == IEtherTokenV06.deposit.selector) { | ||||
|                 require( | ||||
|                     i == 0, | ||||
|                     "MultiplexFeature::_multiHopFill/DEPOSIT_FIRST_HOP_ONLY" | ||||
|                 ); | ||||
|                 uint256 ethValue = LibSafeMathV06.min256(outputTokenAmount, remainingEth); | ||||
|                 // Wrap ETH. | ||||
|                 weth.deposit{value: ethValue}(); | ||||
|                 nextTarget = _computeHopRecipient(fillData.calls, i); | ||||
|                 weth.transfer(nextTarget, ethValue); | ||||
|                 remainingEth -= ethValue; | ||||
|             } else if (wrappedCall.selector == IEtherTokenV06.withdraw.selector) { | ||||
|                 require( | ||||
|                     i == fillData.calls.length - 1, | ||||
|                     "MultiplexFeature::_multiHopFill/WITHDRAW_LAST_HOP_ONLY" | ||||
|                 ); | ||||
|                 // Unwrap WETH and send to `msg.sender`. | ||||
|                 weth.withdraw(outputTokenAmount); | ||||
|                 _transferEth(msg.sender, outputTokenAmount); | ||||
|                 nextTarget = address(0); | ||||
|             } else { | ||||
|                 revert("MultiplexFeature::_multiHopFill/UNRECOGNIZED_SELECTOR"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Similar to the UniswapFeature, but with a couple of differences: | ||||
|     // - Does not perform the transfer in if `pairAddress` is given, | ||||
|     //   which indicates that the transfer in was already performed | ||||
|     //   in the previous hop of a multi-hop fill. | ||||
|     // - Does not include a minBuyAmount check (which is performed in | ||||
|     //   either `batchFill` or `multiHopFill`). | ||||
|     // - Takes a `recipient` address parameter, so the output of the | ||||
|     //   final `swap` call can be sent to an address other than `msg.sender`. | ||||
|     function _sellToUniswap( | ||||
|         address[] memory tokens, | ||||
|         uint256 sellAmount, | ||||
|         bool isSushi, | ||||
|         address pairAddress, | ||||
|         address recipient | ||||
|     ) | ||||
|         public | ||||
|         returns (uint256 outputTokenAmount) | ||||
|     { | ||||
|         require(tokens.length > 1, "MultiplexFeature::_sellToUniswap/InvalidTokensLength"); | ||||
|  | ||||
|         if (pairAddress == address(0)) { | ||||
|             pairAddress = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi); | ||||
|             _transferERC20Tokens( | ||||
|                 IERC20TokenV06(tokens[0]), | ||||
|                 msg.sender, | ||||
|                 pairAddress, | ||||
|                 sellAmount | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         for (uint256 i = 0; i < tokens.length - 1; i++) { | ||||
|             (address inputToken, address outputToken) = (tokens[i], tokens[i + 1]); | ||||
|             outputTokenAmount = _computeUniswapOutputAmount( | ||||
|                 pairAddress, | ||||
|                 inputToken, | ||||
|                 outputToken, | ||||
|                 sellAmount | ||||
|             ); | ||||
|             (uint256 amount0Out, uint256 amount1Out) = inputToken < outputToken | ||||
|                 ? (uint256(0), outputTokenAmount) | ||||
|                 : (outputTokenAmount, uint256(0)); | ||||
|             address to = i < tokens.length - 2 | ||||
|                 ? _computeUniswapPairAddress(outputToken, tokens[i + 2], isSushi) | ||||
|                 : recipient; | ||||
|             IUniswapV2Pair(pairAddress).swap( | ||||
|                 amount0Out, | ||||
|                 amount1Out, | ||||
|                 to, | ||||
|                 new bytes(0) | ||||
|             ); | ||||
|             pairAddress = to; | ||||
|             sellAmount = outputTokenAmount; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Same as the LiquidityProviderFeature, but without the transfer in | ||||
|     // (which is potentially done in the previous hop of a multi-hop fill) | ||||
|     // and without the minBuyAmount check (which is performed at the top, i.e. | ||||
|     // in either `batchFill` or `multiHopFill`). | ||||
|     function _sellToLiquidityProvider( | ||||
|         IERC20TokenV06 inputToken, | ||||
|         IERC20TokenV06 outputToken, | ||||
|         uint256 inputTokenAmount, | ||||
|         ILiquidityProvider provider, | ||||
|         address recipient, | ||||
|         bytes memory auxiliaryData | ||||
|     ) | ||||
|         public | ||||
|         returns (uint256 outputTokenAmount) | ||||
|     { | ||||
|         uint256 balanceBefore = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient); | ||||
|         if (IERC20TokenV06(inputToken).isTokenETH()) { | ||||
|             sandbox.executeSellEthForToken( | ||||
|                 provider, | ||||
|                 outputToken, | ||||
|                 recipient, | ||||
|                 0, | ||||
|                 auxiliaryData | ||||
|             ); | ||||
|         } else if (IERC20TokenV06(outputToken).isTokenETH()) { | ||||
|             sandbox.executeSellTokenForEth( | ||||
|                 provider, | ||||
|                 inputToken, | ||||
|                 recipient, | ||||
|                 0, | ||||
|                 auxiliaryData | ||||
|             ); | ||||
|         } else { | ||||
|             sandbox.executeSellTokenForToken( | ||||
|                 provider, | ||||
|                 inputToken, | ||||
|                 outputToken, | ||||
|                 recipient, | ||||
|                 0, | ||||
|                 auxiliaryData | ||||
|             ); | ||||
|         } | ||||
|         outputTokenAmount = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient) | ||||
|             .safeSub(balanceBefore); | ||||
|         emit LiquidityProviderSwap( | ||||
|             address(inputToken), | ||||
|             address(outputToken), | ||||
|             inputTokenAmount, | ||||
|             outputTokenAmount, | ||||
|             address(provider), | ||||
|             recipient | ||||
|         ); | ||||
|         return outputTokenAmount; | ||||
|     } | ||||
|  | ||||
|     function _transferEth(address payable recipient, uint256 amount) | ||||
|         private | ||||
|     { | ||||
|         (bool success,) = recipient.call{value: amount}(""); | ||||
|         require(success, "MultiplexFeature::_transferEth/TRANSFER_FALIED"); | ||||
|     } | ||||
|  | ||||
|     // Some liquidity sources (e.g. Uniswap, Sushiswap, and PLP) can be passed | ||||
|     // a `recipient` parameter so the boguht tokens are transferred to the | ||||
|     // `recipient` address rather than `msg.sender`. | ||||
|     // Some liquidity sources (also Uniswap, Sushiswap, and PLP incidentally) | ||||
|     // support a "transfer then execute" pattern, where the token being sold | ||||
|     // can be transferred into the contract before calling a swap function to | ||||
|     // execute the trade. | ||||
|     // If the current hop in a multi-hop fill satisfies the first condition, | ||||
|     // and the next hop satisfies the second condition, the tokens bought | ||||
|     // in the current hop can be directly sent to the target contract of | ||||
|     // the next hop to save a transfer. | ||||
|     function _computeHopRecipient( | ||||
|         WrappedMultiHopCall[] memory calls, | ||||
|         uint256 i | ||||
|     ) | ||||
|         private | ||||
|         view | ||||
|         returns (address recipient) | ||||
|     { | ||||
|         recipient = msg.sender; | ||||
|         if (i < calls.length - 1) { | ||||
|             WrappedMultiHopCall memory nextCall = calls[i + 1]; | ||||
|             if (nextCall.selector == this._sellToUniswap.selector) { | ||||
|                 (address[] memory tokens, bool isSushi) = abi.decode( | ||||
|                     nextCall.data, | ||||
|                     (address[], bool) | ||||
|                 ); | ||||
|                 recipient = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi); | ||||
|             } else if (nextCall.selector == this._sellToLiquidityProvider.selector) { | ||||
|                 (recipient,) = abi.decode( | ||||
|                     nextCall.data, | ||||
|                     (address, bytes) | ||||
|                 ); | ||||
|             } else if (nextCall.selector == IEtherTokenV06.withdraw.selector) { | ||||
|                 recipient = address(this); | ||||
|             } else if (nextCall.selector == ITransformERC20Feature._transformERC20.selector) { | ||||
|                 recipient = address( | ||||
|                     ITransformERC20Feature(address(this)).getTransformWallet() | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         require( | ||||
|             recipient != address(0), | ||||
|             "MultiplexFeature::_computeHopRecipient/RECIPIENT_IS_NULL" | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     // Computes the the amount of output token that would be bought | ||||
|     // from Uniswap/Sushiswap given the input amount. | ||||
|     function _computeUniswapOutputAmount( | ||||
|         address pairAddress, | ||||
|         address inputToken, | ||||
|         address outputToken, | ||||
|         uint256 inputAmount | ||||
|     ) | ||||
|         private | ||||
|         view | ||||
|         returns (uint256 outputAmount) | ||||
|     { | ||||
|         require( | ||||
|             inputAmount > 0, | ||||
|             "MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_INPUT_AMOUNT" | ||||
|         ); | ||||
|         (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairAddress).getReserves(); | ||||
|         require( | ||||
|             reserve0 > 0 && reserve1 > 0, | ||||
|             'MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_LIQUIDITY' | ||||
|         ); | ||||
|         (uint256 inputReserve, uint256 outputReserve) = inputToken < outputToken | ||||
|             ? (reserve0, reserve1) | ||||
|             : (reserve1, reserve0); | ||||
|         uint256 inputAmountWithFee = inputAmount.safeMul(997); | ||||
|         uint256 numerator = inputAmountWithFee.safeMul(outputReserve); | ||||
|         uint256 denominator = inputReserve.safeMul(1000).safeAdd(inputAmountWithFee); | ||||
|         return numerator / denominator; | ||||
|     } | ||||
|  | ||||
|     // Computes the Uniswap/Sushiswap pair contract address for the | ||||
|     // given tokens. | ||||
|     function _computeUniswapPairAddress( | ||||
|         address tokenA, | ||||
|         address tokenB, | ||||
|         bool isSushi | ||||
|     ) | ||||
|         private | ||||
|         pure | ||||
|         returns (address pairAddress) | ||||
|     { | ||||
|         (address token0, address token1) = tokenA < tokenB | ||||
|             ? (tokenA, tokenB) | ||||
|             : (tokenB, tokenA); | ||||
|         if (isSushi) { | ||||
|             return address(uint256(keccak256(abi.encodePacked( | ||||
|                 hex'ff', | ||||
|                 SUSHISWAP_FACTORY, | ||||
|                 keccak256(abi.encodePacked(token0, token1)), | ||||
|                 SUSHISWAP_PAIR_INIT_CODE_HASH | ||||
|             )))); | ||||
|         } else { | ||||
|             return address(uint256(keccak256(abi.encodePacked( | ||||
|                 hex'ff', | ||||
|                 UNISWAP_FACTORY, | ||||
|                 keccak256(abi.encodePacked(token0, token1)), | ||||
|                 UNISWAP_PAIR_INIT_CODE_HASH | ||||
|             )))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -26,8 +26,8 @@ import "../errors/LibOwnableRichErrors.sol"; | ||||
| import "../storage/LibOwnableStorage.sol"; | ||||
| import "../migrations/LibBootstrap.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "./IFeature.sol"; | ||||
| import "./IOwnableFeature.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/IOwnableFeature.sol"; | ||||
| import "./SimpleFunctionRegistryFeature.sol"; | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										423
									
								
								contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										423
									
								
								contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,423 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "../fixins/FixinCommon.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/IPancakeSwapFeature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev VIP pancake fill functions. | ||||
| contract PancakeSwapFeature is | ||||
|     IFeature, | ||||
|     IPancakeSwapFeature, | ||||
|     FixinCommon | ||||
| { | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "PancakeSwapFeature"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1); | ||||
|     /// @dev WBNB contract. | ||||
|     IEtherTokenV06 private immutable WBNB; | ||||
|  | ||||
|     // 0xFF + address of the PancakeSwap factory contract. | ||||
|     uint256 constant private FF_PANCAKESWAP_FACTORY = 0xffbcfccbde45ce874adcb698cc183debcf179528120000000000000000000000; | ||||
|     // 0xFF + address of the BakerySwap factory contract. | ||||
|     uint256 constant private FF_BAKERYSWAP_FACTORY = 0xff01bf7c66c6bd861915cdaae475042d3c4bae16a70000000000000000000000; | ||||
|     // 0xFF + address of the SushiSwap factory contract. | ||||
|     uint256 constant private FF_SUSHISWAP_FACTORY = 0xffc35DADB65012eC5796536bD9864eD8773aBc74C40000000000000000000000; | ||||
|     // Init code hash of the PancakeSwap pair contract. | ||||
|     uint256 constant private PANCAKESWAP_PAIR_INIT_CODE_HASH = 0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66; | ||||
|     // Init code hash of the BakerySwap pair contract. | ||||
|     uint256 constant private BAKERYSWAP_PAIR_INIT_CODE_HASH = 0xe2e87433120e32c4738a7d8f3271f3d872cbe16241d67537139158d90bac61d3; | ||||
|     // Init code hash of the SushiSwap pair contract. | ||||
|     uint256 constant private SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303; | ||||
|     // Mask of the lower 20 bytes of a bytes32. | ||||
|     uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; | ||||
|     // BNB pseudo-token address. | ||||
|     uint256 constant private ETH_TOKEN_ADDRESS_32 = 0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee; | ||||
|     // Maximum token quantity that can be swapped against the PancakeSwapPair contract. | ||||
|     uint256 constant private MAX_SWAP_AMOUNT = 2**112; | ||||
|  | ||||
|     // bytes4(keccak256("executeCall(address,bytes)")) | ||||
|     uint256 constant private ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32 = 0xbca8c7b500000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("getReserves()")) | ||||
|     uint256 constant private PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32 = 0x0902f1ac00000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("swap(uint256,uint256,address,bytes)")) | ||||
|     uint256 constant private PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x022c0d9f00000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("swap(uint256,uint256,address)")) | ||||
|     uint256 constant private BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x6d9a640a00000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("transferFrom(address,address,uint256)")) | ||||
|     uint256 constant private TRANSFER_FROM_CALL_SELECTOR_32 = 0x23b872dd00000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("allowance(address,address)")) | ||||
|     uint256 constant private ALLOWANCE_CALL_SELECTOR_32 = 0xdd62ed3e00000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("withdraw(uint256)")) | ||||
|     uint256 constant private WETH_WITHDRAW_CALL_SELECTOR_32 = 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("deposit()")) | ||||
|     uint256 constant private WETH_DEPOSIT_CALL_SELECTOR_32 = 0xd0e30db000000000000000000000000000000000000000000000000000000000; | ||||
|     // bytes4(keccak256("transfer(address,uint256)")) | ||||
|     uint256 constant private ERC20_TRANSFER_CALL_SELECTOR_32 = 0xa9059cbb00000000000000000000000000000000000000000000000000000000; | ||||
|  | ||||
|     /// @dev Construct this contract. | ||||
|     /// @param wbnb The WBNB contract. | ||||
|     constructor(IEtherTokenV06 wbnb) public { | ||||
|         WBNB = wbnb; | ||||
|     } | ||||
|  | ||||
|     /// @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.sellToPancakeSwap.selector); | ||||
|         return LibMigrate.MIGRATE_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     /// @dev Efficiently sell directly to pancake/BakerySwap/SushiSwap. | ||||
|     /// @param tokens Sell path. | ||||
|     /// @param sellAmount of `tokens[0]` Amount to sell. | ||||
|     /// @param minBuyAmount Minimum amount of `tokens[-1]` to buy. | ||||
|     /// @param fork The protocol fork to use. | ||||
|     /// @return buyAmount Amount of `tokens[-1]` bought. | ||||
|     function sellToPancakeSwap( | ||||
|         IERC20TokenV06[] calldata tokens, | ||||
|         uint256 sellAmount, | ||||
|         uint256 minBuyAmount, | ||||
|         ProtocolFork fork | ||||
|     ) | ||||
|         external | ||||
|         payable | ||||
|         override | ||||
|         returns (uint256 buyAmount) | ||||
|     { | ||||
|         require(tokens.length > 1, "PancakeSwapFeature/InvalidTokensLength"); | ||||
|         { | ||||
|             // Load immutables onto the stack. | ||||
|             IEtherTokenV06 wbnb = WBNB; | ||||
|  | ||||
|             // Store some vars in memory to get around stack limits. | ||||
|             assembly { | ||||
|                 // calldataload(mload(0xA00)) == first element of `tokens` array | ||||
|                 mstore(0xA00, add(calldataload(0x04), 0x24)) | ||||
|                 // mload(0xA20) == fork | ||||
|                 mstore(0xA20, fork) | ||||
|                 // mload(0xA40) == WBNB | ||||
|                 mstore(0xA40, wbnb) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         assembly { | ||||
|             // numPairs == tokens.length - 1 | ||||
|             let numPairs := sub(calldataload(add(calldataload(0x04), 0x4)), 1) | ||||
|             // We use the previous buy amount as the sell amount for the next | ||||
|             // pair in a path. So for the first swap we want to set it to `sellAmount`. | ||||
|             buyAmount := sellAmount | ||||
|             let buyToken | ||||
|             let nextPair := 0 | ||||
|  | ||||
|             for {let i := 0} lt(i, numPairs) {i := add(i, 1)} { | ||||
|                 // sellToken = tokens[i] | ||||
|                 let sellToken := loadTokenAddress(i) | ||||
|                 // buyToken = tokens[i+1] | ||||
|                 buyToken := loadTokenAddress(add(i, 1)) | ||||
|                 // The canonical ordering of this token pair. | ||||
|                 let pairOrder := lt(normalizeToken(sellToken), normalizeToken(buyToken)) | ||||
|  | ||||
|                 // Compute the pair address if it hasn't already been computed | ||||
|                 // from the last iteration. | ||||
|                 let pair := nextPair | ||||
|                 if iszero(pair) { | ||||
|                     pair := computePairAddress(sellToken, buyToken) | ||||
|                     nextPair := 0 | ||||
|                 } | ||||
|  | ||||
|                 if iszero(i) { | ||||
|                     // This is the first token in the path. | ||||
|                     switch eq(sellToken, ETH_TOKEN_ADDRESS_32) | ||||
|                         case 0 { // Not selling BNB. Selling an ERC20 instead. | ||||
|                             // Make sure BNB was not attached to the call. | ||||
|                             if gt(callvalue(), 0) { | ||||
|                                 revert(0, 0) | ||||
|                             } | ||||
|                             // For the first pair we need to transfer sellTokens into the | ||||
|                             // pair contract. | ||||
|                             moveTakerTokensTo(sellToken, pair, sellAmount) | ||||
|                         } | ||||
|                         default { | ||||
|                             // If selling BNB, we need to wrap it to WBNB and transfer to the | ||||
|                             // pair contract. | ||||
|                             if iszero(eq(callvalue(), sellAmount)) { | ||||
|                                 revert(0, 0) | ||||
|                             } | ||||
|                             sellToken := mload(0xA40)// Re-assign to WBNB | ||||
|                             // Call `WBNB.deposit{value: sellAmount}()` | ||||
|                             mstore(0xB00, WETH_DEPOSIT_CALL_SELECTOR_32) | ||||
|                             if iszero(call(gas(), sellToken, sellAmount, 0xB00, 0x4, 0x00, 0x0)) { | ||||
|                                 bubbleRevert() | ||||
|                             } | ||||
|                             // Call `WBNB.transfer(pair, sellAmount)` | ||||
|                             mstore(0xB00, ERC20_TRANSFER_CALL_SELECTOR_32) | ||||
|                             mstore(0xB04, pair) | ||||
|                             mstore(0xB24, sellAmount) | ||||
|                             if iszero(call(gas(), sellToken, 0, 0xB00, 0x44, 0x00, 0x0)) { | ||||
|                                 bubbleRevert() | ||||
|                             } | ||||
|                         } | ||||
|                     // No need to check results, if deposit/transfers failed the PancakeSwapPair will | ||||
|                     // reject our trade (or it may succeed if somehow the reserve was out of sync) | ||||
|                     // this is fine for the taker. | ||||
|                 } | ||||
|  | ||||
|                 // Call pair.getReserves(), store the results at `0xC00` | ||||
|                 mstore(0xB00, PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32) | ||||
|                 if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) { | ||||
|                     bubbleRevert() | ||||
|                 } | ||||
|                 // Revert if the pair contract does not return at least two words. | ||||
|                 if lt(returndatasize(), 0x40) { | ||||
|                     mstore(0, pair) | ||||
|                     revert(0, 32) | ||||
|                 } | ||||
|  | ||||
|                 // Sell amount for this hop is the previous buy amount. | ||||
|                 let pairSellAmount := buyAmount | ||||
|                 // Compute the buy amount based on the pair reserves. | ||||
|                 { | ||||
|                     let sellReserve | ||||
|                     let buyReserve | ||||
|                     switch iszero(pairOrder) | ||||
|                         case 0 { | ||||
|                             // Transpose if pair order is different. | ||||
|                             sellReserve := mload(0xC00) | ||||
|                             buyReserve := mload(0xC20) | ||||
|                         } | ||||
|                         default { | ||||
|                             sellReserve := mload(0xC20) | ||||
|                             buyReserve := mload(0xC00) | ||||
|                         } | ||||
|                     // Ensure that the sellAmount is < 2¹¹². | ||||
|                     if gt(pairSellAmount, MAX_SWAP_AMOUNT) { | ||||
|                         revert(0, 0) | ||||
|                     } | ||||
|                     // Pairs are in the range (0, 2¹¹²) so this shouldn't overflow. | ||||
|                     // buyAmount = (pairSellAmount * 997 * buyReserve) / | ||||
|                     //     (pairSellAmount * 997 + sellReserve * 1000); | ||||
|                     let sellAmountWithFee := mul(pairSellAmount, 997) | ||||
|                     buyAmount := div( | ||||
|                         mul(sellAmountWithFee, buyReserve), | ||||
|                         add(sellAmountWithFee, mul(sellReserve, 1000)) | ||||
|                     ) | ||||
|                 } | ||||
|  | ||||
|                 let receiver | ||||
|                 // Is this the last pair contract? | ||||
|                 switch eq(add(i, 1), numPairs) | ||||
|                     case 0 { | ||||
|                         // Not the last pair contract, so forward bought tokens to | ||||
|                         // the next pair contract. | ||||
|                         nextPair := computePairAddress( | ||||
|                             buyToken, | ||||
|                             loadTokenAddress(add(i, 2)) | ||||
|                         ) | ||||
|                         receiver := nextPair | ||||
|                     } | ||||
|                     default { | ||||
|                         // The last pair contract. | ||||
|                         // Forward directly to taker UNLESS they want BNB back. | ||||
|                         switch eq(buyToken, ETH_TOKEN_ADDRESS_32) | ||||
|                             case 0 { | ||||
|                                 receiver := caller() | ||||
|                             } | ||||
|                             default { | ||||
|                                 receiver := address() | ||||
|                             } | ||||
|                     } | ||||
|  | ||||
|                 // Call pair.swap() | ||||
|                 switch mload(0xA20) // fork | ||||
|                     case 1 { | ||||
|                         mstore(0xB00, BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32) | ||||
|                     } | ||||
|                     default { | ||||
|                         mstore(0xB00, PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32) | ||||
|                     } | ||||
|                 switch pairOrder | ||||
|                     case 0 { | ||||
|                         mstore(0xB04, buyAmount) | ||||
|                         mstore(0xB24, 0) | ||||
|                     } | ||||
|                     default { | ||||
|                         mstore(0xB04, 0) | ||||
|                         mstore(0xB24, buyAmount) | ||||
|                     } | ||||
|                 mstore(0xB44, receiver) | ||||
|                 mstore(0xB64, 0x80) | ||||
|                 mstore(0xB84, 0) | ||||
|                 if iszero(call(gas(), pair, 0, 0xB00, 0xA4, 0, 0)) { | ||||
|                     bubbleRevert() | ||||
|                 } | ||||
|             } // End for-loop. | ||||
|  | ||||
|             // If buying BNB, unwrap the WBNB first | ||||
|             if eq(buyToken, ETH_TOKEN_ADDRESS_32) { | ||||
|                 // Call `WBNB.withdraw(buyAmount)` | ||||
|                 mstore(0xB00, WETH_WITHDRAW_CALL_SELECTOR_32) | ||||
|                 mstore(0xB04, buyAmount) | ||||
|                 if iszero(call(gas(), mload(0xA40), 0, 0xB00, 0x24, 0x00, 0x0)) { | ||||
|                     bubbleRevert() | ||||
|                 } | ||||
|                 // Transfer BNB to the caller. | ||||
|                 if iszero(call(gas(), caller(), buyAmount, 0xB00, 0x0, 0x00, 0x0)) { | ||||
|                     bubbleRevert() | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Functions /////////////////////////////////////////////////////// | ||||
|  | ||||
|             // Load a token address from the `tokens` calldata argument. | ||||
|             function loadTokenAddress(idx) -> addr { | ||||
|                 addr := and(ADDRESS_MASK, calldataload(add(mload(0xA00), mul(idx, 0x20)))) | ||||
|             } | ||||
|  | ||||
|             // Convert BNB pseudo-token addresses to WBNB. | ||||
|             function normalizeToken(token) -> normalized { | ||||
|                 normalized := token | ||||
|                 // Translate BNB pseudo-tokens to WBNB. | ||||
|                 if eq(token, ETH_TOKEN_ADDRESS_32) { | ||||
|                     normalized := mload(0xA40) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Compute the address of the PancakeSwapPair contract given two | ||||
|             // tokens. | ||||
|             function computePairAddress(tokenA, tokenB) -> pair { | ||||
|                 // Convert BNB pseudo-token addresses to WBNB. | ||||
|                 tokenA := normalizeToken(tokenA) | ||||
|                 tokenB := normalizeToken(tokenB) | ||||
|                 // There is one contract for every combination of tokens, | ||||
|                 // which is deployed using CREATE2. | ||||
|                 // The derivation of this address is given by: | ||||
|                 //   address(keccak256(abi.encodePacked( | ||||
|                 //       bytes(0xFF), | ||||
|                 //       address(PANCAKESWAP_FACTORY_ADDRESS), | ||||
|                 //       keccak256(abi.encodePacked( | ||||
|                 //           tokenA < tokenB ? tokenA : tokenB, | ||||
|                 //           tokenA < tokenB ? tokenB : tokenA, | ||||
|                 //       )), | ||||
|                 //       bytes32(PANCAKESWAP_PAIR_INIT_CODE_HASH), | ||||
|                 //   ))); | ||||
|  | ||||
|                 // Compute the salt (the hash of the sorted tokens). | ||||
|                 // Tokens are written in reverse memory order to packed encode | ||||
|                 // them as two 20-byte values in a 40-byte chunk of memory | ||||
|                 // starting at 0xB0C. | ||||
|                 switch lt(tokenA, tokenB) | ||||
|                     case 0 { | ||||
|                         mstore(0xB14, tokenA) | ||||
|                         mstore(0xB00, tokenB) | ||||
|                     } | ||||
|                     default { | ||||
|                         mstore(0xB14, tokenB) | ||||
|                         mstore(0xB00, tokenA) | ||||
|                     } | ||||
|                 let salt := keccak256(0xB0C, 0x28) | ||||
|                 // Compute the pair address by hashing all the components together. | ||||
|                 switch mload(0xA20) // fork | ||||
|                     case 0 { | ||||
|                         mstore(0xB00, FF_PANCAKESWAP_FACTORY) | ||||
|                         mstore(0xB15, salt) | ||||
|                         mstore(0xB35, PANCAKESWAP_PAIR_INIT_CODE_HASH) | ||||
|                     } | ||||
|                     case 1 { | ||||
|                         mstore(0xB00, FF_BAKERYSWAP_FACTORY) | ||||
|                         mstore(0xB15, salt) | ||||
|                         mstore(0xB35, BAKERYSWAP_PAIR_INIT_CODE_HASH) | ||||
|                     } | ||||
|                     default { | ||||
|                         mstore(0xB00, FF_SUSHISWAP_FACTORY) | ||||
|                         mstore(0xB15, salt) | ||||
|                         mstore(0xB35, SUSHISWAP_PAIR_INIT_CODE_HASH) | ||||
|                     } | ||||
|                 pair := and(ADDRESS_MASK, keccak256(0xB00, 0x55)) | ||||
|             } | ||||
|  | ||||
|             // Revert with the return data from the most recent call. | ||||
|             function bubbleRevert() { | ||||
|                 returndatacopy(0, 0, returndatasize()) | ||||
|                 revert(0, returndatasize()) | ||||
|             } | ||||
|  | ||||
|             // Move `amount` tokens from the taker/caller to `to`. | ||||
|             function moveTakerTokensTo(token, to, amount) { | ||||
|                 // Perform a `transferFrom()` | ||||
|                 mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32) | ||||
|                 mstore(0xB04, caller()) | ||||
|                 mstore(0xB24, to) | ||||
|                 mstore(0xB44, amount) | ||||
|  | ||||
|                 let success := call( | ||||
|                     gas(), | ||||
|                     token, | ||||
|                     0, | ||||
|                     0xB00, | ||||
|                     0x64, | ||||
|                     0xC00, | ||||
|                     // Copy only the first 32 bytes of return data. We | ||||
|                     // only care about reading a boolean in the success | ||||
|                     // case. We will use returndatacopy() in the failure case. | ||||
|                     0x20 | ||||
|                 ) | ||||
|  | ||||
|                 let rdsize := returndatasize() | ||||
|  | ||||
|                 // Check for ERC20 success. ERC20 tokens should | ||||
|                 // return a boolean, but some return nothing or | ||||
|                 // extra data. We accept 0-length return data as | ||||
|                 // success, or at least 32 bytes that starts with | ||||
|                 // a 32-byte boolean true. | ||||
|                 success := and( | ||||
|                     success,                         // call itself succeeded | ||||
|                     or( | ||||
|                         iszero(rdsize),              // no return data, or | ||||
|                         and( | ||||
|                             iszero(lt(rdsize, 32)),  // at least 32 bytes | ||||
|                             eq(mload(0xC00), 1)      // starts with uint256(1) | ||||
|                         ) | ||||
|                     ) | ||||
|                 ) | ||||
|  | ||||
|                 if iszero(success) { | ||||
|                     // Revert with the data returned from the transferFrom call. | ||||
|                     returndatacopy(0, 0, rdsize) | ||||
|                     revert(0, rdsize) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Revert if we bought too little. | ||||
|         require(buyAmount >= minBuyAmount, "PancakeSwapFeature/UnderBought"); | ||||
|     } | ||||
| } | ||||
| @@ -26,8 +26,8 @@ import "../storage/LibProxyStorage.sol"; | ||||
| import "../storage/LibSimpleFunctionRegistryStorage.sol"; | ||||
| import "../errors/LibSimpleFunctionRegistryRichErrors.sol"; | ||||
| import "../migrations/LibBootstrap.sol"; | ||||
| import "./IFeature.sol"; | ||||
| import "./ISimpleFunctionRegistryFeature.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/ISimpleFunctionRegistryFeature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Basic registry management features. | ||||
|   | ||||
| @@ -1,137 +0,0 @@ | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
| /* | ||||
|  | ||||
|   Copyright 2020 ZeroEx Intl. | ||||
|  | ||||
|   Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   you may not use this file except in compliance with the License. | ||||
|   You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|   Unless required by applicable law or agreed to in writing, software | ||||
|   distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   See the License for the specific language governing permissions and | ||||
|   limitations under the License. | ||||
|  | ||||
| */ | ||||
|  | ||||
| pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; | ||||
| import "../errors/LibSpenderRichErrors.sol"; | ||||
| import "../fixins/FixinCommon.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "../external/IAllowanceTarget.sol"; | ||||
| import "../storage/LibTokenSpenderStorage.sol"; | ||||
| import "./ITokenSpenderFeature.sol"; | ||||
| import "./IFeature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Feature that allows spending token allowances. | ||||
| contract TokenSpenderFeature is | ||||
|     IFeature, | ||||
|     ITokenSpenderFeature, | ||||
|     FixinCommon | ||||
| { | ||||
|     // solhint-disable | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "TokenSpender"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); | ||||
|     // solhint-enable | ||||
|  | ||||
|     using LibRichErrorsV06 for bytes; | ||||
|  | ||||
|     /// @dev Initialize and register this feature. Should be delegatecalled | ||||
|     ///      into during a `Migrate.migrate()`. | ||||
|     /// @param allowanceTarget An `allowanceTarget` instance, configured to have | ||||
|     ///        the ZeroeEx contract as an authority. | ||||
|     /// @return success `MIGRATE_SUCCESS` on success. | ||||
|     function migrate(IAllowanceTarget allowanceTarget) | ||||
|         external | ||||
|         returns (bytes4 success) | ||||
|     { | ||||
|         LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget; | ||||
|         _registerFeatureFunction(this.getAllowanceTarget.selector); | ||||
|         _registerFeatureFunction(this._spendERC20Tokens.selector); | ||||
|         _registerFeatureFunction(this.getSpendableERC20BalanceOf.selector); | ||||
|         return LibMigrate.MIGRATE_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     /// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within. | ||||
|     /// @param token The token to spend. | ||||
|     /// @param owner The owner of the tokens. | ||||
|     /// @param to The recipient of the tokens. | ||||
|     /// @param amount The amount of `token` to transfer. | ||||
|     function _spendERC20Tokens( | ||||
|         IERC20TokenV06 token, | ||||
|         address owner, | ||||
|         address to, | ||||
|         uint256 amount | ||||
|     ) | ||||
|         external | ||||
|         override | ||||
|         onlySelf | ||||
|     { | ||||
|         IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget; | ||||
|         // Have the allowance target execute an ERC20 `transferFrom()`. | ||||
|         (bool didSucceed, bytes memory resultData) = address(spender).call( | ||||
|             abi.encodeWithSelector( | ||||
|                 IAllowanceTarget.executeCall.selector, | ||||
|                 address(token), | ||||
|                 abi.encodeWithSelector( | ||||
|                     IERC20TokenV06.transferFrom.selector, | ||||
|                     owner, | ||||
|                     to, | ||||
|                     amount | ||||
|                 ) | ||||
|             ) | ||||
|         ); | ||||
|         if (didSucceed) { | ||||
|             resultData = abi.decode(resultData, (bytes)); | ||||
|         } | ||||
|         if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) { | ||||
|             LibSpenderRichErrors.SpenderERC20TransferFromFailedError( | ||||
|                 address(token), | ||||
|                 owner, | ||||
|                 to, | ||||
|                 amount, | ||||
|                 resultData | ||||
|             ).rrevert(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @dev Gets the maximum amount of an ERC20 token `token` that can be | ||||
|     ///      pulled from `owner` by the token spender. | ||||
|     /// @param token The token to spend. | ||||
|     /// @param owner The owner of the tokens. | ||||
|     /// @return amount The amount of tokens that can be pulled. | ||||
|     function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner) | ||||
|         external | ||||
|         override | ||||
|         view | ||||
|         returns (uint256 amount) | ||||
|     { | ||||
|         return LibSafeMathV06.min256( | ||||
|             token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)), | ||||
|             token.balanceOf(owner) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /// @dev Get the address of the allowance target. | ||||
|     /// @return target The target of token allowances. | ||||
|     function getAllowanceTarget() | ||||
|         external | ||||
|         override | ||||
|         view | ||||
|         returns (address target) | ||||
|     { | ||||
|         return address(LibTokenSpenderStorage.getStorage().allowanceTarget); | ||||
|     } | ||||
| } | ||||
| @@ -33,8 +33,8 @@ import "../external/FlashWallet.sol"; | ||||
| import "../storage/LibTransformERC20Storage.sol"; | ||||
| import "../transformers/IERC20Transformer.sol"; | ||||
| import "../transformers/LibERC20Transformer.sol"; | ||||
| import "./ITransformERC20Feature.sol"; | ||||
| import "./IFeature.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/ITransformERC20Feature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Feature to composably transform between ERC20 tokens. | ||||
| @@ -60,10 +60,7 @@ contract TransformERC20Feature is | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 1); | ||||
|  | ||||
|     constructor(bytes32 greedyTokensBloomFilter) | ||||
|         public | ||||
|         FixinTokenSpender(greedyTokensBloomFilter) | ||||
|     {} | ||||
|     constructor() public {} | ||||
|  | ||||
|     /// @dev Initialize and register this feature. | ||||
|     ///      Should be delegatecalled by `Migrate.migrate()`. | ||||
| @@ -313,7 +310,7 @@ contract TransformERC20Feature is | ||||
|             to.transfer(msg.value); | ||||
|         } | ||||
|         // Transfer input tokens. | ||||
|         if (!LibERC20Transformer.isTokenETH(inputToken)) { | ||||
|         if (!LibERC20Transformer.isTokenETH(inputToken) && amount != 0) { | ||||
|             // Token is not ETH, so pull ERC20 tokens. | ||||
|             _transferERC20Tokens( | ||||
|                 inputToken, | ||||
|   | ||||
| @@ -23,10 +23,9 @@ pragma experimental ABIEncoderV2; | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; | ||||
| import "../migrations/LibMigrate.sol"; | ||||
| import "../external/IAllowanceTarget.sol"; | ||||
| import "../fixins/FixinCommon.sol"; | ||||
| import "./IFeature.sol"; | ||||
| import "./IUniswapFeature.sol"; | ||||
| import "./interfaces/IFeature.sol"; | ||||
| import "./interfaces/IUniswapFeature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev VIP uniswap fill functions. | ||||
| @@ -38,13 +37,9 @@ contract UniswapFeature is | ||||
|     /// @dev Name of this feature. | ||||
|     string public constant override FEATURE_NAME = "UniswapFeature"; | ||||
|     /// @dev Version of this feature. | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1); | ||||
|     /// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails. | ||||
|     bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER; | ||||
|     uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 2); | ||||
|     /// @dev WETH contract. | ||||
|     IEtherTokenV06 private immutable WETH; | ||||
|     /// @dev AllowanceTarget instance. | ||||
|     IAllowanceTarget private immutable ALLOWANCE_TARGET; | ||||
|  | ||||
|     // 0xFF + address of the UniswapV2Factory contract. | ||||
|     uint256 constant private FF_UNISWAP_FACTORY = 0xFF5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f0000000000000000000000; | ||||
| @@ -80,16 +75,8 @@ contract UniswapFeature is | ||||
|  | ||||
|     /// @dev Construct this contract. | ||||
|     /// @param weth The WETH contract. | ||||
|     /// @param allowanceTarget The AllowanceTarget contract. | ||||
|     /// @param greedyTokensBloomFilter The bloom filter for greedy tokens. | ||||
|     constructor( | ||||
|         IEtherTokenV06 weth, | ||||
|         IAllowanceTarget allowanceTarget, | ||||
|         bytes32 greedyTokensBloomFilter | ||||
|     ) public { | ||||
|     constructor(IEtherTokenV06 weth) public { | ||||
|         WETH = weth; | ||||
|         ALLOWANCE_TARGET = allowanceTarget; | ||||
|         GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter; | ||||
|     } | ||||
|  | ||||
|     /// @dev Initialize and register this feature. | ||||
| @@ -124,8 +111,6 @@ contract UniswapFeature is | ||||
|         { | ||||
|             // Load immutables onto the stack. | ||||
|             IEtherTokenV06 weth = WETH; | ||||
|             IAllowanceTarget allowanceTarget = ALLOWANCE_TARGET; | ||||
|             bytes32 greedyTokensBloomFilter = GREEDY_TOKENS_BLOOM_FILTER; | ||||
|  | ||||
|             // Store some vars in memory to get around stack limits. | ||||
|             assembly { | ||||
| @@ -135,10 +120,6 @@ contract UniswapFeature is | ||||
|                 mstore(0xA20, isSushi) | ||||
|                 // mload(0xA40) == WETH | ||||
|                 mstore(0xA40, weth) | ||||
|                 // mload(0xA60) == ALLOWANCE_TARGET | ||||
|                 mstore(0xA60, allowanceTarget) | ||||
|                 // mload(0xA80) == GREEDY_TOKENS_BLOOM_FILTER | ||||
|                 mstore(0xA80, greedyTokensBloomFilter) | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -373,38 +354,7 @@ contract UniswapFeature is | ||||
|  | ||||
|             // Move `amount` tokens from the taker/caller to `to`. | ||||
|             function moveTakerTokensTo(token, to, amount) { | ||||
|  | ||||
|                 // If the token is possibly greedy, we check the allowance rather | ||||
|                 // than relying on letting the transferFrom() call fail and | ||||
|                 // falling through to legacy allowance target because the token | ||||
|                 // will eat all our gas. | ||||
|                 if isTokenPossiblyGreedy(token) { | ||||
|                     // Check if we have enough direct allowance by calling | ||||
|                     // `token.allowance()`` | ||||
|                     mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32) | ||||
|                     mstore(0xB04, caller()) | ||||
|                     mstore(0xB24, address()) | ||||
|                     let success := staticcall(gas(), token, 0xB00, 0x44, 0xC00, 0x20) | ||||
|                     if iszero(success) { | ||||
|                         // Call to allowance() failed. | ||||
|                         bubbleRevert() | ||||
|                     } | ||||
|                     // Make sure the allowance call returned at least a word. | ||||
|                     if lt(returndatasize(), 0x20) { | ||||
|                         revert(0, 0) | ||||
|                     } | ||||
|                     // Call succeeded. | ||||
|                     // Result is stored in 0xC00-0xC20. | ||||
|                     if lt(mload(0xC00), amount) { | ||||
|                         // We don't have enough direct allowance, so try | ||||
|                         // going through the legacy allowance taregt. | ||||
|                         moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) | ||||
|                         leave | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Otherwise we will optimistically try to perform a `transferFrom()` | ||||
|                 // directly then if it fails we will go through the legacy allowance target. | ||||
|                 // Perform a `transferFrom()` | ||||
|                 mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32) | ||||
|                 mstore(0xB04, caller()) | ||||
|                 mstore(0xB24, to) | ||||
| @@ -419,8 +369,7 @@ contract UniswapFeature is | ||||
|                     0xC00, | ||||
|                     // Copy only the first 32 bytes of return data. We | ||||
|                     // only care about reading a boolean in the success | ||||
|                     // case, and we discard the return data in the | ||||
|                     // failure case. | ||||
|                     // case. We will use returndatacopy() in the failure case. | ||||
|                     0x20 | ||||
|                 ) | ||||
|  | ||||
| @@ -443,37 +392,11 @@ contract UniswapFeature is | ||||
|                 ) | ||||
|  | ||||
|                 if iszero(success) { | ||||
|                     // Try to fall back to the allowance target. | ||||
|                     moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) | ||||
|                     // Revert with the data returned from the transferFrom call. | ||||
|                     returndatacopy(0, 0, rdsize) | ||||
|                     revert(0, rdsize) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Move tokens by going through the legacy allowance target contract. | ||||
|             function moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) { | ||||
|                 mstore(0xB00, ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32) | ||||
|                 mstore(0xB04, token) | ||||
|                 mstore(0xB24, 0x40) | ||||
|                 mstore(0xB44, 0x64) | ||||
|                 mstore(0xB64, TRANSFER_FROM_CALL_SELECTOR_32) | ||||
|                 mstore(0xB68, caller()) | ||||
|                 mstore(0xB88, to) | ||||
|                 mstore(0xBA8, amount) | ||||
|                 if iszero(call(gas(), mload(0xA60), 0, 0xB00, 0xC8, 0x00, 0x0)) { | ||||
|                     bubbleRevert() | ||||
|                 } | ||||
|                 // If this fall back failed, the swap will most likely fail | ||||
|                 // so there's no need to validate the result. | ||||
|             } | ||||
|  | ||||
|             // Checks if a token possibly belongs to the GREEDY_TOKENS_BLOOM_FILTER | ||||
|             // bloom filter. | ||||
|             function isTokenPossiblyGreedy(token) -> isPossiblyGreedy { | ||||
|                 // The hash is given by: | ||||
|                 // (1 << (keccak256(token) % 256)) | (1 << (token % 256)) | ||||
|                 mstore(0, token) | ||||
|                 let h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1)) | ||||
|                 isPossiblyGreedy := eq(and(h, mload(0xA80)), h) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Revert if we bought too little. | ||||
|   | ||||
| @@ -0,0 +1,70 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "../libs/LibNativeOrder.sol"; | ||||
| import "../libs/LibSignature.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Feature for batch/market filling limit and RFQ orders. | ||||
| interface IBatchFillNativeOrdersFeature { | ||||
|  | ||||
|     /// @dev Fills multiple limit orders. | ||||
|     /// @param orders Array of limit orders. | ||||
|     /// @param signatures Array of signatures corresponding to each order. | ||||
|     /// @param takerTokenFillAmounts Array of desired amounts to fill each order. | ||||
|     /// @param revertIfIncomplete If true, reverts if this function fails to | ||||
|     ///        fill the full fill amount for any individual order. | ||||
|     /// @return takerTokenFilledAmounts Array of amounts filled, in taker token. | ||||
|     /// @return makerTokenFilledAmounts Array of amounts filled, in maker token. | ||||
|     function batchFillLimitOrders( | ||||
|         LibNativeOrder.LimitOrder[] calldata orders, | ||||
|         LibSignature.Signature[] calldata signatures, | ||||
|         uint128[] calldata takerTokenFillAmounts, | ||||
|         bool revertIfIncomplete | ||||
|     ) | ||||
|         external | ||||
|         payable | ||||
|         returns ( | ||||
|             uint128[] memory takerTokenFilledAmounts, | ||||
|             uint128[] memory makerTokenFilledAmounts | ||||
|         ); | ||||
|  | ||||
|     /// @dev Fills multiple RFQ orders. | ||||
|     /// @param orders Array of RFQ orders. | ||||
|     /// @param signatures Array of signatures corresponding to each order. | ||||
|     /// @param takerTokenFillAmounts Array of desired amounts to fill each order. | ||||
|     /// @param revertIfIncomplete If true, reverts if this function fails to | ||||
|     ///        fill the full fill amount for any individual order. | ||||
|     /// @return takerTokenFilledAmounts Array of amounts filled, in taker token. | ||||
|     /// @return makerTokenFilledAmounts Array of amounts filled, in maker token. | ||||
|     function batchFillRfqOrders( | ||||
|         LibNativeOrder.RfqOrder[] calldata orders, | ||||
|         LibSignature.Signature[] calldata signatures, | ||||
|         uint128[] calldata takerTokenFillAmounts, | ||||
|         bool revertIfIncomplete | ||||
|     ) | ||||
|         external | ||||
|         returns ( | ||||
|             uint128[] memory takerTokenFilledAmounts, | ||||
|             uint128[] memory makerTokenFilledAmounts | ||||
|         ); | ||||
| } | ||||
| @@ -21,7 +21,7 @@ pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
| 
 | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "../vendor/ILiquidityProvider.sol"; | ||||
| import "../../vendor/ILiquidityProvider.sol"; | ||||
| 
 | ||||
| 
 | ||||
| /// @dev Feature to swap directly with an on-chain liquidity provider. | ||||
| @@ -21,7 +21,7 @@ pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
| 
 | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "./libs/LibSignature.sol"; | ||||
| import "../libs/LibSignature.sol"; | ||||
| 
 | ||||
| /// @dev Meta-transactions feature. | ||||
| interface IMetaTransactionsFeature { | ||||
| @@ -0,0 +1,117 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
|  | ||||
|  | ||||
| interface IMultiplexFeature { | ||||
|  | ||||
|     // Parameters for `batchFill`. | ||||
|     struct BatchFillData { | ||||
|         // The token being sold. | ||||
|         IERC20TokenV06 inputToken; | ||||
|         // The token being bought. | ||||
|         IERC20TokenV06 outputToken; | ||||
|         // The amount of `inputToken` to sell. | ||||
|         uint256 sellAmount; | ||||
|         // The nested calls to perform. | ||||
|         WrappedBatchCall[] calls; | ||||
|     } | ||||
|  | ||||
|     // Represents a call nested within a `batchFill`. | ||||
|     struct WrappedBatchCall { | ||||
|         // The selector of the function to call. | ||||
|         bytes4 selector; | ||||
|         // Amount of `inputToken` to sell. | ||||
|         uint256 sellAmount; | ||||
|         // ABI-encoded parameters needed to perform the call. | ||||
|         bytes data; | ||||
|     } | ||||
|  | ||||
|     // Parameters for `multiHopFill`. | ||||
|     struct MultiHopFillData { | ||||
|         // The sell path, i.e. | ||||
|         // tokens = [inputToken, hopToken1, ..., hopTokenN, outputToken] | ||||
|         address[] tokens; | ||||
|         // The amount of `tokens[0]` to sell. | ||||
|         uint256 sellAmount; | ||||
|         // The nested calls to perform. | ||||
|         WrappedMultiHopCall[] calls; | ||||
|     } | ||||
|  | ||||
|     // Represents a call nested within a `multiHopFill`. | ||||
|     struct WrappedMultiHopCall { | ||||
|         // The selector of the function to call. | ||||
|         bytes4 selector; | ||||
|         // ABI-encoded parameters needed to perform the call. | ||||
|         bytes data; | ||||
|     } | ||||
|  | ||||
|     event LiquidityProviderSwap( | ||||
|         address inputToken, | ||||
|         address outputToken, | ||||
|         uint256 inputTokenAmount, | ||||
|         uint256 outputTokenAmount, | ||||
|         address provider, | ||||
|         address recipient | ||||
|     ); | ||||
|  | ||||
|     event ExpiredRfqOrder( | ||||
|         bytes32 orderHash, | ||||
|         address maker, | ||||
|         uint64 expiry | ||||
|     ); | ||||
|  | ||||
|     /// @dev Executes a batch of fills selling `fillData.inputToken` | ||||
|     ///      for `fillData.outputToken` in sequence. Refer to the | ||||
|     ///      internal variant `_batchFill` for the allowed nested | ||||
|     ///      operations. | ||||
|     /// @param fillData Encodes the input/output tokens, the sell | ||||
|     ///        amount, and the nested operations for this batch fill. | ||||
|     /// @param minBuyAmount The minimum amount of `fillData.outputToken` | ||||
|     ///        to buy. Reverts if this amount is not met. | ||||
|     /// @return outputTokenAmount The amount of the output token bought. | ||||
|     function batchFill( | ||||
|         BatchFillData calldata fillData, | ||||
|         uint256 minBuyAmount | ||||
|     ) | ||||
|         external | ||||
|         payable | ||||
|         returns (uint256 outputTokenAmount); | ||||
|  | ||||
|     /// @dev Executes a sequence of fills "hopping" through the | ||||
|     ///      path of tokens given by `fillData.tokens`. Refer to the | ||||
|     ///      internal variant `_multiHopFill` for the allowed nested | ||||
|     ///      operations. | ||||
|     /// @param fillData Encodes the path of tokens, the sell amount, | ||||
|     ///        and the nested operations for this multi-hop fill. | ||||
|     /// @param minBuyAmount The minimum amount of the output token | ||||
|     ///        to buy. Reverts if this amount is not met. | ||||
|     /// @return outputTokenAmount The amount of the output token bought. | ||||
|     function multiHopFill( | ||||
|         MultiHopFillData calldata fillData, | ||||
|         uint256 minBuyAmount | ||||
|     ) | ||||
|         external | ||||
|         payable | ||||
|         returns (uint256 outputTokenAmount); | ||||
| } | ||||
| @@ -0,0 +1,116 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "../libs/LibSignature.sol"; | ||||
| import "../libs/LibNativeOrder.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Events emitted by NativeOrdersFeature. | ||||
| interface INativeOrdersEvents { | ||||
|  | ||||
|     /// @dev Emitted whenever a `LimitOrder` is filled. | ||||
|     /// @param orderHash The canonical hash of the order. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param taker The taker of the order. | ||||
|     /// @param feeRecipient Fee recipient of the order. | ||||
|     /// @param takerTokenFilledAmount How much taker token was filled. | ||||
|     /// @param makerTokenFilledAmount How much maker token was filled. | ||||
|     /// @param protocolFeePaid How much protocol fee was paid. | ||||
|     /// @param pool The fee pool associated with this order. | ||||
|     event LimitOrderFilled( | ||||
|         bytes32 orderHash, | ||||
|         address maker, | ||||
|         address taker, | ||||
|         address feeRecipient, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint128 takerTokenFilledAmount, | ||||
|         uint128 makerTokenFilledAmount, | ||||
|         uint128 takerTokenFeeFilledAmount, | ||||
|         uint256 protocolFeePaid, | ||||
|         bytes32 pool | ||||
|     ); | ||||
|  | ||||
|     /// @dev Emitted whenever an `RfqOrder` is filled. | ||||
|     /// @param orderHash The canonical hash of the order. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param taker The taker of the order. | ||||
|     /// @param takerTokenFilledAmount How much taker token was filled. | ||||
|     /// @param makerTokenFilledAmount How much maker token was filled. | ||||
|     /// @param pool The fee pool associated with this order. | ||||
|     event RfqOrderFilled( | ||||
|         bytes32 orderHash, | ||||
|         address maker, | ||||
|         address taker, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint128 takerTokenFilledAmount, | ||||
|         uint128 makerTokenFilledAmount, | ||||
|         bytes32 pool | ||||
|     ); | ||||
|  | ||||
|     /// @dev Emitted whenever a limit or RFQ order is cancelled. | ||||
|     /// @param orderHash The canonical hash of the order. | ||||
|     /// @param maker The order maker. | ||||
|     event OrderCancelled( | ||||
|         bytes32 orderHash, | ||||
|         address maker | ||||
|     ); | ||||
|  | ||||
|     /// @dev Emitted whenever Limit orders are cancelled by pair by a maker. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param makerToken The maker token in a pair for the orders cancelled. | ||||
|     /// @param takerToken The taker token in a pair for the orders cancelled. | ||||
|     /// @param minValidSalt The new minimum valid salt an order with this pair must | ||||
|     ///        have. | ||||
|     event PairCancelledLimitOrders( | ||||
|         address maker, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint256 minValidSalt | ||||
|     ); | ||||
|  | ||||
|     /// @dev Emitted whenever RFQ orders are cancelled by pair by a maker. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param makerToken The maker token in a pair for the orders cancelled. | ||||
|     /// @param takerToken The taker token in a pair for the orders cancelled. | ||||
|     /// @param minValidSalt The new minimum valid salt an order with this pair must | ||||
|     ///        have. | ||||
|     event PairCancelledRfqOrders( | ||||
|         address maker, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint256 minValidSalt | ||||
|     ); | ||||
|  | ||||
|     /// @dev Emitted when new addresses are allowed or disallowed to fill | ||||
|     ///      orders with a given txOrigin. | ||||
|     /// @param origin The address doing the allowing. | ||||
|     /// @param addrs The address being allowed/disallowed. | ||||
|     /// @param allowed Indicates whether the address should be allowed. | ||||
|     event RfqOrderOriginsAllowed( | ||||
|         address origin, | ||||
|         address[] addrs, | ||||
|         bool allowed | ||||
|     ); | ||||
| } | ||||
| @@ -21,98 +21,15 @@ pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
| 
 | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "./libs/LibSignature.sol"; | ||||
| import "./libs/LibNativeOrder.sol"; | ||||
| import "../libs/LibSignature.sol"; | ||||
| import "../libs/LibNativeOrder.sol"; | ||||
| import "./INativeOrdersEvents.sol"; | ||||
| 
 | ||||
| 
 | ||||
| /// @dev Feature for interacting with limit orders. | ||||
| interface INativeOrdersFeature { | ||||
| 
 | ||||
|     /// @dev Emitted whenever a `LimitOrder` is filled. | ||||
|     /// @param orderHash The canonical hash of the order. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param taker The taker of the order. | ||||
|     /// @param feeRecipient Fee recipient of the order. | ||||
|     /// @param takerTokenFilledAmount How much taker token was filled. | ||||
|     /// @param makerTokenFilledAmount How much maker token was filled. | ||||
|     /// @param protocolFeePaid How much protocol fee was paid. | ||||
|     /// @param pool The fee pool associated with this order. | ||||
|     event LimitOrderFilled( | ||||
|         bytes32 orderHash, | ||||
|         address maker, | ||||
|         address taker, | ||||
|         address feeRecipient, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint128 takerTokenFilledAmount, | ||||
|         uint128 makerTokenFilledAmount, | ||||
|         uint128 takerTokenFeeFilledAmount, | ||||
|         uint256 protocolFeePaid, | ||||
|         bytes32 pool | ||||
|     ); | ||||
| 
 | ||||
|     /// @dev Emitted whenever an `RfqOrder` is filled. | ||||
|     /// @param orderHash The canonical hash of the order. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param taker The taker of the order. | ||||
|     /// @param takerTokenFilledAmount How much taker token was filled. | ||||
|     /// @param makerTokenFilledAmount How much maker token was filled. | ||||
|     /// @param pool The fee pool associated with this order. | ||||
|     event RfqOrderFilled( | ||||
|         bytes32 orderHash, | ||||
|         address maker, | ||||
|         address taker, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint128 takerTokenFilledAmount, | ||||
|         uint128 makerTokenFilledAmount, | ||||
|         bytes32 pool | ||||
|     ); | ||||
| 
 | ||||
|     /// @dev Emitted whenever a limit or RFQ order is cancelled. | ||||
|     /// @param orderHash The canonical hash of the order. | ||||
|     /// @param maker The order maker. | ||||
|     event OrderCancelled( | ||||
|         bytes32 orderHash, | ||||
|         address maker | ||||
|     ); | ||||
| 
 | ||||
|     /// @dev Emitted whenever Limit orders are cancelled by pair by a maker. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param makerToken The maker token in a pair for the orders cancelled. | ||||
|     /// @param takerToken The taker token in a pair for the orders cancelled. | ||||
|     /// @param minValidSalt The new minimum valid salt an order with this pair must | ||||
|     ///        have. | ||||
|     event PairCancelledLimitOrders( | ||||
|         address maker, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint256 minValidSalt | ||||
|     ); | ||||
| 
 | ||||
|     /// @dev Emitted whenever RFQ orders are cancelled by pair by a maker. | ||||
|     /// @param maker The maker of the order. | ||||
|     /// @param makerToken The maker token in a pair for the orders cancelled. | ||||
|     /// @param takerToken The taker token in a pair for the orders cancelled. | ||||
|     /// @param minValidSalt The new minimum valid salt an order with this pair must | ||||
|     ///        have. | ||||
|     event PairCancelledRfqOrders( | ||||
|         address maker, | ||||
|         address makerToken, | ||||
|         address takerToken, | ||||
|         uint256 minValidSalt | ||||
|     ); | ||||
| 
 | ||||
|     /// @dev Emitted when new addresses are allowed or disallowed to fill | ||||
|     ///      orders with a given txOrigin. | ||||
|     /// @param origin The address doing the allowing. | ||||
|     /// @param addrs The address being allowed/disallowed. | ||||
|     /// @param allowed Indicates whether the address should be allowed. | ||||
|     event RfqOrderOriginsAllowed( | ||||
|         address origin, | ||||
|         address[] addrs, | ||||
|         bool allowed | ||||
|     ); | ||||
| interface INativeOrdersFeature is | ||||
|     INativeOrdersEvents | ||||
| { | ||||
| 
 | ||||
|     /// @dev Transfers protocol fees from the `FeeCollector` pools into | ||||
|     ///      the staking contract. | ||||
| @@ -0,0 +1,50 @@ | ||||
| // 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.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev VIP PancakeSwap/BakerySwap/SushiSwap fill functions. | ||||
| interface IPancakeSwapFeature { | ||||
|  | ||||
|     enum ProtocolFork { | ||||
|         PancakeSwap, | ||||
|         BakerySwap, | ||||
|         SushiSwap | ||||
|     } | ||||
|  | ||||
|     /// @dev Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap. | ||||
|     /// @param tokens Sell path. | ||||
|     /// @param sellAmount of `tokens[0]` Amount to sell. | ||||
|     /// @param minBuyAmount Minimum amount of `tokens[-1]` to buy. | ||||
|     /// @param fork The protocol fork to use. | ||||
|     /// @return buyAmount Amount of `tokens[-1]` bought. | ||||
|     function sellToPancakeSwap( | ||||
|         IERC20TokenV06[] calldata tokens, | ||||
|         uint256 sellAmount, | ||||
|         uint256 minBuyAmount, | ||||
|         ProtocolFork fork | ||||
|     ) | ||||
|         external | ||||
|         payable | ||||
|         returns (uint256 buyAmount); | ||||
| } | ||||
| @@ -21,8 +21,8 @@ pragma solidity ^0.6.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
| 
 | ||||
| import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | ||||
| import "../transformers/IERC20Transformer.sol"; | ||||
| import "../external/IFlashWallet.sol"; | ||||
| import "../../transformers/IERC20Transformer.sol"; | ||||
| import "../../external/IFlashWallet.sol"; | ||||
| 
 | ||||
| 
 | ||||
| /// @dev Feature to composably transform between ERC20 tokens. | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user