Shield NFTs
Shield one or many ERC-721 tokens into RAILGUN private balances in a single transaction. ERC-1155 not supported at this time.
Imports
import {
NETWORK_CONFIG,
TXIDVersion,
type NetworkName,
type RailgunNFTAmountRecipient,
} from "@railgun-community/shared-models";
import {
gasEstimateForShield,
populateShield,
} from "@railgun-community/wallet";
import { type Wallet, type HDNodeWallet, Contract, ZeroAddress } from "ethers";
import {
getGasDetailsForTransaction,
getShieldSignature,
serializeERC721Transfer,
} from "../util";
import {
TEST_NETWORK,
TEST_NFT_ADDRESS,
TEST_NFT_SUBID,
} from "../../utils/constants";
import { getProviderWallet } from "../../utils/provider";
Gas Estimate
The erc721ShieldGasEstimate
function calculates the estimated gas cost required for shielding a given amount of ERC-721 tokens. It uses the network and wallet information to derive a shield signature from the sender's wallet address, and the intended token recipients to compute the gas estimate.
export const erc721ShieldGasEstimate = async (
network: NetworkName,
wallet: Wallet | HDNodeWallet,
erc721AmountRecipients: RailgunNFTAmountRecipient[]
) => {
const shieldPrivateKey = await getShieldSignature(wallet);
// Address of public wallet we are shielding from
const fromWalletAddress = wallet.address;
const { gasEstimate } = await gasEstimateForShield(
TXIDVersion.V2_PoseidonMerkle,
network,
shieldPrivateKey,
[],
erc721AmountRecipients, // nftAmountRecipients
fromWalletAddress
);
return gasEstimate;
};
Populate Transaction
The erc721PopulateShieldTransaction
function constructs a transaction that authorizes and transfers the ERC-721 tokens to the RAILGUN shield. It checks if each token's transfer approval has been granted to the network's proxy contract and processes the transactions accordingly. It then calculates the gas details and populates the transaction with necessary privacy protocol data before returning the transaction information.
export const erc721PopulateShieldTransaction = async (
network: NetworkName,
wallet: Wallet | HDNodeWallet,
erc721AmountRecipients: RailgunNFTAmountRecipient[],
sendWithPublicWallet: boolean
) => {
const spender = NETWORK_CONFIG[network].proxyContract;
for (const amountRecipient of erc721AmountRecipients) {
const contract = new Contract(
amountRecipient.nftAddress,
[
"function getApproved(uint256 tokenId) view returns (address)",
"function approve(address to, uint256 tokenId) external returns (bool)",
],
wallet
);
const allowance = await contract.getApproved(amountRecipient.tokenSubID);
if (allowance === ZeroAddress) {
throw new Error("ERC721 Not minted.");
}
if (allowance.toLowerCase() == spender.toLowerCase()) {
console.log("already have enough allowance");
continue;
}
const tx = await contract.approve(spender, amountRecipient.tokenSubID);
await tx.wait();
}
const gasEstimate = await erc721ShieldGasEstimate(
network,
wallet,
erc721AmountRecipients
);
const shieldPrivateKey = await getShieldSignature(wallet);
const gasDetails = await getGasDetailsForTransaction(
network,
gasEstimate,
sendWithPublicWallet,
wallet
);
const { transaction, nullifiers } = await populateShield(
TXIDVersion.V2_PoseidonMerkle, // this is for V2 of the railgun protocol
network,
shieldPrivateKey,
[],
erc721AmountRecipients,
gasDetails
);
return {
gasEstimate,
gasDetails,
transaction,
nullifiers,
};
};
Example Usage
Finally, the TEST_shieldERC721
function demonstrates how to use these processes by shielding an example NFT to a specified RAILGUN wallet address. It showcases assembling the transaction, sending it with the wallet, and handling the transaction receipt.
export const TEST_shieldERC721 = async (railgunWalletAddress: string) => {
const { wallet } = getProviderWallet();
const nftAddress = TEST_NFT_ADDRESS;
const tokenSubID = TEST_NFT_SUBID;
const erc721AmountRecipients = [
serializeERC721Transfer(nftAddress, tokenSubID, railgunWalletAddress),
];
const { gasEstimate, gasDetails, transaction, nullifiers } =
await erc721PopulateShieldTransaction(
TEST_NETWORK,
wallet,
erc721AmountRecipients,
true
);
const tx = await wallet.sendTransaction(transaction);
console.log("tx: ", tx);
await tx.wait();
};
Last updated