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.

Gas Estimate

import {
  NetworkName
} from '@railgun-community/shared-models';
import {
  gasEstimateForShieldBaseToken,
  getShieldPrivateKeySignatureMessage,
} from '@railgun-community/wallet';
import { keccak256, Wallet } from 'ethers';

// Receiver of the shield.
const railgunAddress = '0zk123...456';

// The shieldPrivateKey enables the sender to decrypt 
// the receiver's address in the future.
const wallet = new Wallet(pKey);
const shieldSignatureMessage = getShieldPrivateKeySignatureMessage();
const shieldPrivateKey = keccak256(
  await wallet.signMessage(shieldSignatureMessage),
);

// Formatted wrapped token amount.
// Tokens will shield as ETH and auto-wrap into wETH.
const wrappedERC20Amount: RailgunERC20Amount = {
  tokenAddress: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // wETH
  amount: BigInt('0x10'), // hexadecimal amount
};

// Public wallet to shield from.
const fromWalletAddress = '0xab5...c9b';

const { gasEstimate } = await gasEstimateForShieldBaseToken(
  NetworkName.Ethereum,
  railgunAddress,
  shieldPrivateKey,
  wrappedERC20Amount,  
  fromWalletAddress,
);

Populate Transaction

import {
  ...
  EVMGasType,
  TransactionGasDetails,
  getEVMGasTypeForTransaction,
} from '@railgun-community/shared-models';
import {
  ...
  populateShield,
} from '@railgun-community/wallet';

...

const sendWithPublicWallet = true; // Always true for Shield transactions
const evmGasType: EVMGasType = getEVMGasTypeForTransaction(
  NetworkName.Ethereum,
  sendWithPublicWallet
);
const gasEstimate = ...; // Output from gasEstimateForShield in above example

let gasDetails: TransactionGasDetails;
switch (evmGasType) {
  case EVMGasType.Type0:
  case EVMGasType.Type1:
    gasDetails = {
      evmGasType,
      gasEstimate,
      gasPrice: BigInt('0x100000'), // Proper calculation of network gasPrice is not covered in this guide
    }
    break;
  case EVMGasType.Type2:
    // Proper calculation of gas Max Fee and gas Max Priority Fee is not covered in this guide. See: https://docs.alchemy.com/docs/how-to-build-a-gas-fee-estimator-using-eip-1559
    const maxFeePerGas: BigInt('0x100000');
    const maxPriorityFeePerGas: BigInt('0x010000');

    gasDetails = {
      evmGasType,
      gasEstimate,
      maxFeePerGas,
      maxPriorityFeePerGas,
    }
    break;
}

const { transaction } = await populateShieldBaseToken(
  NetworkName.Ethereum,
  railgunAddress,
  shieldPrivateKey,
  wrappedERC20Amount,  
  gasDetails,
);

// Public wallet to shield from.
transaction.from = '0xab58...c9b';

Last updated