blog.logrocket.com
Open in
urlscan Pro
2606:4700:10::6816:494
Public Scan
URL:
https://blog.logrocket.com/how-to-market-make-transact-hashflow
Submission: On December 20 via api from US — Scanned from DE
Submission: On December 20 via api from US — Scanned from DE
Form analysis
4 forms found in the DOM<form id="commentform" class="comment-form">
<iframe title="Comment Form"
src="https://jetpack.wordpress.com/jetpack-comment/?blogid=217016018&postid=126272&comment_registration=0&require_name_email=1&stc_enabled=1&stb_enabled=1&show_avatars=1&avatar_default=mystery&greeting=Leave+a+Reply&jetpack_comments_nonce=7510150a09&greeting_reply=Leave+a+Reply+to+%25s&color_scheme=light&lang=en_US&jetpack_version=14.0&iframe_unique_id=1&show_cookie_consent=10&has_cookie_consent=0&is_current_user_subscribed=0&token_key=%3Bnormal%3B&sig=2f55815097ba14884686cec49f1390f1ca87081b#parent=https%3A%2F%2Fblog.logrocket.com%2Fhow-to-market-make-transact-hashflow"
name="jetpack_remote_comment" style="width: 100%; height: 2px; border: 0px;" class="jetpack_remote_comment" id="jetpack_remote_comment" sandbox="allow-same-origin allow-top-navigation allow-scripts allow-forms allow-popups" scrolling="no">
</iframe>
<!--[if !IE]><!-->
<script>
document.addEventListener('DOMContentLoaded', function() {
var commentForms = document.getElementsByClassName('jetpack_remote_comment');
for (var i = 0; i < commentForms.length; i++) {
commentForms[i].allowTransparency = false;
commentForms[i].scrolling = 'no';
}
});
</script>
<!--<![endif]-->
</form>
<form id="jp-carousel-comment-form">
<label for="jp-carousel-comment-form-comment-field" class="screen-reader-text">Write a Comment...</label>
<textarea name="comment" class="jp-carousel-comment-form-field jp-carousel-comment-form-textarea" id="jp-carousel-comment-form-comment-field" placeholder="Write a Comment..."></textarea>
<div id="jp-carousel-comment-form-submit-and-info-wrapper">
<div id="jp-carousel-comment-form-commenting-as">
<fieldset>
<label for="jp-carousel-comment-form-email-field">Email (Required)</label>
<input type="text" name="email" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-email-field">
</fieldset>
<fieldset>
<label for="jp-carousel-comment-form-author-field">Name (Required)</label>
<input type="text" name="author" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-author-field">
</fieldset>
<fieldset>
<label for="jp-carousel-comment-form-url-field">Website</label>
<input type="text" name="url" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-url-field">
</fieldset>
</div>
<input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="Post Comment">
</div>
</form>
<form id="mktoForm_1107" style="display: none; font-family: Helvetica, Arial, sans-serif; font-size: 13px; color: rgb(51, 51, 51); width: 1px;" novalidate="novalidate" class="mktoForm mktoHasWidth mktoLayoutLeft">
<style type="text/css">
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton {
color: #fff;
border: 1px solid #75ae4c;
padding: 0.4em 1em;
font-size: 1em;
background-color: #99c47c;
background-image: -webkit-gradient(linear, left top, left bottom, from(#99c47c), to(#75ae4c));
background-image: -webkit-linear-gradient(top, #99c47c, #75ae4c);
background-image: -moz-linear-gradient(top, #99c47c, #75ae4c);
background-image: linear-gradient(to bottom, #99c47c, #75ae4c);
}
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton:hover {
border: 1px solid #447f19;
}
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton:focus {
outline: none;
border: 1px solid #447f19;
}
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton:active {
background-color: #75ae4c;
background-image: -webkit-gradient(linear, left top, left bottom, from(#75ae4c), to(#99c47c));
background-image: -webkit-linear-gradient(top, #75ae4c, #99c47c);
background-image: -moz-linear-gradient(top, #75ae4c, #99c47c);
background-image: linear-gradient(to bottom, #75ae4c, #99c47c);
}
</style>
<div class="mktoFormRow">
<div class="mktoFieldDescriptor mktoFormCol" style="margin-bottom: 10px;">
<div class="mktoOffset" style="width: 10px;"></div>
<div class="mktoFieldWrap"><label for="FirstName" id="LblFirstName" class="mktoLabel mktoHasWidth" style="width: 100px;">
<div class="mktoAsterix">*</div>First Name:
</label>
<div class="mktoGutter mktoHasWidth" style="width: 10px;"></div><input id="FirstName" name="FirstName" maxlength="255" aria-labelledby="LblFirstName InstructFirstName" type="text" class="mktoField mktoTextField mktoHasWidth"
style="width: 150px;"><span id="InstructFirstName" tabindex="-1" class="mktoInstruction"></span>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow">
<div class="mktoFieldDescriptor mktoFormCol" style="margin-bottom: 10px;">
<div class="mktoOffset" style="width: 10px;"></div>
<div class="mktoFieldWrap"><label for="LastName" id="LblLastName" class="mktoLabel mktoHasWidth" style="width: 100px;">
<div class="mktoAsterix">*</div>Last Name:
</label>
<div class="mktoGutter mktoHasWidth" style="width: 10px;"></div><input id="LastName" name="LastName" maxlength="255" aria-labelledby="LblLastName InstructLastName" type="text" class="mktoField mktoTextField mktoHasWidth"
style="width: 150px;"><span id="InstructLastName" tabindex="-1" class="mktoInstruction"></span>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow">
<div class="mktoFieldDescriptor mktoFormCol" style="margin-bottom: 10px;">
<div class="mktoOffset" style="width: 10px;"></div>
<div class="mktoFieldWrap"><label for="Email" id="LblEmail" class="mktoLabel mktoHasWidth" style="width: 100px;">
<div class="mktoAsterix">*</div>Email Address:
</label>
<div class="mktoGutter mktoHasWidth" style="width: 10px;"></div><input id="Email" name="Email" maxlength="255" aria-labelledby="LblEmail InstructEmail" type="email" class="mktoField mktoEmailField mktoHasWidth" style="width: 150px;"><span
id="InstructEmail" tabindex="-1" class="mktoInstruction"></span>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoButtonRow"><span class="mktoButtonWrap mktoSimple" style="margin-left: 120px;"><button type="submit" class="mktoButton">Submit</button></span></div><input type="hidden" name="formid" class="mktoField mktoFieldDescriptor"
value="1107"><input type="hidden" name="munchkinId" class="mktoField mktoFieldDescriptor" value="740-LKM-263">
</form>
<form style="display: none; font-family: Helvetica, Arial, sans-serif; font-size: 13px; color: rgb(51, 51, 51); visibility: hidden; position: absolute; top: -500px; left: -1000px; width: 1600px;" novalidate="novalidate"
class="mktoForm mktoHasWidth mktoLayoutLeft"></form>
Text Content
ADVISORY BOARDS AREN’T ONLY FOR EXECUTIVES. JOIN THE LOGROCKET CONTENT ADVISORY BOARD TODAY → * Blog * Dev * Product Management * UX Design * Podcast * Features * Solutions * Solve User-Reported Issues * Find Issues Faster * Optimize Conversion and Adoption * Start Monitoring for Free * Sign In 2022-07-29 1924 #web3 Victor Jonah 126272 Jul 29, 2022 ⋅ 6 min read HOW TO MARKET MAKE AND TRANSACT WITH HASHFLOW Victor Jonah I am a Software Developer with over three years of experience working with JavaScript and its frameworks. I currently work as a remote software developer for a tech agency. Table of contents * What is Hashflow and what is its goal? * How Hashflow works * Hashflow tokens * How to market make * Connect to WebSocket * Create a Pool * Receive RFQ and respond with a quote * Support signing quotes * Get the first signed RFQ * Conclusion Introducing Galileo AI LogRocket’s Galileo AI watches every session, surfacing impactful user struggle and key behavior patterns. READ THE BLOG POST SEE HOW LOGROCKET'S AI-POWERED ERROR TRACKING WORKS NO SIGNUP REQUIRED Check it out close this ✕ There has been a rise in the amount of DeFi traders in recent years which has resulted in more decentralized exchanges (DEXs) being brought to the market as well. Web3 is increasing its impact on major industries, and the trading industry is no exception. We had seen the deficiencies in centralized exchanges like a lack of transparency, insecurity, financial exclusiveness, and privacy, and this has provided the need for DEXs. DEX is a way of exchanging cryptocurrencies without any intermediary like brokers or third parties. They follow a different approach from centralized exchanges by relying on smart contracts to handle transactions peer-to-peer. The business model focuses on privacy for its users where transactions are carried out anonymously, even though the transaction details are stored on the blockchain. We have a lot of DEX platforms on the market, like Coinbase and Binance, but in this article, we will focus on Hashflow. We will look at how traders are connected with pinpoints via a Hashflow token, how it works differently from others, how to market make, and how to trade. * What is Hashflow and what is its goal? * How Hashflow works * Hashflow tokens * How to market make * Connect to WebSocket * Create a Pool * Receive RFQ and respond with quote * Support signing quotes * Get the first signed RFQ WHAT IS HASHFLOW AND WHAT IS ITS GOAL? First of all, Hashflow is known for its bridge-less cross-chain swaps, which means it is highly interoperable, has zero slippage, and has no MEV. This is possible because Hashflow ensures market makers sign the price quotes so it remains unchanged during the trade. Liquidity providers and traders are connected with professional market makers on Hashflow and its core features offer better flexibility for its market makers — no slippage, MEV-resistant, better price quotes, and a cheaper trading fee are all advantages of it. All the above mentioned are only possible because of the architecture Hashflow uses, which is the Pool-based architecture. HOW HASHFLOW WORKS Starting with a transaction, the user has to connect his wallet to Hashflow, input a number they would like to trade, and then a quote is displayed to them. -------------------------------------------------------------------------------- OVER 200K DEVELOPERS USE LOGROCKET TO CREATE BETTER DIGITAL EXPERIENCES Learn more → -------------------------------------------------------------------------------- If the user accepts, the order is submitted and that transaction is verified and added to the Hashflow network (or blockchain). On the other end, there are maker markets that deal with the issuing of price quotes that the user had already accepted. The market maker then signs the trade and it is executed without slippage (whatever order was submitted by the user stands). Unlike other DEXs, which do usually have AMMs (Automated Market Maker) that handles market making and assets pricing on-chain using Lazy Liquidity Provision, Hashflow goes traditionally just like the order-book mechanism. The pricing is done off-chain but the trade is executed on-chain. Hashflow was in 2021 and has grown significantly, offering the best prices because of its optimized gas fee and zero slippage. As of the time of this article being published, Hashflow currently supports bridge-less cross-chain swaps which makes you initiate a swap on EVM-compatible chains, and it is expected to include Solana integration, smart order Routing, gasless trading, limit orders, and Hashverse in future. In summary, Hashflow is a DeFi protocol that serves as a decentralized exchange running on the Ethereum blockchain. HASHFLOW TOKENS Hashflow provides its token called the HFT (Hashflow token), which is an ERC-20 on the Ethereum chain deployed on December 22, 2021. With one billion units of HFT in supply, Hashflow distributed this cryptocurrency in this manner: 19% to the core team; 25% to the early investors; and 56% to the ecosystem. A further 6.75% will be given to early users as a reward. The Hashflow NFT, which is also called the Hashbots, will be used in the Hashverse that will be released in the future. NFT holders will receive HFT that tallies to the value of the NFT, because each NFT will have an HFT value. And Hashflow states that after four years, 5% of HFT will be issued to the community. HOW TO MARKET MAKE Market markers are essentially important in Hashflow and in the market as a whole. They are the liquidity providers that make the market functional by buying and selling assets. This doesn’t mean that they can buy and sell at any time right away, but they are always on standby, ready to buy and sell. In most decentralized exchanges, the market makers are automatic and smart contracts are deployed to find market pairs in AMMs (Automatic Market Makers) but they are prone to attacks and also a bit laggy because the price quotes are handled on-chain. -------------------------------------------------------------------------------- MORE GREAT ARTICLES FROM LOGROCKET: * Don't miss a moment with The Replay, a curated newsletter from LogRocket * Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app * Use React's useEffect to optimize your application's performance * Switch between multiple versions of Node * Discover how to use the React children prop with TypeScript * Explore creating a custom mouse cursor with CSS * Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag. -------------------------------------------------------------------------------- In Hashflow, the market makers are firms, institutions, or individuals that are given the privilege to make these market decisions using their professionalism, and this is done off-chain, which in turn reduces gas fees and slippage, as mentioned earlier. To market make on Hashflow, we will follow the steps below. Integrating with Hashflow as a market maker is quite simple. CONNECT TO WEBSOCKET You will need to connect to the WebSocket from Hashflow API just as you see on line 10 below, and you may need to contact the team on Telegram or Discord to be added to the “allowlist” makers. const PING_PONG_INTERVAL_MS = 30000; const PING_PONG_GRACE_PERIOD_MS = 1000; function getWebsocketConnection( marketMakerName, onMessageCallBack, onCloseCallback, onHeartbeatCallback, ) { const ws = new WebSocket(`${process.env.HASHFLOW_WS_API}/maker/v1`, { headers: { marketmaker: marketMakerName, } }); const heartbeat = () => { if (ws.pingTimeout) { clearTimeout(ws.pingTimeout); } ws.pingTimeout = setTimeout(() => { ws.terminate(); }, PING_PONG_INTERVAL_MS + PING_PONG_GRACE_PERIOD_MS); onHeartbeatCallback(); } ws.on('open', heartbeat); ws.on('ping', heartbeat); ws.on('message', message => onMessageCallBack(message)); ws.on('close', () => { if (ws.pingTimeout) { clearTimeout(ws.pingTimeout); } setTimeout(() => { ws.removeAllListeners(); onCloseCallback(); }, 5000); }); ws.on('error', err => {}); ws.on('unexpected-response', (_, res) => { let message = ''; res.on('data', (chunk) => { message += chunk; }); res.on('end', () => { if (res.statusCode === 401) { logger.error(`WS access not authorized. ${message}`); } else { logger.error(`Unexpexted response from server: [${res.statusCode}] ${message}.`); } ws.close() }); }); return ws; } And Hashflow is connected to you, as seen in the following: // TODO: Replace this with your market maker name (once added to the backend) const MARKET_MAKER_NAME = 'TestMM'; // TODO: Set true if you want to MM on 1inch, etc – and have signed legal agreements const SUPPORT_AGGREGATORS = false; const levelsInterval = SUPPORT_AGGREGATORS ? setInterval(() => publishPriceLevels(mainSocket), 1000) : undefined; const onMessageCallback = message => processMessage(mainSocket, message); const onHeartbeatCallback = () => { for (const networkId of Object.keys(SUPPORTED_PAIRS)) { sendMessage(mainSocket, 'subscribeToTrades', { networkId, pool: POOL }); } }; const onCloseCallback = () => { if (SUPPORT_AGGREGATORS) { clearInterval(levelsInterval); } mainSocket = connectToHashflow(); }; const connectToHashflow = () => { return getWebsocketConnection( MARKET_MAKER_NAME, onMessageCallback, onCloseCallback, onHeartbeatCallback, ); } let mainSocket = connectToHashflow(); The ‘TestMM’ is replaced with yours after contacting the team. You should also refer to this sample codebase to see how to connect to the WebSocket from Hashflow. CREATE A POOL You will need to connect to your wallet after connecting to the WebServer to create a Pool that will offer quotes. To do this, head over using this link and fill in your Pool name, signer address, and finally identify if you want your Pool to be public or private. After completing the process, you should get a page like this: RECEIVE RFQ AND RESPOND WITH A QUOTE Now to the interesting part; Hashflow receives a request-for-quote from a user when they want to trade to our server which then supplies the data to the specified market maker: { "messageType": "rfq", "message": { // This is a unique RFQ ID -- you need to use this when sending back a quote. "rfqId": string, // This will be something like: hashflow, 1inch. This is useful // since 1inch charge fees for their trades "source": string, // 1 for ETH L1 "networkId": number, // Base token (the token the trader sells). "baseToken": string, // contract address "baseTokenName": string, // token name (e.g. USDC, ETH, ...) "baseTokenNumDecimals": number, // token decimals (e.g. DAI: 18, USDC: 6) // Quote token (the token the trader buys). "quoteToken": string, // contract address "quoteTokenName": string, // token name (e.g. USDC, ETH, ...) "quoteTokenNumDecimals": number, // token decimals (e.g. DAI: 18, USDC: 6) // Exactly one of the following fields will be present in the RFQ. // If baseTokenAmount is present, quoteTokenAmount needs to be filled by the quote. // If quoteTokenAmount is present, baseTokenAmount needs to be filled by the quore. // Amounts are in decimals, e.g. "1000000" for 1 USDT. "baseTokenAmount": ?string, "quoteTokenAmount": ?string, // The trader wallet address that will swap with the contract. This can be a proxy // contract (e.g. 1inch) "trader": string, // The wallet address of the actual trader (e.g. end user wallet for 1inch). // This is helpful in order to understand user behavior. // If effectiveTrader is not present, you can assume that trader == effectiveTrader. "effectiveTrader": ?string, } } And based on your implemented logic in your codebase, a quote is returned to the user in this format as well: { "messageType": "quote", "message": { "rfqId": string, // This should be the same rfqId that was sent by the server "pool": string, // This should be the contract address of the pool. // This is optional. If using an EOA (externally owned account), this should // contain the wallet address of the EOA. // The EOA needs to have allowance set to the Pool. "eoa": ?string, // Same as RFQ "baseToken": string, "quoteToken": string, // Amounts are in decimals. "baseTokenAmount": string, "quoteTokenAmount": string, // Set this to "0" for private pool / EOA trading. "fees": string, // The unix timestamp when the quote expires, in seconds. "quoteExpiry": number, } } SUPPORT SIGNING QUOTES This only happens if the user doesn’t supply a specific market maker, and in that context, Hashflow will make a request for a quote from all market makers and picks the best one. When the best quote is picked, we send a signQuote message type back to the market maker for them to sign (get a signature from them). The request is also in this format: "messageType": "signQuote", "message": { // The RFQ ID that generated the quote. "rfqId": string, "networkId": number, // The chain ID (e.g. 1 for Ethereum mainnet) "quoteData": { "txid": string, // Unique identifier of the quote -- different from the RFQ ID. "pool": string, "eoa": string, "baseToken": string, "quoteToken": string, "baseTokenAmount": string, "quoteTokenAmount": string, "fees": string, "quoteExpiry": number, // The account that will be executing the swap. For 1inch, this is the 1inch proxy. "trader": string, // Trader actually executing the swap, if different from 'trader'. "effectiveTrader": ?string, // The following parameter is internal to hashflow contracts. // It is leveraged to mitigate quote replay. "nonce": number } } } GET THE FIRST SIGNED RFQ You can test your WebServer to see if you get a signed RFQ from it by making a request to Hashflow’s staging API with the following body params as a sample: POST https://api-staging.hashflow.com/taker/v1/quote/signedRfq { "networkId": 42, // 42 is Kovan, 1 is Mainnet "source": "hashflow", "baseToken": "0x07de306ff27a2b630b1141956844eb1552b956b5", // USDT (Kovan) "quoteToken": "0xa0a5ad2296b38bd3e3eb59aaeaf1589e8d9a29a9", // WBTC (Kovan) "trader": "0x2150DD462B496bd5e4A37b5411c13319427BE83E", "baseTokenAmount": "1000000", "marketMaker": "TestMM" // don't forget to change this } Once you get a response, then your market maker is set. You should also refer to the documentation for more edge cases. CONCLUSION In this article, we focused on demystifying the connection between Hashflow, its users, and market makers and the key aspects of Hashflow — namely, that it offers bridgeless cross-chain swaps and off-chain market decisions; i.e., quotes are done without automated makers. Hashflow is bringing the multichain world closer to us just as Vitalik of Ethereum had speculated. We are hoping to get more Hashflow support for other chains as this is currently under mass adoption. To also view its metric, head over to this dashboard. JOIN ORGANIZATIONS LIKE BITSO AND COINSQUARE WHO USE LOGROCKET TO PROACTIVELY MONITOR THEIR WEB3 APPS Client-side issues that impact users’ ability to activate and transact in your apps can drastically affect your bottom line. If you’re interested in monitoring UX issues, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.https://logrocket.com/signup/ LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors. Modernize how you debug web and mobile apps — Start monitoring for free. SHARE THIS: * Click to share on Twitter (Opens in new window) * Click to share on Reddit (Opens in new window) * Click to share on LinkedIn (Opens in new window) * Click to share on Facebook (Opens in new window) * * #web3 Hey there, want to help make our blog better? Yea No Thanks Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag. Sign up now STOP GUESSING ABOUT YOUR DIGITAL EXPERIENCE WITH LOGROCKET Get started for free RECENT POSTS: WORKING WITH URLS IN JAVASCRIPT Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc. Joe Attardi Dec 19, 2024 ⋅ 6 min read LAZY LOADING VS. EAGER LOADING In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps. Njong Emy Dec 18, 2024 ⋅ 5 min read HOW TO MIGRATE YOUR NODE.JS APP TO DENO 2.0 Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […] Yashodhan Joshi Dec 17, 2024 ⋅ 7 min read GENERATING OPENAPI API CLIENTS FOR ANGULAR Generate OpenAPI API clients in Angular to speed up frontend development, reduce errors, and ensure consistency with this hands-on guide. Shalitha Suranga Dec 16, 2024 ⋅ 9 min read View all posts LEAVE A REPLYCANCEL REPLY Loading Comments... Write a Comment... Email (Required) Name (Required) Website * First Name: * Last Name: * Email Address: Submit