HokusaiAMM Smart Contract
The HokusaiAMM contract implements a Constant Reserve Ratio (CRR) bonding curve for each model token, enabling continuous buying and selling with USDC reserves.
Contract Overview
Purpose: Provide always-available liquidity for model tokens through a deterministic bonding curve mechanism.
Key Features:
- CRR-based buy/sell formulas
- Seven-day buy-only bonding round
- API fee deposit mechanism
- Slippage and deadline protection
- Emergency pause capability
- Governance-controlled parameters
Repository: hokusai-token
Architecture
HokusaiToken (ERC20)
↕
HokusaiAMM ←→ USDC Reserve
↑
|
UsageFeeRouter (deposits API fees)
State Variables
Core Parameters
// Token being traded
IERC20 public immutable token;
// Reserve token (USDC)
IERC20 public immutable reserveToken;
// Constant Reserve Ratio (5% to 50%)
uint256 public reserveRatio;
// Trade fee (0% to 10%)
uint256 public tradeFee;
// Protocol fee (0% to 50% of trade fee)
uint256 public protocolFee;
// End of buy-only period (timestamp)
uint256 public immutable buyOnlyUntil;
// Emergency pause flag
bool public paused;
Constants
uint256 public constant PRECISION = 1e18;
uint256 public constant MAX_RESERVE_RATIO = 50e16; // 50%
uint256 public constant MIN_RESERVE_RATIO = 5e16; // 5%
uint256 public constant MAX_TRADE_FEE = 10e16; // 10%
uint256 public constant MAX_PROTOCOL_FEE = 50e16; // 50%
Core Functions
buy()
Purchase tokens by depositing USDC.
function buy(
uint256 minTokens,
uint256 deadline
) external payable nonReentrant returns (uint256 tokensBought);
Parameters:
minTokens: Minimum tokens expected (slippage protection)deadline: Transaction deadline timestamp
Returns: Number of tokens purchased
Example:
const usdc = await ethers.getContractAt("IERC20", usdcAddress);
const amm = await ethers.getContractAt("HokusaiAMM", ammAddress);
// Approve USDC spending
await usdc.approve(ammAddress, ethers.parseUnits("1000", 6));
// Get quote first
const quote = await amm.getBuyQuote(ethers.parseUnits("1000", 6));
const minTokens = quote * 99n / 100n; // 1% slippage tolerance
// Execute buy
const deadline = Math.floor(Date.now() / 1000) + 300; // 5 min
const tx = await amm.buy(minTokens, deadline, {
value: ethers.parseUnits("1000", 6)
});
await tx.wait();
console.log(`Bought ${ethers.formatUnits(quote, 18)} tokens`);
Formula:
T = S × ((1 + E/R)^w - 1)
Where:
T = Tokens minted
S = Current supply
R = Current reserve
E = USDC deposited (after fees)
w = Reserve ratio
Events Emitted:
event Buy(
address indexed buyer,
uint256 usdcSpent,
uint256 tokensBought,
uint256 tradeFee,
uint256 protocolFee
);
sell()
Sell tokens back for USDC.
function sell(
uint256 tokenAmount,
uint256 minUSDC,
uint256 deadline
) external nonReentrant returns (uint256 usdcReceived);
Parameters:
tokenAmount: Number of tokens to sellminUSDC: Minimum USDC expected (slippage protection)deadline: Transaction deadline timestamp
Returns: USDC amount received
Restrictions:
- ⚠️ Cannot sell during buy-only period (first 7 days)
- Requires token approval first
Example:
const token = await ethers.getContractAt("HokusaiToken", tokenAddress);
const amm = await ethers.getContractAt("HokusaiAMM", ammAddress);
// Check if bonding round is over
const buyOnlyUntil = await amm.buyOnlyUntil();
if (Date.now() / 1000 < buyOnlyUntil) {
throw new Error("Still in bonding round, cannot sell yet");
}
// Approve token spending
const tokenAmount = ethers.parseUnits("1000", 18);
await token.approve(ammAddress, tokenAmount);
// Get quote
const quote = await amm.getSellQuote(tokenAmount);
const minUSDC = quote * 99n / 100n; // 1% slippage
// Execute sell
const deadline = Math.floor(Date.now() / 1000) + 300;
const tx = await amm.sell(tokenAmount, minUSDC, deadline);
await tx.wait();
console.log(`Received ${ethers.formatUnits(quote, 6)} USDC`);
Formula:
F = R × (1 - (1 - T/S)^(1/w))
Where:
F = USDC returned (after fees)
T = Tokens burned
S = Current supply
R = Current reserve
w = Reserve ratio
Events Emitted:
event Sell(
address indexed seller,
uint256 tokensSold,
uint256 usdcReceived,
uint256 tradeFee,
uint256 protocolFee
);
depositFees()
Deposit profit share from API usage fees directly to reserves (no token minting).
function depositFees(uint256 amount) external onlyFeeDepositor nonReentrant;
Parameters:
amount: USDC amount to deposit (profit share after infrastructure accrual)
Access: Only addresses with FEE_DEPOSITOR_ROLE (typically UsageFeeRouter)
Context: The UsageFeeRouter calculates the profit share based on each model's infrastructureAccrualBps parameter. For example, if a model has 80% infrastructure accrual, only 20% of API revenue reaches this function.
Effect:
- Increases reserve (R ↑)
- Supply unchanged (S same)
- Spot price increases: P = R / (w × S)
Example:
// Called by UsageFeeRouter contract after splitting fees
// If $10,000 API revenue with 80% infrastructure accrual:
// - $8,000 → InfrastructureReserve.deposit()
// - $2,000 → HokusaiAMM.depositFees() (this function)
const amm = await ethers.getContractAt("HokusaiAMM", ammAddress);
const usdc = await ethers.getContractAt("IERC20", usdcAddress);
// Approve and deposit profit share
const profitAmount = ethers.parseUnits("2000", 6);
await usdc.approve(ammAddress, profitAmount);
await amm.depositFees(profitAmount);
// Price increased without minting tokens
Events Emitted:
event FeesDeposited(
address indexed depositor,
uint256 amount,
uint256 newReserve
);
View Functions
spotPrice()
Get current price per token.
function spotPrice() external view returns (uint256);
Returns: Current price in USDC (scaled by PRECISION)
Formula: P = R / (w × S)
Example:
const price = await amm.spotPrice();
console.log(`Current price: ${ethers.formatUnits(price, 18)} USDC per token`);
getBuyQuote()
Calculate tokens received for a given USDC amount.
function getBuyQuote(uint256 usdcAmount) external view returns (uint256);
Parameters:
usdcAmount: USDC to spend
Returns: Tokens that would be received (after fees)
Example:
const usdcAmount = ethers.parseUnits("1000", 6);
const tokensOut = await amm.getBuyQuote(usdcAmount);
console.log(`1000 USDC → ${ethers.formatUnits(tokensOut, 18)} tokens`);
getSellQuote()
Calculate USDC received for a given token amount.
function getSellQuote(uint256 tokenAmount) external view returns (uint256);
Parameters:
tokenAmount: Tokens to sell
Returns: USDC that would be received (after fees)
Example:
const tokenAmount = ethers.parseUnits("1000", 18);
const usdcOut = await amm.getSellQuote(tokenAmount);
console.log(`1000 tokens → ${ethers.formatUnits(usdcOut, 6)} USDC`);
getReserve()
Get current USDC reserve balance.
function getReserve() external view returns (uint256);
Returns: Current reserve in USDC
getTotalSupply()
Get current token supply.
function getTotalSupply() external view returns (uint256);
Returns: Current circulating supply
isBuyOnlyPeriod()
Check if still in seven-day bonding round.
function isBuyOnlyPeriod() external view returns (bool);
Returns: true if selling is disabled, false if full trading enabled
Example:
const isBuyOnly = await amm.isBuyOnlyPeriod();
if (isBuyOnly) {
console.log("Still in bonding round - buys only");
} else {
console.log("Full trading enabled");
}
Governance Functions
updateParameters()
Update AMM parameters (owner only).
function updateParameters(
uint256 newReserveRatio,
uint256 newTradeFee,
uint256 newProtocolFee
) external onlyOwner;
Parameters:
newReserveRatio: New CRR (5% to 50%)newTradeFee: New trade fee (0% to 10%)newProtocolFee: New protocol fee (0% to 50% of trade fee)
Events Emitted:
event ParametersUpdated(
uint256 reserveRatio,
uint256 tradeFee,
uint256 protocolFee
);
pause() / unpause()
Emergency pause trading.
function pause() external onlyOwner;
function unpause() external onlyOwner;
Effect: Disables buy/sell when paused, re-enables when unpaused
withdrawTreasury()
Withdraw accumulated protocol fees.
function withdrawTreasury(uint256 amount) external onlyOwner;
Parameters:
amount: USDC amount to withdraw from protocol fee balance