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:
| Name | Type | Description |
|---|---|---|
name | string | Human-readable token name (e.g., "Ghost USDC"). |
symbol | string | Token ticker symbol (e.g., "gUSDC"). |
decimals | uint8 | Number of decimal places (e.g., 6 for USDC-like, 18 for ETH-like). |
salt | bytes32 | A unique salt for deterministic address generation via CREATE2. |
Returns:
| Name | Type | Description |
|---|---|---|
| (unnamed) | address | The address of the newly deployed GhostERC20 contract. |
Behavior:
- Computes the
CREATE2address from the factory address,salt, and theGhostERC20bytecode + constructor arguments. - Deploys the
GhostERC20contract at the deterministic address. - Calls
enableGhost()on the newly deployed token, which registers it withAssetGuard. - Calls
AssetGuard.recordDeployment(tokenAddress, address(this))to record provenance. - Returns the deployed token address.
Reverts if:
- A contract already exists at the computed
CREATE2address (salt collision). - The
AssetGuardregistration 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
CREATE2salt 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
AssetGuardforrecordDeploymentto succeed. - Only the factory owner or authorized callers can deploy tokens (depending on the access control configuration).