From 3adfcdffa8e0aa53d08f3049c4950a9536a390aa Mon Sep 17 00:00:00 2001 From: Kim Persson Date: Thu, 1 Apr 2021 21:45:06 +0200 Subject: [PATCH] Maker PSM integration [TKR-2] (#150) * ADDS basic boilerplate for PSM bridge WIP * ADDS integrate the MakerPSM mixin and fix incorrect naming * fix: take into account PSM fee when buying USDC from PSM * feat: intial stab at a PSM sampler WIP * feat: integrate MakerPsm into AS WIP * refactor: get VAT contract address from PSM instead of passing it in * fix: hardcode PSM Gemtoken to USDC * fix: remove passing in authGem, get from PSM contract instead * fix: use constant modified to avoid using storage variables * fix: incorrect num decimals after multiplication in sampler * fix: PSM buy sampling * fix: use fillData to estimate gas schedule * Rebased on latest development * Guard and use latest Curve LiquidityProvider * `@0x/contract-addresses`: Redeploy FQT on mainnet and ropsten Co-authored-by: Jacob Evans Co-authored-by: Lawrence Forman --- .../transformers/bridges/BridgeAdapter.sol | 10 + .../transformers/bridges/BridgeProtocols.sol | 1 + .../bridges/mixins/MixinMakerPSM.sol | 114 ++++++++ contracts/zero-ex/package.json | 2 +- contracts/zero-ex/src/artifacts.ts | 2 - contracts/zero-ex/src/wrappers.ts | 1 - contracts/zero-ex/test/artifacts.ts | 2 + contracts/zero-ex/test/wrappers.ts | 1 + contracts/zero-ex/tsconfig.json | 2 +- .../contracts/src/ERC20BridgeSampler.sol | 2 + .../contracts/src/MakerPSMSampler.sol | 267 ++++++++++++++++++ packages/asset-swapper/package.json | 3 +- .../utils/market_operation_utils/constants.ts | 37 ++- .../utils/market_operation_utils/orders.ts | 13 + .../sampler_operations.ts | 55 ++++ .../src/utils/market_operation_utils/types.ts | 18 ++ packages/asset-swapper/test/artifacts.ts | 2 + .../test/market_operation_utils_test.ts | 3 + packages/asset-swapper/test/wrappers.ts | 1 + packages/asset-swapper/tsconfig.json | 1 + packages/contract-addresses/CHANGELOG.json | 6 +- packages/contract-addresses/addresses.json | 4 +- .../protocol-utils/src/transformer_utils.ts | 1 + yarn.lock | 44 ++- 24 files changed, 568 insertions(+), 24 deletions(-) create mode 100644 contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol create mode 100644 packages/asset-swapper/contracts/src/MakerPSMSampler.sol diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index 8db42204d8..590d4f1fad 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -30,6 +30,7 @@ import "./mixins/MixinCryptoCom.sol"; import "./mixins/MixinDodo.sol"; import "./mixins/MixinDodoV2.sol"; import "./mixins/MixinKyber.sol"; +import "./mixins/MixinMakerPSM.sol"; import "./mixins/MixinMooniswap.sol"; import "./mixins/MixinMStable.sol"; import "./mixins/MixinNerve.sol"; @@ -49,6 +50,7 @@ contract BridgeAdapter is MixinDodo, MixinDodoV2, MixinKyber, + MixinMakerPSM, MixinMooniswap, MixinMStable, MixinNerve, @@ -68,6 +70,7 @@ contract BridgeAdapter is MixinDodo() MixinDodoV2() MixinKyber(weth) + MixinMakerPSM() MixinMooniswap(weth) MixinMStable() MixinNerve() @@ -123,6 +126,13 @@ contract BridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.MAKERPSM) { + boughtAmount = _tradeMakerPsm( + sellToken, + buyToken, + sellAmount, + order.bridgeData + ); } else if (protocolId == BridgeProtocols.MOONISWAP) { boughtAmount = _tradeMooniswap( sellToken, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol index baebf8ac05..60630fa7cb 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -43,4 +43,5 @@ library BridgeProtocols { uint128 internal constant BANCOR = 13; uint128 internal constant COFIX = 14; uint128 internal constant NERVE = 15; + uint128 internal constant MAKERPSM = 16; } diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol new file mode 100644 index 0000000000..283e9873fc --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol @@ -0,0 +1,114 @@ +// 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/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; + +interface IPSM { + // @dev Get the fee for selling USDC to DAI in PSM + // @return tin toll in [wad] + function tin() external view returns (uint256); + // @dev Get the fee for selling DAI to USDC in PSM + // @return tout toll out [wad] + function tout() external view returns (uint256); + + // @dev Get the address of the PSM state Vat + // @return address of the Vat + function vat() external view returns (address); + + // @dev Get the address of the underlying vault powering PSM + // @return address of gemJoin contract + function gemJoin() external view returns (address); + + // @dev Sell USDC for DAI + // @param usr The address of the account trading USDC for DAI. + // @param gemAmt The amount of USDC to sell in USDC base units + function sellGem( + address usr, + uint256 gemAmt + ) external; + // @dev Buy USDC for DAI + // @param usr The address of the account trading DAI for USDC + // @param gemAmt The amount of USDC to buy in USDC base units + function buyGem( + address usr, + uint256 gemAmt + ) external; +} + +contract MixinMakerPSM { + + using LibERC20TokenV06 for IERC20TokenV06; + using LibSafeMathV06 for uint256; + + struct MakerPsmBridgeData { + address psmAddress; + address gemTokenAddres; + } + + // Maker units + // wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances) + uint256 constant private WAD = 10 ** 18; + // ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios) + uint256 constant private RAY = 10 ** 27; + // rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray) + uint256 constant private RAD = 10 ** 45; + // See https://github.com/makerdao/dss/blob/master/DEVELOPING.md + + function _tradeMakerPsm( + IERC20TokenV06 sellToken, + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) + internal + returns (uint256 boughtAmount) + { + // Decode the bridge data. + MakerPsmBridgeData memory data = abi.decode(bridgeData, (MakerPsmBridgeData)); + uint256 beforeBalance = buyToken.balanceOf(address(this)); + + IPSM psm = IPSM(data.psmAddress); + + if (address(sellToken) == data.gemTokenAddres) { + sellToken.approveIfBelow( + psm.gemJoin(), + sellAmount + ); + + psm.sellGem(address(this), sellAmount); + } else if (address(buyToken) == data.gemTokenAddres) { + uint256 feeDivisor = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% fee [tout is in wad]; + uint256 buyTokenBaseUnit = uint256(10) ** uint256(buyToken.decimals()); + uint256 gemAmount = sellAmount.safeMul(buyTokenBaseUnit).safeDiv(feeDivisor); + + sellToken.approveIfBelow( + data.psmAddress, + sellAmount + ); + psm.buyGem(address(this), gemAmount); + } + + return buyToken.balanceOf(address(this)).safeSub(beforeBalance); + } +} diff --git a/contracts/zero-ex/package.json b/contracts/zero-ex/package.json index 55b7649faa..68e427483d 100644 --- a/contracts/zero-ex/package.json +++ b/contracts/zero-ex/package.json @@ -43,7 +43,7 @@ "config": { "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json" + "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json" }, "repository": { "type": "git", diff --git a/contracts/zero-ex/src/artifacts.ts b/contracts/zero-ex/src/artifacts.ts index ff378adcdc..ea3675d89e 100644 --- a/contracts/zero-ex/src/artifacts.ts +++ b/contracts/zero-ex/src/artifacts.ts @@ -13,7 +13,6 @@ import * as FeeCollector from '../generated-artifacts/FeeCollector.json'; import * as FeeCollectorController from '../generated-artifacts/FeeCollectorController.json'; import * as FillQuoteTransformer from '../generated-artifacts/FillQuoteTransformer.json'; import * as FullMigration from '../generated-artifacts/FullMigration.json'; -import * as IAllowanceTarget from '../generated-artifacts/IAllowanceTarget.json'; import * as IBatchFillNativeOrdersFeature from '../generated-artifacts/IBatchFillNativeOrdersFeature.json'; import * as IERC20Transformer from '../generated-artifacts/IERC20Transformer.json'; import * as IFlashWallet from '../generated-artifacts/IFlashWallet.json'; @@ -43,7 +42,6 @@ export const artifacts = { FullMigration: FullMigration as ContractArtifact, InitialMigration: InitialMigration as ContractArtifact, IFlashWallet: IFlashWallet as ContractArtifact, - IAllowanceTarget: IAllowanceTarget as ContractArtifact, IERC20Transformer: IERC20Transformer as ContractArtifact, IOwnableFeature: IOwnableFeature as ContractArtifact, ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact, diff --git a/contracts/zero-ex/src/wrappers.ts b/contracts/zero-ex/src/wrappers.ts index e853680874..49492899b8 100644 --- a/contracts/zero-ex/src/wrappers.ts +++ b/contracts/zero-ex/src/wrappers.ts @@ -11,7 +11,6 @@ export * from '../generated-wrappers/fee_collector'; export * from '../generated-wrappers/fee_collector_controller'; export * from '../generated-wrappers/fill_quote_transformer'; export * from '../generated-wrappers/full_migration'; -export * from '../generated-wrappers/i_allowance_target'; export * from '../generated-wrappers/i_batch_fill_native_orders_feature'; export * from '../generated-wrappers/i_erc20_transformer'; export * from '../generated-wrappers/i_flash_wallet'; diff --git a/contracts/zero-ex/test/artifacts.ts b/contracts/zero-ex/test/artifacts.ts index d190a8c4e0..c3571e729b 100644 --- a/contracts/zero-ex/test/artifacts.ts +++ b/contracts/zero-ex/test/artifacts.ts @@ -83,6 +83,7 @@ import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json'; import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json'; import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json'; import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json'; +import * as MixinMakerPSM from '../test/generated-artifacts/MixinMakerPSM.json'; import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json'; import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json'; import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json'; @@ -241,6 +242,7 @@ export const artifacts = { MixinDodoV2: MixinDodoV2 as ContractArtifact, MixinKyber: MixinKyber as ContractArtifact, MixinMStable: MixinMStable as ContractArtifact, + MixinMakerPSM: MixinMakerPSM as ContractArtifact, MixinMooniswap: MixinMooniswap as ContractArtifact, MixinNerve: MixinNerve as ContractArtifact, MixinOasis: MixinOasis as ContractArtifact, diff --git a/contracts/zero-ex/test/wrappers.ts b/contracts/zero-ex/test/wrappers.ts index 60bfeca254..a63b610343 100644 --- a/contracts/zero-ex/test/wrappers.ts +++ b/contracts/zero-ex/test/wrappers.ts @@ -82,6 +82,7 @@ export * from '../test/generated-wrappers/mixin_dodo'; export * from '../test/generated-wrappers/mixin_dodo_v2'; export * from '../test/generated-wrappers/mixin_kyber'; export * from '../test/generated-wrappers/mixin_m_stable'; +export * from '../test/generated-wrappers/mixin_maker_p_s_m'; export * from '../test/generated-wrappers/mixin_mooniswap'; export * from '../test/generated-wrappers/mixin_nerve'; export * from '../test/generated-wrappers/mixin_oasis'; diff --git a/contracts/zero-ex/tsconfig.json b/contracts/zero-ex/tsconfig.json index 14a743f46e..3671fbdca4 100644 --- a/contracts/zero-ex/tsconfig.json +++ b/contracts/zero-ex/tsconfig.json @@ -11,7 +11,6 @@ "generated-artifacts/FeeCollectorController.json", "generated-artifacts/FillQuoteTransformer.json", "generated-artifacts/FullMigration.json", - "generated-artifacts/IAllowanceTarget.json", "generated-artifacts/IBatchFillNativeOrdersFeature.json", "generated-artifacts/IERC20Transformer.json", "generated-artifacts/IFlashWallet.json", @@ -114,6 +113,7 @@ "test/generated-artifacts/MixinDodoV2.json", "test/generated-artifacts/MixinKyber.json", "test/generated-artifacts/MixinMStable.json", + "test/generated-artifacts/MixinMakerPSM.json", "test/generated-artifacts/MixinMooniswap.json", "test/generated-artifacts/MixinNerve.json", "test/generated-artifacts/MixinOasis.json", diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol index d438d80317..b2ea67648f 100644 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol @@ -28,6 +28,7 @@ import "./DODOV2Sampler.sol"; import "./Eth2DaiSampler.sol"; import "./KyberSampler.sol"; import "./LiquidityProviderSampler.sol"; +import "./MakerPSMSampler.sol"; import "./MultiBridgeSampler.sol"; import "./MStableSampler.sol"; import "./MooniswapSampler.sol"; @@ -48,6 +49,7 @@ contract ERC20BridgeSampler is Eth2DaiSampler, KyberSampler, LiquidityProviderSampler, + MakerPSMSampler, MStableSampler, MooniswapSampler, MultiBridgeSampler, diff --git a/packages/asset-swapper/contracts/src/MakerPSMSampler.sol b/packages/asset-swapper/contracts/src/MakerPSMSampler.sol new file mode 100644 index 0000000000..5adcf28716 --- /dev/null +++ b/packages/asset-swapper/contracts/src/MakerPSMSampler.sol @@ -0,0 +1,267 @@ +// 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; +pragma experimental ABIEncoderV2; + +import "./SamplerUtils.sol"; +import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol"; + +interface IPSM { + // @dev Get the fee for selling USDC to DAI in PSM + // @return tin toll in [wad] + function tin() external view returns (uint256); + // @dev Get the fee for selling DAI to USDC in PSM + // @return tout toll out [wad] + function tout() external view returns (uint256); + + // @dev Get the address of the PSM state Vat + // @return address of the Vat + function vat() external view returns (address); + + // @dev Get the address of the underlying vault powering PSM + // @return address of gemJoin contract + function gemJoin() external view returns (address); + + // @dev Get the address of DAI + // @return address of DAI contract + function dai() external view returns (address); + + // @dev Sell USDC for DAI + // @param usr The address of the account trading USDC for DAI. + // @param gemAmt The amount of USDC to sell in USDC base units + function sellGem( + address usr, + uint256 gemAmt + ) external; + // @dev Buy USDC for DAI + // @param usr The address of the account trading DAI for USDC + // @param gemAmt The amount of USDC to buy in USDC base units + function buyGem( + address usr, + uint256 gemAmt + ) external; +} + +interface IVAT { + // @dev Get a collateral type by identifier + // @param ilkIdentifier bytes32 identifier. Example: ethers.utils.formatBytes32String("PSM-USDC-A") + // @return ilk + // @return ilk.Art Total Normalised Debt in wad + // @return ilk.rate Accumulated Rates in ray + // @return ilk.spot Price with Safety Margin in ray + // @return ilk.line Debt Ceiling in rad + // @return ilk.dust Urn Debt Floor in rad + function ilks( + bytes32 ilkIdentifier + ) external view returns ( + uint256 Art, + uint256 rate, + uint256 spot, + uint256 line, + uint256 dust + ); +} + +contract MakerPSMSampler is + SamplerUtils +{ + using LibSafeMathV06 for uint256; + + /// @dev Information about which PSM module to use + struct MakerPsmInfo { + address psmAddress; + bytes32 ilkIdentifier; + address gemTokenAddress; + } + + /// @dev Gas limit for MakerPsm calls. + uint256 constant private MAKER_PSM_CALL_GAS = 300e3; // 300k + + + // Maker units + // wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances) + uint256 constant private WAD = 10 ** 18; + // ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios) + uint256 constant private RAY = 10 ** 27; + // rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray) + uint256 constant private RAD = 10 ** 45; + // See https://github.com/makerdao/dss/blob/master/DEVELOPING.m + + /// @dev Sample sell quotes from Maker PSM + function sampleSellsFromMakerPsm( + MakerPsmInfo memory psmInfo, + address takerToken, + address makerToken, + uint256[] memory takerTokenAmounts + ) + public + view + returns (uint256[] memory makerTokenAmounts) + { + _assertValidPair(makerToken, takerToken); + IPSM psm = IPSM(psmInfo.psmAddress); + IVAT vat = IVAT(psm.vat()); + + + uint256 numSamples = takerTokenAmounts.length; + makerTokenAmounts = new uint256[](numSamples); + + if (makerToken != psm.dai() && takerToken != psm.dai()) { + return makerTokenAmounts; + } + + for (uint256 i = 0; i < numSamples; i++) { + uint256 buyAmount = _samplePSMSell(psmInfo, makerToken, takerToken, takerTokenAmounts[i], psm, vat); + + if (buyAmount == 0) { + break; + } + makerTokenAmounts[i] = buyAmount; + } + } + + function sampleBuysFromMakerPsm( + MakerPsmInfo memory psmInfo, + address takerToken, + address makerToken, + uint256[] memory makerTokenAmounts + ) + public + view + returns (uint256[] memory takerTokenAmounts) + { + _assertValidPair(makerToken, takerToken); + IPSM psm = IPSM(psmInfo.psmAddress); + IVAT vat = IVAT(psm.vat()); + + uint256 numSamples = makerTokenAmounts.length; + takerTokenAmounts = new uint256[](numSamples); + if (makerToken != psm.dai() && takerToken != psm.dai()) { + return takerTokenAmounts; + } + + for (uint256 i = 0; i < numSamples; i++) { + uint256 sellAmount = _samplePSMBuy(psmInfo, makerToken, takerToken, makerTokenAmounts[i], psm, vat); + + if (sellAmount == 0) { + break; + } + + takerTokenAmounts[i] = sellAmount; + } + + } + + function _samplePSMSell(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 takerTokenAmount, IPSM psm, IVAT vat) + private + view + returns (uint256) + { + (uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier); + uint256 gemTokenBaseUnit = uint256(1e6); + + if (takerToken == psmInfo.gemTokenAddress) { + // Simulate sellGem + // Selling USDC to the PSM, increasing the total debt + // Convert USDC 6 decimals to 18 decimals [wad] + uint256 takerTokenAmountInWad = takerTokenAmount.safeMul(1e12); + + uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY); + + // PSM is too full to fit + if (newTotalDebtInRad >= debtCeilingInRad) { + return 0; + } + + uint256 feeInWad = takerTokenAmountInWad.safeMul(psm.tin()).safeDiv(WAD); + uint256 makerTokenAmountInWad = takerTokenAmountInWad.safeSub(feeInWad); + + return makerTokenAmountInWad; + } else if (makerToken == psmInfo.gemTokenAddress) { + // Simulate buyGem + // Buying USDC from the PSM, decreasing the total debt + // Selling DAI for USDC, already in 18 decimals [wad] + uint256 takerTokenAmountInWad = takerTokenAmount; + if (takerTokenAmountInWad > totalDebtInWad) { + return 0; + } + uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY); + + // PSM is empty, not enough USDC to buy from it + if (newTotalDebtInRad <= debtFloorInRad) { + return 0; + } + + uint256 feeDivisorInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout; + uint256 makerTokenAmountInGemTokenBaseUnits = takerTokenAmountInWad.safeMul(gemTokenBaseUnit).safeDiv(feeDivisorInWad); + + return makerTokenAmountInGemTokenBaseUnits; + } + + return 0; + } + + function _samplePSMBuy(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 makerTokenAmount, IPSM psm, IVAT vat) + private + view + returns (uint256) + { + (uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier); + + if (takerToken == psmInfo.gemTokenAddress) { + // Simulate sellGem + // Selling USDC to the PSM, increasing the total debt + uint256 makerTokenAmountInWad = makerTokenAmount; + uint256 feeDivisorInWad = WAD.safeSub(psm.tin()); // eg. 0.999 * 10 ** 18 with 0.1% tin; + uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(WAD).safeDiv(feeDivisorInWad); + uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY); + + // PSM is too full to fit + if (newTotalDebtInRad >= debtCeilingInRad) { + return 0; + } + + uint256 takerTokenAmountInGemInGemBaseUnits = (takerTokenAmountInWad.safeDiv(1e12)).safeAdd(1); // Add 1 to deal with cut off decimals converting to lower decimals + + return takerTokenAmountInGemInGemBaseUnits; + } else if (makerToken == psmInfo.gemTokenAddress) { + // Simulate buyGem + // Buying USDC from the PSM, decreasing the total debt + uint256 makerTokenAmountInWad = makerTokenAmount.safeMul(1e12); + uint256 feeMultiplierInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout; + uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(feeMultiplierInWad).safeDiv(WAD); + if (takerTokenAmountInWad > totalDebtInWad) { + return 0; + } + uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY); + + // PSM is empty, not enough USDC to buy + if (newTotalDebtInRad <= debtFloorInRad) { + return 0; + } + + + return takerTokenAmountInWad; + } + + return 0; + } + +} diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index ffa3abe899..e8f23b93ae 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -38,7 +38,7 @@ "config": { "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json", + "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json", "postpublish": { "assets": [] } @@ -77,6 +77,7 @@ "@ethersproject/address": "^5.0.1", "@ethersproject/contracts": "^5.0.1", "@ethersproject/providers": "^5.0.4", + "@ethersproject/strings": "^5.0.10", "axios": "^0.21.1", "axios-mock-adapter": "^1.19.0", "cream-sor": "^0.3.3", diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index 7172e7727c..780571ff44 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -1,6 +1,7 @@ import { ChainId } from '@0x/contract-addresses'; import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; import { BigNumber } from '@0x/utils'; +import { formatBytes32String } from '@ethersproject/strings'; import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder'; @@ -18,7 +19,9 @@ import { KyberSamplerOpts, LiquidityProviderFillData, LiquidityProviderRegistry, + MakerPsmFillData, MultiHopFillData, + PsmInfo, TokenAdjacencyGraph, UniswapV2FillData, } from './types'; @@ -78,6 +81,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.LiquidityProvider, ERC20BridgeSource.CryptoCom, ERC20BridgeSource.Linkswap, + ERC20BridgeSource.MakerPsm, ]), [ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]), [ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]), @@ -127,6 +131,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.LiquidityProvider, ERC20BridgeSource.CryptoCom, ERC20BridgeSource.Linkswap, + ERC20BridgeSource.MakerPsm, ]), [ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]), [ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]), @@ -698,12 +703,28 @@ export const MAX_DODOV2_POOLS_QUERIED = 3; export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId( { - [ChainId.Mainnet]: '0x7a6F6a048fE2Dc1397ABa0bf7879d3eacF371C53', - [ChainId.Ropsten]: '0xAa213dcDFbF104e08cbAeC3d1628eD197553AfCc', + [ChainId.Mainnet]: '0x561b94454b65614ae3db0897b74303f4acf7cc75', + [ChainId.Ropsten]: '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2', }, NULL_ADDRESS, ); +export const MAKER_PSM_INFO_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: { + // Currently only USDC is supported + gemTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + ilkIdentifier: formatBytes32String('PSM-USDC-A'), + psmAddress: '0x89b78cfa322f6c5de0abceecab66aee45393cc5a', + }, + }, + { + gemTokenAddress: NULL_ADDRESS, + ilkIdentifier: NULL_BYTES, + psmAddress: NULL_ADDRESS, + }, +); + export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId( { [ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889', @@ -776,7 +797,7 @@ export const BAKERYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( export const DEFAULT_GAS_SCHEDULE: Required = { [ERC20BridgeSource.Native]: fillData => { // TODO jacob re-order imports so there is no circular rependency with SignedNativeOrder - const nativeFillData = fillData as ({ type: FillQuoteTransformerOrderType }); + const nativeFillData = fillData as { type: FillQuoteTransformerOrderType }; return nativeFillData && nativeFillData.type === FillQuoteTransformerOrderType.Limit ? PROTOCOL_FEE_MULTIPLIER.plus(100e3).toNumber() : // TODO jacob revisit wth v4 LimitOrders @@ -863,6 +884,16 @@ export const DEFAULT_GAS_SCHEDULE: Required = { [ERC20BridgeSource.Balancer]: () => 120e3, [ERC20BridgeSource.Cream]: () => 120e3, [ERC20BridgeSource.MStable]: () => 700e3, + [ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => { + const psmFillData = fillData as MakerPsmFillData; + + // TODO(kimpers): update with more accurate numbers after allowances have been set + if (psmFillData.takerToken === psmFillData.gemTokenAddress) { + return psmFillData.isSellOperation ? 389e3 : 423e3; + } else { + return 444e3; + } + }, [ERC20BridgeSource.Mooniswap]: () => 130e3, [ERC20BridgeSource.Swerve]: () => 150e3, [ERC20BridgeSource.Nerve]: () => 150e3, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts index 54ed12fb06..2d3e26d39b 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -16,6 +16,7 @@ import { GenericRouterFillData, KyberFillData, LiquidityProviderFillData, + MakerPsmFillData, MooniswapFillData, MultiHopFillData, NativeCollapsedFill, @@ -91,6 +92,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s case ERC20BridgeSource.LiquidityProvider: // "LiquidityProvider" is too long to encode (17 characters). return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP'); + case ERC20BridgeSource.MakerPsm: + return encodeBridgeSourceId(BridgeProtocol.MakerPsm, 'MakerPsm'); case ERC20BridgeSource.Mooniswap: return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap'); case ERC20BridgeSource.MStable: @@ -216,6 +219,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder const mStableFillData = (order as OptimizedMarketBridgeOrder).fillData; bridgeData = encoder.encode([mStableFillData.router]); break; + case ERC20BridgeSource.MakerPsm: + const psmFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([psmFillData.psmAddress, psmFillData.gemTokenAddress]); + break; default: throw new Error(AggregationError.NoBridgeForSource); } @@ -255,6 +262,10 @@ const curveEncoder = AbiEncoder.create([ { name: 'fromTokenIdx', type: 'int128' }, { name: 'toTokenIdx', type: 'int128' }, ]); +const makerPsmEncoder = AbiEncoder.create([ + { name: 'psmAddress', type: 'address' }, + { name: 'gemTokenAddress', type: 'address' }, +]); const routerAddressPathEncoder = AbiEncoder.create('(address,address[])'); const tokenAddressEncoder = AbiEncoder.create([{ name: 'tokenAddress', type: 'address' }]); @@ -302,6 +313,8 @@ export const BRIDGE_ENCODERS: { [ERC20BridgeSource.Balancer]: poolEncoder, [ERC20BridgeSource.Cream]: poolEncoder, [ERC20BridgeSource.Uniswap]: poolEncoder, + // Custom integrations + [ERC20BridgeSource.MakerPsm]: makerPsmEncoder, // BSC [ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder, [ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index f727e498b9..c6423afb63 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -25,6 +25,7 @@ import { KYBER_CONFIG_BY_CHAIN_ID, LINKSWAP_ROUTER_BY_CHAIN_ID, LIQUIDITY_PROVIDER_REGISTRY, + MAKER_PSM_INFO_BY_CHAIN_ID, MAX_UINT256, MOONISWAP_REGISTRIES_BY_CHAIN_ID, MSTABLE_ROUTER_BY_CHAIN_ID, @@ -57,8 +58,10 @@ import { KyberSamplerOpts, LiquidityProviderFillData, LiquidityProviderRegistry, + MakerPsmFillData, MooniswapFillData, MultiHopFillData, + PsmInfo, ShellFillData, SourceQuoteOperation, TokenAdjacencyGraph, @@ -857,6 +860,46 @@ export class SamplerOperations { }); } + public getMakerPsmSellQuotes( + psmInfo: PsmInfo, + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.MakerPsm, + fillData: { + isSellOperation: true, + takerToken, + makerToken, + ...psmInfo, + }, + contract: this._samplerContract, + function: this._samplerContract.sampleSellsFromMakerPsm, + params: [psmInfo, takerToken, makerToken, takerFillAmounts], + }); + } + + public getMakerPsmBuyQuotes( + psmInfo: PsmInfo, + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.MakerPsm, + fillData: { + isSellOperation: false, + takerToken, + makerToken, + ...psmInfo, + }, + contract: this._samplerContract, + function: this._samplerContract.sampleBuysFromMakerPsm, + params: [psmInfo, takerToken, makerToken, makerFillAmounts], + }); + } + public getMedianSellRate( sources: ERC20BridgeSource[], makerToken: string, @@ -1118,6 +1161,12 @@ export class SamplerOperations { ERC20BridgeSource.Linkswap, ), ); + case ERC20BridgeSource.MakerPsm: + const psmInfo = MAKER_PSM_INFO_BY_CHAIN_ID[this.chainId]; + if (!isValidAddress(psmInfo.psmAddress)) { + return []; + } + return this.getMakerPsmSellQuotes(psmInfo, makerToken, takerToken, takerFillAmounts); default: throw new Error(`Unsupported sell sample source: ${source}`); } @@ -1298,6 +1347,12 @@ export class SamplerOperations { ERC20BridgeSource.Linkswap, ), ); + case ERC20BridgeSource.MakerPsm: + const psmInfo = MAKER_PSM_INFO_BY_CHAIN_ID[this.chainId]; + if (!isValidAddress(psmInfo.psmAddress)) { + return []; + } + return this.getMakerPsmBuyQuotes(psmInfo, makerToken, takerToken, makerFillAmounts); default: throw new Error(`Unsupported buy sample source: ${source}`); } diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts index 059a0ef029..f992f95d6c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -47,6 +47,7 @@ export enum ERC20BridgeSource { Balancer = 'Balancer', Cream = 'CREAM', Bancor = 'Bancor', + MakerPsm = 'MakerPsm', MStable = 'mStable', Mooniswap = 'Mooniswap', MultiHop = 'MultiHop', @@ -96,6 +97,15 @@ export interface CurveInfo { metaToken: string | undefined; } +/** + * Configuration for a specific PSM vault + */ +export interface PsmInfo { + psmAddress: string; + ilkIdentifier: string; + gemTokenAddress: string; +} + // Internal `fillData` field for `Fill` objects. export interface FillData {} @@ -165,6 +175,14 @@ export interface MultiHopFillData extends FillData { secondHopSource: SourceQuoteOperation; intermediateToken: string; } + +export interface MakerPsmExtendedData { + isSellOperation: boolean; + takerToken: string; +} + +export type MakerPsmFillData = FillData & MakerPsmExtendedData & PsmInfo; + export interface HopInfo { sourceIndex: BigNumber; returnData: string; diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts index f51646085c..1426472111 100644 --- a/packages/asset-swapper/test/artifacts.ts +++ b/packages/asset-swapper/test/artifacts.ts @@ -29,6 +29,7 @@ import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExc import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json'; import * as KyberSampler from '../test/generated-artifacts/KyberSampler.json'; import * as LiquidityProviderSampler from '../test/generated-artifacts/LiquidityProviderSampler.json'; +import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json'; import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json'; import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json'; import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json'; @@ -55,6 +56,7 @@ export const artifacts = { KyberSampler: KyberSampler as ContractArtifact, LiquidityProviderSampler: LiquidityProviderSampler as ContractArtifact, MStableSampler: MStableSampler as ContractArtifact, + MakerPSMSampler: MakerPSMSampler as ContractArtifact, MooniswapSampler: MooniswapSampler as ContractArtifact, MultiBridgeSampler: MultiBridgeSampler as ContractArtifact, NativeOrderSampler: NativeOrderSampler as ContractArtifact, diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 94bd16bcec..81659ff193 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -67,6 +67,7 @@ const DEFAULT_EXCLUDED = [ ERC20BridgeSource.Linkswap, ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.BakerySwap, + ERC20BridgeSource.MakerPsm, ]; const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources; const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources; @@ -305,6 +306,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Linkswap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.PancakeSwap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.BakerySwap]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.MakerPsm]: _.times(NUM_SAMPLES, () => 0), }; const DEFAULT_RATES: RatesBySource = { @@ -369,6 +371,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Linkswap]: { tokenAddressPath: [] }, [ERC20BridgeSource.Uniswap]: { router: randomAddress() }, [ERC20BridgeSource.Eth2Dai]: { router: randomAddress() }, + [ERC20BridgeSource.MakerPsm]: {}, }; const DEFAULT_OPS = { diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts index d13d62bca6..27d1114273 100644 --- a/packages/asset-swapper/test/wrappers.ts +++ b/packages/asset-swapper/test/wrappers.ts @@ -28,6 +28,7 @@ export * from '../test/generated-wrappers/i_uniswap_v2_router01'; export * from '../test/generated-wrappers/kyber_sampler'; export * from '../test/generated-wrappers/liquidity_provider_sampler'; export * from '../test/generated-wrappers/m_stable_sampler'; +export * from '../test/generated-wrappers/maker_p_s_m_sampler'; export * from '../test/generated-wrappers/mooniswap_sampler'; export * from '../test/generated-wrappers/multi_bridge_sampler'; export * from '../test/generated-wrappers/native_order_sampler'; diff --git a/packages/asset-swapper/tsconfig.json b/packages/asset-swapper/tsconfig.json index 544e02da22..4ef9298c50 100644 --- a/packages/asset-swapper/tsconfig.json +++ b/packages/asset-swapper/tsconfig.json @@ -31,6 +31,7 @@ "test/generated-artifacts/KyberSampler.json", "test/generated-artifacts/LiquidityProviderSampler.json", "test/generated-artifacts/MStableSampler.json", + "test/generated-artifacts/MakerPSMSampler.json", "test/generated-artifacts/MooniswapSampler.json", "test/generated-artifacts/MultiBridgeSampler.json", "test/generated-artifacts/NativeOrderSampler.json", diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index f1b849ae01..d85fd29967 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,6 +1,6 @@ [ { - "version": "6.0.0-bsc.0", + "version": "6.0.0", "changes": [ { "note": "Add BSC chain addresses", @@ -13,6 +13,10 @@ { "note": "Redeployed FQT on BSC", "pr": 181 + }, + { + "note": "Redeployed FQT on mainnet and ropsten", + "pr": 150 } ] }, diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index e4fed7822b..a349df4d26 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -36,7 +36,7 @@ "wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7", "payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e", "affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f", - "fillQuoteTransformer": "0x227e767a9b7517681d1cb6b846aa9e541484c7ab", + "fillQuoteTransformer": "0xb8e40acea68db2a7a2020a3eba2664ba4c3b3e3d", "positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd" } }, @@ -77,7 +77,7 @@ "wethTransformer": "0x05ad19aa3826e0609a19568ffbd1dfe86c6c7184", "payTakerTransformer": "0x6d0ebf2bcd9cc93ec553b60ad201943dcca4e291", "affiliateFeeTransformer": "0x6588256778ca4432fa43983ac685c45efb2379e2", - "fillQuoteTransformer": "0x2088a820787ebbe937a0612ef024f1e1d65f9784", + "fillQuoteTransformer": "0xc0c6fc6911978a65fe3b17391bb30b630bfc637d", "positiveSlippageFeeTransformer": "0x8b332f700fd37e71c5c5b26c4d78b5ca63dd33b2" } }, diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index 06ecd1bb33..19169c9c8a 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -125,6 +125,7 @@ export enum BridgeProtocol { Bancor, CoFiX, Nerve, + MakerPsm, } // tslint:enable: enum-naming diff --git a/yarn.lock b/yarn.lock index 7439095bc5..50f31b94c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -646,7 +646,6 @@ "@0x/abi-gen@^5.4.21": version "5.4.21" resolved "https://registry.yarnpkg.com/@0x/abi-gen/-/abi-gen-5.4.21.tgz#96705962c75e116bd5050784287012ede4607564" - integrity sha512-rJPzWUjCeygMC71HfyGQAEeYtfkv5Mi+cjIOrmWfTQmM4SN+6wBraEWKLJHkcFTZ3lmFjkS/ZfbGhQere6/Y4w== dependencies: "@0x/types" "^3.3.1" "@0x/typescript-typings" "^5.1.6" @@ -712,7 +711,6 @@ "@0x/contracts-gen@^2.0.32": version "2.0.32" resolved "https://registry.yarnpkg.com/@0x/contracts-gen/-/contracts-gen-2.0.32.tgz#e4817730685acc0047157e55d58a6cab87596799" - integrity sha512-FkdCn9L48u6M2WyyQOWuDFvmqvYIfv1O47Hlwf8MGVJaxGmhDhqJsCr4HlmE4Jg84dK7I7xIeh0MMs8jPNpRUA== dependencies: "@0x/sol-compiler" "^4.6.1" "@0x/sol-resolver" "^3.1.6" @@ -805,7 +803,6 @@ "@0x/monorepo-scripts@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@0x/monorepo-scripts/-/monorepo-scripts-3.1.1.tgz#2ded9ef1d4885bbf106612794a331e29f391733a" - integrity sha512-thovxmZMivSeESlY+FXLxkf4wrmLe40ZjfGOcJaPhe29me03ddgEtVgToi+k7ZGfTeRWm5gmvlisHak2D6TFwA== dependencies: "@0x/types" "^3.3.1" "@0x/utils" "^6.2.0" @@ -847,7 +844,6 @@ "@0x/sol-compiler@^4.6.1": version "4.6.1" resolved "https://registry.yarnpkg.com/@0x/sol-compiler/-/sol-compiler-4.6.1.tgz#d1f9c1547605ddbd4ad9cc6ebed60b91f5e1ce3d" - integrity sha512-iEFcrn+5OSNCD2yr0WjF9h0+0J/0DNNpEuc1yK5OAD6F4X6U/YChYohSx/odWNhYfJODW+dj1SABWxZfybgIwQ== dependencies: "@0x/assert" "^3.0.21" "@0x/json-schemas" "^5.4.1" @@ -876,7 +872,6 @@ "@0x/sol-coverage@^4.0.31": version "4.0.31" resolved "https://registry.yarnpkg.com/@0x/sol-coverage/-/sol-coverage-4.0.31.tgz#ec958d892dce3773059b925e442e21d7c4f9b104" - integrity sha512-XcD2MntdpjDAV2KWmZyD6T3dI2sgaXwBXVa/cRtlC0cUAJcXp+tblwvtt1VOxc2J6QFCaTG4UTs9oS2nGbxwbA== dependencies: "@0x/sol-tracing-utils" "^7.1.21" "@0x/subproviders" "^6.4.1" @@ -891,7 +886,6 @@ "@0x/sol-profiler@^4.1.21": version "4.1.21" resolved "https://registry.yarnpkg.com/@0x/sol-profiler/-/sol-profiler-4.1.21.tgz#76f4860b3e0c73b8e11ac56ce25e52164cee1476" - integrity sha512-JFDUhFrWJP1WqVIKK1w/2nU/s8g91xNeZW1l4ZydEGhZPsRNgQdk8rFBwGqxPeqMqUY7T+Xm2Y/IVjYvE+Ck3w== dependencies: "@0x/sol-tracing-utils" "^7.1.21" "@0x/subproviders" "^6.4.1" @@ -915,7 +909,6 @@ "@0x/sol-trace@^3.0.31": version "3.0.31" resolved "https://registry.yarnpkg.com/@0x/sol-trace/-/sol-trace-3.0.31.tgz#9ed79139c81ad8953a9947019053ead5a20f9408" - integrity sha512-wQjdVc/IOcyzIfDVfurzFQuhkZlnejwNVNWn+9S6uYXteIqgHcJv0WqD5dZNFxcNG4Jn7tXxHJ/AXxXDygM+cw== dependencies: "@0x/sol-tracing-utils" "^7.1.21" "@0x/subproviders" "^6.4.1" @@ -931,7 +924,6 @@ "@0x/sol-tracing-utils@^7.1.21": version "7.1.21" resolved "https://registry.yarnpkg.com/@0x/sol-tracing-utils/-/sol-tracing-utils-7.1.21.tgz#9ffb9f51fdc6bcf0be8f95841563a6ccccbac474" - integrity sha512-Y0aYJD1h+0IBZe7A9FKorhf7qtWawftahV61FjPCOaHfvQNfVeza7wm9hdMYOb+B2hbFCDFo06GfZaINw7VFVA== dependencies: "@0x/dev-utils" "^4.2.1" "@0x/sol-compiler" "^4.6.1" @@ -1311,18 +1303,38 @@ "@ethersproject/logger" "^5.0.5" bn.js "^4.4.0" +"@ethersproject/bignumber@^5.0.13": + version "5.0.15" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.15.tgz#b089b3f1e0381338d764ac1c10512f0c93b184ed" + dependencies: + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + bn.js "^4.4.0" + "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4": version "5.0.5" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.5.tgz#688b70000e550de0c97a151a21f15b87d7f97d7c" dependencies: "@ethersproject/logger" "^5.0.5" +"@ethersproject/bytes@^5.0.9": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.11.tgz#21118e75b1d00db068984c15530e316021101276" + dependencies: + "@ethersproject/logger" "^5.0.8" + "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4": version "5.0.5" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.5.tgz#0ed19b002e8404bdf6d135234dc86a7d9bcf9b71" dependencies: "@ethersproject/bignumber" "^5.0.7" +"@ethersproject/constants@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.10.tgz#eb0c604fbc44c53ba9641eed31a1d0c9e1ebcadc" + dependencies: + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/contracts@^5.0.1": version "5.0.5" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.5.tgz#64831a341ec8ca225e83ff3e9437c26b970fd5d7" @@ -1361,6 +1373,10 @@ version "5.0.6" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.6.tgz#faa484203e86e08be9e07fef826afeef7183fe88" +"@ethersproject/logger@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.10.tgz#fd884688b3143253e0356ef92d5f22d109d2e026" + "@ethersproject/networks@^5.0.3": version "5.0.4" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.4.tgz#6d320a5e15a0cda804f5da88be0ba846156f6eec" @@ -1436,6 +1452,14 @@ "@ethersproject/constants" "^5.0.4" "@ethersproject/logger" "^5.0.5" +"@ethersproject/strings@^5.0.10": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.10.tgz#ddce1e9724f4ac4f3f67e0cac0b48748e964bfdb" + dependencies: + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.5": version "5.0.6" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.6.tgz#b8b27938be6e9ed671dbdd35fe98af8b14d0df7c" @@ -2995,7 +3019,6 @@ aws4@^1.8.0: axios-mock-adapter@^1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz#9d72e321a6c5418e1eff067aa99761a86c5188a4" - integrity sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg== dependencies: fast-deep-equal "^3.1.3" is-buffer "^2.0.3" @@ -3003,7 +3026,6 @@ axios-mock-adapter@^1.19.0: axios@^0.21.1: version "0.21.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== dependencies: follow-redirects "^1.10.0" @@ -6359,7 +6381,6 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: follow-redirects@^1.10.0: version "1.13.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== for-each@~0.3.3: version "0.3.3" @@ -11692,7 +11713,6 @@ strip-bom@^3.0.0: strip-comments@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" - integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== strip-dirs@^2.0.0: version "2.1.0"