Skip to content

Commit

Permalink
mango v4 sandbox
Browse files Browse the repository at this point in the history
  • Loading branch information
tlrjs committed May 4, 2022
1 parent 8a9a787 commit c889846
Show file tree
Hide file tree
Showing 25 changed files with 3,087 additions and 210 deletions.
6 changes: 6 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
**/node_modules/*
**/out/*
**/.next/*
**/public/charting_library/*
**/public/datafeeds/*
**/components/charting_library/*
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.serverless/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
.env

# dependencies
/node_modules
Expand Down
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.next
yarn.lock
package-lock.json
public
components/charting_library
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"semi": false,
"singleQuote": true
}
82 changes: 82 additions & 0 deletions components/DepositModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState } from 'react'

import mangoStore from '../store/state'
import Button from './shared/Button'
import Loading from './shared/Loading'
import Modal from './shared/Modal'

type DepositModalProps = {
isOpen: boolean
onClose: () => void
}

function DepositModal({ isOpen, onClose }: DepositModalProps) {
const [inputAmount, setInputAmount] = useState('')
const [submitting, setSubmitting] = useState(false)
const [selectedToken, setSelectedToken] = useState('USDC')

const handleDeposit = async () => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const actions = mangoStore.getState().actions
const mangoAccount = mangoStore.getState().mangoAccount
if (!mangoAccount || !group) return
console.log(1)
setSubmitting(true)
const tx = await client.deposit(
group,
mangoAccount,
selectedToken,
parseFloat(inputAmount)
)
console.log(2, tx)
await actions.reloadAccount()
setSubmitting(false)
console.log(3)
onClose()
}

const handleTokenSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedToken(e.target.value)
}

return (
<Modal isOpen={isOpen} onClose={onClose}>
<div>
<div className="relative mt-1 rounded-md shadow-sm">
<div className="absolute inset-y-0 left-0 flex items-center">
<label htmlFor="token" className="sr-only">
Token
</label>
<select
id="token"
name="token"
autoComplete="token"
className="h-full rounded-md border-transparent bg-transparent py-0 pl-3 pr-7 text-gray-500 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
onChange={handleTokenSelect}
>
<option>USDC</option>
<option>BTC</option>
</select>
</div>
<input
type="text"
name="deposit"
id="deposit"
className="block w-full rounded-md border-gray-300 pl-24 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00"
value={inputAmount}
onChange={(e) => setInputAmount(e.target.value)}
/>
</div>
</div>
<div className="mt-4 flex justify-center">
<Button onClick={handleDeposit} className="flex items-center">
{submitting ? <Loading className="mr-2 h-5 w-5" /> : null} Deposit
</Button>
</div>
</Modal>
)
}

export default DepositModal
55 changes: 55 additions & 0 deletions components/MangoAccount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import mangoStore from '../store/state'
import ExplorerLink from './shared/ExplorerLink'

const MangoAccount = () => {
const mangoAccount = mangoStore((s) => s.mangoAccount)
const group = mangoStore((s) => s.group)

if (!mangoAccount) return null

const activeTokens = mangoAccount
? mangoAccount.tokens.filter((ta) => ta.isActive())
: []

const banks = group?.banksMap
? Array.from(group?.banksMap, ([key, value]) => ({ key, value }))
: []

return (
<div key={mangoAccount.publicKey.toString()}>
<div
key={mangoAccount?.publicKey.toString()}
className="rounded border p-4"
>
Mango Account:{' '}
<ExplorerLink address={mangoAccount?.publicKey.toString()} />
{activeTokens.map((ta, idx) => {
return (
<div key={idx} className="mt-2 rounded border p-2">
<div>Token Index {ta.tokenIndex}</div>
<div>Indexed Value {ta.indexedValue.toNumber()}</div>
<div>In Use Count {ta.inUseCount}</div>
</div>
)
})}
<div className="mt-2 space-y-2 rounded border p-2">
{banks.map((bank) => {
return (
<div key={bank.key}>
<div>
Deposit:{' '}
{mangoAccount.getNativeDeposit(bank.value).toNumber()}
</div>
<div>
Borrows: {mangoAccount.getNativeBorrow(bank.value).toNumber()}
</div>
</div>
)
})}
</div>
</div>
</div>
)
}

export default MangoAccount
204 changes: 204 additions & 0 deletions components/SerumOrder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { DEVNET_SERUM3_PROGRAM_ID } from '@blockworks-foundation/mango-v4'
import {
Serum3OrderType,
Serum3SelfTradeBehavior,
Serum3Side,
} from '@blockworks-foundation/mango-v4/dist/accounts/serum3'
import { Order } from '@blockworks-foundation/mango-v4/node_modules/@project-serum/serum/lib/market'
import { useState } from 'react'
import mangoStore from '../store/state'
import Button from './shared/Button'
import ExplorerLink from './shared/ExplorerLink'

const SerumOrder = () => {
const markets = mangoStore((s) => s.markets)
const serumOrders = mangoStore((s) => s.serumOrders)
const actions = mangoStore.getState().actions
const mangoAccount = mangoStore.getState().mangoAccount

console.log('mangoAccount', mangoAccount)

const [tradeForm, setTradeForm] = useState({ side: '', size: '', price: '' })

const handlePlaceOrder = async () => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount

if (!group || !mangoAccount) return

try {
const side = tradeForm.side === 'buy' ? Serum3Side.bid : Serum3Side.ask

const tx = await client.serum3PlaceOrder(
group,
mangoAccount,
DEVNET_SERUM3_PROGRAM_ID,
'BTC/USDC',
side,
parseFloat(tradeForm.price),
parseFloat(tradeForm.size),
Serum3SelfTradeBehavior.decrementTake,
Serum3OrderType.limit,
Date.now(),
10
)
console.log('tx', tx)
actions.reloadAccount()
actions.loadSerumMarket()
} catch (e) {
console.log('Error placing order:', e)
}
}

const cancelOrder = async (order: Order) => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount

if (!group || !mangoAccount) return

try {
const tx = await client.serum3CancelOrder(
group,
mangoAccount,
DEVNET_SERUM3_PROGRAM_ID,
'BTC/USDC',
order.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
order.orderId
)
actions.reloadAccount()
actions.loadSerumMarket()
console.log('tx', tx)
} catch (e) {
console.log('error cancelling order', e)
}
}

return (
<div className="rounded border p-4">
Serum 3
<div className="rounded border p-2">
{markets?.map((m) => {
return (
<div key={m.name}>
<div>
{m.name}: <ExplorerLink address={m.publicKey.toString()} />
</div>
<div>Market Index: {m.marketIndex}</div>
<div>
{serumOrders?.map((o) => {
const ooAddress = o.openOrdersAddress
const myOrder = mangoAccount?.serum3
.map((s) => s.openOrders.toString())
.includes(ooAddress.toString())

return (
<div
key={`${o.side}${o.size}${o.price}`}
className="my-1 rounded border p-2"
>
<div className="flex items-center justify-between">
<div>
<div>Side: {o.side}</div>
<div>Size: {o.size}</div>
<div>Price: {o.price}</div>
</div>
{myOrder ? (
<div>
<Button onClick={() => cancelOrder(o)}>
Cancel
</Button>
</div>
) : null}
</div>
</div>
)
})}
</div>
</div>
)
})}
</div>
<form className="mt-4">
<div>
<label
htmlFor="side"
className="block text-sm font-medium text-gray-700"
>
Side
</label>
<div className="mt-1">
<input
type="text"
name="side"
id="side"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="buy"
value={tradeForm.side}
onChange={(e) =>
setTradeForm((prevState) => ({
...prevState,
side: e.target.value,
}))
}
/>
</div>
</div>
<div>
<label
htmlFor="size"
className="block text-sm font-medium text-gray-700"
>
Size
</label>
<div className="mt-1">
<input
type="number"
name="size"
id="size"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00"
value={tradeForm.size}
onChange={(e) =>
setTradeForm((prevState) => ({
...prevState,
size: e.target.value,
}))
}
/>
</div>
</div>
<div>
<label
htmlFor="price"
className="block text-sm font-medium text-gray-700"
>
Price
</label>
<div className="mt-1">
<input
type="number"
name="price"
id="price"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
placeholder="0.00"
value={tradeForm.price}
onChange={(e) =>
setTradeForm((prevState) => ({
...prevState,
price: e.target.value,
}))
}
/>
</div>
</div>
</form>
<div className="mt-4 flex justify-center">
<Button onClick={handlePlaceOrder}>Place Order</Button>
</div>
</div>
)
}

export default SerumOrder
Loading

0 comments on commit c889846

Please sign in to comment.