FixedPriceSale Contract#
Overview#
The FixedPriceSale contract provides a simple, fixed-price minting mechanism for the AbrahamEarlyWorks NFT collection. Users can purchase NFTs at a fixed price of 0.01 ETH, with automatic minting and payment processing.
Key Features#
- Fixed Pricing: Consistent 0.01 ETH price per NFT
- Automated Minting: Direct integration with NFT contract
- Batch Minting: Support for multiple NFT purchases
- Revenue Withdrawal: Owner can withdraw collected funds
- Refund Protection: Automatic refunds for overpayment
- Gas Optimized: Efficient transaction processing
Architecture#
graph TB
subgraph "FixedPriceSale Contract"
A[User Payment] --> B[Validate Amount]
B --> C[Mint NFT]
C --> D[Process Refund]
D --> E[Store Revenue]
F[Batch Purchase] --> G[Loop Minting]
G --> C
H[Owner Functions] --> I[Withdraw Funds]
I --> J[Transfer ETH]
end
subgraph "External Contracts"
K[AbrahamEarlyWorks NFT]
L[User Wallet]
M[Owner Wallet]
end
C --> K
D --> L
J --> M
Contract Functions#
Public Minting Functions#
mint(string metadataURI)
#
- Purpose: Purchase and mint a single NFT
- Access: Public (payable)
- Price: 0.01 ETH
- Parameters:
metadataURI
: IPFS URI for NFT metadata - Returns:
uint256
- Token ID of minted NFT - Events: Emits
NFTMinted(buyer, tokenId, metadataURI)
batchMint(string[] metadataURIs)
#
- Purpose: Purchase and mint multiple NFTs in one transaction
- Access: Public (payable)
- Price: 0.01 ETH × number of NFTs
- Parameters:
metadataURIs
: Array of IPFS URIs for each NFT - Returns:
uint256[]
- Array of token IDs for minted NFTs - Events: Emits
NFTMinted
for each minted NFT
Owner Functions#
withdraw()
#
- Purpose: Withdraw accumulated revenue to owner
- Access: Owner only
- Transfers: All contract balance to owner address
- Events: Emits
FundsWithdrawn(owner, amount)
withdraw(uint256 amount)
#
- Purpose: Withdraw specific amount to owner
- Access: Owner only
- Parameters:
amount
: Wei amount to withdraw - Requirements: Amount ≤ contract balance
- Events: Emits
FundsWithdrawn(owner, amount)
emergencyWithdraw()
#
- Purpose: Emergency fund recovery (if needed)
- Access: Owner only
- Transfers: All contract balance immediately
View Functions#
PRICE()
#
- Purpose: Get current NFT price
- Access: Public view
- Returns:
uint256
- Price in wei (10^16 = 0.01 ETH)
nftContract()
#
- Purpose: Get linked NFT contract address
- Access: Public view
- Returns:
address
- AbrahamEarlyWorks contract address
getContractBalance()
#
- Purpose: Check contract's current balance
- Access: Public view
- Returns:
uint256
- Balance in wei
Events#
event NFTMinted(
address indexed buyer,
uint256 indexed tokenId,
string metadataURI
);
event FundsWithdrawn(address indexed owner, uint256 amount);
Payment Flow#
sequenceDiagram
participant User as Buyer
participant Sale as FixedPriceSale
participant NFT as AbrahamEarlyWorks
participant Owner as Contract Owner
User->>Sale: mint(metadataURI) + 0.01 ETH
Sale->>Sale: Validate payment ≥ 0.01 ETH
Sale->>NFT: mintTo(metadataURI, user)
NFT->>NFT: Mint NFT to user
NFT-->>Sale: Return tokenId
alt Overpayment
Sale->>User: Refund excess ETH
end
Sale->>Sale: Store 0.01 ETH
Sale-->>User: Emit NFTMinted event
Note over User,Owner: Later...
Owner->>Sale: withdraw()
Sale->>Owner: Transfer all stored ETH
Sale-->>Owner: Emit FundsWithdrawn event
Security Features#
- Reentrancy Protection: ReentrancyGuard on payable functions
- Overflow Protection: Safe math operations
- Access Control: Owner-only functions protected
- Payment Validation: Exact payment verification
- Refund Safety: Automatic overpayment refunds
- Emergency Functions: Emergency withdrawal capability
Gas Optimization#
- Efficient Minting: Direct contract-to-contract calls
- Batch Operations: Reduced gas cost per NFT in batches
- Minimal State: Limited state variables for efficiency
- Single Transaction: Complete purchase in one transaction
Pricing Strategy#
// Fixed price structure
const PRICE_IN_ETH = 0.01;
const PRICE_IN_WEI = ethers.parseEther("0.01"); // 10^16 wei
// Batch pricing scales linearly
const batchPrice = PRICE_IN_WEI * numberOfNFTs;
Usage Examples#
Single NFT Purchase#
import { ethers } from 'ethers';
import FixedPriceSaleABI from './FixedPriceSale.json';
const SALE_CONTRACT_ADDRESS = "0xYourSaleContractAddress";
const PRICE = ethers.parseEther("0.01");
const saleContract = new ethers.Contract(
SALE_CONTRACT_ADDRESS,
FixedPriceSaleABI,
buyerSigner
);
// Purchase single NFT
const tx = await saleContract.mint(
"ipfs://QmYourMetadataHash",
{ value: PRICE }
);
const receipt = await tx.wait();
console.log("NFT purchased! Token ID:", receipt.events[0].args.tokenId);
Batch NFT Purchase#
// Purchase multiple NFTs at once
const metadataURIs = [
"ipfs://QmHash1",
"ipfs://QmHash2",
"ipfs://QmHash3"
];
const totalPrice = PRICE * BigInt(metadataURIs.length);
const tx = await saleContract.batchMint(metadataURIs, {
value: totalPrice
});
const receipt = await tx.wait();
console.log("Batch minted NFTs:", receipt.events.map(e => e.args.tokenId));
Revenue Withdrawal (Owner)#
// Check current balance
const balance = await saleContract.getContractBalance();
console.log("Contract balance:", ethers.formatEther(balance), "ETH");
// Withdraw all funds
if (balance > 0) {
const tx = await saleContract.withdraw();
await tx.wait();
console.log("Funds withdrawn successfully");
}
// Or withdraw specific amount
const partialAmount = ethers.parseEther("1.0");
await saleContract.withdraw(partialAmount);
Frontend Integration#
// React component example
function NFTPurchase() {
const [metadataURI, setMetadataURI] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handlePurchase = async () => {
setIsLoading(true);
try {
const tx = await saleContract.mint(metadataURI, {
value: ethers.parseEther("0.01")
});
await tx.wait();
alert("NFT purchased successfully!");
} catch (error) {
alert("Purchase failed: " + error.message);
} finally {
setIsLoading(false);
}
};
return (
<div>
<input
value={metadataURI}
onChange={(e) => setMetadataURI(e.target.value)}
placeholder="Enter IPFS metadata URI"
/>
<button onClick={handlePurchase} disabled={isLoading}>
{isLoading ? "Minting..." : "Purchase NFT (0.01 ETH)"}
</button>
</div>
);
}
Testing#
Comprehensive test coverage includes:
describe("FixedPriceSale", function() {
it("Should mint NFT for correct payment", async function() {
const price = ethers.parseEther("0.01");
const metadataURI = "ipfs://test";
const tx = await saleContract
.connect(buyer)
.mint(metadataURI, { value: price });
await expect(tx).to.emit(saleContract, "NFTMinted");
});
it("Should reject insufficient payment", async function() {
const lowPrice = ethers.parseEther("0.005");
await expect(
saleContract.mint("ipfs://test", { value: lowPrice })
).to.be.revertedWith("Insufficient payment");
});
it("Should refund overpayment", async function() {
const overpayment = ethers.parseEther("0.02");
const initialBalance = await buyer.getBalance();
await saleContract
.connect(buyer)
.mint("ipfs://test", { value: overpayment });
// Buyer should have paid exactly 0.01 ETH (plus gas)
const finalBalance = await buyer.getBalance();
// Assert refund logic here...
});
});
Error Handling#
The contract provides clear error messages:
"Insufficient payment"
- Payment below 0.01 ETH"NFT minting failed"
- Underlying NFT contract reverted"Withdrawal failed"
- ETH transfer unsuccessful"Empty metadata array"
- Batch mint with no URIs"Max supply reached"
- NFT contract at capacity
Related Contracts#
- AbrahamEarlyWorks: Target NFT contract
- AgentRegistry: Alternative minting system
- ThirteenYearAuction: Auction-based sales
Contract Status: Production Ready
Last Updated: September 1, 2025