Skip to main content

Privacy Transactions

Use the RP1 SDK to perform private transactions.

Setup

import { RP1Client, Wallet, PrivacyModule } from '@rp1/sdk';

const client = new RP1Client('https://rpc.rp.one');
const wallet = Wallet.fromMnemonic('your mnemonic...');
const privacy = new PrivacyModule(client);

Shield Tokens

Convert public tokens to private:

// Shield 1 RP1
const tx = await privacy.shield(wallet, '1000000', 'urp1');

console.log(`Shielded!`);
console.log(`TX: ${tx.hash}`);
console.log(`Commitment: ${tx.commitment}`);

Get Shielded Balance

// Requires your spending key
const balance = await privacy.getShieldedBalance(wallet);

console.log(`Shielded balance: ${balance.amount} urp1`);

Private Transfer

Transfer without revealing sender, recipient, or amount:

const tx = await privacy.transfer(wallet, 'rp1recipient...', '500000');

console.log(`Private transfer complete!`);
console.log(`TX: ${tx.hash}`);
// Note: On-chain, only nullifier and new commitment are visible

Unshield Tokens

Convert private tokens back to public:

const tx = await privacy.unshield(wallet, '500000', 'urp1');

console.log(`Unshielded!`);
console.log(`TX: ${tx.hash}`);
console.log(`Nullifier: ${tx.nullifier}`);

Viewing Keys

Create Viewing Key

Grant an auditor access to view your transactions:

const tx = await privacy.createViewingKey(wallet, {
grantee: 'rp1auditor...',
type: 'full', // or 'incoming', 'outgoing', 'balance'
expiresAt: '2025-12-31T23:59:59Z',
});

console.log(`Viewing key created!`);

View Key Types

type ViewingKeyType =
| 'full' // See everything
| 'incoming' // See incoming only
| 'outgoing' // See outgoing only
| 'balance'; // See balance only

Revoke Viewing Key

const tx = await privacy.revokeViewingKey(wallet, 'rp1auditor...');

console.log(`Viewing key revoked!`);

List Viewing Keys

const grants = await privacy.getViewingKeyGrants(wallet.address);

grants.forEach(grant => {
console.log(`Grantee: ${grant.grantee}`);
console.log(`Type: ${grant.type}`);
console.log(`Expires: ${grant.expiresAt}`);
});

Use Viewing Key (Auditor)

// As an auditor with a viewing key
const viewingKey = 'viewing_key_from_grantor...';

const history = await privacy.viewTransactions(
'rp1owner...',
viewingKey
);

history.forEach(tx => {
console.log(`Type: ${tx.type}`);
console.log(`Amount: ${tx.amount}`);
console.log(`Time: ${tx.timestamp}`);
});

Complete Privacy Flow

async function privatePayment() {
const privacy = new PrivacyModule(client);

// 1. Check public balance
const publicBalance = await client.getBalance(wallet.address, 'urp1');
console.log(`Public balance: ${publicBalance.amount}`);

// 2. Shield tokens
console.log('Shielding tokens...');
await privacy.shield(wallet, '10000000', 'urp1');

// 3. Wait for confirmation
await new Promise(resolve => setTimeout(resolve, 2000));

// 4. Check shielded balance
const shieldedBalance = await privacy.getShieldedBalance(wallet);
console.log(`Shielded balance: ${shieldedBalance.amount}`);

// 5. Private transfer to recipient
console.log('Sending private transfer...');
const transferTx = await privacy.transfer(
wallet,
'rp1recipient...',
'5000000'
);
console.log(`Private transfer: ${transferTx.hash}`);

// 6. Check remaining shielded balance
const remaining = await privacy.getShieldedBalance(wallet);
console.log(`Remaining shielded: ${remaining.amount}`);

// 7. Unshield remaining
console.log('Unshielding remaining...');
await privacy.unshield(wallet, remaining.amount, 'urp1');

// 8. Check final public balance
const finalBalance = await client.getBalance(wallet.address, 'urp1');
console.log(`Final public balance: ${finalBalance.amount}`);
}

Shielded DEX Swap

import { DEXModule } from '@rp1/sdk';

const dex = new DEXModule(client);

// Execute private swap
const tx = await dex.shieldedSwap(wallet, {
poolId: '1',
tokenIn: 'urp1',
amountIn: '1000000',
minTokenOut: '980000',
});

// On-chain, only ZK proof is visible
// Amount, tokens, and trader are hidden
console.log(`Shielded swap: ${tx.hash}`);

Notes Storage

Private notes are stored locally and encrypted:

// Export notes (backup)
const notesBackup = await privacy.exportNotes(wallet, 'encryption_password');
console.log('Save this backup:', notesBackup);

// Import notes (restore)
await privacy.importNotes(wallet, notesBackup, 'encryption_password');

Best Practices

1. Wait Between Operations

// Let transactions settle before next operation
await privacy.shield(wallet, amount, denom);
await new Promise(r => setTimeout(r, 3000));
await privacy.transfer(wallet, recipient, amount);

2. Use Standard Amounts

// Better for anonymity - use round numbers
await privacy.shield(wallet, '1000000', 'urp1'); // Good
await privacy.shield(wallet, '1234567', 'urp1'); // Less anonymous

3. Backup Notes Regularly

// After each shield/transfer, backup notes
const backup = await privacy.exportNotes(wallet, password);
// Store securely

4. Handle Errors

try {
await privacy.transfer(wallet, recipient, amount);
} catch (error) {
if (error.message.includes('nullifier spent')) {
console.log('Note already spent - sync your wallet');
await privacy.syncNotes(wallet);
}
}

Next Steps