Skip to main content

Email TipLinks Guide

The following snippets are from reference code for a live example you can try yourself.

Send Escrow Example

Why use an escrow?

Escrows allow you to create TipLinks without knowing the private key. This works great for multisig situations and also adds a layer of security and recoverability.

Limitations

Escrows currently only support SOL and SPL tokens like USDC (not NFTs). Support for other assets is in development.

Backend

Please run the following code on your backend to not expose your API key, and send the results to your frontend if needed.

import { createReceiverTipLink } from "@tiplink/api";

// Create Receiver TipLink which associates a keypair with an email address
// We only get the public key back, the private key is sent to the email recipient
const receiverTipLink = await createReceiverTipLink(
process.env.MAILER_API_KEY as string,
toEmail,
);

Frontend or Backend

import { sendAndConfirmTransaction } from "@solana/web3.js";
import { EscrowTipLink } from "@tiplink/api";

// Create Escrow
const escrowTipLink = await EscrowTipLink.create({
connection,
amount: amountSol * LAMPORTS_PER_SOL, // Convert to lamports
toEmail,
depositor: depositor.publicKey,
receiverTipLink,
});

// Deposit funds
const tx = await escrowTipLink.depositTx(connection);
await sendAndConfirmTransaction(connection, tx, [depositor]);

Backend

Please run the following code on your backend to not expose your API key.

import { mailEscrow } from "@tiplink/api";

// Mail it to the email associated with the Receiver TipLink
await mailEscrow({
apiKey: process.env.MAILER_API_KEY as string,
toEmail,
pda,
receiverTipLink,
toName,
replyEmail,
replyName,
});

Claimback Escrow Example

Escrow TipLinks can be claimed back by the same public key that deposited them. The pda is required to construct the transaction.

Frontend or Backend

import { getEscrowReceiverTipLink } from "@tiplink/api";

// Get Receiver TipLink associated with the escrow
// We only get the public key back
const receiverTipLink = await getEscrowReceiverTipLink(connection, pda);

Backend

Please run the following code on your backend to not expose your API key, and send the results to your frontend if needed.

import { getReceiverEmail } from "@tiplink/api";

// Get email address associated with the Receiver TipLink
const receiverEmail = await getReceiverEmail(
process.env.MAILER_API_KEY as string,
receiverTipLink,
);

Frontend or Backend

import { sendAndConfirmTransaction } from "@solana/web3.js";
import { EscrowTipLink } from "@tiplink/api";

// Reconstruct EscrowTipLink object
const escrowTipLink = await EscrowTipLink.get({
connection,
pda,
receiverEmail,
});

// Withdraw funds back to depositor
const tx = await escrowTipLink.withdrawTx(
connection,
depositor, // authority
depositor // destination
);
await sendAndConfirmTransaction(connection, tx, [depositor]);

Non-Escrow Example

Optionally, you can email regular TipLinks.

Limitations

Claimbacks are not natively supported and only possible if the TipLink URL is saved somewhere. Anyone can withdraw with the URL, so store it securely if using this method.

Frontend or Backend

// Create regular TipLink and add funds
// ...

Backend

import { mail } from "@tiplink/api";

// Mail it to whoever
await mail(
process.env.MAILER_API_KEY as string,
tipLink,
toEmail,
toName,
replyEmail,
replyName,
);