Shield base token
RAILGUN only supports shielding ERC-20 tokens. Base tokens cannot be shielded directly, as they are not ERC-20 tokens. They must be shielded as a wrapped token, eg. wETH for the Ethereum network.
Base token shields are executed as a multi-call through the RAILGUN Relay Adapt contract, which wraps the ETH into wETH at 1:1 ratio, and shields the result into your balance. This must be performed in isolation and cannot support shielding other ERC-20 tokens as part of the same transaction.
Imports
import { type HDNodeWallet, type Wallet } from "ethers";
import {
getGasDetailsForTransaction,
getShieldSignature,
serializeERC20Transfer,
} from "../util";
import {
NETWORK_CONFIG,
NetworkName,
TXIDVersion,
type RailgunERC20AmountRecipient,
} from "@railgun-community/shared-models";
import {
gasEstimateForShieldBaseToken,
populateShieldBaseToken,
} from "@railgun-community/wallet";
import { TEST_NETWORK, TEST_TOKEN } from "../../utils/constants";
import { getProviderWallet } from "../../utils/provider";
Gas Estimate
The baseShieldGasEstimate
function is used to estimate the gas required for shielding a base token, such as ETH wrapped as an ERC-20 token (e.g., wETH), using a designated network. It requires parameters such as the network name, a user wallet, recipient details, and the Railgun wallet address. The function retrieves a private key for shielding signatures and calculates the gas estimate required for the operation.
export const baseShieldGasEstimate = async (
network: NetworkName,
wallet: Wallet | HDNodeWallet,
erc20AmountRecipient: RailgunERC20AmountRecipient,
railgunWalletAddress: string
) => {
const shieldPrivateKey = await getShieldSignature(wallet);
// Address of public wallet we are shielding from
const fromWalletAddress = wallet.address;
const { gasEstimate } = await gasEstimateForShieldBaseToken(
TXIDVersion.V2_PoseidonMerkle,
network,
railgunWalletAddress,
shieldPrivateKey,
erc20AmountRecipient,
fromWalletAddress
);
return gasEstimate;
};
Populate Transaction
The basePopulateShieldTransaction
function goes a step further to prepare the transaction for execution. It not only estimates the gas required but also collects necessary gas details and nullifiers to formulate a complete transaction object. The function considers whether the transaction is executed with a public wallet and ensures necessary tokens are approved for the transaction spender. This prepares a transaction that can be sent to the blockchain, facilitating the seamless shielding of tokens into the user's Railgun balance, while maintaining privacy and integrity.
export const basePopulateShieldTransaction = async (
network: NetworkName,
wallet: Wallet | HDNodeWallet,
erc20AmountRecipient: RailgunERC20AmountRecipient,
sendWithPublicWallet: boolean,
railgunWalletAddress: string
) => {
// get gas estimate for tx,
// populate tx with gas estimate
// approve token to spender.
// approve token to spender
const spender = NETWORK_CONFIG[network].proxyContract;
const gasEstimate = await baseShieldGasEstimate(
network,
wallet,
erc20AmountRecipient,
railgunWalletAddress
);
const shieldPrivateKey = await getShieldSignature(wallet);
const gasDetails = await getGasDetailsForTransaction(
network,
gasEstimate,
sendWithPublicWallet,
wallet
);
const { transaction, nullifiers } = await populateShieldBaseToken(
TXIDVersion.V2_PoseidonMerkle, // this is for V2 of the railgun protocol
network,
railgunWalletAddress,
shieldPrivateKey,
erc20AmountRecipient,
gasDetails
);
return {
gasEstimate,
gasDetails,
transaction,
nullifiers,
};
};
Example Usage
In the example usage (TEST_shieldBASE
), these functions are utilized to shield a small amount of a test token (e.g., wETH) into a Railgun wallet address, showcasing the entire process from gas estimation to transaction execution and confirmation on the blockchain.
export const TEST_shieldBASE = async (railgunWalletAddress: string) => {
console.log("TEST_shieldBASE");
const { wallet } = getProviderWallet();
const erc20AmountRecipients = [
serializeERC20Transfer(
TEST_TOKEN, // WETH
1n,
railgunWalletAddress
),
];
const { gasEstimate, gasDetails, transaction, nullifiers } =
await basePopulateShieldTransaction(
TEST_NETWORK,
wallet,
erc20AmountRecipients[0],
true,
railgunWalletAddress
);
const tx = await wallet.sendTransaction(transaction);
console.log("tx: ", tx);
await tx.wait();
};
Last updated