Alpha Points provides a robust, quota-managed point system built on Sui blockchain. This guide covers integration patterns, rules, and best practices for developers building applications that mint, manage, and interact with Alpha Points.
- All point minting must go through a PartnerCap - no exceptions
- PartnerCap enforces daily and lifetime quotas automatically
- Prevents abuse and ensures sustainable point economics
- Get your PartnerCap from the Partner Dashboard
- Points minting never fails due to quota limits
- Functions return actual points minted (may be less than requested)
- Silent degradation provides better user experience
- Check quotas proactively when possible
- New integration functions are additive only
- Existing functions remain unchanged (package upgrade compatibility)
- Multiple integration patterns available for different use cases
Use Case: Frontend applications, direct user interactions
public entry fun mint_points_with_partner_cap(
partner_cap: &mut PartnerCapFlex,
ledger: &mut Ledger,
user: address,
requested_amount: u64,
clock: &Clock,
ctx: &mut TxContext
)Benefits:
- ✅ Single transaction call
- ✅ No complex transaction building
- ✅ Automatic quota validation
- ✅ Silent failure handling
Use Case: Backend services, complex applications, Move modules
public fun mint_points_simple(
partner_cap: &mut PartnerCapFlex,
ledger: &mut Ledger,
user: address,
requested_amount: u64,
clock: &Clock,
ctx: &mut TxContext
): u64 // Returns actual amount mintedBenefits:
- ✅ Returns actual minted amount
- ✅ Can be composed with other operations
- ✅ Programmatic response handling
Use Case: Advanced DeFi applications with staking positions
public fun mint_points_with_staking<T: store>(
partner_cap: &mut PartnerCapFlex,
ledger: &mut Ledger,
user: address,
requested_amount: u64,
stake_opt: &std::option::Option<StakePosition<T>>,
liq_share: u64,
clock: &Clock,
ctx: &mut TxContext
): u64Benefits:
- ✅ Weight curve validation
- ✅ Stake position integration
- ✅ Advanced DeFi composability
-
Daily Limits
- Each PartnerCap has a daily point minting limit
- Resets every 24 hours based on epoch timing
- Based on 3% of effective collateral value
-
Lifetime Limits
- Total points that can ever be minted by a PartnerCap
- Based on 1000 points per 1 USDC of effective collateral value
- Cannot be exceeded under any circumstances
-
Collateral Requirements
- PartnerCaps must have locked collateral (SUI, USDC, or NFTs)
- Different assets have different LTV ratios:
- USDC: 100% LTV (stable)
- SUI: Market rate conversion to USDC
- NFTs: 70% LTV (more volatile)
-
Transaction Requirements
- Must include Clock object (
0x6) - Must pass mutable references to PartnerCap and Ledger
- Gas costs are paid by transaction sender
- Must include Clock object (
-
Address Validation
- All addresses must be valid Sui addresses (64-character hex with 0x prefix)
- Invalid addresses will cause transaction failure
-
Amount Limits
- Minimum: 1 point
- Maximum: Limited by available quota
- Cannot mint 0 points
-
PartnerCap Ownership
- Only PartnerCap owner can authorize point minting
- PartnerCap must be passed as mutable reference
- Ownership is enforced at transaction level
-
Quota Enforcement
- Cannot bypass quota limits through any means
- All mint functions enforce the same quota rules
- Quota state is updated atomically with point minting
-
Paused State
- PartnerCaps can be paused (yield optimization mode)
- No points can be minted when paused
- Returns 0 points silently when paused
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
tx.moveCall({
target: `${PACKAGE_ID}::ledger::mint_points_with_partner_cap`,
arguments: [
tx.object(PARTNER_CAP_ID),
tx.object(LEDGER_OBJECT_ID),
tx.pure.address(userAddress),
tx.pure.u64(pointAmount),
tx.object('0x6'), // Clock
],
});
const result = await suiClient.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});import pysui
tx = pysui.Transaction()
tx.move_call(
target=f"{PACKAGE_ID}::ledger::mint_points_with_partner_cap",
arguments=[
PARTNER_CAP_ID,
LEDGER_OBJECT_ID,
user_address,
point_amount,
"0x6" # Clock
]
)
result = await client.execute_transaction(tx)use sui_sdk::types::transactions::TransactionBuilder;
let mut tx_builder = TransactionBuilder::new();
tx_builder.move_call(
format!("{}::ledger::mint_points_with_partner_cap", PACKAGE_ID),
vec![
PARTNER_CAP_ID,
LEDGER_OBJECT_ID,
user_address,
point_amount.to_string(),
"0x6", // Clock
]
)?;
let result = client.execute_transaction(tx_builder.build()).await?;public fun reward_user_with_points(
partner_cap: &mut PartnerCapFlex,
ledger: &mut Ledger,
user: address,
reward_amount: u64,
clock: &Clock,
ctx: &mut TxContext
): u64 {
// Your custom logic here
let points_to_mint = calculate_reward_points(reward_amount);
// Mint points with quota validation
alpha_points::ledger::mint_points_simple(
partner_cap,
ledger,
user,
points_to_mint,
clock,
ctx
)
}// Check quota before attempting large mints
const quota = await alphaPointsClient.getAvailableQuota();
const actualAmount = Math.min(requestedAmount, quota.maxMintable);
if (actualAmount < requestedAmount) {
console.log(`Reduced amount to ${actualAmount} due to quota limits`);
}// Handle quota exhaustion gracefully
const result = await mintPoints(userAddress, amount);
if (result.pointsMinted === 0) {
showUserMessage("Daily quota reached. Points will be available tomorrow.");
} else if (result.pointsMinted < amount) {
showUserMessage(`Minted ${result.pointsMinted} points (quota limited)`);
} else {
showUserMessage(`Successfully minted ${result.pointsMinted} points!`);
}// For backend services - batch multiple users
let batch_request = BatchMintRequest {
mints: users.iter().map(|user| MintPointsRequest {
user_address: user.address.clone(),
point_amount: calculate_user_points(user),
}).collect(),
};
let batch_result = client.batch_mint_points(batch_request).await?;// Validate configuration on startup
export function validateConfig() {
const errors = [];
if (!PACKAGE_ID || PACKAGE_ID === '0x...') {
errors.push('PACKAGE_ID must be configured');
}
if (!PARTNER_CAP_ID || PARTNER_CAP_ID === '0x...') {
errors.push('PARTNER_CAP_ID must be configured');
}
if (errors.length > 0) {
throw new Error(`Configuration errors: ${errors.join(', ')}`);
}
}-
Hardcoding Object IDs in Code
// Bad - hardcoded IDs const PARTNER_CAP = "0xabc123...";
-
Assuming Full Amount Will Be Minted
// Bad - assumes requested amount is always minted await mintPoints(userAddress, 1000); userBalance += 1000; // Might be wrong due to quota limits
-
Ignoring Quota Limits
// Bad - no quota checking for (const user of users) { await mintPoints(user.address, 10000); // Might fail for later users }
-
Environment-Based Configuration
// Good - configurable const PARTNER_CAP = process.env.PARTNER_CAP_ID;
-
Handle Actual Minted Amount
// Good - use actual result const result = await mintPoints(userAddress, 1000); userBalance += result.pointsMinted;
-
Proactive Quota Management
// Good - check quota first const quota = await getAvailableQuota(); const batchSize = Math.min(users.length, quota.maxMintable / 100);
-
Get Your PartnerCap
- Visit the Partner Dashboard
- Create PartnerCap with appropriate collateral
- Copy the PartnerCap Object ID
-
Configure Your Application
- Set package ID, PartnerCap ID, and Ledger ID
- Choose appropriate integration pattern
- Implement quota checking and error handling
-
Test Integration
- Start with small amounts
- Verify quota enforcement works
- Test error scenarios
-
Deploy to Production
- Use environment variables for configuration
- Implement proper logging and monitoring
- Set up quota alerts if needed
- Package Documentation: See
sources/ledger.movefor function signatures - Integration Examples: Check
examples/directory for complete implementations - Partner Dashboard: Get your PartnerCap and monitor quotas
- Sui Documentation: https://docs.sui.io
// Check available quota
public fun get_mintable_quota(partner_cap: &PartnerCapFlex): u64
// Get user balances
public fun get_available_balance(ledger: &Ledger, user: address): u64
public fun get_locked_balance(ledger: &Ledger, user: address): u64
public fun get_total_balance(ledger: &Ledger, user: address): u64// Primary entry function - for transactions
public entry fun mint_points_with_partner_cap(...)
// Programmable functions - for Move modules
public fun mint_points_simple(...): u64
public fun mint_points_with_staking<T>(...): u64This guide covers the essential patterns for integrating with Alpha Points. For specific implementation questions, refer to the example code in the examples/ directory.