Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

🔂 Liquidity Provider Asset Swapper integration #2505

Merged
merged 33 commits into from
Mar 6, 2020

Conversation

PirosB3
Copy link
Contributor

@PirosB3 PirosB3 commented Feb 29, 2020

Description

This PR contains the implementation of "liquidity providers". This new feature allows third-party developers to plug in their own private liquidity sources that adhere to the IERC20Bridge.bridgeTransferFrom() interface.

Testing instructions

Tested using a mocked liquidity provider, unit tests, and more testing to come!

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • Prefix PR title with [WIP] if necessary.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

@cleancoindev
Copy link

I have a review: the circleci says:

https://circleci.com/gh/0xProject/0x-monorepo/110617?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link

Failure due to missing or extra semicolons in various places on the first static-tests check screen of fail?

@cleancoindev
Copy link

and this one test-rest another review:

https://circleci.com/gh/0xProject/0x-monorepo/110620?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link

You are missing a {} says the interpreter error?

@coveralls
Copy link

coveralls commented Mar 2, 2020

Coverage Status

Coverage increased (+0.05%) to 79.739% when pulling 659e899 on feature/plp-integration into b8ec7f5 on development.

Copy link
Contributor

@moodlezoup moodlezoup left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we exporting DummyLiquidityProvider and DummyLiquidityProviderRegistry in @0x/contract-wrappers and @0x/contract-artifacts? We should avoid doing so if possible

Comment on lines 23 to 24
returns (uint256 makerTokenAmount) {
makerTokenAmount = sellAmount - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit:

Suggested change
returns (uint256 makerTokenAmount) {
makerTokenAmount = sellAmount - 1;
returns (uint256 makerTokenAmount)
{
makerTokenAmount = sellAmount - 1;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 38 to 39
returns (uint256 takerTokenAmount) {
takerTokenAmount = buyAmount + 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

address takerToken,
address makerToken,
address poolAddress
) external
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
) external
)
external

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 7 to 10
constructor()
public
// solhint-disable-next-line no-empty-blocks
{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no constructor needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 9 to 12
constructor()
public
// solhint-disable-next-line no-empty-blocks
{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no constructor needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

expect(sellQuotes.length).to.eql(1);
const iquidityPoolSellQuotes: DexSample[] = sellQuotes[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const iquidityPoolSellQuotes: DexSample[] = sellQuotes[0];
const liquidityPoolSellQuotes: DexSample[] = sellQuotes[0];

@@ -141,6 +141,7 @@ describe('MarketOperationUtils tests', () => {
makerToken: string,
takerToken: string,
fillAmounts: BigNumber[],
liquidityProviderAddress?: string | undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The | undefined is redundant with the ?, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed! thanks

@@ -172,14 +195,47 @@ describe('MarketOperationUtils tests', () => {
makerToken: string,
takerToken: string,
fillAmounts: BigNumber[],
liquidityProviderAddress?: string | undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed! thanks

const yAsset = randomAddress();
const toSell = Web3Wrapper.toBaseUnitAmount(10, 18);

const [getSellQuiotesParams, getSellQuotesFn] = callTradeOperationAndRetainLiquidityProviderParams(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const [getSellQuiotesParams, getSellQuotesFn] = callTradeOperationAndRetainLiquidityProviderParams(
const [getSellQuotesParams, getSellQuotesFn] = callTradeOperationAndRetainLiquidityProviderParams(

getLiquidityProviderFn,
] = getLiquidityProviderFromRegistryAndReturnCallParameters(liquidityPoolAddress);
replaceSamplerOps({
getOrderFillableTakerAmounts: () => [new BigNumber(0)],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getOrderFillableTakerAmounts: () => [new BigNumber(0)],
getOrderFillableTakerAmounts: () => [constants.ZERO_AMOUNT],

@@ -130,6 +146,7 @@ export class MarketOperationUtils {
makerToken,
collapsePath(optimalPath, false),
_opts.bridgeSlippage,
liquidityPoolAddress,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit - liquidityPool -> liquidityProvider

private _createBuyOrdersPathFromSamplerResultIfExists(
nativeOrders: SignedOrder[],
makerAmount: BigNumber,
nativeOrderFillableAmounts: BigNumber[],
dexQuotes: DexSample[][],
ethToTakerAssetRate: BigNumber,
opts: GetMarketOrdersOpts,
liquidityPoolAddress?: string | undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit - liquidityPool -> liquidityProvider

/// selling `sellAmount` of `takerToken`.
/// @param sellAmount Amount of `takerToken` to sell.
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
function getSellQuote(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified that this matches the spec ✅

/// order to obtain `buyAmount` of `makerToken`.
/// @param buyAmount Amount of `makerToken` to buy.
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
function getBuyQuote(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified that this matches the spec ✅

/// @param makerToken Second asset managed by pool.
/// @param poolAddress Address of pool.
function setLiquidityProviderForMarket(
address takerToken,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit - this naming may be confusing because either asset could be the taker/maker token on a given trade. Something like xToken / yToken or firstToken / secondToken may be better 🤷‍♂.

Copy link
Contributor

@hysz hysz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Verified the Solidity against the spec and it looks good. It could be good for an engineer more familiar with Asset Swapper to give this a review as well.

Daniel Pyrathon added 7 commits March 3, 2020 14:02
…eature/plp-integration

# Conflicts:
#	packages/asset-swapper/package.json
#	packages/asset-swapper/src/utils/market_operation_utils/sampler.ts
#	packages/asset-swapper/src/utils/market_operation_utils/types.ts
#	packages/asset-swapper/test/market_operation_utils_test.ts
view
returns (uint256 makerTokenAmount)
{
makerTokenAmount = sellAmount - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be good to use LibSafeMath here and below to avoid wonky output in future testing.

Copy link
Contributor

@dorothy-zbornak dorothy-zbornak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job wrangling this nightmare-level codebase! Just some (small?) requests.

contracts/utils/contracts/src/DeploymentConstants.sol Outdated Show resolved Hide resolved
@@ -0,0 +1,346 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never used to expose this contract before. Why now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exposing this contract allows me to deploy it on-chain (Ganache) and perform E2E simulations of sampling PLP liquidity

Comment on lines 69 to 70
takerToken: string,
makerToken: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we flip the order of these? Just to be consistent with the other ops. I know it's the other way in the contract.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 94 to 95
takerToken: string,
makerToken: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +235 to +236
takerToken: string,
makerToken: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -326,6 +341,104 @@ describe('DexSampler tests', () => {
);
expect(quotes).to.deep.eq(expectedQuotes);
});

describe('LiquidityProvider Operations', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how I feel about these tests. These seem be partially testing the test contracts. I'd also rather we didn't do on-chain tests in this package.

Do you want to unit test the DexOrderSampler ops? If so, extending the MockSamplerContract is the way to go, like how the other tests in this file do it. This would also remove the need for the new test contracts.

But if you want to unit test ERC20BridgeSampler integration with liquidity providers, that should be done in the contracts/erc20-bridge-sampler package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a wise point of view, I'm going to move my tests in erc20-bridge-sampler test package

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored and moved those unit tests to erc20-bridge-sampler. I also added extra unit tests using MockSamplerContract

const liquidityProviderAddress = randomAddress();
const xAsset = randomAddress();
const yAsset = randomAddress();
const toSell = Web3Wrapper.toBaseUnitAmount(10, 18);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: There's a better version of this in @0x/utils (fromTokenUnitAmount(), toTokenUnitAmount()).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I didn't know about this utility :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

const toSell = Web3Wrapper.toBaseUnitAmount(10, 18);

const [getSellQuotesParams, getSellQuotesFn] = callTradeOperationAndRetainLiquidityProviderParams(
createGetMultipleSellQuotesOperationFromRates,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this whole test case should be with the "sell" test cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, my bad. Fixing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

ORDER_DOMAIN,
registryAddress,
);
const result = await sampler.getMarketSellOrdersAsync(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, definitely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, my bad. Fixing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

@dorothy-zbornak dorothy-zbornak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy changes then all good! Fantastic job!

@@ -31,7 +31,7 @@
"wrappers:generate": "abi-gen --abis ${npm_package_config_abis} --output src/generated-wrappers --backend ethers"
},
"config": {
"abis": "../contract-artifacts/artifacts/@(DevUtils|ERC20Token|ERC721Token|Exchange|Forwarder|IAssetData|LibTransactionDecoder|WETH9|Coordinator|Staking|StakingProxy|IERC20BridgeSampler|ERC20BridgeSampler|GodsUnchainedValidator|Broker|ILiquidityProvider|ILiquidityProviderRegistry|DummyLiquidityProvider|DummyLiquidityProviderRegistry).json"
"abis": "../contract-artifacts/artifacts/@(DevUtils|ERC20Token|ERC721Token|Exchange|Forwarder|IAssetData|LibTransactionDecoder|WETH9|Coordinator|Staking|StakingProxy|IERC20BridgeSampler|ERC20BridgeSampler|GodsUnchainedValidator|Broker|ILiquidityProvider|ILiquidityProviderRegistry|DummyLiquidityProvider|DummyLiquidityProviderRegistry|MaximumGasPrice).json"
Copy link
Contributor

@dorothy-zbornak dorothy-zbornak Mar 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make sure there are no Dummy* contracts exported by this package? No one would use them outside of tests. If they want them, they can import them directly from the erc20-bridge-sampler package.

@@ -716,6 +718,93 @@ blockchainTests('erc20-bridge-sampler', env => {
});
});

blockchainTests.resets('getLiquidityProviderFromRegistry', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: All the test cases here are constant, so no chain state to reset. This can just be a regular describe() block.

Comment on lines 729 to 730
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync()
.txHashPromise;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to call createTokenExchanges. It just creates uniswap exchanges, which you aren't using.

);
await registryContract
.setLiquidityProviderForMarket(xAsset, yAsset, liquidityProvider.address)
.awaitTransactionSuccessAsync().txHashPromise;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.awaitTransactionSuccessAsync().txHashPromise;
.awaitTransactionSuccessAsync();

@@ -10,6 +10,8 @@ import { Order } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';

import { DummyLiquidityProviderContract, DummyLiquidityProviderRegistryContract } from '../src';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pull these in from './wrappers'.

@PirosB3 PirosB3 merged commit 68207aa into development Mar 6, 2020
@PirosB3 PirosB3 deleted the feature/plp-integration branch March 6, 2020 20:34
@PirosB3 PirosB3 mentioned this pull request Mar 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants