{"componentChunkName":"component---src-templates-project-jsx","path":"/work/gloom","result":{"data":{"prismicProject":{"uid":"gloom","data":{"project_title":{"html":"<h1>Gloom</h1>","text":"Gloom"},"project_hero_image":{"url":"https://images.prismic.io/michael-portfolio/bc56d486-adcc-4842-9f5a-820f18ff4654_gloom-hero.png?auto=compress%2Cformat","alt":"Gloom logo"},"body":[{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<p>How decentralized is <strong>DeFi</strong>? If you look at the <a target=\"_blank\" rel=\"noopener\" href=\"https://twitter.com/simoneconti_/status/1291396627165569026\">ownership of governance tokens</a>, the answer is &quot;not very.&quot; With few exceptions, a handful of addresses hold majority stakes in most DeFi projects.</p>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/328a7c52-b4b5-49bd-9ce9-3338156c903b_defi-token-holder-analysis.png?auto=compress,format\" alt=\"DeFi token holders analysis\" copyright=\"\">\n    </p>\n  <p>Let&#39;s say you&#39;re a founder or investor in a top project like <strong>Compound</strong>, <strong>Maker</strong>, <strong>Synthetix</strong>, <strong>Aave</strong> or <strong>Chainlink</strong> and you&#39;d like to sell a large stake. The market caps for the tokens of these projects is in the billions, so even a 5% stake is worth hundreds of millions of USD.</p>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/ae6367ab-150f-4427-90a3-22a5d20af8ad_defi-token-market-caps.png?auto=compress,format\" alt=\"DeFi token market capitalizations\" copyright=\"\">\n    </p>\n  <p>How could you go about selling your tokens? There wouldn&#39;t be enough order book liquidity on a centralized exchange like <strong>Binance</strong>, nor enough <a target=\"_blank\" rel=\"noopener\" href=\"https://info.uniswap.org/pairs\">pair liquidity</a> on a decentralized AMM like <strong>Uniswap</strong>.  </p>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/c0bc2d79-c4ec-4919-af1b-b74f907e218d_uniswap-defi-pairs.png?auto=compress,format\" alt=\"ETH pair liquidity for DeFi tokens on Uniswap\" copyright=\"\">\n    </p>\n  <p>Large transaction today are typically negotiated off-chain through personal relationships, or in a centralized way through market makers. I think there&#39;s a better, decentralized way: a private auction – akin to an <strong>M&amp;A sales process</strong> in traditional finance – conducted on the Ethereum blockchain.</p><p>This idea is the inspiration for <strong>Gloom</strong>, a platform I built at the ConsenSys Blockchain Developer bootcamp which enables a seller to hold an invite-only auction of ERC-20 tokens. Using Gloom, a seller invites bidders (contacts and/or &quot;whales&quot;) to bid on the tokens, with the seller and winning bidder exchanging tokens and payment (in ETH) through an escrow contract.</p>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/74a53a35-3c34-4a59-abfb-c28f693d549b_gloom-home.png?auto=compress,format\" alt=\"Gloom homepage\" copyright=\"\">\n    </p>\n  <h2>Auction process</h2><ol><li><strong>Setup</strong>: seller configures the type and amount of tokens, makes an ETH deposit into the auction contract, invites bidders (Ethereum addresses) and sets a bidder ETH deposit requirement.</li><li><strong>Commit</strong>: bidders deposit into the auction contract and present their bids (in ETH) for the tokens. Bids are hidden (salted and hashed) and recorded on the blockchain.</li><li><strong>Reveal</strong>: bidders reveal their bids (only if they match the earlier commits), with the winner determined and an escrow contract deployed.</li><li><strong>Deliver</strong>: seller and winning bidder deliver tokens and payment, respectively, to the escrow contract.</li><li><strong>Withdraw</strong>: seller and winning bidder withdraw proceeds and tokens, respectively, from the escrow contract. Everyone withdraws their deposits from the auction contract.</li></ol><h2>Links</h2><ul><li><a target=\"_blank\" rel=\"noopener\" href=\"https://github.com/michaelsmueller/gloom\"><strong>GitHub</strong></a> (mono repo with a React project and Truffle project with Solidity smart contracts)</li><li><a target=\"_blank\" rel=\"noopener\" href=\"https://vimeo.com/493971676\"><strong>Video demo walkthrough</strong></a> (12 minutes)</li></ul><h2>Minimal-proxy</h2>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/c5e477e9-36d3-4fa1-b33c-25a0412bd461_gloom-auction-setup-320.gif?auto=compress,format\" alt=\"Gloom auction setup\" copyright=\"\">\n    </p>\n  <p>As the first step in conducting an auction, the seller configures the type and amount of ERC-20 tokens they wish to sell. On submit, the React frontend (using ethers.js) calls a function on the auction factory  which deploys a new auction  using a <strong>minimal-proxy</strong> pattern, implemented using <strong>Open Zeppelin&#39;s</strong> <a target=\"_blank\" rel=\"noopener\" href=\"https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/packages/lib/contracts/upgradeability/ProxyFactory.sol\">ProxyFactory</a> contract.</p><p>Minimal proxies are a way to clone contracts in order to save on transaction <strong>gas fees</strong>. When I first implemented the factory using a traditional, <a target=\"_blank\" rel=\"noopener\" href=\"https://consensys.net/diligence/blog/2019/09/factories-improve-smart-contract-security/\">non-proxy pattern</a>, creating an auction required more than 1 million gwei of gas vs. a little over 200k currently. Essentially, the minimal proxy reuses the bytecode of a pre-deployed &quot;logic&quot; contract using delegate calls, a pattern standardized in <a target=\"_blank\" rel=\"noopener\" href=\"https://eips.ethereum.org/EIPS/eip-1167\">EIP-1167</a>.</p>"}}},{"__typename":"PrismicProjectBodyCodeSolidity","slice_type":"code_solidity","primary":{"code_text":{"text":"function createAuction(\n  address logic,\n  uint256 tokenAmount,\n  address tokenContractAddress\n) external whenNotPaused() {\n  address seller = msg.sender;\n  bytes memory payload =\n    abi.encodeWithSignature('initialize(address,uint256,address)', seller, tokenAmount, tokenContractAddress);\n  address auction = deployMinimal(logic, payload);\n  auctionAddresses.push(auction);\n  auctionExists[auction] = true;\n  auctionBy[seller] = auction;\n  emit LogAuctionCreated(auction, seller);\n}"}}},{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<h2>Commit-reveal</h2>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/aebb3bdb-d20d-49d1-8747-694eef165f37_gloom-reveal-bid.gif?auto=compress,format\" alt=\"Gloom reveal bid\" copyright=\"\">\n    </p>\n  <p>Since data on the Ethereum blockchain is publicly accessible, Gloom uses a <strong>commit-reveal pattern</strong> to conceal bids. In the commit phase, bid hashes are stored on the blockchain, hiding the amounts. Users supply passwords that are used as salts when hashing in order to prevent brute-force attacks.</p><p>In the reveal phase, bidders re-enter their bids and if the hashes match (which forces bidders to be honest), the bids are stored on the blockchain, with the winner declared. My code is based on <a target=\"_blank\" rel=\"noopener\" href=\"https://medium.com/gitcoin/commit-reveal-scheme-on-ethereum-25d1d1a25428\">Austin Griffith&#39;s commit-reveal implementation</a>.</p>"}}},{"__typename":"PrismicProjectBodyCodeSolidity","slice_type":"code_solidity","primary":{"code_text":{"text":"function getSaltedHash(bytes32 data, bytes32 salt) public view returns (bytes32) {\n  return keccak256(abi.encodePacked(address(this), data, salt));\n}\n\nfunction commitBid(bytes32 dataHash) private {\n  bidders[msg.sender].bidCommit = dataHash;\n  bidders[msg.sender].bidCommitBlock = uint64(block.number);\n  bidders[msg.sender].isBidRevealed = false;\n  emit LogBidCommitted(msg.sender, bidders[msg.sender].bidCommit, bidders[msg.sender].bidCommitBlock);\n}\n\nfunction revealBid(bytes32 bidHex, bytes32 salt) external onlyBidder inReveal {\n  require(bidders[msg.sender].isBidRevealed == false, 'Bid already revealed');\n  require(getSaltedHash(bidHex, salt) == bidders[msg.sender].bidCommit, 'Revealed hash does not match');\n  bidders[msg.sender].isBidRevealed = true;\n  bidders[msg.sender].bidHex = bidHex;\n  emit LogBidRevealed(msg.sender, bidHex, salt);\n}"}}},{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<h2>Escrow delivery </h2>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/a6fb2b68-e3ea-4956-b531-c394a99328e8_gloom-deliver.gif?auto=compress,format\" alt=\"Escrow token & ETH delivery\" copyright=\"\">\n    </p>\n  <p>When the seller triggers the deliver phase, the auction contract declares the winner and deploys a new escrow contract. The seller and bidder then <strong>deposit</strong> their tokens and payment (in ETH), respectively, into the contract, with checks to ensure both have deposited before allowing withdrawals.</p>"}}},{"__typename":"PrismicProjectBodyCodeSolidity","slice_type":"code_solidity","primary":{"code_text":{"text":"function sellerDelivery() external onlySeller {\n  tokenBalance += tokenAmount;\n  sellerOk = true;\n  require(IERC20(tokenContractAddress).transferFrom(msg.sender, address(this), tokenAmount), 'Transfer failed');\n  emit LogSellerDelivered(msg.sender, tokenAmount);\n}\n\nfunction buyerPayment() external payable onlyBuyer {\n  require(msg.value == winningBid, 'Incorrect amount');\n  balance += msg.value;\n  buyerOk = true;\n  emit LogBuyerPaid(msg.sender, msg.value);\n}"}}},{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<h2>Escrow withdrawal</h2>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/5820569d-36b9-40f3-8312-62982de31f28_gloom-withdraw.gif?auto=compress,format\" alt=\"\" copyright=\"\">\n    </p>\n  <p>Once the deposits have been completed, the seller and winning bidder are able to withdraw their deposits from the auction contract, and their sale proceeds and tokens, respectively, from the escrow contract. For <strong>withdrawals</strong>, state updates are performed before transfers made in order to prevent reentrancy attacks, one example of the <a target=\"_blank\" rel=\"noopener\" href=\"https://github.com/michaelsmueller/gloom/blob/main/design_pattern_decisions.md\">design patterns</a> used in Gloom aimed at <a target=\"_blank\" rel=\"noopener\" href=\"https://github.com/michaelsmueller/gloom/blob/main/avoiding_common_attacks.md\">avoiding common attacks</a>. </p>"}}},{"__typename":"PrismicProjectBodyCodeSolidity","slice_type":"code_solidity","primary":{"code_text":{"text":"function sellerWithdraw() external payable onlySeller {\n  require(bothOk(), 'Escrow is not complete');\n  require(withdrawOk, 'Action not authorized now');\n  require(address(this).balance >= winningBid, 'Insufficient balance');\n  balance -= winningBid;\n  (bool success, ) = msg.sender.call.value(winningBid)('');\n  require(success, 'Transfer failed');\n  emit LogSellerWithdrew(msg.sender, winningBid);\n}\n\nfunction buyerWithdraw() external onlyBuyer {\n  require(bothOk(), 'Escrow is not complete');\n  require(withdrawOk, 'Action not authorized now');\n  require(IERC20(tokenContractAddress).balanceOf(address(this)) >= tokenAmount, 'Insufficient balance');\n  tokenBalance -= tokenAmount;\n  require(IERC20(tokenContractAddress).transfer(msg.sender, tokenAmount), 'Transfer failed');\n  emit LogBuyerWithdrew(msg.sender, tokenAmount);\n}"}}},{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<h2>Tests</h2><p>I wrote <strong>unit tests</strong> for the Solidity smart contracts in JavaScript using Truffle&#39;s test framework, which builds on top of Mocha and uses Chai for assertions. I added the <a target=\"_blank\" rel=\"noopener\" href=\"https://www.npmjs.com/package/truffle-assertions\">Truffle Assertions</a> package for testing expected revert conditions (e.g. unauthorized message sender).</p>\n    <p class=\" block-img\">\n      <img src=\"https://images.prismic.io/michael-portfolio/7ee8d8a1-810d-42e1-8f74-06fe931db950_gloom-unit-tests.png?auto=compress,format\" alt=\"Gloom unit tests\" copyright=\"\">\n    </p>\n  <h2><strong>Web3-react</strong></h2><p>On the React frontend, Gloom uses the <a target=\"_blank\" rel=\"noopener\" href=\"https://github.com/NoahZinsmeister/web3-react\">web3-react package</a> by Uniswap&#39;s Noah Zinsmeister to connect with the web3 provider that is injected into the browser (e.g. by MetaMask), putting it into a Context.</p>"}}},{"__typename":"PrismicProjectBodyCodeJsx","slice_type":"code_jsx","primary":{"code_text":{"text":"// web3Context.js\nexport default function Web3ContextProvider({ children }) {\n  const web3Context = useWeb3React();\n  const { activate } = web3Context;\n  useEffect(() => {\n    const injectedConnector = new InjectedConnector({ supportedChainIds: [42, 1337] });\n    activate(injectedConnector);\n  }, [activate]);\n  return <Web3Context.Provider value={{ web3Context }}>{children}</Web3Context.Provider>;\n}\n\n// AppRoute.js\nexport default function AppRoute({ exact, path, component: Component }) {\n  return (\n    <Web3ReactProvider getLibrary={getLibrary}>\n      <Web3ContextProvider>\n        <LoadingContextProvider>\n          <Route\n            exact={exact}\n            path={path}\n            component={() => (\n              <>\n                <Banner />\n                <Component />\n              </>\n            )}\n          />\n        </LoadingContextProvider>\n      </Web3ContextProvider>\n    </Web3ReactProvider>\n  );\n}\n\n// App.js\nexport default function App() {\n  return (\n    <Router>\n      <Head />\n      <GlobalStyle />\n      <Switch>\n        <Route exact path='/' component={Home} />\n        <AppRoute exact path='/seller' component={SellerDashboard} />\n        <AppRoute exact path='/bidder' component={BidderDashboard} />\n        <AppRoute path='*' component={NotFound} />\n      </Switch>\n      <ToastContainer />\n    </Router>\n  );\n}"}}},{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<h2><strong>Hooks &amp; ethers.js</strong></h2><p>A React webapp will typical fetch data from an API, but a dApp like Gloom pulls data from the blockchain using a library like <a target=\"_blank\" rel=\"noopener\" href=\"https://docs.ethers.io/\"><strong>ethers.js</strong></a>, which can connect to Ethereum nodes through an injected provider (e.g. MetaMask, which connects to a node through the Infura API). Effectively, the smart contracts serve here as the <strong>backend</strong>.</p><p>I also frequently used ethers.js to set up contract event listeners within Effect Hooks to watch for state changes on the blockchain and then update the UI once they fire.</p>"}}},{"__typename":"PrismicProjectBodyCodeJsx","slice_type":"code_jsx","primary":{"code_text":{"text":"// useWinner.js Hook\nexport default function useWinner(auctionContract) {\n  const { web3Context } = useContext(Web3Context);\n  const { active } = web3Context;\n  const [winningBid, setWinningBid] = useState(0);\n  const [winningBidder, setWinningBidder] = useState('');\n\n  useEffect(() => {\n    if (!active || !auctionContract) return;\n    const getWinner = async () => {\n      const [bidder, bid] = await auctionContract.getWinner();\n      if (bidder !== '0x0000000000000000000000000000000000000000') {\n        setWinningBidder(bidder);\n        setWinningBid(bid);\n      }\n    };\n    getWinner();\n  }, [active, auctionContract]);\n\n  return { winningBid, setWinningBid, winningBidder, setWinningBidder };\n}\n\n// SellerPhaseSwitcher.js watches for winner to be declared\nexport default function SellerPhaseSwitcher({ auctionAddress }) {\n  const { web3Context } = useContext(Web3Context);\n  const { active } = web3Context;\n  const auctionContract = useContractAt(Auction, auctionAddress);\n  const { setWinningBid, winningBidder, setWinningBidder } = useWinner(auctionContract);\n\n  useEffect(() => {\n    if (!active || !auctionContract) return null;\n    auctionContract.once('LogSetWinner', (bidder, bid) => {\n      toast.success(`${bidder} won the auction with a bid of ${formatEther(bid)}`);\n      setWinningBidder(bidder);\n      setWinningBid(bid);\n    });\n    return () => auctionContract.removeAllListeners('LogSetWinner');\n  });"}}},{"__typename":"PrismicProjectBodyText","slice_type":"text","primary":{"rich_text":{"html":"<h2>Design</h2><p>As the saying goes, &quot;good artists copy, great artists steal.&quot; Gloom&#39;s <strong>neumorphism</strong> &quot;soft UI&quot; design is based on code I forked from <a target=\"_blank\" rel=\"noopener\" href=\"https://github.com/martamullor/neomorphism-frontend\"><strong>Marta Mullor</strong></a> and <a target=\"_blank\" rel=\"noopener\" href=\"https://www.codingnepalweb.com/2020/05/css3-neumorphic-social-media-buttons.html\"><strong>CodingNepal</strong></a>.</p>"}}},{"__typename":"PrismicProjectBodyCodeJavascript","slice_type":"code_javascript","primary":{"code_text":{"text":"// globalStyles.js\nimport { createGlobalStyle } from 'styled-components/macro';\n\nconst GlobalStyle = createGlobalStyle`\n  :root {\n    --textPrimary: rgba(0, 0, 0, 0.8);\n    --textSecondary: rgba(0, 0, 0, 0.60);\n    --textDisabled:rgba(0, 0, 0, 0.38);\n    --nearWhite: #ffffff73;\n    --backgroundPrimary: #dde1e7;\n    --gloomBlue: #536791;\n    --shadow: rgba(94, 104, 121, 0.288);\n    --primary: rgba(188, 0, 45, 1);\n  }\n`;\n\n// buttonStyles.js\nimport styled from 'styled-components/macro';\n\nexport const Button = styled.button`\n  margin: 10px 5px;\n  padding: 10px;\n  outline: none;\n  border: none;\n  border-radius: 20px;\n  background: var(--backgroundPrimary);\n  box-shadow: -3px -3px 5px var(--nearWhite), 3px 3px 3px var(--shadow);\n  font-weight: 600;\n  font-size: ${props => (props.large ? '1.1em' : '0.8em')};\n  transition: 0.1s ease-out;\n\n  color: ${props => (props.active ? 'var(--primary)' : null)};\n  box-shadow: ${props => (props.inactive ? null : '-3px -3px 5px var(--nearWhite), 3px 3px 3px var(--shadow);')};\n\n  &:hover {\n    cursor: pointer;\n    color: var(--primary);\n  }\n\n  &:active {\n    color: var(--primary);\n    box-shadow: inset -3px -3px 5px var(--nearWhite), inset 3px 3px 3px var(--shadow);\n  }\n`;"}}}]}},"site":{"siteMetadata":{"title":"Michael Mueller | web / blockchain & financial advisor","description":"My work portfolio.","author":"Michael Mueller","siteUrl":"https://michaelmueller.dev"}}},"pageContext":{"uid":"gloom"}},"staticQueryHashes":["3649515864"]}