partial withdrawl
This commit is contained in:
parent
d92fc12187
commit
7a8afd56c3
|
@ -1,10 +1,14 @@
|
|||
import { createInvoice, decodePaymentRequest, payViaPaymentRequest } from 'ln-service'
|
||||
import { createInvoice, decodePaymentRequest, subscribeToPayViaRequest } from 'ln-service'
|
||||
import { UserInputError, AuthenticationError } from 'apollo-server-micro'
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
invoice: async (parent, { id }, { me, models, lnd }) => {
|
||||
return await models.invoice.findUnique({ where: { id: Number(id) } })
|
||||
},
|
||||
withdrawl: async (parent, { id }, { me, models, lnd }) => {
|
||||
console.log(models)
|
||||
return await models.withdrawl.findUnique({ where: { id: Number(id) } })
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -21,7 +25,12 @@ export default {
|
|||
// set expires at to 3 hours into future
|
||||
const expiresAt = new Date(new Date().setHours(new Date().getHours() + 3))
|
||||
const description = `${amount} sats for @${me.name} on stacker.news`
|
||||
const invoice = await createInvoice({ description, lnd, tokens: amount, expires_at: expiresAt })
|
||||
const invoice = await createInvoice({
|
||||
description,
|
||||
lnd,
|
||||
tokens: amount,
|
||||
expires_at: expiresAt
|
||||
})
|
||||
|
||||
const data = {
|
||||
hash: invoice.id,
|
||||
|
@ -46,24 +55,29 @@ export default {
|
|||
const decoded = await decodePaymentRequest({ lnd, request: invoice })
|
||||
|
||||
// create withdrawl transactionally (id, bolt11, amount, fee)
|
||||
const withdrawl =
|
||||
await models.$queryRaw`SELECT confirm_withdrawl(${decoded.id}, ${invoice},
|
||||
${decoded.mtokens}, ${Number(maxFee)}, ${me.name})`
|
||||
const [withdrawl] =
|
||||
await models.$queryRaw`SELECT * FROM create_withdrawl(${decoded.id}, ${invoice},
|
||||
${Number(decoded.mtokens)}, ${Number(maxFee)}, ${me.name})`
|
||||
|
||||
// create the payment, subscribing to its status
|
||||
const sub = subscribeToPayViaRequest({ lnd, request: invoice, max_fee_mtokens: maxFee, pathfinding_timeout: 30000 })
|
||||
const sub = subscribeToPayViaRequest({
|
||||
lnd,
|
||||
request: invoice,
|
||||
max_fee_mtokens: maxFee,
|
||||
pathfinding_timeout: 30000
|
||||
})
|
||||
|
||||
// if it's confirmed, update confirmed
|
||||
sub.on('confirmed', recordStatus)
|
||||
sub.on('confirmed', console.log)
|
||||
|
||||
// if the payment fails, we need to
|
||||
// 1. transactionally return the funds to the user
|
||||
// 2. transactionally update the widthdrawl as failed
|
||||
sub.on('failed', recordStatus)
|
||||
sub.on('failed', console.log)
|
||||
|
||||
// in walletd
|
||||
// for each payment that hasn't failed or succeede
|
||||
return 0
|
||||
return withdrawl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@ import { gql } from 'apollo-server-micro'
|
|||
export default gql`
|
||||
extend type Query {
|
||||
invoice(id: ID!): Invoice!
|
||||
withdrawl(id: ID!): Withdrawl!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createInvoice(amount: Int!): Invoice!
|
||||
createWithdrawl(invoice: String!, maxFee: Int!): Int
|
||||
createWithdrawl(invoice: String!, maxFee: Int!): Withdrawl!
|
||||
}
|
||||
|
||||
type Invoice {
|
||||
|
@ -19,4 +20,16 @@ export default gql`
|
|||
confirmedAt: String
|
||||
msatsReceived: Int
|
||||
}
|
||||
|
||||
type Withdrawl {
|
||||
id: ID!
|
||||
createdAt: String!
|
||||
hash: String!
|
||||
bolt11: String!
|
||||
msatsPaying: Int!
|
||||
msatsPaid: Int
|
||||
msatsFeePaying: Int!
|
||||
msatsFeePaid: Int
|
||||
status: String!
|
||||
}
|
||||
`
|
||||
|
|
|
@ -40,7 +40,8 @@ export function WalletForm () {
|
|||
}
|
||||
|
||||
export const FundSchema = Yup.object({
|
||||
amount: Yup.number('must be a number').required('required').positive('must be positive').integer('must be whole')
|
||||
amount: Yup.number().typeError('must be a number').required('required')
|
||||
.positive('must be positive').integer('must be whole')
|
||||
})
|
||||
|
||||
export function FundForm () {
|
||||
|
@ -81,10 +82,12 @@ export function FundForm () {
|
|||
|
||||
export const WithdrawlSchema = Yup.object({
|
||||
invoice: Yup.string().required('required'),
|
||||
maxFee: Yup.number('must be a number').required('required').positive('must be positive').integer('must be whole')
|
||||
maxFee: Yup.number().typeError('must be a number').required('required')
|
||||
.min(0, 'must be positive').integer('must be whole')
|
||||
})
|
||||
|
||||
export function WithdrawlForm () {
|
||||
const router = useRouter()
|
||||
const query = gql`
|
||||
{
|
||||
me {
|
||||
|
@ -95,7 +98,9 @@ export function WithdrawlForm () {
|
|||
|
||||
const [createWithdrawl] = useMutation(gql`
|
||||
mutation createWithdrawl($invoice: String!, $maxFee: Int!) {
|
||||
createWithdrawl(invoice: $invoice, maxFee: $maxFee)
|
||||
createWithdrawl(invoice: $invoice, maxFee: $maxFee) {
|
||||
id
|
||||
}
|
||||
}`)
|
||||
|
||||
return (
|
||||
|
@ -106,13 +111,13 @@ export function WithdrawlForm () {
|
|||
<Form
|
||||
className='pt-3'
|
||||
initial={{
|
||||
destination: '',
|
||||
maxFee: 0,
|
||||
amount: 0
|
||||
invoice: '',
|
||||
maxFee: 0
|
||||
}}
|
||||
schema={WithdrawlSchema}
|
||||
onSubmit={async ({ invoice, maxFee }) => {
|
||||
await createWithdrawl({ variables: { invoice, maxFee: Number(maxFee) } })
|
||||
const { data } = await createWithdrawl({ variables: { invoice, maxFee: Number(maxFee) } })
|
||||
router.push(`/withdrawls/${data.createWithdrawl.id}`)
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { useQuery } from '@apollo/client'
|
||||
import gql from 'graphql-tag'
|
||||
import LayoutCenter from '../../components/layout-center'
|
||||
|
||||
export async function getServerSideProps ({ params: { id } }) {
|
||||
return {
|
||||
props: {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Withdrawl ({ id }) {
|
||||
const query = gql`
|
||||
{
|
||||
withdrawl(id: ${id}) {
|
||||
bolt11
|
||||
}
|
||||
}`
|
||||
return (
|
||||
<LayoutCenter>
|
||||
<LoadWithdrawl query={query} />
|
||||
</LayoutCenter>
|
||||
)
|
||||
}
|
||||
|
||||
function LoadWithdrawl ({ query }) {
|
||||
const { loading, error, data } = useQuery(query, { pollInterval: 1000 })
|
||||
if (error) return <div>error</div>
|
||||
if (!data || loading) {
|
||||
return <div>withdrawl loading</div>
|
||||
}
|
||||
|
||||
return <div>hi</div>
|
||||
}
|
|
@ -65,27 +65,4 @@ BEGIN
|
|||
END IF;
|
||||
RETURN 0;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION create_withdrawl(lnd_id TEXT, bolt11 TEXT, msats_amount INTEGER, msats_max_fee INTEGER, username TEXT)
|
||||
RETURNS INTEGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
user_id INTEGER;
|
||||
user_msats INTEGER;
|
||||
withdrawl "Withdrawl";
|
||||
BEGIN
|
||||
SELECT msats, id INTO user_msats, user_id FROM users WHERE name = username;
|
||||
IF msats_amount + msats_max_fee > user_msats THEN
|
||||
RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
|
||||
END IF;
|
||||
|
||||
INSERT INTO "Withdrawl" (hash, bolt11, "msatsPaying", "msatsFeePaying", "userId", updated_at)
|
||||
VALUES (lnd_id, bolt11, msats_amount, msats_max_fee, user_id, 'now') RETURNING * INTO withdrawl;
|
||||
|
||||
UPDATE users SET msats = msats - msats_amount - msats_max_fee WHERE id = user_id;
|
||||
|
||||
RETURN withdrawl;
|
||||
END;
|
||||
$$;
|
|
@ -0,0 +1,28 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "WithdrawlStatus" AS ENUM ('INSUFFICIENT_BALANCE', 'INVALID_PAYMENT', 'PATHFINDING_TIMEOUT', 'ROUTE_NOT_FOUND');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Withdrawl" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"hash" TEXT NOT NULL,
|
||||
"bolt11" TEXT NOT NULL,
|
||||
"msatsPaying" INTEGER NOT NULL,
|
||||
"msatsPaid" INTEGER,
|
||||
"msatsFeePaying" INTEGER NOT NULL,
|
||||
"msatsFeePaid" INTEGER,
|
||||
"status" "WithdrawlStatus",
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Withdrawl.hash_unique" ON "Withdrawl"("hash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Withdrawl.userId_index" ON "Withdrawl"("userId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Withdrawl" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -0,0 +1,44 @@
|
|||
CREATE OR REPLACE FUNCTION create_withdrawl(lnd_id TEXT, invoice TEXT, msats_amount INTEGER, msats_max_fee INTEGER, username TEXT)
|
||||
RETURNS "Withdrawl"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
user_id INTEGER;
|
||||
user_msats INTEGER;
|
||||
withdrawl "Withdrawl";
|
||||
BEGIN
|
||||
SELECT msats, id INTO user_msats, user_id FROM users WHERE name = username;
|
||||
IF (msats_amount + msats_max_fee) > user_msats THEN
|
||||
RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
|
||||
END IF;
|
||||
|
||||
INSERT INTO "Withdrawl" (hash, bolt11, "msatsPaying", "msatsFeePaying", "userId", updated_at)
|
||||
VALUES (lnd_id, invoice, msats_amount, msats_max_fee, user_id, 'now') RETURNING * INTO withdrawl;
|
||||
|
||||
UPDATE users SET msats = msats - msats_amount - msats_max_fee WHERE id = user_id;
|
||||
|
||||
RETURN withdrawl;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION confirm_withdrawl(lnd_id TEXT, msats_paid INTEGER, msats_fee_paid INTEGER)
|
||||
RETURNS INTEGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
|
||||
BEGIN
|
||||
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION reverse_withdrawl(lnd_id TEXT, msats_paid INTEGER, msats_fee_paid INTEGER)
|
||||
RETURNS INTEGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
|
||||
BEGIN
|
||||
|
||||
END;
|
||||
$$;
|
Loading…
Reference in New Issue