Skip to main content

GhostERC20Factory

The GhostERC20Factory deploys new GhostERC20 token contracts using the CREATE2 opcode, ensuring deterministic and predictable addresses. Deployed tokens are automatically registered with AssetGuard.

Deployed address: 0xE842ffe639a770a162e0b7EB9f274E49aCA8Fb95

Functions

deployToken

function deployToken(
string calldata name,
string calldata symbol,
uint8 decimals,
bytes32 salt
) external returns (address)

Deploys a new GhostERC20 token contract via CREATE2.

Parameters:

NameTypeDescription
namestringHuman-readable token name (e.g., "Ghost USDC").
symbolstringToken ticker symbol (e.g., "gUSDC").
decimalsuint8Number of decimal places (e.g., 6 for USDC-like, 18 for ETH-like).
saltbytes32A unique salt for deterministic address generation via CREATE2.

Returns:

NameTypeDescription
(unnamed)addressThe address of the newly deployed GhostERC20 contract.

Behavior:

  1. Computes the CREATE2 address from the factory address, salt, and the GhostERC20 bytecode + constructor arguments.
  2. Deploys the GhostERC20 contract at the deterministic address.
  3. Calls enableGhost() on the newly deployed token, which registers it with AssetGuard.
  4. Calls AssetGuard.recordDeployment(tokenAddress, address(this)) to record provenance.
  5. Returns the deployed token address.

Reverts if:

  • A contract already exists at the computed CREATE2 address (salt collision).
  • The AssetGuard registration fails.

CREATE2 Address Derivation

The deployed address is deterministic and can be computed before deployment:

address = keccak256(
0xff,
factoryAddress,
salt,
keccak256(creationCode)
)[12:]

Where creationCode is:

abi.encodePacked(
type(GhostERC20).creationCode,
abi.encode(name, symbol, decimals, assetGuardAddress, poseidonT3Address)
)

Precomputing the Address

You can compute the deployment address off-chain before submitting the transaction:

// On-chain helper (if available):
function computeAddress(
string calldata name,
string calldata symbol,
uint8 decimals,
bytes32 salt
) external view returns (address)
// Off-chain (ethers.js):
const { ethers } = require("ethers");

const factoryAddress = "0xE842ffe639a770a162e0b7EB9f274E49aCA8Fb95";
const salt = ethers.utils.id("my-unique-salt");

const creationCode = ethers.utils.solidityPack(
["bytes", "bytes"],
[
GhostERC20Bytecode,
ethers.utils.defaultAbiCoder.encode(
["string", "string", "uint8", "address", "address"],
[name, symbol, decimals, assetGuardAddress, poseidonT3Address]
),
]
);

const deployedAddress = ethers.utils.getCreate2Address(
factoryAddress,
salt,
ethers.utils.keccak256(creationCode)
);

Usage Example

Deploying a New Privacy Token

// Deploy a new GhostERC20 via the factory
address ghostUSDC = GhostERC20Factory(factoryAddress).deployToken(
"Ghost USDC",
"gUSDC",
6,
keccak256("ghost-usdc-v1") // salt
);

// The token is now:
// 1. Deployed at a deterministic address
// 2. Registered with AssetGuard
// 3. Ready for deposits into CommitRevealVault

// Users can now approve and commit:
IERC20(ghostUSDC).approve(vaultAddress, amount);
CommitRevealVault(vaultAddress).commit(ghostUSDC, amount, commitment, qCommitment);

Deploying Multiple Tokens

address ghostDAI = factory.deployToken("Ghost DAI", "gDAI", 18, keccak256("ghost-dai-v1"));
address ghostWBTC = factory.deployToken("Ghost WBTC", "gWBTC", 8, keccak256("ghost-wbtc-v1"));
address ghostUSDT = factory.deployToken("Ghost USDT", "gUSDT", 6, keccak256("ghost-usdt-v1"));

Security Notes

  • The CREATE2 salt must be unique per deployment. Reusing a salt with different constructor arguments will produce a different bytecode hash and thus a different address. Reusing a salt with identical arguments will revert because the address is already occupied.
  • The factory must be registered as an authorized factory in AssetGuard for recordDeployment to succeed.
  • Only the factory owner or authorized callers can deploy tokens (depending on the access control configuration).