pull down to refresh
0 sats \ 8 replies \ @justin_shocknet 15h \ on: Implementing CLINK Static Offers nostr
Is your wallet connected to the bootstrap node or did you stand up your own pub?
Does the noffer string work from the https://clinkme.dev/offers.html demo?
Yeah it worked on the demo site -- decoded the string and then created an invoice, hence I was excited to integrate. I will definitely look into standing up my own pub once I get this working.
reply
If the pubkey and such are ok to post publicly, I can do that. I'm assuming it is since it's using the bootstrap node?
reply
yea since its bootstrap the key/relay are already known, only thing unique to you are the offer id and any fields you might have added... you can dm the offer string to me on nostr too if you prefer
is your project in a repo I can see the full context? the snippet is used in the demo site and I dont see anything obvious here yet
reply
Repo is private at the moment. Sent you a DM on Nostr.
reply
Couldn't repro, but we did just bump the SDK to address some potential
To mitigate a possible a version mismatch between with nostr-tools gen secret, I updated the example to use the clink wrapped nostr-tools... i dont think this was the problem though otherwise the log would show an error about it
Potentially are race with clock skew since the sub is set up after the request, moved that around just in case
Update CLINK to 1.5.1
NPM registry seems to not be rendering the readme yet, but its on github... pasting it here jic
import { ClinkSDK, NofferData, generateSecretKey, decodeBech32 } from '@shocknet/clink-sdk';
// First, decode the offer string to get the relay and pubkey
const nofferString = 'noffer1qvqsyqjqxuurvwpcxc6rvvrxxsurqep5vfjk2wf4v33nsenrxumnyvesxfnrswfkvycrwdp3x93xydf5xg6rzce4vv6xgdfh8quxgct9x5erxvspremhxue69uhhgetnwskhyetvv9ujumrfva58gmnfdenjuur4vgqzpccxc30wpf78wf2q78wg3vq008fd8ygtl4qy06gstpye3h5unc47xmee6z';
const decoded = decodeBech32(nofferString);
if (decoded.type !== 'noffer') throw new Error('Invalid offer string');
// Create SDK instance with the decoded relay and pubkey
const sdk = new ClinkSDK({
privateKey: generateSecretKey(), // Uint8Array
relays: [decoded.data.relay],
toPubKey: decoded.data.pubkey,
});
const request: NofferData = {
offer: decoded.data.offer,
amount_sats: 1000, // sats (optional for variable/spontaneous offers)
description: 'coffee for bob', // optional, max 100 chars
expires_in_seconds: 3600, // optional
payer_data: { name: 'Alice' }, // optional
};
const receiptCallback = (receipt) => {
console.log("got receipt", receipt)
}
sdk.Noffer(request, receiptCallback).then(response => {
if ('bolt11' in response) {
console.log('Invoice:', response.bolt11);
} else {
console.error('Error:', response.error);
}
});
reply
I noticed the example is using SendNofferRequest while the NPM snippet is using sdk.Noffer. Does that matter?
const handleGetInvoice = async () => {
if (!decodedOffer) {
alert("Offer data is missing. Please decode a new offer first.");
return;
}
nofferInput.disabled = true;
offerActions.style.display = 'none';
resultHeader.textContent = 'Invoice';
resultData.textContent = 'Requesting invoice...';
try {
const amountSats = amountInput.value ? parseInt(amountInput.value, 10) : undefined;
// Use the CLINK SDK to send a request for an invoice from the offer provider.
// This request is sent over Nostr and signed with our ephemeral client key.
const response = await SendNofferRequest(
pool, clientPrivateKey, [decodedOffer.relay], decodedOffer.pubkey,
{ offer: decodedOffer.offer, amount_sats: amountSats }
);
if ('bolt11' in response && typeof response.bolt11 === 'string') {
resultData.textContent = response.bolt11;
qrCanvas.style.display = 'block';
QRCode.toCanvas(qrCanvas, response.bolt11.toUpperCase(), { width: 256, margin: 1 });
} else {
resultHeader.textContent = 'Error Response';
resultData.textContent = JSON.stringify(response, null, 2);
}
} catch (error) {
console.error("Error getting invoice:", error);
resultHeader.textContent = 'Error';
resultData.textContent = `Error: ${error instanceof Error ? error.message : String(error)}`;
} finally {
isInvoiceDisplayed = true;
decodeOfferButton.textContent = 'Reset';
decodeOfferButton.style.display = 'block';
scrollIntoView();
}
};
reply
Shouldn't matter, they're the same... sdk one just handle creating the pool
reply
Hmm ok. I’ll try a bit more later and keep you posted.