Broadcasters
The Broadcaster network, forward private transactions on behalf of users and covering the associated gas costs. Users pay a small premium on top of the gas cost from their private balance to the Broadcaster's private balance in exchange for the service.
Broadcasters are only needed for transactions that originate from a 0zk address (i.e. not needed for Shield transactions). Through Broadcaster, private transactions within the RAILGUN privacy system remain unlinked to public addresses, ensuring anonymity even during interactions with smart contracts.
Broadcasters broadcast their fees and accept transactions via the Waku Network (as opposed to using HTTP requests) so that nobody can snoop on a user's IP address when submitting a transaction.
Use the @railgun-community/waku-broadcaster-client package to find Broadcasters and submit transactions anonymously.
For web projects install:
npm install @railgun-community/waku-broadcaster-client-web
For mobile projects install:
npm install @railgun-community/waku-broadcaster-client-node
Imports
import {
NETWORK_CONFIG,
type CachedTokenFee,
type FeeTokenDetails,
type NetworkName,
type SelectedBroadcaster,
type TransactionGasDetails,
} from "@railgun-community/shared-models";
import { calculateBroadcasterFeeERC20Amount } from "@railgun-community/wallet";
const wakuModule = import("@railgun-community/waku-broadcaster-client-node");
let WakuBroadcasterClient: any;
let BroadcasterTransaction: any;
let waku: any;
Initialize the broadcaster client
export const initializeBroadcasters = async (network: NetworkName) => {
if (!waku) {
waku = await wakuModule;
}
WakuBroadcasterClient = waku.WakuBroadcasterClient;
BroadcasterTransaction = waku.BroadcasterTransaction;
const { chain } = NETWORK_CONFIG[network];
const options = {};
const callback = (chain: any, status: any) => {
if (status !== "Connected") {
console.log(`WAKU ${chain.id}:${chain.type} ${status}`);
}
};
const debugLogger = {
log: (_msg: string) => {
console.log(msg);
},
error: (error: Error) => {
console.error(error);
},
};
WakuBroadcasterClient.start(chain, options, callback, debugLogger);
};
The initialized RailgunWakuBroadcasterClient will continue monitoring the connection to the Broadcaster network throughout the app session. It will call the statusCallback
function with updates, and you must wait to use a Broadcaster until the status is Connected
.
Finding a Broadcaster
When you are connected to the Broadcaster network and you are ready to submit a private transaction, you must first select a token from the user's private balance to pay the Broadcaster fee (likely from user input).
Next, we need to find the best Broadcaster based on the user's selected fee token and the fee amounts that the Broadcasters are broadcasting.
export const getBroadcasterFeeInfo = async (
network: NetworkName,
tokenAddress: string
): Promise<SelectedBroadcaster> => {
const { chain } = NETWORK_CONFIG[network];
return await WakuBroadcasterClient.findBestBroadcaster(
chain,
tokenAddress,
true
);
};
If no Broadcaster is available that accepts fees in the selected token, then the findBestBroadcaster
function will return undefined.
Calculating the Broadcaster Fee
Once we have a SelectedBroadcaster
, we need to calculate the total Broadcaster fee for our transaction based on the current gas price, gas estimate of the transaction, and the fee that the Broadcaster charges.
export const getBroadcasterFeeRecipientDetails = async (
selectedBroadcaster: SelectedBroadcaster,
estimatedGasDetails: TransactionGasDetails,
feeTokenDetails: FeeTokenDetails
) => {
const broadcasterFeeAmountDetails = calculateBroadcasterFeeERC20Amount(
feeTokenDetails,
estimatedGasDetails
);
const broadcasterFeeERC20AmountRecipient = {
tokenAddress: broadcasterFeeAmountDetails.tokenAddress,
amount: broadcasterFeeAmountDetails.amount,
recipientAddress: selectedBroadcaster.railgunAddress,
};
return broadcasterFeeERC20AmountRecipient;
};
Submit via Broadcaster
import {
RailgunPopulateTransactionResponse
} from '@railgun-community/shared-models';
const populateResponse: RailgunPopulateTransactionResponse = ...; // See the examples in the "Transactions" section for getting this value
const overallBatchMinGasPrice: bigint = ...; // See the examples in the "Transactions" section for getting this value
const selectedBroadcaster = ...; // Same as examples above
const chain: Chain = ...; // Same as examples above
const useRelayAdapt = ...; // Same as examples above
const nullifiers: string[] = populateResponse.nullifiers ?? [];
const broadcasterTransaction = await BroadcasterTransaction.create(
populateResponse.transaction.to,
populateResponse.transaction.data,
selectedBroadcaster.railgunAddress,
selectedBroadcaster.tokenFee.feesID,
chain,
nullifiers,
overallBatchMinGasPrice,
useRelayAdapt,
);
const txHash = await broadcasterTransaction.send();
Last updated