Add Synthetix Atomic Swap support [TKR-324] (#518)

* Implement SyntehtixSampler

* Implement MixinSynthetix

* Update transformer_utils.ts

* Add Synthetix mainnet support

* Add Synthetix optimism support

* Fine-tune gas schedule

* Pass read proxy dynamically to SynthetixSampler

* Pass read proxy dynamically to SynthetixMixin

* Fetch Synthetix address from sampler

* Pass Synthetix address directly to MixinSynthetix

* Update CHANGELOG.json
This commit is contained in:
Kyu 2022-07-25 21:17:27 -07:00 committed by GitHub
parent 661cc4669d
commit a5babb9a34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 551 additions and 11 deletions

View File

@ -1,4 +1,13 @@
[ [
{
"version": "0.36.0",
"changes": [
{
"note": "Add Synthetix support in Ethereum and Optimism bridge adapters",
"pr": 518
}
]
},
{ {
"version": "0.35.0", "version": "0.35.0",
"changes": [ "changes": [

View File

@ -57,4 +57,5 @@ library BridgeProtocols {
uint128 internal constant PLATYPUS = 27; uint128 internal constant PLATYPUS = 27;
uint128 internal constant BANCORV3 = 28; uint128 internal constant BANCORV3 = 28;
uint128 internal constant VELODROME = 29; uint128 internal constant VELODROME = 29;
uint128 internal constant SYNTHETIX = 30;
} }

View File

@ -41,6 +41,7 @@ import "./mixins/MixinMooniswap.sol";
import "./mixins/MixinMStable.sol"; import "./mixins/MixinMStable.sol";
import "./mixins/MixinNerve.sol"; import "./mixins/MixinNerve.sol";
import "./mixins/MixinShell.sol"; import "./mixins/MixinShell.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswap.sol"; import "./mixins/MixinUniswap.sol";
import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol"; import "./mixins/MixinUniswapV3.sol";
@ -67,6 +68,7 @@ contract EthereumBridgeAdapter is
MixinMStable, MixinMStable,
MixinNerve, MixinNerve,
MixinShell, MixinShell,
MixinSynthetix,
MixinUniswap, MixinUniswap,
MixinUniswapV2, MixinUniswapV2,
MixinUniswapV3, MixinUniswapV3,
@ -260,6 +262,12 @@ contract EthereumBridgeAdapter is
sellAmount, sellAmount,
order.bridgeData order.bridgeData
); );
} else if (protocolId == BridgeProtocols.SYNTHETIX) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeSynthetix(
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) { } else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); } if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge( boughtAmount = _tradeZeroExBridge(

View File

@ -25,6 +25,7 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinCurve.sol"; import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol"; import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinNerve.sol"; import "./mixins/MixinNerve.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswapV3.sol"; import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinVelodrome.sol"; import "./mixins/MixinVelodrome.sol";
import "./mixins/MixinZeroExBridge.sol"; import "./mixins/MixinZeroExBridge.sol";
@ -34,6 +35,7 @@ contract OptimismBridgeAdapter is
MixinCurve, MixinCurve,
MixinCurveV2, MixinCurveV2,
MixinNerve, MixinNerve,
MixinSynthetix,
MixinUniswapV3, MixinUniswapV3,
MixinVelodrome, MixinVelodrome,
MixinZeroExBridge MixinZeroExBridge
@ -93,6 +95,12 @@ contract OptimismBridgeAdapter is
sellAmount, sellAmount,
order.bridgeData order.bridgeData
); );
} else if (protocolId == BridgeProtocols.SYNTHETIX) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeSynthetix(
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) { } else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); } if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge( boughtAmount = _tradeZeroExBridge(

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2022 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
interface ISynthetix {
// Ethereum Mainnet
function exchangeAtomically(
bytes32 sourceCurrencyKey,
uint256 sourceAmount,
bytes32 destinationCurrencyKey,
bytes32 trackingCode,
uint256 minAmount
) external returns (uint256 amountReceived);
// Optimism
function exchangeWithTracking(
bytes32 sourceCurrencyKey,
uint256 sourceAmount,
bytes32 destinationCurrencyKey,
address rewardAddress,
bytes32 trackingCode
) external returns (uint256 amountReceived);
}
contract MixinSynthetix {
address private constant rewardAddress =
0x5C80239D97E1eB216b5c3D8fBa5DE5Be5d38e4C9;
bytes32 constant trackingCode =
0x3058000000000000000000000000000000000000000000000000000000000000;
function _tradeSynthetix(uint256 sellAmount, bytes memory bridgeData)
public
returns (uint256 boughtAmount)
{
(
ISynthetix synthetix,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
) = abi.decode(
bridgeData,
(ISynthetix, bytes32, bytes32)
);
boughtAmount = exchange(
synthetix,
sourceCurrencyKey,
destinationCurrencyKey,
sellAmount
);
}
function exchange(
ISynthetix synthetix,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey,
uint256 sellAmount
) internal returns (uint256 boughtAmount) {
uint256 chainId;
assembly {
chainId := chainid()
}
if (chainId == 1) {
boughtAmount = synthetix.exchangeAtomically(
sourceCurrencyKey,
sellAmount,
destinationCurrencyKey,
trackingCode,
0
);
} else {
boughtAmount = synthetix.exchangeWithTracking(
sourceCurrencyKey,
sellAmount,
destinationCurrencyKey,
rewardAddress,
trackingCode
);
}
}
}

View File

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

View File

@ -124,6 +124,7 @@ import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json'; import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
import * as MixinPlatypus from '../test/generated-artifacts/MixinPlatypus.json'; import * as MixinPlatypus from '../test/generated-artifacts/MixinPlatypus.json';
import * as MixinShell from '../test/generated-artifacts/MixinShell.json'; import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
import * as MixinSynthetix from '../test/generated-artifacts/MixinSynthetix.json';
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json'; import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json'; import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json'; import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json';
@ -347,6 +348,7 @@ export const artifacts = {
MixinNerve: MixinNerve as ContractArtifact, MixinNerve: MixinNerve as ContractArtifact,
MixinPlatypus: MixinPlatypus as ContractArtifact, MixinPlatypus: MixinPlatypus as ContractArtifact,
MixinShell: MixinShell as ContractArtifact, MixinShell: MixinShell as ContractArtifact,
MixinSynthetix: MixinSynthetix as ContractArtifact,
MixinUniswap: MixinUniswap as ContractArtifact, MixinUniswap: MixinUniswap as ContractArtifact,
MixinUniswapV2: MixinUniswapV2 as ContractArtifact, MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
MixinUniswapV3: MixinUniswapV3 as ContractArtifact, MixinUniswapV3: MixinUniswapV3 as ContractArtifact,

View File

@ -122,6 +122,7 @@ export * from '../test/generated-wrappers/mixin_mooniswap';
export * from '../test/generated-wrappers/mixin_nerve'; export * from '../test/generated-wrappers/mixin_nerve';
export * from '../test/generated-wrappers/mixin_platypus'; export * from '../test/generated-wrappers/mixin_platypus';
export * from '../test/generated-wrappers/mixin_shell'; export * from '../test/generated-wrappers/mixin_shell';
export * from '../test/generated-wrappers/mixin_synthetix';
export * from '../test/generated-wrappers/mixin_uniswap'; export * from '../test/generated-wrappers/mixin_uniswap';
export * from '../test/generated-wrappers/mixin_uniswap_v2'; export * from '../test/generated-wrappers/mixin_uniswap_v2';
export * from '../test/generated-wrappers/mixin_uniswap_v3'; export * from '../test/generated-wrappers/mixin_uniswap_v3';

View File

@ -161,6 +161,7 @@
"test/generated-artifacts/MixinNerve.json", "test/generated-artifacts/MixinNerve.json",
"test/generated-artifacts/MixinPlatypus.json", "test/generated-artifacts/MixinPlatypus.json",
"test/generated-artifacts/MixinShell.json", "test/generated-artifacts/MixinShell.json",
"test/generated-artifacts/MixinSynthetix.json",
"test/generated-artifacts/MixinUniswap.json", "test/generated-artifacts/MixinUniswap.json",
"test/generated-artifacts/MixinUniswapV2.json", "test/generated-artifacts/MixinUniswapV2.json",
"test/generated-artifacts/MixinUniswapV3.json", "test/generated-artifacts/MixinUniswapV3.json",

View File

@ -5,6 +5,10 @@
{ {
"note": "Refactor `TokenAdjacency` and `TokenAdjacencyBuilder`", "note": "Refactor `TokenAdjacency` and `TokenAdjacencyBuilder`",
"pr": 517 "pr": 517
},
{
"note": "Add Synthetix support`",
"pr": 518
} }
] ]
}, },

View File

@ -39,6 +39,7 @@ import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol"; import "./NativeOrderSampler.sol";
import "./PlatypusSampler.sol"; import "./PlatypusSampler.sol";
import "./ShellSampler.sol"; import "./ShellSampler.sol";
import "./SynthetixSampler.sol";
import "./TwoHopSampler.sol"; import "./TwoHopSampler.sol";
import "./UniswapSampler.sol"; import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol"; import "./UniswapV2Sampler.sol";
@ -67,6 +68,7 @@ contract ERC20BridgeSampler is
NativeOrderSampler, NativeOrderSampler,
PlatypusSampler, PlatypusSampler,
ShellSampler, ShellSampler,
SynthetixSampler,
TwoHopSampler, TwoHopSampler,
UniswapSampler, UniswapSampler,
UniswapV2Sampler, UniswapV2Sampler,

View File

@ -0,0 +1,173 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2022 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
interface IReadProxyAddressResolver {
function target() external view returns (address);
}
interface IAddressResolver {
function getAddress(bytes32 name) external view returns (address);
}
interface IExchanger {
// Ethereum Mainnet
function getAmountsForAtomicExchange(
uint256 sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint256 amountReceived,
uint256 fee,
uint256 exchangeFeeRate
);
// Optimism
function getAmountsForExchange(
uint256 sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint256 amountReceived,
uint256 fee,
uint256 exchangeFeeRate
);
}
contract SynthetixSampler {
/// @dev Sample sell quotes from Synthetix Atomic Swap.
/// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell).
/// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order).
/// @return synthetix Synthetix address.
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
function sampleSellsFromSynthetix(
IReadProxyAddressResolver readProxy,
bytes32 takerTokenSymbol,
bytes32 makerTokenSymbol,
uint256[] memory takerTokenAmounts
) public view returns (address synthetix, uint256[] memory makerTokenAmounts) {
synthetix = getSynthetixAddress(readProxy);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
if (numSamples == 0) {
return (synthetix, makerTokenAmounts);
}
makerTokenAmounts[0] = exchange(
readProxy,
takerTokenAmounts[0],
takerTokenSymbol,
makerTokenSymbol
);
// Synthetix atomic swap has a fixed rate. Calculate the rest based on the first value (and save gas).
for (uint256 i = 1; i < numSamples; i++) {
makerTokenAmounts[i] =
(makerTokenAmounts[0] * takerTokenAmounts[i]) /
takerTokenAmounts[0];
}
}
/// @dev Sample buy quotes from Synthetix Atomic Swap.
/// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell).
/// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample (sorted in ascending order).
/// @return synthetix Synthetix address.
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
function sampleBuysFromSynthetix(
IReadProxyAddressResolver readProxy,
bytes32 takerTokenSymbol,
bytes32 makerTokenSymbol,
uint256[] memory makerTokenAmounts
) public view returns (address synthetix, uint256[] memory takerTokenAmounts) {
synthetix = getSynthetixAddress(readProxy);
// Since Synthetix atomic have a fixed rate, we can pick any reasonablely size takerTokenAmount (fixed to 1 ether here) and calculate the rest.
uint256 amountReceivedForEther = exchange(
readProxy,
1 ether,
takerTokenSymbol,
makerTokenSymbol
);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] =
(1 ether * makerTokenAmounts[i]) /
amountReceivedForEther;
}
}
function exchange(
IReadProxyAddressResolver readProxy,
uint256 sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
) private view returns (uint256 amountReceived) {
IExchanger exchanger = getExchanger(readProxy);
uint256 chainId;
assembly {
chainId := chainid()
}
if (chainId == 1) {
(amountReceived, , ) = exchanger.getAmountsForAtomicExchange(
sourceAmount,
sourceCurrencyKey,
destinationCurrencyKey
);
} else {
(amountReceived, , ) = exchanger.getAmountsForExchange(
sourceAmount,
sourceCurrencyKey,
destinationCurrencyKey
);
}
}
function getSynthetixAddress(IReadProxyAddressResolver readProxy)
private
view
returns (address)
{
return IAddressResolver(readProxy.target()).getAddress("Synthetix");
}
function getExchanger(IReadProxyAddressResolver readProxy)
private
view
returns (IExchanger)
{
return
IExchanger(
IAddressResolver(readProxy.target()).getAddress("Exchanger")
);
}
}

View File

@ -40,7 +40,7 @@
"config": { "config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker", "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).json", "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SynthetixSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).json",
"postpublish": { "postpublish": {
"assets": [] "assets": []
} }

View File

@ -1,7 +1,7 @@
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import { formatBytes32String } from '@ethersproject/strings'; import { formatBytes32String, parseBytes32String } from '@ethersproject/strings';
import { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from '../token_adjacency_graph'; import { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from '../token_adjacency_graph';
@ -32,6 +32,7 @@ import {
MultiHopFillData, MultiHopFillData,
PlatypusInfo, PlatypusInfo,
PsmInfo, PsmInfo,
SynthetixFillData,
UniswapV2FillData, UniswapV2FillData,
UniswapV3FillData, UniswapV3FillData,
} from './types'; } from './types';
@ -107,6 +108,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2, ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap, ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Synapse, ERC20BridgeSource.Synapse,
ERC20BridgeSource.Synthetix,
// TODO: enable after FQT has been redeployed on Ethereum mainnet // TODO: enable after FQT has been redeployed on Ethereum mainnet
// ERC20BridgeSource.AaveV2, // ERC20BridgeSource.AaveV2,
// ERC20BridgeSource.Compound, // ERC20BridgeSource.Compound,
@ -216,6 +218,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2, ERC20BridgeSource.CurveV2,
ERC20BridgeSource.MultiHop, ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Velodrome, ERC20BridgeSource.Velodrome,
ERC20BridgeSource.Synthetix,
]), ]),
}, },
new SourceFilters([]), new SourceFilters([]),
@ -255,6 +258,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2, ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap, ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Synapse, ERC20BridgeSource.Synapse,
ERC20BridgeSource.Synthetix,
// TODO: enable after FQT has been redeployed on Ethereum mainnet // TODO: enable after FQT has been redeployed on Ethereum mainnet
// ERC20BridgeSource.AaveV2, // ERC20BridgeSource.AaveV2,
// ERC20BridgeSource.Compound, // ERC20BridgeSource.Compound,
@ -364,6 +368,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2, ERC20BridgeSource.CurveV2,
ERC20BridgeSource.MultiHop, ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Velodrome, ERC20BridgeSource.Velodrome,
ERC20BridgeSource.Synthetix,
]), ]),
}, },
new SourceFilters([]), new SourceFilters([]),
@ -460,6 +465,11 @@ export const MAINNET_TOKENS = {
EURS: '0xdb25f211ab05b1c97d595516f45794528a807ad8', EURS: '0xdb25f211ab05b1c97d595516f45794528a807ad8',
sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620', sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620',
sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb', sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb',
sJPY: '0xf6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d',
sGBP: '0x97fe22e7341a0cd8db6f6c021a24dc8f4dad855f',
sAUD: '0xf48e200eaf9906362bb1442fca31e0835773b8b4',
sKRW: '0x269895a3df4d73b077fc823dd6da1b95f72aaf9b',
sCHF: '0x0f83287ff768d1c1e17a42f44d644d7f22e8ee1d',
stETH: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', stETH: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84',
wstETH: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', wstETH: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0',
LINK: '0x514910771af9ca656af840dff83e8264ecf986ca', LINK: '0x514910771af9ca656af840dff83e8264ecf986ca',
@ -642,10 +652,6 @@ export const FANTOM_TOKENS = {
gMIM: '0xc664fc7b8487a3e10824cda768c1d239f2403bbe', gMIM: '0xc664fc7b8487a3e10824cda768c1d239f2403bbe',
}; };
export const GEIST_FANTOM_POOLS = {
lendingPool: '0x9fad24f572045c7869117160a571b2e50b10d068',
};
export const OPTIMISM_TOKENS = { export const OPTIMISM_TOKENS = {
WETH: '0x4200000000000000000000000000000000000006', WETH: '0x4200000000000000000000000000000000000006',
USDC: '0x7f5c764cbc14f9669b88837ca1490cca17c31607', USDC: '0x7f5c764cbc14f9669b88837ca1490cca17c31607',
@ -654,6 +660,21 @@ export const OPTIMISM_TOKENS = {
WBTC: '0x68f180fcce6836688e9084f035309e29bf0a2095', WBTC: '0x68f180fcce6836688e9084f035309e29bf0a2095',
nETH: '0x809dc529f07651bd43a172e8db6f4a7a0d771036', nETH: '0x809dc529f07651bd43a172e8db6f4a7a0d771036',
sWETH: '0x121ab82b49b2bc4c7901ca46b8277962b4350204', sWETH: '0x121ab82b49b2bc4c7901ca46b8277962b4350204',
// Synthetix synths:
sAAVE: '0x00b8d5a5e1ac97cb4341c4bc4367443c8776e8d9',
sAVAX: '0xb2b42b231c68cbb0b4bf2ffebf57782fd97d3da4',
sBTC: '0x298b9b95708152ff6968aafd889c6586e9169f1d',
sETH: '0xe405de8f52ba7559f9df3c368500b6e6ae6cee49',
sEUR: '0xfbc4198702e81ae77c06d58f81b629bdf36f0a71',
sLINK: '0xc5db22719a06418028a40a9b5e9a7c02959d0d08',
sMATIC: '0x81ddfac111913d3d5218dea999216323b7cd6356',
sSOL: '0x8b2f7ae8ca8ee8428b6d76de88326bb413db2766',
sUNI: '0xf5a6115aa582fd1beea22bc93b7dc7a785f60d03',
sUSD: '0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9',
};
export const GEIST_FANTOM_POOLS = {
lendingPool: '0x9fad24f572045c7869117160a571b2e50b10d068',
}; };
export const CURVE_POOLS = { export const CURVE_POOLS = {
@ -947,8 +968,19 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
builder.addBidirectional(MAINNET_TOKENS.OHMV2, MAINNET_TOKENS.BTRFLY); builder.addBidirectional(MAINNET_TOKENS.OHMV2, MAINNET_TOKENS.BTRFLY);
// Lido // Lido
builder.addBidirectional(MAINNET_TOKENS.stETH, MAINNET_TOKENS.wstETH); builder.addBidirectional(MAINNET_TOKENS.stETH, MAINNET_TOKENS.wstETH);
// Synthetix Atomic Swap
builder.addCompleteSubgraph([
MAINNET_TOKENS.sBTC,
MAINNET_TOKENS.sETH,
MAINNET_TOKENS.sUSD,
MAINNET_TOKENS.sEUR,
MAINNET_TOKENS.sJPY,
MAINNET_TOKENS.sGBP,
MAINNET_TOKENS.sAUD,
MAINNET_TOKENS.sKRW,
MAINNET_TOKENS.sCHF,
]);
}) })
// Build
.build(), .build(),
[ChainId.BSC]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC]).build(), [ChainId.BSC]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC]).build(),
[ChainId.Polygon]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon]) [ChainId.Polygon]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon])
@ -968,9 +1000,23 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Fantom], DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Fantom],
).build(), ).build(),
[ChainId.Celo]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Celo]).build(), [ChainId.Celo]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Celo]).build(),
[ChainId.Optimism]: new TokenAdjacencyGraphBuilder( [ChainId.Optimism]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Optimism])
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Optimism], .tap(builder => {
).build(), // Synthetix Atomic Swap
builder.addCompleteSubgraph([
OPTIMISM_TOKENS.sAAVE,
OPTIMISM_TOKENS.sAVAX,
OPTIMISM_TOKENS.sBTC,
OPTIMISM_TOKENS.sETH,
OPTIMISM_TOKENS.sEUR,
OPTIMISM_TOKENS.sLINK,
OPTIMISM_TOKENS.sMATIC,
OPTIMISM_TOKENS.sSOL,
OPTIMISM_TOKENS.sUNI,
OPTIMISM_TOKENS.sUSD,
]);
})
.build(),
}, },
TokenAdjacencyGraph.getEmptyGraph(), TokenAdjacencyGraph.getEmptyGraph(),
); );
@ -2365,6 +2411,47 @@ export const VELODROME_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
NULL_ADDRESS, NULL_ADDRESS,
); );
export const SYNTHETIX_READ_PROXY_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x4e3b31eb0e5cb73641ee1e65e7dcefe520ba3ef2',
[ChainId.Optimism]: '0x1cb059b7e74fd21665968c908806143e744d5f30',
},
NULL_ADDRESS,
);
export const SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID = valueByChainId<Map<string, string>>(
{
// There is no easy way to find out what synths are supported on mainnet.
// The below list is based on https://sips.synthetix.io/sccp/sccp-190.
[ChainId.Mainnet]: new Map([
[MAINNET_TOKENS.sAUD, 'sAUD'],
[MAINNET_TOKENS.sBTC, 'sBTC'],
[MAINNET_TOKENS.sCHF, 'sCHF'],
[MAINNET_TOKENS.sETH, 'sETH'],
[MAINNET_TOKENS.sEUR, 'sEUR'],
[MAINNET_TOKENS.sGBP, 'sGBP'],
[MAINNET_TOKENS.sJPY, 'sJPY'],
[MAINNET_TOKENS.sKRW, 'sKRW'],
[MAINNET_TOKENS.sUSD, 'sUSD'],
]),
// Supported assets can be find through SynthUtil::synthsRates.
// Low liquidity tokens can be excluded.
[ChainId.Optimism]: new Map([
[OPTIMISM_TOKENS.sAAVE, 'sAAVE'],
[OPTIMISM_TOKENS.sAVAX, 'sAVAX'],
[OPTIMISM_TOKENS.sBTC, 'sBTC'],
[OPTIMISM_TOKENS.sETH, 'sETH'],
[OPTIMISM_TOKENS.sEUR, 'sEUR'],
[OPTIMISM_TOKENS.sLINK, 'sLINK'],
[OPTIMISM_TOKENS.sMATIC, 'sMATIC'],
[OPTIMISM_TOKENS.sSOL, 'sSOL'],
[OPTIMISM_TOKENS.sUNI, 'sUNI'],
[OPTIMISM_TOKENS.sUSD, 'sUSD'],
]),
},
new Map(),
);
export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>( export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>(
{ {
[ChainId.Mainnet]: [ [ChainId.Mainnet]: [
@ -2548,7 +2635,28 @@ export const DEFAULT_GAS_SCHEDULE: Required<GasSchedule> = {
return compoundFillData.takerToken === wethAddress ? 210e3 : 250e3; return compoundFillData.takerToken === wethAddress ? 210e3 : 250e3;
} }
}, },
[ERC20BridgeSource.Synthetix]: (fillData?: FillData) => {
const { chainId, makerTokenSymbolBytes32, takerTokenSymbolBytes32 } = fillData as SynthetixFillData;
const makerTokenSymbol = parseBytes32String(makerTokenSymbolBytes32);
const takerTokenSymbol = parseBytes32String(takerTokenSymbolBytes32);
// Gas cost widely varies by token on mainnet.
if (chainId === ChainId.Mainnet) {
if (takerTokenSymbol === 'sBTC' || makerTokenSymbol === 'sBTC') {
return 800e3;
}
if (takerTokenSymbol === 'sETH' || makerTokenSymbol === 'sETH') {
return 700e3;
}
return 580e3;
}
// Optimism
if (takerTokenSymbol === 'sUSD' || makerTokenSymbol === 'sUSD') {
return 480e3;
}
return 580e3;
},
// //
// BSC // BSC
// //

View File

@ -38,6 +38,7 @@ import {
OrderDomain, OrderDomain,
PlatypusFillData, PlatypusFillData,
ShellFillData, ShellFillData,
SynthetixFillData,
UniswapV2FillData, UniswapV2FillData,
UniswapV3FillData, UniswapV3FillData,
UniswapV3PathAmount, UniswapV3PathAmount,
@ -210,6 +211,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.BancorV3, 'BancorV3'); return encodeBridgeSourceId(BridgeProtocol.BancorV3, 'BancorV3');
case ERC20BridgeSource.Velodrome: case ERC20BridgeSource.Velodrome:
return encodeBridgeSourceId(BridgeProtocol.Velodrome, 'Velodrome'); return encodeBridgeSourceId(BridgeProtocol.Velodrome, 'Velodrome');
case ERC20BridgeSource.Synthetix:
return encodeBridgeSourceId(BridgeProtocol.Synthetix, 'Synthetix');
default: default:
throw new Error(AggregationError.NoBridgeForSource); throw new Error(AggregationError.NoBridgeForSource);
} }
@ -391,6 +394,14 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const velodromeFillData = (order as OptimizedMarketBridgeOrder<VelodromeFillData>).fillData; const velodromeFillData = (order as OptimizedMarketBridgeOrder<VelodromeFillData>).fillData;
bridgeData = encoder.encode([velodromeFillData.router, velodromeFillData.stable]); bridgeData = encoder.encode([velodromeFillData.router, velodromeFillData.stable]);
break; break;
case ERC20BridgeSource.Synthetix:
const fillData = (order as OptimizedMarketBridgeOrder<SynthetixFillData>).fillData;
bridgeData = encoder.encode([
fillData.synthetix,
fillData.takerTokenSymbolBytes32,
fillData.makerTokenSymbolBytes32,
]);
break;
default: default:
throw new Error(AggregationError.NoBridgeForSource); throw new Error(AggregationError.NoBridgeForSource);
} }
@ -517,6 +528,7 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'), [ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'), [ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'),
[ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'), [ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'),
[ERC20BridgeSource.Synthetix]: AbiEncoder.create('(address,bytes32,bytes32)'),
}; };
function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] { function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@ -1,6 +1,7 @@
import { ChainId } from '@0x/contract-addresses'; import { ChainId } from '@0x/contract-addresses';
import { LimitOrderFields } from '@0x/protocol-utils'; import { LimitOrderFields } from '@0x/protocol-utils';
import { BigNumber, logUtils } from '@0x/utils'; import { BigNumber, logUtils } from '@0x/utils';
import { formatBytes32String } from '@ethersproject/strings';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler'; import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler';
@ -47,6 +48,8 @@ import {
NULL_ADDRESS, NULL_ADDRESS,
PLATYPUS_ROUTER_BY_CHAIN_ID, PLATYPUS_ROUTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID, SELL_SOURCE_FILTER_BY_CHAIN_ID,
SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID,
SYNTHETIX_READ_PROXY_BY_CHAIN_ID,
UNISWAPV1_ROUTER_BY_CHAIN_ID, UNISWAPV1_ROUTER_BY_CHAIN_ID,
UNISWAPV3_CONFIG_BY_CHAIN_ID, UNISWAPV3_CONFIG_BY_CHAIN_ID,
VELODROME_ROUTER_BY_CHAIN_ID, VELODROME_ROUTER_BY_CHAIN_ID,
@ -94,6 +97,7 @@ import {
ShellFillData, ShellFillData,
SourceQuoteOperation, SourceQuoteOperation,
SourcesWithPoolsCache, SourcesWithPoolsCache,
SynthetixFillData,
UniswapV2FillData, UniswapV2FillData,
UniswapV3FillData, UniswapV3FillData,
VelodromeFillData, VelodromeFillData,
@ -1309,6 +1313,60 @@ export class SamplerOperations {
}); });
} }
public getSynthetixSellQuotes(
readProxy: string,
takerTokenSymbol: string,
makerTokenSymbol: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<SynthetixFillData> {
const takerTokenSymbolBytes32 = formatBytes32String(takerTokenSymbol);
const makerTokenSymbolBytes32 = formatBytes32String(makerTokenSymbol);
return new SamplerContractOperation({
source: ERC20BridgeSource.Synthetix,
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromSynthetix,
params: [readProxy, takerTokenSymbolBytes32, makerTokenSymbolBytes32, takerFillAmounts],
callback: (callResults: string, fillData: SynthetixFillData): BigNumber[] => {
const [synthetix, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
'sampleSellsFromSynthetix',
callResults,
);
fillData.synthetix = synthetix;
fillData.takerTokenSymbolBytes32 = takerTokenSymbolBytes32;
fillData.makerTokenSymbolBytes32 = makerTokenSymbolBytes32;
fillData.chainId = this.chainId;
return samples;
},
});
}
public getSynthetixBuyQuotes(
readProxy: string,
takerTokenSymbol: string,
makerTokenSymbol: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<SynthetixFillData> {
const takerTokenSymbolBytes32 = formatBytes32String(takerTokenSymbol);
const makerTokenSymbolBytes32 = formatBytes32String(makerTokenSymbol);
return new SamplerContractOperation({
source: ERC20BridgeSource.Synthetix,
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromSynthetix,
params: [readProxy, takerTokenSymbolBytes32, makerTokenSymbolBytes32, makerFillAmounts],
callback: (callResults: string, fillData: SynthetixFillData): BigNumber[] => {
const [synthetix, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
'sampleBuysFromSynthetix',
callResults,
);
fillData.synthetix = synthetix;
fillData.takerTokenSymbolBytes32 = takerTokenSymbolBytes32;
fillData.makerTokenSymbolBytes32 = makerTokenSymbolBytes32;
fillData.chainId = this.chainId;
return samples;
},
});
}
/** /**
* Returns the best price for the native token * Returns the best price for the native token
* Best is calculated according to the fee schedule, so the price of the * Best is calculated according to the fee schedule, so the price of the
@ -1736,6 +1794,21 @@ export class SamplerOperations {
takerFillAmounts, takerFillAmounts,
); );
} }
case ERC20BridgeSource.Synthetix: {
const readProxy = SYNTHETIX_READ_PROXY_BY_CHAIN_ID[this.chainId];
const currencyKeyMap = SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID[this.chainId];
const takerTokenSymbol = currencyKeyMap.get(takerToken.toLowerCase());
const makerTokenSymbol = currencyKeyMap.get(makerToken.toLowerCase());
if (takerTokenSymbol === undefined || makerTokenSymbol === undefined) {
return [];
}
return this.getSynthetixSellQuotes(
readProxy,
takerTokenSymbol,
makerTokenSymbol,
takerFillAmounts,
);
}
default: default:
throw new Error(`Unsupported sell sample source: ${source}`); throw new Error(`Unsupported sell sample source: ${source}`);
} }
@ -2068,6 +2141,21 @@ export class SamplerOperations {
makerFillAmounts, makerFillAmounts,
); );
} }
case ERC20BridgeSource.Synthetix: {
const readProxy = SYNTHETIX_READ_PROXY_BY_CHAIN_ID[this.chainId];
const currencyKeyMap = SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID[this.chainId];
const takerTokenSymbol = currencyKeyMap.get(takerToken.toLowerCase());
const makerTokenSymbol = currencyKeyMap.get(makerToken.toLowerCase());
if (takerTokenSymbol === undefined || makerTokenSymbol === undefined) {
return [];
}
return this.getSynthetixBuyQuotes(
readProxy,
takerTokenSymbol,
makerTokenSymbol,
makerFillAmounts,
);
}
default: default:
throw new Error(`Unsupported buy sample source: ${source}`); throw new Error(`Unsupported buy sample source: ${source}`);
} }

View File

@ -1,3 +1,4 @@
import { ChainId } from '@0x/contract-addresses';
import { import {
FillQuoteTransformerLimitOrderInfo, FillQuoteTransformerLimitOrderInfo,
FillQuoteTransformerOrderType, FillQuoteTransformerOrderType,
@ -67,6 +68,7 @@ export enum ERC20BridgeSource {
Compound = 'Compound', Compound = 'Compound',
Synapse = 'Synapse', Synapse = 'Synapse',
BancorV3 = 'BancorV3', BancorV3 = 'BancorV3',
Synthetix = 'Synthetix',
// BSC only // BSC only
PancakeSwap = 'PancakeSwap', PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2', PancakeSwapV2 = 'PancakeSwap_V2',
@ -381,6 +383,14 @@ export interface VelodromeFillData extends FillData {
stable: boolean; stable: boolean;
} }
export interface SynthetixFillData extends FillData {
synthetix: string;
takerTokenSymbolBytes32: string;
makerTokenSymbolBytes32: string;
// Only needed for gas estimation.
chainId: ChainId;
}
/** /**
* Represents a node on a fill path. * Represents a node on a fill path.
*/ */

View File

@ -43,6 +43,7 @@ import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSamp
import * as PlatypusSampler from '../test/generated-artifacts/PlatypusSampler.json'; import * as PlatypusSampler from '../test/generated-artifacts/PlatypusSampler.json';
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json'; import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json'; import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
import * as SynthetixSampler from '../test/generated-artifacts/SynthetixSampler.json';
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json'; import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json'; import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json'; import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
@ -76,6 +77,7 @@ export const artifacts = {
PlatypusSampler: PlatypusSampler as ContractArtifact, PlatypusSampler: PlatypusSampler as ContractArtifact,
SamplerUtils: SamplerUtils as ContractArtifact, SamplerUtils: SamplerUtils as ContractArtifact,
ShellSampler: ShellSampler as ContractArtifact, ShellSampler: ShellSampler as ContractArtifact,
SynthetixSampler: SynthetixSampler as ContractArtifact,
TwoHopSampler: TwoHopSampler as ContractArtifact, TwoHopSampler: TwoHopSampler as ContractArtifact,
UniswapSampler: UniswapSampler as ContractArtifact, UniswapSampler: UniswapSampler as ContractArtifact,
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact, UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,

View File

@ -41,6 +41,7 @@ export * from '../test/generated-wrappers/native_order_sampler';
export * from '../test/generated-wrappers/platypus_sampler'; export * from '../test/generated-wrappers/platypus_sampler';
export * from '../test/generated-wrappers/sampler_utils'; export * from '../test/generated-wrappers/sampler_utils';
export * from '../test/generated-wrappers/shell_sampler'; export * from '../test/generated-wrappers/shell_sampler';
export * from '../test/generated-wrappers/synthetix_sampler';
export * from '../test/generated-wrappers/test_native_order_sampler'; export * from '../test/generated-wrappers/test_native_order_sampler';
export * from '../test/generated-wrappers/two_hop_sampler'; export * from '../test/generated-wrappers/two_hop_sampler';
export * from '../test/generated-wrappers/uniswap_sampler'; export * from '../test/generated-wrappers/uniswap_sampler';

View File

@ -44,6 +44,7 @@
"test/generated-artifacts/PlatypusSampler.json", "test/generated-artifacts/PlatypusSampler.json",
"test/generated-artifacts/SamplerUtils.json", "test/generated-artifacts/SamplerUtils.json",
"test/generated-artifacts/ShellSampler.json", "test/generated-artifacts/ShellSampler.json",
"test/generated-artifacts/SynthetixSampler.json",
"test/generated-artifacts/TestNativeOrderSampler.json", "test/generated-artifacts/TestNativeOrderSampler.json",
"test/generated-artifacts/TwoHopSampler.json", "test/generated-artifacts/TwoHopSampler.json",
"test/generated-artifacts/UniswapSampler.json", "test/generated-artifacts/UniswapSampler.json",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "11.16.0",
"changes": [
{
"note": "Add Synthetix support`",
"pr": 518
}
]
},
{ {
"version": "11.15.0", "version": "11.15.0",
"changes": [ "changes": [

View File

@ -140,6 +140,7 @@ export enum BridgeProtocol {
Platypus, Platypus,
BancorV3, BancorV3,
Velodrome, Velodrome,
Synthetix,
} }
// tslint:enable: enum-naming // tslint:enable: enum-naming