partial withdrawl

This commit is contained in:
keyan 2021-05-12 20:51:37 -05:00
parent d92fc12187
commit 7a8afd56c3
7 changed files with 156 additions and 40 deletions

View File

@ -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
}
}
}

View File

@ -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!
}
`

View File

@ -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

35
pages/withdrawls/[id].js Normal file
View File

@ -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>
}

View File

@ -66,26 +66,3 @@ BEGIN
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;
$$;

View File

@ -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;

View File

@ -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;
$$;