-
-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Blink wallet sending attachment (#1293)
* blink attachment * support staging * add staging dashboard link * Revert "add staging dashboard link" This reverts commit a43fa22. * Revert "support staging" This reverts commit 93c15aa. * handle pending payments, code cleanup and comments * stable sats -> stablesats * catch HTTP errors * print wallet currency in debug * disable autocomplete * schema without test() * Fix save since default is not applied for empty strings Formik validation must see 'currency' as undefined and apply the default but the validation before save sees an empty string. * Save transformed config * Remove unnecessary defaults * Prefix HTTP error with text --------- Co-authored-by: ekzyis <[email protected]>
- Loading branch information
1 parent
3d8ae4a
commit 2d139be
Showing
6 changed files
with
238 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,4 @@ mz | |
btcbagehot | ||
felipe | ||
benalleng | ||
rblb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
import { galoyBlinkUrl } from 'wallets/blink' | ||
export * from 'wallets/blink' | ||
|
||
export async function testConnectClient ({ apiKey, currency }, { logger }) { | ||
currency = currency ? currency.toUpperCase() : 'BTC' | ||
logger.info('trying to fetch ' + currency + ' wallet') | ||
await getWallet(apiKey, currency) | ||
logger.ok(currency + ' wallet found') | ||
} | ||
|
||
export async function sendPayment (bolt11, { apiKey, currency }) { | ||
const wallet = await getWallet(apiKey, currency) | ||
const preImage = await payInvoice(apiKey, wallet, bolt11) | ||
return { preImage } | ||
} | ||
|
||
async function payInvoice (authToken, wallet, invoice) { | ||
const walletId = wallet.id | ||
const out = await request(authToken, ` | ||
mutation LnInvoicePaymentSend($input: LnInvoicePaymentInput!) { | ||
lnInvoicePaymentSend(input: $input) { | ||
status | ||
errors { | ||
message | ||
path | ||
code | ||
} | ||
transaction { | ||
settlementVia { | ||
... on SettlementViaIntraLedger { | ||
preImage | ||
} | ||
... on SettlementViaLn { | ||
preImage | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`, | ||
{ | ||
input: { | ||
paymentRequest: invoice, | ||
walletId | ||
} | ||
}) | ||
const status = out.data.lnInvoicePaymentSend.status | ||
const errors = out.data.lnInvoicePaymentSend.errors | ||
if (errors && errors.length > 0) { | ||
throw new Error('failed to pay invoice ' + errors.map(e => e.code + ' ' + e.message).join(', ')) | ||
} | ||
|
||
// payment was settled immediately | ||
if (status === 'SUCCESS') { | ||
const preimage = out.data.lnInvoicePaymentSend.transaction.settlementVia.preImage | ||
if (!preimage) throw new Error('no preimage') | ||
return preimage | ||
} | ||
|
||
// payment failed immediately | ||
if (status === 'FAILED') { | ||
throw new Error('failed to pay invoice') | ||
} | ||
|
||
// payment couldn't be settled (or fail) immediately, so we wait for a result | ||
if (status === 'PENDING') { | ||
while (true) { | ||
// at some point it should either be settled or fail on the backend, so the loop will exit | ||
await new Promise(resolve => setTimeout(resolve, 100)) | ||
|
||
const txInfo = await getTxInfo(authToken, wallet, invoice) | ||
// settled | ||
if (txInfo.status === 'SUCCESS') { | ||
if (!txInfo.preImage) throw new Error('no preimage') | ||
return txInfo.preImage | ||
} | ||
// failed | ||
if (txInfo.status === 'FAILED') { | ||
throw new Error(txInfo.error || 'failed to pay invoice') | ||
} | ||
// still pending | ||
// retry later | ||
} | ||
} | ||
|
||
// this should never happen | ||
throw new Error('unexpected error') | ||
} | ||
|
||
async function getTxInfo (authToken, wallet, invoice) { | ||
const walletId = wallet.id | ||
let out | ||
try { | ||
out = await request(authToken, ` | ||
query GetTxInfo($walletId: WalletId!, $paymentRequest: LnPaymentRequest!) { | ||
me { | ||
defaultAccount { | ||
walletById(walletId: $walletId) { | ||
transactionsByPaymentRequest(paymentRequest: $paymentRequest) { | ||
status | ||
direction | ||
settlementVia { | ||
... on SettlementViaIntraLedger { | ||
preImage | ||
} | ||
... on SettlementViaLn { | ||
preImage | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`, | ||
{ | ||
paymentRequest: invoice, | ||
walletId | ||
}) | ||
} catch (e) { | ||
// something went wrong during the query, | ||
// maybe the connection was lost, so we just return | ||
// a pending status, the caller can retry later | ||
return { | ||
status: 'PENDING', | ||
preImage: null, | ||
error: '' | ||
} | ||
} | ||
const tx = out.data.me.defaultAccount.walletById.transactionsByPaymentRequest.find(t => t.direction === 'SEND') | ||
if (!tx) { | ||
// the transaction was not found, something went wrong | ||
return { | ||
status: 'FAILED', | ||
preImage: null, | ||
error: 'transaction not found' | ||
} | ||
} | ||
const status = tx.status | ||
const preImage = tx.settlementVia.preImage | ||
return { | ||
status, | ||
preImage, | ||
error: '' | ||
} | ||
} | ||
|
||
async function getWallet (authToken, currency) { | ||
const out = await request(authToken, ` | ||
query me { | ||
me { | ||
defaultAccount { | ||
wallets { | ||
id | ||
walletCurrency | ||
} | ||
} | ||
} | ||
} | ||
`, {}) | ||
const wallets = out.data.me.defaultAccount.wallets | ||
for (const wallet of wallets) { | ||
if (wallet.walletCurrency === currency) { | ||
return wallet | ||
} | ||
} | ||
throw new Error(`wallet ${currency} not found`) | ||
} | ||
|
||
async function request (authToken, query, variables = {}) { | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-API-KEY': authToken | ||
}, | ||
body: JSON.stringify({ query, variables }) | ||
} | ||
const res = await fetch(galoyBlinkUrl, options) | ||
if (res.status >= 400 && res.status <= 599) { | ||
if (res.status === 401) { | ||
throw new Error('unauthorized') | ||
} else { | ||
throw new Error('API responded with HTTP ' + res.status) | ||
} | ||
} | ||
return res.json() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { blinkSchema } from '@/lib/validate' | ||
|
||
export const galoyBlinkUrl = 'https://api.blink.sv/graphql' | ||
export const galoyBlinkDashboardUrl = 'https://dashboard.blink.sv/' | ||
|
||
export const name = 'blink' | ||
|
||
export const fields = [ | ||
{ | ||
name: 'apiKey', | ||
label: 'api key', | ||
type: 'password', | ||
help: `you can get an API key from [Blink Dashboard](${galoyBlinkDashboardUrl})`, | ||
placeholder: 'blink_...' | ||
}, | ||
{ | ||
name: 'currency', | ||
label: 'wallet type', | ||
type: 'text', | ||
help: 'the blink wallet to use (BTC or USD for stablesats)', | ||
placeholder: 'BTC', | ||
optional: true, | ||
clear: true, | ||
autoComplete: 'off' | ||
} | ||
] | ||
|
||
export const card = { | ||
title: 'Blink', | ||
subtitle: 'use [Blink](https://blink.sv/) for payments', | ||
badges: ['send only'] | ||
} | ||
|
||
export const fieldValidation = blinkSchema |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters