Implement "sell all shares" functionality in sellshares and expose API (#696)

* Change `sellshares` to be able to sell all shares

* Sell all shares properly on bet panel UI

* Add API route for selling shares, document
This commit is contained in:
Marshall Polaris 2022-07-26 12:47:19 -07:00 committed by GitHub
parent ad46a60c4f
commit b506e96548
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 5 deletions

View File

@ -579,6 +579,26 @@ $ curl https://manifold.markets/api/v0/market/{marketId}/resolve -X POST \
]}' ]}'
``` ```
### `POST /v0/market/[marketId]/sell`
Sells some quantity of shares in a market on behalf of the authorized user.
Parameters:
- `outcome`: Required. One of `YES`, `NO`, or a `number` indicating the numeric
bucket ID, depending on the market type.
- `shares`: Optional. The amount of shares to sell of the outcome given
above. If not provided, all the shares you own will be sold.
Example request:
```
$ curl https://manifold.markets/api/v0/market/{marketId}/sell -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"outcome": "YES", "shares": 10}'
```
### `GET /v0/bets` ### `GET /v0/bets`
Gets a list of bets, ordered by creation date descending. Gets a list of bets, ordered by creation date descending.

View File

@ -16,7 +16,7 @@ import { redeemShares } from './redeem-shares'
const bodySchema = z.object({ const bodySchema = z.object({
contractId: z.string(), contractId: z.string(),
shares: z.number(), shares: z.number().optional(), // leave it out to sell all shares
outcome: z.enum(['YES', 'NO']), outcome: z.enum(['YES', 'NO']),
}) })
@ -49,11 +49,12 @@ export const sellshares = newEndpoint({}, async (req, auth) => {
const outcomeBets = userBets.filter((bet) => bet.outcome == outcome) const outcomeBets = userBets.filter((bet) => bet.outcome == outcome)
const maxShares = sumBy(outcomeBets, (bet) => bet.shares) const maxShares = sumBy(outcomeBets, (bet) => bet.shares)
const sharesToSell = shares ?? maxShares
if (!floatingLesserEqual(shares, maxShares)) if (!floatingLesserEqual(sharesToSell, maxShares))
throw new APIError(400, `You can only sell up to ${maxShares} shares.`) throw new APIError(400, `You can only sell up to ${maxShares} shares.`)
const soldShares = Math.min(shares, maxShares) const soldShares = Math.min(sharesToSell, maxShares)
const unfilledBetsSnap = await transaction.get( const unfilledBetsSnap = await transaction.get(
getUnfilledBetsQuery(contractDoc) getUnfilledBetsQuery(contractDoc)

View File

@ -771,7 +771,9 @@ export function SellPanel(props: {
const betDisabled = isSubmitting || !amount || error const betDisabled = isSubmitting || !amount || error
// Sell all shares if remaining shares would be < 1 // Sell all shares if remaining shares would be < 1
const sellQuantity = amount === Math.floor(shares) ? shares : amount const isSellingAllShares = amount === Math.floor(shares)
const sellQuantity = isSellingAllShares ? shares : amount
async function submitSell() { async function submitSell() {
if (!user || !amount) return if (!user || !amount) return
@ -780,7 +782,7 @@ export function SellPanel(props: {
setIsSubmitting(true) setIsSubmitting(true)
await sellShares({ await sellShares({
shares: sellQuantity, shares: isSellingAllShares ? undefined : amount,
outcome: sharesOutcome, outcome: sharesOutcome,
contractId: contract.id, contractId: contract.id,
}) })

View File

@ -0,0 +1,28 @@
import { NextApiRequest, NextApiResponse } from 'next'
import {
CORS_ORIGIN_MANIFOLD,
CORS_ORIGIN_LOCALHOST,
} from 'common/envs/constants'
import { applyCorsHeaders } from 'web/lib/api/cors'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
export const config = { api: { bodyParser: true } }
export default async function route(req: NextApiRequest, res: NextApiResponse) {
await applyCorsHeaders(req, res, {
origin: [CORS_ORIGIN_MANIFOLD, CORS_ORIGIN_LOCALHOST],
methods: 'POST',
})
const { id } = req.query
const contractId = id as string
if (req.body) req.body.contractId = contractId
try {
const backendRes = await fetchBackend(req, 'sellshares')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ message: 'Error communicating with backend.' })
}
}