ProofSuite / OrFeed

DeFi & Distributed Data Service Aggregator
https://www.orfeed.org
Apache License 2.0
241 stars 152 forks source link

0x Protocol Price Discovery Riddle (No Code, just WHHHEEERRRREEE!!!!) #10

Closed mikedeshazer closed 4 years ago

mikedeshazer commented 4 years ago

Riddle: How do you do price discovery through 0x Protocol's Exchange smart contract?

Facts and hints collected while attempting this along the way. If you want to chat about this, just ping me on telegram: @mikeproof.

This obviously can be done... dYdX queries pricing data from 0x's protocol like this: https://github.com/dydxprotocol/exchange-wrappers/blob/master/contracts/exchange-wrappers/ZeroExV2ExchangeWrapper.sol Their actual deployed contracts are here: https://docs.dydx.exchange/#/contracts However, it does indeed reference an old 0x contract. Also if you actually use the deployed contract it won't seem to work if you're using Etherscan's contract read feature, because it returns a struct and web3 doesn't support tuples/structs... so hard to test if it works... We would need a smart contract to call it, pull data out of the struct, etc. This 0x application can do price discovery by querying smart contracts: https://0x.org/docs/guides/contract-fillable-liquidity#step-2-passing-0x-orders-to-a-smart-contract Hints to the answer are probably here too: https://0x.org/docs/tools/asset-swapper/v2.1.0-beta.3#class-swapquoter So, it's possible, because it's getting data from the blockchain, right? Further hint, maybe: https://github.com/0xProject/0x-monorepo/blob/development/contracts/exchange/contracts/src/Exchange.sol Here are 0x's contracts on mainnet: https://0x.org/docs/guides/0x-cheat-sheet

This issue is just identifying an existing contract that we can use as an interface (somewhere somehow) and method within it we can call with 2 tokens, a buy or sell order side, and amount to get price. From there, we can integrate it in. You can see how we're doing it with other platforms here: https://github.com/ProofSuite/OrFeedSmartContracts/blob/master/contracts/draftpremium.sol via view functions that do not require gas to get the data, etc.

gitcoinbot commented 4 years ago

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


This issue now has a funding of 100.0 DAI (100.0 USD @ $1.0/DAI) attached to it.

gitcoinbot commented 4 years ago

Issue Status: 1. Open 2. Cancelled


Work has been started.

These users each claimed they can complete the work by 3 weeks, 2 days ago. Please review their action plans below:

1) mul1sh has started work.

I'll research this further and get back with an answer by this time tomorrow. Thanks.

Learn more on the Gitcoin Issue Details page.

janus commented 4 years ago

@mikedeshazer One can use the below to get quantity and from that get price. I used this when I was converting from ETH to DAI and DAI to ETH.

function getBuyAmount()

function getPayAmount()

mikedeshazer commented 4 years ago

@janus specifically what mainnet smart contract address would you call for this?

janus commented 4 years ago

@mikedeshazer I was referring MakeDAO to point that there should be something similar;

getMarketSellSwapQuoteAsync getMarketBuySwapQuoteAsync Inside https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L253. But this is JavaScript and it is not free.

mul1sh commented 4 years ago

@mikedeshazer To do price discovery using the 0x protocol, you need to deploy a relayer where makers post a orders asking for the token swap and then takers via the ethereum blockchain send orders to the relayer and can choose which maker they would like to trade with and then afterwards the swap occurs via 0x swap contract Exchange.sol. This is done off-chain via an order book which one can now query to get available orders and the prices you will get for the token swaps.

As for the terms maker and taker, this article explains this better than I can.

So there is really no "common" mainnet contract that facilitates this exchange and enables you to do price discovery. Each 0x relay has to deploy its own relayer where orders are posted.

One of the best examples of a dApp implementing the 0x protocol to do price discovery is radar relay. It even has an api where you can query their order books and get to see the various offers for various token swaps and the prices too.

They also have a good explainer about the 0x relay network here

Hope this helps, but if it doesn't let me know and I will gladly explain it even more. I just didn't want to write a whole book here 😄 because there are a lot of nitty gritty details that need to be followed to get this to work, but the above is the basic concept of order matching, price discovery & token swaps via the 0x protocol.

gitcoinbot commented 4 years ago

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Work for 100.0 DAI (100.0 USD @ $1.0/DAI) has been submitted by:

  1. @mul1sh

@mikedeshazer please take a look at the submitted work:


janus commented 4 years ago

@mikedeshazer What is wrong in following this sequence?

Asset Swapper provides a simple interface to fetch orders from a provided Standard Relayer API and give a price quote for the desired token swap.

Once orders are found, they need to be passed through the integrator’s smart contracts. Up until now, all interactions with 0x protocol have been off-chain. To consume the output fetched off-chain in a smart contract, it needs to be transmitted on-chain. To then pass 0x orders on-chain, integrators provide additional parameters for smart contract calls. Asset Swapper includes a SwapQuoteConsumer utility tool that converts quotes provided by SwapQuoter to formats that are well-suited for contract calls.

https://blog.0xproject.com/contract-fillable-liquidity-made-simple-8b9cf1b2f2f2

janus commented 4 years ago

@mikedeshazer

Hints to the answer are probably here too: https://0x.org/docs/tools/asset-swapper/v2.1.0-beta.3#class-swapquoter So, it's possible, because it's getting data from the blockchain, right? Yes .. but it not a direct call to contract method. It is partially processed

And this is the last interesting method called before sending to wire. It throws if it fails. And gas price is also paid

    private async _getSwapQuoteAsync(
        makerAssetData: string,
        takerAssetData: string,
        assetFillAmount: BigNumber,
        marketOperation: MarketOperation,
        options: Partial<SwapQuoteRequestOpts>,
    ): Promise<SwapQuote> {
        const { slippagePercentage } = _.merge({}, constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, options);
        assert.isString('makerAssetData', makerAssetData);
        assert.isString('takerAssetData', takerAssetData);
        assert.isNumber('slippagePercentage', slippagePercentage);
        let gasPrice: BigNumber;
        if (!!options.gasPrice) {
            gasPrice = options.gasPrice;
            assert.isBigNumber('gasPrice', gasPrice);
        } else {
            gasPrice = await protocolFeeUtils.getGasPriceEstimationOrThrowAsync();
        }
        // get the relevant orders for the makerAsset
        const prunedOrders = await this.getPrunedSignedOrdersAsync(makerAssetData, takerAssetData);
        if (prunedOrders.length === 0) {
            throw new Error(
                `${
                    SwapQuoterError.AssetUnavailable
                }: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`,
            );
        }

        let swapQuote: SwapQuote;

        if (marketOperation === MarketOperation.Buy) {
            swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote(
                prunedOrders,
                assetFillAmount,
                slippagePercentage,
                gasPrice,
            );
        } else {
            swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote(
                prunedOrders,
                assetFillAmount,
                slippagePercentage,
                gasPrice,
            );
        }

        return swapQuote;
    }
}

This one would return data you can easily feed to smart contract https://0x.org/asset-swapper

import { SwapQuoteConsumer } from '@0x/asset-swapper';

const swapQuoteConsumer = new SwapQuoteConsumer(web3.provider);

const calldataInfo = await swapQuoteConsumer.getCalldataOrThrowAsync(quote);

const { calldataHexString } = calldataInfo;

const txHash = await yourSmartContract.methods
  .liquidityRequiringMethod(calldataHexString)
  .call();

Finally, you can wait for this https://0x.org/docs/api-explorer

Let me know your position.

mikedeshazer commented 4 years ago

@janus @mul1sh thanks for the info! Im surprised that when trades are executed via exchange.sol that they are not saved. Think that is a bug, as it wouldnt add much in gas costs for them to save that data so pricing data could be queried on chain from smart contracts. @janus yes aware that the node app can get the data... was hoping it was possible directly from another smart contract. You would think a blockchain-based platform might leave records of trades onchain. Today a new feature was added to orfeed that can hopefully serve as a stepping stone for off feed data. So far, just added 10 most popular stocks and 5 most popular etfs. Surprised that getting pricing data from a blockchain based protocol will require the same method as getting data from offchain data. Its weird, but it is what it is.

mikedeshazer commented 4 years ago

@janus regarding your comment, do you know where to find the smart contract/its method for the contract-fillable aspect you referred to... seems like what were looking for... looked through the blog post but it wasnt immediately apparent.

janus commented 4 years ago

https://github.com/0xProject/0x-monorepo/tree/development/packages/asset-swapper const { calldataHexString, ethAmount } = calldataInfo;

Which one are you referring?

janus commented 4 years ago

@mikedeshazer

Please follow the below steps, one after the other. Step 1 output is feed into step 2 function call, swapQuoteConsumer.getCalldataOrThrowAsync. If it is okay with you... I would submit it as work done... Step 1 Automatically find orders for any token trading pair

import { SwapQuoter } from '@0x/asset-swapper';

const apiUrl = 'https://api.0x.org/sra/'; const daiTokenAddress = '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359'; const wethTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';

const quoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl( web3.provider, apiUrl );

// Get a quote to buy three units of DAI const quote = await quoter.getMarketBuySwapQuoteAsync( daiTokenAddress, wethTokenAddress, web3.utils.fromWei(3, 'ether'), );

Step 2 Easily transform orders into a contract fillable format

import { SwapQuoteConsumer } from '@0x/asset-swapper';

const swapQuoteConsumer = new SwapQuoteConsumer(web3.provider);

const calldataInfo = await swapQuoteConsumer.getCalldataOrThrowAsync(quote);

const { calldataHexString, ethAmount } = calldataInfo;

const txHash = await yourSmartContract.methods .liquidityRequiringMethod(calldataHexString) .sendTransactionAsync({ value: ethAmount });

Step 3

Quickly execute orders to fulfill your desired use case contract MyContract {

public address zeroExExchangeAddress;

function liquidityRequiringMethod(bytes calldata calldataHexString) payable { zeroExExchangeAddress.call.value(msg.value)(calldataHexString); } }

gitcoinbot commented 4 years ago

Issue Status: 1. Open 2. Cancelled


The funding of 100.0 DAI (100.0 USD @ $1.0/DAI) attached to this issue has been cancelled by the bounty submitter