Skip to content

Commit

Permalink
Merge pull request #2207 from kaloudis/twelvecash
Browse files Browse the repository at this point in the history
BOLT 12: offers + Twelve.cash lightning address
  • Loading branch information
kaloudis authored May 28, 2024
2 parents dbec8ee + 98a3072 commit b4ef7a5
Show file tree
Hide file tree
Showing 27 changed files with 1,443 additions and 48 deletions.
27 changes: 27 additions & 0 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ import NostrKeys from './views/Settings/LightningAddress/NostrKeys';
import NostrRelays from './views/Settings/LightningAddress/NostrRelays';
import ChangeAddress from './views/Settings/LightningAddress/ChangeAddress';

// BOLT 12
import PayCodes from './views/PayCodes';
import PayCode from './views/PayCode';
import CreatePayCode from './views/PayCodeCreate';
import Bolt12Address from './views/Settings/Bolt12Address';

//Embedded Node
import EmbeddedNode from './views/Settings/EmbeddedNode';
import DisasterRecovery from './views/Settings/EmbeddedNode/DisasterRecovery';
Expand Down Expand Up @@ -202,6 +208,7 @@ export default class App extends React.PureComponent {
LSPStore={Stores.lspStore}
LightningAddressStore={Stores.lightningAddressStore}
ChannelBackupStore={Stores.channelBackupStore}
OffersStore={Stores.offersStore}
>
<AppContainer>
<PushNotificationManager>
Expand Down Expand Up @@ -735,6 +742,26 @@ export default class App extends React.PureComponent {
ChangeAddress
}
/>
<Stack.Screen
name="PayCodes"
component={PayCodes}
/>
<Stack.Screen
name="PayCode"
component={PayCode}
/>
<Stack.Screen
name="CreatePayCode"
component={
CreatePayCode
}
/>
<Stack.Screen
name="Bolt12Address"
component={
Bolt12Address
}
/>
<Stack.Screen
name="SocialMedia"
component={SocialMedia}
Expand Down
3 changes: 3 additions & 0 deletions assets/images/SVG/AtSign.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 34 additions & 2 deletions backends/CLightningREST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export default class CLightningREST extends LND {
id: `${data.addr.pubkey}@${data.addr.host}`
});
decodePaymentRequest = (urlParams?: Array<string>) =>
this.getRequest(`/v1/pay/decodePay/${urlParams && urlParams[0]}`);
this.getRequest(`/v1/utility/decode/${urlParams && urlParams[0]}`);
payLightningInvoice = (data: any) =>
this.postRequest('/v1/pay', {
invoice: data.payment_request,
Expand Down Expand Up @@ -246,6 +246,33 @@ export default class CLightningREST extends LND {
};
};

// BOLT 12 / Offers
listOffers = () => this.getRequest('/v1/offers/listOffers');
createOffer = ({
description,
label,
singleUse
}: {
description?: string;
label?: string;
singleUse?: boolean;
}) =>
this.postRequest('/v1/offers/offer', {
amount: 'any',
description,
label,
single_use: singleUse || false
});
disableOffer = ({ offer_id }: { offer_id: string }) =>
this.deleteRequest(`/v1/offers/disableOffer/${offer_id}`);
fetchInvoiceFromOffer = async (bolt12: string, amountSatoshis: string) => {
return await this.postRequest('/v1/offers/fetchInvoice', {
offer: bolt12,
msatoshi: Number(amountSatoshis) * 1000,
timeout: 60
});
};

supportsMessageSigning = () => true;
supportsLnurlAuth = () => true;
supportsOnchainSends = () => true;
Expand Down Expand Up @@ -274,7 +301,12 @@ export default class CLightningREST extends LND {
supportsSweep = () => true;
supportsOnchainBatching = () => false;
supportsChannelBatching = () => false;
isLNDBased = () => false;
supportsLSPS1customMessage = () => false;
supportsLSPS1rest = () => true;
supportsOffers = async () => {
const res = await this.getRequest('/v1/utility/listConfigs');
const supportsOffers: boolean = res['experimental-offers'] || false;
return supportsOffers;
};
isLNDBased = () => false;
}
3 changes: 2 additions & 1 deletion backends/Eclair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,10 @@ export default class Eclair {
supportsSweep = () => false;
supportsOnchainBatching = () => false;
supportsChannelBatching = () => false;
isLNDBased = () => false;
supportsLSPS1customMessage = () => false;
supportsLSPS1rest = () => true;
supportsOffers = () => false;
isLNDBased = () => false;
}

const mapInvoice =
Expand Down
3 changes: 2 additions & 1 deletion backends/EmbeddedLND.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ export default class EmbeddedLND extends LND {
supportsSweep = () => true;
supportsOnchainBatching = () => true;
supportsChannelBatching = () => true;
isLNDBased = () => true;
supportsLSPS1customMessage = () => true;
supportsLSPS1rest = () => false;
supportsOffers = () => false;
isLNDBased = () => true;
}
3 changes: 2 additions & 1 deletion backends/LND.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,8 @@ export default class LND {
supportsSweep = () => true;
supportsOnchainBatching = () => true;
supportsChannelBatching = () => true;
isLNDBased = () => true;
supportsLSPS1customMessage = () => true;
supportsLSPS1rest = () => false;
supportsOffers = (): Promise<boolean> | boolean => false;
isLNDBased = () => true;
}
3 changes: 2 additions & 1 deletion backends/LightningNodeConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ export default class LightningNodeConnect {
supportsSweep = () => true;
supportsOnchainBatching = () => true;
supportsChannelBatching = () => true;
isLNDBased = () => true;
supportsLSPS1customMessage = () => true;
supportsLSPS1rest = () => false;
supportsOffers = () => false;
isLNDBased = () => true;
}
3 changes: 2 additions & 1 deletion backends/LndHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ export default class LndHub extends LND {
supportsSweep = () => false;
supportsOnchainBatching = () => false;
supportsChannelBatching = () => true;
isLNDBased = () => false;
supportsLSPS1customMessage = () => false;
supportsLSPS1rest = () => false;
supportsOffers = () => false;
isLNDBased = () => false;
}
3 changes: 2 additions & 1 deletion backends/Spark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,8 @@ export default class Spark {
supportsSweep = () => false;
supportsOnchainBatching = () => false;
supportsChannelBatching = () => true;
isLNDBased = () => false;
supportsLSPS1customMessage = () => false;
supportsLSPS1rest = () => true;
supportsOffers = () => false;
isLNDBased = () => false;
}
24 changes: 23 additions & 1 deletion components/LayerBalances/LightningSwipeableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export default class LightningSwipeableRow extends Component<

if (text === localeString('general.receive')) {
this.props.navigation.navigate('Receive');
} else if (text === localeString('general.paycodes')) {
this.props.navigation.navigate('PayCodes');
} else if (text === localeString('general.routing')) {
this.props.navigation.navigate('Routing');
} else if (text === localeString('general.send')) {
Expand Down Expand Up @@ -83,6 +85,16 @@ export default class LightningSwipeableRow extends Component<
height={30}
/>
)}
{text === localeString('general.paycodes') && (
<Receive
fill={
themeColor('action') ||
themeColor('highlight')
}
width={30}
height={30}
/>
)}
{text === localeString('general.receive') && (
<Receive
fill={
Expand Down Expand Up @@ -120,7 +132,11 @@ export default class LightningSwipeableRow extends Component<
private renderActions = (progress: Animated.AnimatedInterpolation) => {
const width =
BackendUtils.supportsRouting() &&
BackendUtils.supportsLightningSends()
BackendUtils.supportsLightningSends() &&
BackendUtils.supportsOffers()
? 280
: BackendUtils.supportsRouting() &&
BackendUtils.supportsLightningSends()
? 210
: BackendUtils.supportsRouting() ||
BackendUtils.supportsLightningSends()
Expand All @@ -139,6 +155,12 @@ export default class LightningSwipeableRow extends Component<
width,
progress
)}
{BackendUtils.supportsOffers() &&
this.renderAction(
localeString('general.paycodes'),
width,
progress
)}
{BackendUtils.supportsRouting() &&
this.renderAction(
localeString('general.routing'),
Expand Down
23 changes: 23 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"general.received": "Received",
"general.date": "Date",
"general.routing": "Routing",
"general.paycode": "Pay code",
"general.paycodes": "Pay codes",
"general.coins": "Coins",
"general.address": "Address",
"general.utxo": "UTXO",
Expand Down Expand Up @@ -110,6 +112,11 @@
"general.automatic": "Automatic",
"general.custom": "Custom",
"general.skip": "Skip",
"general.type": "Type",
"general.label": "Label",
"general.noLabel": "No label",
"general.used": "Used",
"general.unused": "Unused",
"restart.title": "Restart required",
"restart.msg": "ZEUS has to be restarted before the new configuration is applied.",
"restart.msg1": "Would you like to restart now?",
Expand Down Expand Up @@ -1102,6 +1109,22 @@
"views.Settings.CustodialWalletWarning.graph3": "ZEUS has the ability to create a self-custodial wallet in the app. This wallet provides you with a 24-word seed phrase that gives you full control of your funds.",
"views.Settings.CustodialWalletWarning.graph4": "To get started with your own self-custodial wallet, press the button below, and hit the 'Create mainnet wallet' button on the next screen.",
"views.Settings.CustodialWalletWarning.create": "Create self-custodial wallet",
"views.PayCode.bolt12": "BOLT 12",
"views.PayCode.offerId": "Offer ID",
"views.PayCode.createOffer": "Create pay code",
"views.PayCode.disableOffer": "Disable pay code",
"views.PayCode.confirmDisableOffer": "Confirm disable",
"views.PayCode.singleUse": "Single use",
"views.PayCode.multiUse": "Multi use",
"views.PayCode.internalLabel": "Internal label",
"views.Settings.Bolt12Address": "BOLT 12 address",
"views.Settings.Bolt12Address.requestButton": "Request Paycode",
"views.Settings.Bolt12Address.changeButton": "Change Paycode",
"views.Settings.Bolt12Address.handle": "Your handle",
"views.Settings.Bolt12Address.error.noBolt12": "No BOLT 12 offer found",
"views.Settings.Bolt12Address.error.failedToGetOffer": "Failed to get offer",
"views.Settings.Bolt12Address.error.handleTaken": "Handle is taken",
"views.Settings.Bolt12Address.error.failedToCreate": "Failed to create address",
"view.Settings.LSPServicesList.title": "LSP Services",
"view.Settings.LSPServicesList.flow2": "Just-in-time channels",
"view.Settings.LSPServicesList.lsps1": "Request channels in advance",
Expand Down
22 changes: 22 additions & 0 deletions models/Contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default class Contact extends BaseModel {
id: string; // deprecated
public contactId: string;
public lnAddress: Array<string>;
public bolt12Address: Array<string>;
public onchainAddress: Array<string>;
public pubkey: Array<string>;
public nip05: Array<string>;
Expand All @@ -27,6 +28,18 @@ export default class Contact extends BaseModel {
this.lnAddress &&
this.lnAddress.length === 1 &&
this.lnAddress[0] !== '' &&
(!this.bolt12Address[0] || this.bolt12Address[0] === '') &&
(!this.onchainAddress[0] || this.onchainAddress[0] === '') &&
(!this.pubkey[0] || this.pubkey[0] === '')
);
}

@computed public get isSingleBolt12Address(): boolean {
return (
this.bolt12Address &&
this.bolt12Address.length === 1 &&
this.bolt12Address[0] !== '' &&
(!this.lnAddress[0] || this.lnAddress[0] === '') &&
(!this.onchainAddress[0] || this.onchainAddress[0] === '') &&
(!this.pubkey[0] || this.pubkey[0] === '')
);
Expand All @@ -38,6 +51,7 @@ export default class Contact extends BaseModel {
this.onchainAddress.length === 1 &&
this.onchainAddress[0] !== '' &&
(!this.lnAddress[0] || this.lnAddress[0] === '') &&
(!this.bolt12Address[0] || this.bolt12Address[0] === '') &&
(!this.pubkey[0] || this.pubkey[0] === '')
);
}
Expand All @@ -48,6 +62,7 @@ export default class Contact extends BaseModel {
this.pubkey.length === 1 &&
this.pubkey[0] !== '' &&
(!this.lnAddress[0] || this.lnAddress[0] === '') &&
(!this.bolt12Address[0] || this.bolt12Address[0] === '') &&
(!this.onchainAddress[0] || this.onchainAddress[0] === '')
);
}
Expand All @@ -56,6 +71,10 @@ export default class Contact extends BaseModel {
return this.lnAddress?.length > 0 && this.lnAddress[0] !== '';
}

@computed public get hasBolt12Address(): boolean {
return this.bolt12Address?.length > 0 && this.bolt12Address[0] !== '';
}

@computed public get hasOnchainAddress(): boolean {
return this.onchainAddress?.length > 0 && this.onchainAddress[0] !== '';
}
Expand All @@ -69,6 +88,9 @@ export default class Contact extends BaseModel {
this.lnAddress.forEach((address) => {
if (address && address !== '') count++;
});
this.bolt12Address.forEach((address) => {
if (address && address !== '') count++;
});
this.onchainAddress.forEach((address) => {
if (address && address !== '') count++;
});
Expand Down
6 changes: 4 additions & 2 deletions models/Invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export default class Invoice extends BaseModel {
public paid_at: number;
public expires_at: number;
public status: string;
public amount_msat: number;
public invoice_amount_msat: string | number;
// pay req
public timestamp?: string | number;
public destination?: string;
Expand Down Expand Up @@ -172,8 +174,8 @@ export default class Invoice extends BaseModel {
const msatoshi = this.amount_msat.toString();
return Number(msatoshi.replace('msat', '')) / 1000;
}
if (this.millisatoshis) {
const msatoshi = this.millisatoshis;
if (this.millisatoshis || this.invoice_amount_msat) {
const msatoshi = this.millisatoshis || this.invoice_amount_msat;
return Number(msatoshi) / 1000;
}
return Number(this.num_satoshis || 0);
Expand Down
Loading

0 comments on commit b4ef7a5

Please sign in to comment.