-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
US Treasury Auction Example #12
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# US Treasury Auction Example | ||
|
||
This example demonstrates how the US Treasury could use SUAVE to run their auctions. | ||
|
||
While efficient and confidential execution is not an issue the Treasury has, we feel they might benefit from verifiability after each auction. For instance, they may be able to more readily prove that [hacks of the ICBC](https://twitter.com/jameslavish/status/1724541469476991139) did indeed result in lower participation, if that was actually the case. | ||
|
||
This example provides support for multiple types of auctions: TBills, Notes, Bonds, TIPS and FRNs could all potentially be run as different `auctionType`s using this same contract. Please read [this thread](https://twitter.com/jameslavish/status/1577334009092198400) for an explanation of each. | ||
|
||
Anyone can use this contract to run auctions - there are no special privileges. We assume that Treasury would specify the auction that they create and direct traders and other relevant parties to them. | ||
|
||
This example does not move securities once the auction is settled: it simply runs the auction itself. We assume Treasuries would distribute the relevant securities based on the verifiable auction results once it has been run. | ||
|
||
For on-chain assets, you might wish to write another contract the handles the distribution of whatever has been auctioned immediately and without intermediation once the auction has been completed. | ||
|
||
## How to use | ||
|
||
Run `Suave` in development mode: | ||
|
||
``` | ||
cd suave-geth | ||
suave --suave.dev | ||
``` | ||
|
||
Execute the deployment script: | ||
|
||
``` | ||
go run main.go | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.8; | ||
|
||
import "../../suave-geth/suave/sol/libraries/Suave.sol"; | ||
|
||
contract TAuction { | ||
struct Auction { | ||
uint256 id; | ||
string auctionType; | ||
address creator; | ||
uint64 startBlock; | ||
uint64 endBlock; | ||
uint256 totalAmount; | ||
uint16 coupon; | ||
} | ||
|
||
struct Bid { | ||
uint256 amount; | ||
uint16 rate; | ||
} | ||
|
||
Auction[] public auctions; | ||
mapping(uint256 => Suave.BidId[]) private _bids; | ||
|
||
event AuctionCreated(uint256 indexed auctionId, string auctionType, address indexed creator, uint256 startBlock, uint256 endBlock, uint16 coupon); | ||
event AuctionCompleted(uint256 indexed auctionId, uint256 btc, uint16 stopRate); | ||
event BidSubmitted(uint256 indexed auctionId, Suave.BidId bidId); | ||
|
||
// Create a new auction | ||
function createAuction(string memory auctionType, uint256 totalAmount, uint64 duration, uint8 coupon) external returns (uint256) { | ||
uint256 auctionId = auctions.length; | ||
auctions.push(Auction(auctionId, auctionType, msg.sender, block.number, block.number + duration, totalAmount, coupon)); | ||
emit AuctionCreated(auctionId, auctionType, msg.sender, uint64(block.number), uint64(block.number + duration), coupon); | ||
return auctionId; | ||
} | ||
|
||
// Submit a bid (confidentially) | ||
// the decryption condition will be the startBlock | ||
// TODO: why are decryption conditions uint64s, but often we use block numbers, which are generally uint256? | ||
function submitBid(address[] memory bidAllowedPeekers, address[] memory bidAllowedStores, uint64 blockHeight) external payable { | ||
require(Suave.isConfidential(), "Execution must be confidential"); | ||
|
||
bytes memory confidentialInputs = Suave.confidentialInputs(); | ||
(uint256 auctionId, uint256 amount, uint16 rate) = abi.decode(confidentialInputs, (uint256, uint256, uint16)); | ||
|
||
require(auctionId < auctions.length, "Invalid auction ID"); | ||
require(auctions[auctionId].startBlock > block.number, "Auction yet to start"); | ||
require(auctions[auctionId].endBlock < block.number, "Auction ended"); | ||
|
||
if (rate == 0) { | ||
// Store non-competitive bid | ||
Suave.Bid memory bid = Suave.newBid(auctions[auctionId].startBlock, bidAllowedPeekers, bidAllowedStores, "tauction:v0:noncompetitive"); | ||
Suave.confidentialStore(bid.id, "tauction:v0:noncompetitive", confidentialInputs); | ||
// Calculate current total and store for later | ||
bytes memory totalData = Suave.confidentialRetrieve(bid.id, "tauction:v0:noncompetitivetotal"); | ||
uint256 currentTotal = totalData.length > 0 ? abi.decode(totalData, (uint256)) : 0; | ||
uint256 newTotal = currentTotal + amount; | ||
Suave.confidentialStore(bid.id, "tauction:v0:noncompetitivetotal", abi.encode(newTotal)); | ||
emit BidSubmitted(auctionId, bid.id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the current iteration, events cannot be emitted off-chain |
||
} else { | ||
// Store competitive bids, in order of rate offered | ||
Suave.Bid memory bid = Suave.newBid(auctions[auctionId].startBlock, bidAllowedPeekers, bidAllowedStores, "tauction:v0:competitive"); | ||
Suave.Bid[] memory allCompetitiveSortedBids = Suave.fetchBids(blockHeight, "tauction:v0:competitiveSorted"); | ||
|
||
// TODO: I want to store competitive bids in ascending order, where | ||
// 1 is the lowest rate, 2 is slightly higher etc. | ||
// overwriting ids won't work, and I am not sure how to add a new field to the confidentialInputs | ||
|
||
// no sorted bids yet - goes in at rank 1 | ||
if (allCompetitiveSortedBids.length == 0) { | ||
Suave.confidentialStore(1, "tauction:v0:competitiveSorted", confidentialInputs); | ||
} | ||
// some bids in the sorted array, need to insert new one | ||
bool inserted = false; | ||
uint i = 0; | ||
|
||
for (; i < allCompetitiveSortedBids.length; i++) { | ||
// TODO: is this the correct way to retrieve the rate stored for any given bid? | ||
uint16 rateToCompare = abi.decode(Suave.confidentialRetrieve(i, "tauction:v0:competitiveSorted"), (uint16)); | ||
|
||
if (rate < rateToCompare) { | ||
// Insert the current bid here and shift the others | ||
shiftAndStoreBids(i, allCompetitiveSortedBids.length, confidentialInputs); | ||
Suave.confidentialStore(i, "tauction:v0:competitiveSorted", confidentialInputs); | ||
inserted = true; | ||
break; | ||
} | ||
} | ||
|
||
if (!inserted) { | ||
// If the current bid's rate is higher than all, insert at the end | ||
Suave.confidentialStore(allCompetitiveSortedBids.length, "tauction:v0:competitiveSorted", confidentialInputs); | ||
} | ||
|
||
emit BidSubmitted(auctionId, bid.id); | ||
} | ||
} | ||
|
||
// Complete an auction and emit results | ||
// Anyone can call this, as the creator sets the duration | ||
// question: can/should it emit all bids for public verification? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does not matter if you emit the bids, they are in the confidential store so they are not public. Normally, the pattern is to use the onchain callback to return the leak data of the bid that you want to expose. |
||
function completeAuction(uint256 auctionId) external { | ||
require(auctionId < auctions.length, "Invalid auction ID"); | ||
require(auctions[auctionId].endBlock < block.number, "Auction not complete"); | ||
|
||
(uint256 btc, uint16 stopRate) = calculateAuctionOutcome(auctionId); | ||
emit AuctionCompleted(auctionId, btc, stopRate); | ||
} | ||
|
||
|
||
// Calculate the btc and stop rate | ||
function calculateAuctionOutcome(uint256 auctionId) private view returns (uint256 btc, uint256 stopRate) { | ||
Auction memory auction = auctions[auctionId]; | ||
bytes memory nonCompetitiveTotalData = Suave.confidentialRetrieve(auction.startBlock, "tauction:v0:noncompetitivetotal"); | ||
uint256 nonCompetitiveTotal = nonCompetitiveTotalData.length > 0 ? abi.decode(nonCompetitiveTotalData, (uint256)) : 0; | ||
|
||
uint256 remainingAmount = auction.totalAmount - nonCompetitiveTotal; | ||
uint256 totalCompetitiveBidAmount = 0; | ||
bool stopRateSet = false; | ||
|
||
Suave.Bid[] memory competitiveBids = Suave.fetchBids(auction.startBlock, "tauction:v0:competitiveSorted"); | ||
for (uint i = 0; i < competitiveBids.length; i++) { | ||
(uint256 bidAmount, uint16 bidRate) = abi.decode(Suave.confidentialRetrieve(competitiveBids[i].id, "tauction:v0:competitive"), (uint256, uint16)); | ||
totalCompetitiveBidAmount += bidAmount; | ||
|
||
if (!stopRateSet && remainingAmount > 0) { | ||
if (remainingAmount <= bidAmount) { | ||
stopRate = bidRate; | ||
stopRateSet = true; | ||
} | ||
remainingAmount -= bidAmount; | ||
} | ||
} | ||
|
||
btc = (nonCompetitiveTotal + totalCompetitiveBidAmount) / auction.totalAmount; | ||
return (btc, stopRate); | ||
} | ||
|
||
function shiftAndStoreBids(uint startIdx, uint length, bytes memory currentBidData) private { | ||
for (uint j = length; j > startIdx; j--) { | ||
bytes memory bidDataToShift = Suave.confidentialRetrieve(j - 1, "tauction:v0:competitiveSorted"); | ||
Suave.confidentialStore(j, "tauction:v0:competitiveSorted", bidDataToShift); | ||
} | ||
Suave.confidentialStore(startIdx, "tauction:v0:competitiveSorted", currentBidData); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/flashbots/suapp-examples/framework" | ||
) | ||
|
||
func main() { | ||
fr := framework.New() | ||
fr.DeployContract("TAuction.sol/TAuction.json") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you expand the example to also send the transactions? |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used?