Lauren Stephanian
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    ## (AirFoil Copy) Introducing SUIFT: Safe User Inception for Tickets Authored by [Matt Stephenson](https://twitter.com/stephensonmatt), [Xynyuan Sun](https://twitter.com/sxysun1), and [Lauren Stephanian](https://twitter.com/lstephanian) Every few years artists go on tour and try to sell fans tickets at the price they deem fair. And each time they do this, many of those under-priced tickets are intercepted by bots and brokers, costing the artist and her fans vast amounts of money almost certainly in the billions of dollars. But there’s now an answer: SUIFT, the Safe User Inception For Tickets. SUIFT uses a unique auction mechanism to ensure: a. The ticket sales process is trustworthy b. The only purchasers of an artist’s tickets should be the true fans, not resellers. c. Ticket buyers pay only the artist’s chosen face value of the tickets. Sound too good to be true? Let’s dive in. ## The Ticketing Industry Famous artists are in a repeated game with their fans. When they go on tour, they seek to choose “cooperate” by charging their fans a little less than they might be willing to pay.[^1] But this is surprisingly difficult to implement in practice, [with some experts claiming](https://www.ftc.gov/system/files/documents/public_events/1413898/slides-online-events-tickets-6-11-19.pdf) the only solution is to effectively charge the “defect” price or to try and prevent ticket resale which has [proven extremely difficult](https://www.rollingstone.com/music/music-news/the-cure-ticketmaster-fees-scalpers-1234698953/). The problem is that, as much as an artist might like to “cooperate”, forcing them to “defect” on their fans can be worth billions. Comparably enormous resale profits go to professional brokers who are not publicly known. But they have gamed the system so thoroughly that, according to a [government report](https://www.gao.gov/products/gao-18-347), they now “represent either the majority or overwhelming majority of ticket sales”. Artists want to sell to fans, at a cooperative price, but professional brokers intercept. ## How SUIFT Fixes This Right now tickets are typically sold by just opening the floodgates on an ostensibly first come, first serve basis. The result, as described above, is that specialized professional resellers win the majority, if not the “overwhelming majority” of the tickets intended for fans.[^2] SUIFT can improve on this by running an effective auction using Flashbots’ SUAVE. SUAVE allows all parties to verify that the auction is being run fairly, while also protecting the valuable information that can otherwise allow auctions to be manipulated.[^3] While running a credible auction is an improvement, we have not yet addressed the artist’s wishes that fans only pay the cooperative face value for the tickets. To accomplish this, we can do two things: 1. Refund the difference between the auction price and the “Face value price” to everyone who attends the show. This means, when a fan enters the venue the night of the show, they get refunded the difference between what they paid in auction and the intended face value. Assume the ticket’s face value is $100, but it went at auction for $300. The night of the concert, when a fan shows their ticket at the venue, they are admitted to the show AND refunded $200.[^4] 2. Allow artists to “amplify” the bids of those they have identified as true fans. We explain this further in the appendix, but the essential upshot is that an artist can use our SUIFT contract to select some set of fans who get their bids automatically increased at no cost to them. That is, if the artist chooses double the bid of every fan, and a fan bids $100, it will be as if they had bid $200. If the winning price is $200, the fan wins the ticket but pays only $100. The “refund at the show” approach uniquely benefits fans based on their willingness to actually attend the show. And the “amplify the bids” approach rewards fans who might be budget constrained or, offer some additional value to the show beyond what they are willing to pay. These two together segment the market such that only true fans should ever want to hold a ticket. The price is right, but only for true fans. # SUIFT Model: Consider a standard setup with $n$ risk neutral bidders indexed $i = (1, 2, \dots, n)$. Each has some valuation $v_i$ which is independently and privately drawn from a common knowledge probability distribution $F_i(v_i)$ on support $([\underline{v}_i, \overline{v}_i])$. Bidder $i$ bids $b_i$ for a good, paying only if they win. Add to this setup a “fan value”; a value some bidders are assumed to provide in addition to the amount they pay for the tickets. Artists who intentionally “underprice” their tickets may do so because there is some benefit to audience members cheering, or singing along or, as discussed earlier, engaging in some longer-term “cooperation” to which underpricing is a response. This “fan value”, assumed (initially) to accrue only to the artist, is the vector $(c_i,\dots, c_n)$.[^5] Ticketmaster's "Verified Fan" program can be thought of as an attempt to set a threshold fan value, such that any fan with a sufficiently high $c$ gets a cheaper ticket. Here we allow the fan value to be more precise, while also being shielded from the fan so they continue to bid their true value. This shielding also frustrates hackers who might seek gain access to a fan's account in order to buy tickets for cheaper. Such a hacker would not be assured of success until they had submitted a bid. Formally, we allow the Artist to specify augmenting bids $(d_i,\dots, d_n)$ which add differentially to each bid $b_i$. We then treat a Vickrey-style auction in which tickets are awarded to the bidder for whom $b_i+d_i>b_j+d_j$ for all $j \ne i$. If the winner pays $\text{max}_{j \ne i} (b_j+d_j)-d_i$, and the Artist pays the difference between the winner's bid and the highest bid, this auction is efficient. Truthful bidding is (weakly) dominant so each bidder $i$ bids their true valuation $b_i=v_i$, and the Artist is similarly truthful about their augmenting bid, setting the vector of bids equal to the fan value, $(d_i,\dots, d_n)=(c_i,\dots, c_n)$. Thus we have fans bidding their truthful valuations for tickets, with some of those fans' bids being increased by the artist themselves. Though the preceding model treats a single ticket, it can be extended to multiple tickets as in a uniform price auction. If we assume that the artist is able to identify their fans (and thus set $(d_i,\dots, d_n)=(c_i,\dots, c_n)$ we have effectively reduced the price while removing re-sellers from the market. ## Adding a Rebate But what if an artist is unable to perfectly identify her fans?[^6] If some non-fans are able to get a "fan bonus", for instance, they can then re-sell tickets with the expectation of profit. Fortunately, a further mechanism can be used to weaken the bargaining power of re-sellers even further: offer the partial bid rebate upon attendance of the concert. To see this consider some fan signal $S$, that can be used by an artist to assess who is or is not a true fan. In the scenario where brokers get tickets, we have a pooling equilibrium where $S$ is insufficient to ensure brokers don't bid. We suggest a refund conditional on attendance may implement the desired separating equilibrium in which only fans bid. Recall that bidders bid $b_i=v_i$ but pay only $b_j<b_i$ due to the Vickrey format. The SUIFT mechanism refunds $b_i-b_j$ conditional on concert entry, which is enforceable since concert entry is tightly controlled (otherwise the tickets would have no value.) Such a refund increases the effective price for Brokers, since they by definition do not want to attend the concert. Brokers who are non-local, perhaps bidding from another country, would find it especially onerous to attend for a refund. On the other hand, Fans face no cost from attending the concert (they rather benefit from it of course.) The figure below depicts the seperating equilibrium, in which Fans get a higher rebate $R_f$ because they find paying the additional fan signal $\bar{S}-S*$ to be incentive compatible. ![image](https://hackmd.io/_uploads/ByiNerZL6.png) [^1]: The artist's fans may "cooperate" by, for instance, being loyal during a difficult period, or by listening to a challenging album multiple times to see if it sticks. [^2]: For an excellent deep-dive into this industry, see Jason Koebler's [reporting.](https://www.vice.com/en/article/m7g45a/why-ticket-brokers-can-get-taylor-swift-tickets-ticketmaster-live-nation-monopoly) [^3]: See for example [the recent lawsuit against Google](https://techpolicy.press/how-google-manipulated-digital-ad-prices-and-hurt-publishers-per-doj/#:~:text=But%20Google%20Ads%20allegedly%20submitted,which%20it%20dubbed%20%E2%80%9CBernanke.%E2%80%9D) alleging that it did not credibly run the auctions as it claimed it did. [^4]: Or $100 if the Swifty already had their bid amplified as in 2. [^5]: It can be thought of as the value of participating and singing along, which makes the concert better for everyone. Or, in other settings, the value of attending a conference and asking good questions, or attending a football game and yelling to influence the refs, etc. [^6]: Complementarity of tickets and bidder budget constraints can introduce similar problems. --- ## A Look at the Code This contract is created by taking in a ticket address, which is the address of the contract where we've described the ERC1155 tokens as tickets, the ticket ID, which is the ticket "type" (changing this would allow us to run different auctions for e.g. different sections) and a ticket supply, indicating the max number of tickets that can be sold, and a charity address, which we will explain a little later on. Please note that this code is untested and unaudited. As of the time of publishing, it is to be used for educational purposes only. ``` pragma solidity ^0.8.8; import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "../suave-geth/suave/sol/libraries/Suave.sol"; import { Tickets } from './Tickets.sol'; contract Auction is ERC1155Holder, Ownable { address [] winners; address [] bidders; uint [] allBidValues; mapping(address => bool) private attendees; mapping(address => uint) private accountForWithdrawals; mapping(address => uint) private boosted; address public immutable TICKET_ADDRESS; uint public immutable TICKET_RESERVE_PRICE; uint public immutable TICKET_SUPPLY; uint public immutable AUCTION_TICKETS_TYPE; address public immutable CHARITY_ADDRESS; uint private immutable DECRYPTION_CONDITION = 10; uint private currMinBid; bool public auctionEnded = false; bool public rebatePeriodEnded = false; struct AuctionBid { address beneficiary; uint256 amount; uint256 timestamp; } AuctionBid[] private bids; AuctionBid[] private winningBids; Tickets tickets = new Tickets(); event BitRefundReceived(address indexed beneficiary, uint256 indexed amount); event MinBidUpdated(uint256 indexed amount); event AuctionCreated(address ticketsAddress, uint auctionTicketsId, uint ticketSupply, uint ticketReservePrice, address charity); event AttendedEvent(address eventgoer); event AuctionEnded(address[] winners); constructor (address _ticketsAddress, uint _auctionTicketsId, uint _ticketSupply, uint _ticketReservePrice, address _charity) { require(_auctionTicketsId == 1 || _auctionTicketsId == 2 || _auctionTicketsId == 3, "Token does not exist"); require(_ticketSupply > 0, 'Must provide supply'); require(_ticketReservePrice > 0, 'Must provide reserve price'); CHARITY_ADDRESS = _charity; AUCTION_TICKETS_TYPE = _auctionTicketsId; TICKET_SUPPLY = _ticketSupply; TICKET_RESERVE_PRICE = _ticketReservePrice; TICKET_ADDRESS = _ticketsAddress; //emit event emit AuctionCreated(_ticketsAddress, _auctionTicketsId, _ticketSupply, _ticketReservePrice, _charity); } ``` In this contract, an artist can graciously identify certain superfans to "boost" i.e. give them an additional amount of cash to boost their bid higher. This is like a fine-tunable version of Ticketmaster’s “Verified Fan” program. Rather than set a single threshold condition for which “Verified” fans get preferential access, here we can provide different “boosts” to different bidders, effectively matching their bids. The result is a more customizable was to help real fans win tickets while paying less. ``` function setBoosted(address _bidder, uint boostAmount) public onlyOwner { require(boostAmount > 0, "boost amount must be greater than 0"); boosted[_bidder] = boostAmount; } function _getBoosted(address _bidder) private onlyOwner { return(boosted[_bidder]); } ``` Here's where the fun begins: a bidder enters their "verbal" bid, which is an indication of what they plan to pay and at the same time sends the base value of the ticket as their "deposit." This deposit can be lost if the bidder wins a ticket in the auction and does not pay the delta between their bid and the face value of the ticket. This is to deter bidders who have no intention of paying from participating. The verbal bid is placed in Suave's confidential store, which uses advanced hardware-based encryption to prevent anyone from viewing or accessing the bid. This is new technology enabling privacy from all other parties in the auction, while providing verifiable guarantees that the auction process was handled fairly. This privacy is essential for ensuring the auction cannot be gamed at the expense of the bidders. Because we are calling an off-chain function (_sendBidToConfidentialStore(), described later below) from within the sendBid function, it will also be off-chain. This means we will need it to return a function selector and accompanying inputs to an on-chain function call to update the data. ``` function sendBid(uint _bidAmount) public returns(bytes) { require(auctionEnded == false, "The auction has ended"); require(_bidAmount >= TICKET_RESERVE_PRICE, "The bid cannot be lower than ticket value"); // check if bidder got a boost from the owner // if so, boost their bid if (_getBoosted(msg.sender) > 0) { _bidAmount += _getBoosted(msg.sender); } // creates a new auction bid, which tracks bidder, their verbal amount, and the time of bid AuctionBid auctionBid = new AuctionBid(msg.sender, _bidAmount, block.timestamp); // send the bid to the confidential store and add bid amount to bid value array _sendBidToConfidentialStore(auctionBid); allBidValues.push(_bidAmount); uint latestMinBid = _getMinBid(); if (currMinBid != latestMinBid){ currMinBid = latestMinBid; emit MinBidUpdated(latestMinBid); } return abi.encodeWithSelector(this._receiveBidInfo.selector, msg.sender, msg.value); } function _receiveBidInfo(address sender, uint value) external returns(bytes) { //note: without this, anyone could call this and modify our storage require(sender == 0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829, "sender should be kettle"); // if bidder hasn't previously bid, they need to pay ticket reserve amount // add them to the bidders list if (bidders[sender] == false){ require(value == TICKET_RESERVE_PRICE, "Required to send ticket reserve amount"); bidders.push(sender); } } function _sendBidToConfidentialStore(AuctionBid _bid) internal view { require(Suave.isConfidential()); address[] memory allowedList = new address[](1); allowedList[0] = 0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829; // wildcard address so any kettle can access // initialize confidential store // note: this is creating a new bid data for every bid, might be good to go back one day and put multiple // bids in one bid data store Suave.Bid memory bid = Suave.newBid(DECRYPTION_CONDITION, allowedList, allowedList, "auctionBid"); // save bid in confidential store Suave.confidentialStore(bid.id, "auctionBid", abi.encode(_bid)); } ``` The owner of the contract can end the auction. Once this is done, the winners are retrieved from SUAVE's confidential store and returned. Because you cannot yet delete from the confidential store (or at least it's non-trivial) we will need a sorting function to sort through the winners. This leaves room for future improvement in gas efficiency. ``` function auctionEnd() public onlyOwner { auctionEnded = true; //determine winners and emit list winningBids = _getWinningBids(); emit AuctionEnded(winningBids); } ``` Once winners are announced, they will need to pay for their tickets. The payment due is the delta between their bid amount and the ticket purchase amount. Again, if they don't end up paying this, they will lose their initial deposit. Again, we will be accessing an off-chain function (_checkIfWinner()), so we will need to return the function selector and accompanying inputs, keeping the off-chain actions off-chain and the on-chain actions on-chain. ``` function payForTickets() public payable returns(bytes) { require(auctionEnded, "Auction still ongoing"); require(_checkIfWinner(msg.sender), "Did not win tickets"); //assume payment / msg.value works for now require(msg.value >= (_getAmountOwed(msg.sender) - TICKET_RESERVE_PRICE), "Payment amout incorrect"); return abi.encodeWithSelector(this._onchainPayForTickets.selector, msg.sender); } function _onchainPayForTickets(address _sender) public { require(sender == 0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829, "sender should be kettle"); // mint tickets to msg.sender tickets.mint(msg.sender, AUCTION_TICKETS_TYPE, 1); } ``` Post the event occurance, wow do we know who attended the concert? Easy, the owner of the contract will mark them as “attended.” Ultimately you might want to automate this step of the process, by pulling in data from a ticket scanning API. However, for ease of understanding, in this example the owner manually marks each participant as "attended." Once a participant is marked as having attended the event they are able to collect their rebate - the delta between their bid amount and the face value of the ticket. They receive this only if they attended the concert. All auction winners who don't attend the concert - taking up the spot that otherwise could have gone to a devoted fan - will not be able to withdraw this rebate. A future design may allow this rebate address to be changed by the attendee, to prevent smart-contract enabled rebate transfer. ``` function setAttendConcert(address _participant) public onlyOwner returns(bytes) { require(auctionEnded, "Auction still ongoing"); require(_checkIfWinner(_participant), "Did not win tickets"); return abi.encodeWithSelector(this._onChainSetAttendConcert.selector, _participant); } function _onChainSetAttendConcert(address _participant) public { require(sender == 0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829, "sender should be kettle"); attendees[_participant] = true; emit AttendedEvent(_participant); // allow particpant to now withdraw rebate - add rebate to their "account" for (uint i = 0; i < bids.length; i++) { if (bids[i].beneficiary == _participant) { accountForWithdrawals[bids[i].beneficiary] += bids[i].amount; } } } //Enable withdrawals for bids that have been overbid function rebateWithdraw() external returns (bool) { require(rebatePeriodEnded == false, "It's not rebate period"); require(_getAttendedConcert(msg.sender), "You did not attend the event"); require(accountForWithdrawals[msg.sender] > 0, "Nothing to withdraw"); uint amountAvailable = accountForWithdrawals[msg.sender]; //mark the rebate as withdrawn and send rebate (bool success,) = payable(msg.sender).call{ value: amountAvailable }(''); emit BitRefundReceived(msg.sender, amountAvailable); return(success); } ``` In this contract, after the rebate period (which the owner of the contract will determine), the uncollected rebates will either be burned or sent to the charity contract address. This ensures that no one maliciously prevents attendees from attending the concert in order to collect their rebates. ``` function burnOrSendRebate(address participant) public onlyOwner { require(rebatePeriodEnded && auctionEnded, "Rebate period is not over yet"); uint burnAmt; for (uint i = 0 ; i < bids.length; i++){ bytes32 encodedAddy = keccak256(abi.encode(bids[i].beneficiary)); if (encodedAddy == keccak256(abi.encode(msg.sender))){ burnAmt = accountForWithdrawals[participant]; (bool sent,) = CHARITY_ADDRESS.call{value: burnAmt}(""); require(sent, "Failed to send Ether"); } } } ``` Now, we get to the meat of the off-chain Suave-related functions: sendBidtoConfidentialStore() and getWinningBids(). These both rely on Suave's precompiles, which are MEVM contracts implemented in native code instead of bytecode. According to documentation, MEVM supports all Ethereum precompiles up to Dencun as well as four classes of additional precompiles: * offchain computation that is too expensive in solidity * calls to API methods to interact with the Confidential Data Store * calls to suavex API Methods to interact with Domain-Specific Services * calls to retrieve context for the confidential compute requests ``` // Internal function to save order details confidentially function _sendBidToConfidentialStore(AuctionBid _bid) internal view { require(Suave.isConfidential()); address[] memory allowedList = new address[](1); allowedList[0] = 0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829; // wildcard address so any kettle can access // initialize confidential store // note: this is creating a new bid data for every bid, might be good to go back one day and put multiple // bids in one bid data store Suave.Bid memory bid = Suave.newBid(DECRYPTION_CONDITION, allowedList, allowedList, "auctionBid"); // save bid in confidential store Suave.confidentialStore(bid.id, "auctionBid", abi.encode(_bid)); } function _getWinningBids() internal returns(AuctionBid []) { currMinBid = _getMinBid(); Suave.Bid[] memory fetchedBids = bid.fetchBids(DECRYPTION_CONDITION, "auctionBid"); // extract bids from confidential store for (uint i = 0; i < fetchedBids.length; i++) { bytes memory bidStruct = (Suave.confidentialRetrieve(fetchedBids[i], "auctionBid")); AuctionBid restoredBid = abi.decode(data, AuctionBid); uint bidVal = restoredBid.amount; // only collecting bids that have won // todo: sort by timestamp and only include ticket_supply number of bids if (bidVal >= currMinBid) { bids[i] = Suave.confidentialRetrieve(bid[i], "auctionBid"); } // if bids have lost, allow bidders to withdraw by adding to their withdrawal account if (bidVal < currMinBid) { accountForWithdrawals[bids[i].beneficiary] += bids[i].amount; } } return bids; } ``` Finally, this section simply describes some internal functions, getters and sorts, for supporting some of the important functions above. ``` function _getAttendedConcert(address _participant) internal returns(bool) { return(attendees[_participant]); } function _checkIfWinner(address _bidder) internal returns (bool) { require(auctionEnded, "auction still ongoing"); _getWinningBids(); for (uint i=0; i < bids.length; i++){ if (bids[i].beneficiary == _bidder) { return true; } } return false; } function _getAmountOwed(address _bidder) internal returns (uint) { require(auctionEnded, "auction still ongoing"); _getWinningBids(); for (uint i=0; i < bids.length; i++){ if (bids[i].beneficiary == _bidder) { return bids[i].amount; } } } function _getMinBid() internal returns(uint) { (uint left, uint i) = allBidValues[0]; (uint right, uint j) = allBidValues[allBidValues.length - 1]; if (i == j) return; uint pivot = bids[uint(left + (right - left) / 2)]; while (i <= j) { while (bids[uint(i)] > pivot) i++; while (pivot > bids[uint(j)]) j--; if (i <= j) { (bids[uint(i)], bids[uint(j)]) = (bids[uint(j)], bids[uint(i)]); i++; j--; } } if (left < j) { _getMinBid(bids, left, j); } if (i < right) { _getMinBid(bids, i, right); } return(allBidValues[TICKET_SUPPLY-1]); } } ``` Note on DDOS and Gas Efficiency: one challenge faced in this auction design was the fact that it's difficult to send and store actual funds on SUAVE privately. Thus we chose to impose penalties for bidders who have bid and then removed the funds they bid before the auction is completed. You can view the code in full on GitHub [here](https://github.com/PanteraCapital/SUIFT-Backend).

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully