Purchase Contract
Overview
The Purchase Contract provides an automated system for handling token purchases with stablecoin payments. It integrates directly with the RestrictedLockupToken and InterestPayment contracts to provide atomic purchase transactions that include originator payments, admin fees, interest prefunding, and token minting.
Key Features
- Atomic Purchase Transactions: All operations (payments, fees, minting) occur in a single transaction
- Multi-Party Payment Distribution: Supports originator payments, admin fees, and interest prefunding
- AML/KYC Integration: Validates both payer and recipient compliance status
- Purchase ID Tracking: Prevents duplicate transactions with unique purchase identifiers
- Automation-Ready: Designed for automated execution by external systems
- Flexible Configuration: Configurable wallet addresses for different payment recipients
Usage
Architecture Overview
Purchase Execution Flow
1. Purchase Parameters
Each purchase requires a structured set of parameters:
// For purchases with interest prefunding
struct PurchaseParamsWithInterest {
string purchaseId; // Unique identifier
address payerAddress; // Who pays USDC
address tokenRecipientAddress; // Who receives tokens
uint256 mintAmount; // Number of tokens to mint
uint256 originatorPurchaseAmount; // Payment to originator
uint256 prefundedInterestAmount; // Interest prefunding
uint256 adminFeeExcludingPrefundedInterestAmount; // Admin fees
uint256 totalAmount; // Total USDC required
}
// For basic purchases without interest
struct PurchaseParams {
string purchaseId; // Unique identifier
address payerAddress; // Who pays USDC
address tokenRecipientAddress; // Who receives tokens
uint256 originatorPurchaseAmount; // Payment to originator
uint256 mintAmount; // Number of tokens to mint
uint256 adminFeeAmount; // Admin fees
uint256 totalAmount; // Total USDC required
}
2. Execution Process
3. Token Minting
The contract mints the specified number of tokens to the recipient address. The mint amount is provided as a parameter in the purchase struct and must be calculated off-chain based on the purchase terms.
Example:
- Purchase amount: $50,000 USDC
- Token price: $1 per token
- Tokens to mint: 50,000 tokens (calculated off-chain)
- The
mintAmountparameter would be set to 50,000
Configuration Management
Wallet Address Management
The contract supports configurable wallet addresses for different payment types:
// Set originator payment wallet
purchaseContract.updateOriginatorPaymentWallet(newOriginatorWallet);
// Set admin fee wallet
purchaseContract.updateAdminFeeWallet(newAdminFeeWallet);
// Set canceled purchase wallet
purchaseContract.updateCanceledPurchaseWallet(newCanceledPurchaseWallet);
// Set automation admin (who can execute purchases)
purchaseContract.updateAutomationAdmin(newAutomationAdmin);
Purchase Cancellation
In case of issues, purchases can be canceled by the automation admin:
Validation and Security Features
AML/KYC Validation
Both payer and token recipient must pass AML/KYC validation:
// Validated automatically during purchase execution
require(tokenContract.isAmlKycPassed(params.tokenRecipientAddress), "Recipient AML/KYC failed");
require(tokenContract.isAmlKycPassed(params.payerAddress), "Payer AML/KYC failed");
Purchase ID Uniqueness
Each purchase must have a unique identifier to prevent duplicate executions:
require(!usedPurchaseIds[params.purchaseId], "Purchase ID already used");
usedPurchaseIds[params.purchaseId] = true;
Amount Validation
The contract validates that the total amount equals the sum of all components:
require(
params.totalAmount ==
params.originatorPurchaseAmount +
params.prefundedInterestAmount +
params.adminFeeExcludingPrefundedInterestAmount,
"Invalid total amount"
);
Integration Example
Here's a complete example of executing a purchase:
// 1. Prepare purchase parameters (with interest prefunding)
PurchaseParamsWithInterest memory params = PurchaseParamsWithInterest({
purchaseId: "PURCHASE-2024-001",
payerAddress: investor,
tokenRecipientAddress: investor,
mintAmount: 100000e18, // 100,000 tokens (18 decimals)
originatorPurchaseAmount: 100000e6, // $100,000 USDC
prefundedInterestAmount: 5000e6, // $5,000 USDC interest prefunding
adminFeeExcludingPrefundedInterestAmount: 500e6, // $500 USDC admin fee
totalAmount: 105500e6 // $105,500 USDC total
});
// 2. Investor approves USDC spending
usdcToken.approve(purchaseContractAddress, params.totalAmount);
// 3. Automation admin executes purchase with interest
purchaseContract.executePurchaseWithInterest(params);
// Result:
// - Originator receives: $100,000 USDC
// - Interest payment contract receives: $5,000 USDC
// - Admin wallet receives: $500 USDC
// - Investor receives: 100,000 security tokens
Administrative Functions
Owner Functions
updateAutomationAdmin(): Change who can execute purchasesupdateAdminFeeWallet(): Update admin fee recipientupdateCanceledPurchaseWallet(): Update cancellation refund recipientupdateOriginatorPaymentWallet(): Update originator payment recipient
Automation Admin Functions
executePurchase(): Execute a basic purchase transaction (without interest prefunding)executePurchaseWithInterest(): Execute a purchase transaction with interest prefundingcancelPurchase(): Cancel and refund a purchase
Error Handling
PurchaseContract_PurchaseIdAlreadyUsed: Duplicate purchase IDPurchaseContract_TokenRecipientAmlKycNotPassed: Recipient compliance failurePurchaseContract_PayerAmlKycNotPassed: Payer compliance failurePurchaseContract_InvalidTotalAmount: Amount calculation mismatchPurchaseContract_AdminFeeWalletNotSet: Required wallet address not configured
API Reference
Constructor
constructor(address contractAdmin_, address trustedForwarder_, address tokenContract_, address interestPayment_, address paymentToken_)
Initializes the PurchaseContract with configuration parameters.
Parameters:
contractAdmin_(address): The contract owner/admin addresstrustedForwarder_(address): ERC-2771 trusted forwarder address for meta-transactionstokenContract_(address): RestrictedLockupToken contract addressinterestPayment_(address): InterestPayment contract address (can be zero address)paymentToken_(address): ERC-20 payment token address (typically USDC)
Requirements:
- All addresses must be non-zero except
interestPayment_ - If
interestPayment_is provided, its payment token must matchpaymentToken_ - If
interestPayment_is provided, its restricted token must matchtokenContract_
Administrative Functions (API)
updateAutomationAdmin(address newAutomationAdmin_)
Updates the automation admin address who can execute purchases.
Parameters:
newAutomationAdmin_(address): New automation admin address
Requirements:
- Caller must be contract owner
- Address must not be zero
Emits: AutomationAdminUpdated(oldAdmin, newAutomationAdmin_)
updateAdminFeeWallet(address newAdminFeeWallet_)
Updates the admin fee wallet address.
Parameters:
newAdminFeeWallet_(address): New admin fee wallet address
Requirements:
- Caller must be contract owner
- Address must not be zero
Emits: AdminFeeWalletUpdated(oldWallet, newAdminFeeWallet_)
updateCanceledPurchaseWallet(address newCanceledPurchaseWallet_)
Updates the canceled purchase wallet address.
Parameters:
newCanceledPurchaseWallet_(address): New canceled purchase wallet address
Requirements:
- Caller must be contract owner
- Address must not be zero
Emits: CanceledPurchaseWalletUpdated(oldWallet, newCanceledPurchaseWallet_)
updateOriginatorPaymentWallet(address newOriginatorPaymentWallet_)
Updates the originator payment wallet address.
Parameters:
newOriginatorPaymentWallet_(address): New originator payment wallet address
Requirements:
- Caller must be contract owner
- Address must not be zero
Emits: OriginatorPaymentWalletUpdated(oldWallet, newOriginatorPaymentWallet_)
Purchase Functions
executePurchaseWithInterest(PurchaseParamsWithInterest params)
Executes a token purchase with USDC payment and interest funding.
Parameters:
params(PurchaseParamsWithInterest): Purchase parameters struct
PurchaseParamsWithInterest Structure:
struct PurchaseParamsWithInterest {
string purchaseId; // Unique purchase identifier
address payerAddress; // Address paying for the purchase
address tokenRecipientAddress; // Address receiving the tokens
uint256 mintAmount; // Number of tokens to mint
uint256 originatorPurchaseAmount; // Payment amount to originator
uint256 prefundedInterestAmount; // Amount to prefund interest
uint256 adminFeeExcludingPrefundedInterestAmount; // Admin fee amount
uint256 totalAmount; // Total payment amount
}
Requirements:
- Caller must be automation admin
- Interest payment contract must be configured
- Purchase ID must be unique
- Admin fee wallet and originator payment wallet must be set
- Mint amount must be greater than zero
- Total amount must equal sum of all component amounts
- Both payer and token recipient must pass AML/KYC validation
Emits: PurchaseExecuted(purchaseId, payerAddress, tokenRecipientAddress, authorityAddress, originatorPurchaseAmount, prefundedInterestAmount, adminFeeExcludingPrefundedInterestAmount, mintAmount, payerAddress)
executePurchase(PurchaseParams params)
Executes a token purchase with USDC payment (without interest funding).
Parameters:
params(PurchaseParams): Purchase parameters struct
PurchaseParams Structure:
struct PurchaseParams {
string purchaseId; // Unique purchase identifier
address payerAddress; // Address paying for the purchase
address tokenRecipientAddress; // Address receiving the tokens
uint256 originatorPurchaseAmount; // Payment amount to originator
uint256 mintAmount; // Number of tokens to mint
uint256 adminFeeAmount; // Admin fee amount
uint256 totalAmount; // Total payment amount
}
Requirements:
- Caller must be automation admin
- Purchase ID must be unique
- Admin fee wallet and originator payment wallet must be set
- Both payer and token recipient must pass AML/KYC validation
- Total amount must equal originator amount plus admin fee amount
- Mint amount must be greater than zero
Emits: PurchaseExecuted(purchaseId, payerAddress, tokenRecipientAddress, authorityAddress, originatorPurchaseAmount, 0, adminFeeAmount, mintAmount, payerAddress)
cancelPurchase(address paymentTokenAddress_, uint256 amount_)
Cancels a purchase and transfers the amount to the canceled purchase wallet.
Parameters:
paymentTokenAddress_(address): Address of the payment token to transferamount_(uint256): Amount to transfer to canceled purchase wallet
Requirements:
- Caller must be automation admin
- Amount must be greater than zero
- Canceled purchase wallet must be set
Emits: PurchaseCanceled(authorityAddress, paymentTokenAddress_, canceledPurchaseWallet, amount_)
Query Functions
isPurchaseIdUsed(string purchaseId_) → bool
Checks if a purchase ID has been used.
Parameters:
purchaseId_(string): The purchase ID to check
Returns:
bool: True if the purchase ID has been used, false otherwise
automationAdmin() → address
Returns the current automation admin address.
Returns:
address: The automation admin address
adminFeeWallet() → address
Returns the current admin fee wallet address.
Returns:
address: The admin fee wallet address
canceledPurchaseWallet() → address
Returns the current canceled purchase wallet address.
Returns:
address: The canceled purchase wallet address
originatorPaymentWallet() → address
Returns the current originator payment wallet address.
Returns:
address: The originator payment wallet address
interestPayment() → address
Returns the InterestPayment contract address.
Returns:
address: The InterestPayment contract address
tokenContract() → address
Returns the RestrictedLockupToken contract address.
Returns:
address: The RestrictedLockupToken contract address
paymentToken() → address
Returns the payment token (USDC) contract address.
Returns:
address: The payment token contract address
usedPurchaseIds(string purchaseId) → bool
Returns whether a specific purchase ID has been used.
Parameters:
purchaseId(string): The purchase ID to query
Returns:
bool: True if the purchase ID has been used
Inherited Functions
Ownable Functions
owner() → address
Returns the current contract owner.
Returns:
address: The owner address
transferOwnership(address newOwner)
Transfers ownership to a new address.
Parameters:
newOwner(address): New owner address
Requirements:
- Caller must be current owner
Emits: OwnershipTransferred(previousOwner, newOwner)
renounceOwnership()
Renounces ownership, leaving the contract without an owner.
Requirements:
- Caller must be current owner
Emits: OwnershipTransferred(owner, address(0))
ERC-2771 Functions
isTrustedForwarder(address forwarder) → bool
Checks if an address is a trusted forwarder for meta-transactions.
Parameters:
forwarder(address): Address to check
Returns:
bool: True if the address is a trusted forwarder
trustedForwarder() → address
Returns the trusted forwarder address.
Returns:
address: The trusted forwarder address
Events
PurchaseExecuted(string indexed purchaseId, address indexed payerAddress, address indexed tokenRecipientAddress, address authorityAddress, uint256 originatorPurchaseAmount, uint256 prefundedInterestAmount, uint256 adminFeeExcludingPrefundedInterestAmount, uint256 mintAmount, address originatorAddress)
Emitted when a purchase is successfully executed.
Parameters:
purchaseId(string, indexed): Unique purchase identifierpayerAddress(address, indexed): Address that paid for the purchasetokenRecipientAddress(address, indexed): Address that received the tokensauthorityAddress(address): Address that executed the purchase (automation admin)originatorPurchaseAmount(uint256): Amount paid to originatorprefundedInterestAmount(uint256): Amount used to prefund interestadminFeeExcludingPrefundedInterestAmount(uint256): Admin fee amountmintAmount(uint256): Number of tokens mintedoriginatorAddress(address): Originator address (same as payer)
PurchaseCanceled(address indexed authorityAddress, address indexed paymentTokenAddress, address indexed canceledPurchaseWallet, uint256 amount)
Emitted when a purchase is canceled.
Parameters:
authorityAddress(address, indexed): Address that canceled the purchasepaymentTokenAddress(address, indexed): Payment token addresscanceledPurchaseWallet(address, indexed): Wallet that received the refundamount(uint256): Amount refunded
Administrative Events
AutomationAdminUpdated(address indexed oldAdmin, address indexed newAdmin)
Emitted when the automation admin is updated.
Parameters:
oldAdmin(address, indexed): Previous automation admin addressnewAdmin(address, indexed): New automation admin address
AdminFeeWalletUpdated(address indexed oldWallet, address indexed newWallet)
Emitted when the admin fee wallet is updated.
Parameters:
oldWallet(address, indexed): Previous admin fee wallet addressnewWallet(address, indexed): New admin fee wallet address
CanceledPurchaseWalletUpdated(address indexed oldWallet, address indexed newWallet)
Emitted when the canceled purchase wallet is updated.
Parameters:
oldWallet(address, indexed): Previous canceled purchase wallet addressnewWallet(address, indexed): New canceled purchase wallet address
OriginatorPaymentWalletUpdated(address indexed oldWallet, address indexed newWallet)
Emitted when the originator payment wallet is updated.
Parameters:
oldWallet(address, indexed): Previous originator payment wallet addressnewWallet(address, indexed): New originator payment wallet address