Documentation Index Fetch the complete documentation index at: https://docs.fene.app/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The NFTPassport contract manages the whitelist system for Fenines Network. Validators create referral keys to invite delegators, establishing a permissioned staking system with built-in attribution tracking.
Contract Address : 0x0000000000000000000000000000000000001001 (System Contract)
Core Concepts
Referral Keys Unique cryptographic keys created by validators to invite delegators
Whitelist Delegator must be whitelisted to a validator before staking
Multi-Use Codes Optional promo-code style keys with usage limits
Direct Invites Validators can directly whitelist addresses without keys
Workflow
Validator Creates Key
Validator generates a referral key (one-time or multi-use) const key = web3 . utils . keccak256 ( 'my-unique-referral-code' );
await nftPassport . methods . createReferralKey ( key ). send ({ from: validator });
Delegator Uses Key
Delegator uses the key to get whitelisted await nftPassport . methods . useReferralKey ( key ). send ({ from: delegator });
Delegator Can Stake
Now delegator is whitelisted and can stake to that validator await systemContract . methods . stakeToValidator ( validator ). send ({
from: delegator ,
value: stakeAmount
});
Validator Functions
createReferralKey
Create a one-time use referral key.
function createReferralKey ( bytes32 key ) external
Unique key hash. Generate using keccak256 of a random string or nonce.
Generate & Create
Using Helper
// Generate key from random string
const randomCode = 'my-referral-' + Date . now () + Math . random ();
const key = web3 . utils . keccak256 ( randomCode );
await nftPassport . methods . createReferralKey ( key ). send ({
from: validatorAddress
});
// Share the code (NOT the hash) with delegator
console . log ( 'Share this code:' , randomCode );
Effects:
Creates key with isActive = true
Key can be used once
Added to validator’s key list
Increments validatorActiveKeyCount
createReferralKeyWithExpiry
Create a one-time key with expiration.
function createReferralKeyWithExpiry (
bytes32 key ,
uint256 expiresInBlocks
) external
Number of blocks until expiration (e.g., 28800 = ~24 hours at 3s blocks)
const key = web3 . utils . keccak256 ( 'limited-time-offer' );
const blocksPerDay = 28800 ; // ~24 hours
await nftPassport . methods . createReferralKeyWithExpiry (
key ,
blocksPerDay * 7 // Expires in 7 days
). send ({ from: validatorAddress });
createMultiUseKey
Create a multi-use referral code (like a promo code).
function createMultiUseKey (
bytes32 key ,
uint256 maxUsage ,
uint256 expiresInBlocks
) external
Maximum number of uses (0 = unlimited)
Blocks until expiration (0 = never expires)
Multi-Use Code
Limited Campaign
const promoCode = 'FENINE100' ;
const key = web3 . utils . keccak256 ( promoCode );
await nftPassport . methods . createMultiUseKey (
key ,
100 , // Max 100 uses
0 // Never expires
). send ({ from: validatorAddress });
// Share the promo code
console . log ( 'Promo code:' , promoCode );
Use Cases:
Public promo codes for marketing campaigns
Partner referral programs
Community growth initiatives
Event-specific invitations
revokeReferralKey
Deactivate a referral key.
function revokeReferralKey ( bytes32 key ) external
const key = web3 . utils . keccak256 ( 'compromised-key' );
await nftPassport . methods . revokeReferralKey ( key ). send ({
from: validatorAddress
});
Effects:
Sets isActive = false
Key can no longer be used
Decrements validatorActiveKeyCount
Does NOT affect already-whitelisted users
directInvite
Directly whitelist a delegator without requiring a key.
function directInvite ( address delegator ) external
await nftPassport . methods . directInvite ( delegatorAddress ). send ({
from: validatorAddress
});
Requirements:
Delegator cannot be self (validator)
Delegator not already whitelisted
Contract not paused
Use Cases:
VIP delegators
Partnership agreements
Manual whitelist management
Testing/development
batchDirectInvite
Whitelist multiple delegators at once.
function batchDirectInvite ( address [] calldata delegators ) external
const delegators = [
'0x1111...' ,
'0x2222...' ,
'0x3333...'
];
await nftPassport . methods . batchDirectInvite ( delegators ). send ({
from: validatorAddress ,
gas: 500000 // Increase gas for batch operation
});
Gas-efficient for whitelisting many addresses. Automatically skips invalid/duplicate addresses.
revokeWhitelist
Remove a delegator from whitelist.
function revokeWhitelist ( address delegator ) external
await nftPassport . methods . revokeWhitelist ( delegatorAddress ). send ({
from: validatorAddress
});
This does NOT automatically unstake the delegator. They must unstake manually from FenineSystem.
Delegator Functions
useReferralKey
Use a referral key to get whitelisted.
function useReferralKey ( bytes32 key ) external
Delegator Side
Check Before Using
// Validator shared the code (not the hash)
const code = 'my-referral-code-from-validator' ;
// Hash it
const key = web3 . utils . keccak256 ( code );
// Use it
await nftPassport . methods . useReferralKey ( key ). send ({
from: delegatorAddress
});
// Now can stake
const systemContract = new web3 . eth . Contract ( systemABI , systemAddress );
await systemContract . methods . stakeToValidator ( validatorAddress ). send ({
from: delegatorAddress ,
value: web3 . utils . toWei ( '1000' , 'ether' )
});
Requirements:
Key must exist
Key must be active
Key not expired (if expiry set)
Not already whitelisted to this validator
Usage limit not reached (for multi-use keys)
Effects:
Increments usageCount
One-time keys: deactivated after use
Delegator added to whitelist
Sets delegatorValidator[delegator] = validator
exitFromValidator
Self-remove from validator’s whitelist.
function exitFromValidator ( address validator ) external
// Delegator unstakes first (from FenineSystem)
await systemContract . methods . unstakeDelegator ( validatorAddress ). send ({
from: delegatorAddress
});
// Then exit whitelist
await nftPassport . methods . exitFromValidator ( validatorAddress ). send ({
from: delegatorAddress
});
Best practice: Unstake from FenineSystem first, then exit whitelist. Cannot re-enter without new referral key.
View Functions
isWhitelisted
Check if a delegator is whitelisted to a validator.
function isWhitelisted ( address delegator , address validator )
external view returns ( bool )
const canStake = await nftPassport . methods . isWhitelisted (
delegatorAddress ,
validatorAddress
). call ();
if ( canStake ) {
console . log ( '✓ Can stake to this validator' );
} else {
console . log ( '✗ Need referral key first' );
}
This function is called internally by FenineSystem.stakeToValidator() to verify permission.
getKeyInfo
Get detailed information about a referral key.
function getKeyInfo ( bytes32 key ) external view returns (
address validator ,
bool isActive ,
bool isMultiUse ,
uint256 usageCount ,
uint256 maxUsage ,
uint256 createdAt ,
uint256 expiresAt ,
bool isExpired ,
bool isUsable
)
const code = 'PROMO2024' ;
const key = web3 . utils . keccak256 ( code );
const info = await nftPassport . methods . getKeyInfo ( key ). call ();
console . log ({
validator: info . validator ,
isActive: info . isActive ,
isMultiUse: info . isMultiUse ,
usageCount: info . usageCount + '/' + ( info . maxUsage || '∞' ),
createdBlock: info . createdAt ,
expiresBlock: info . expiresAt || 'Never' ,
isExpired: info . isExpired ,
canUseNow: info . isUsable
});
getValidatorKeys
Get all referral keys created by a validator.
function getValidatorKeys ( address validator )
external view returns ( bytes32 [] memory )
const keys = await nftPassport . methods . getValidatorKeys ( validatorAddress ). call ();
console . log ( 'Total keys created:' , keys . length );
// Get details for each
for ( const key of keys ) {
const info = await nftPassport . methods . getKeyInfo ( key ). call ();
console . log ({
key: key ,
active: info . isActive ,
used: info . usageCount + '/' + ( info . maxUsage || '∞' )
});
}
getValidatorStats
Get validator’s referral statistics.
function getValidatorStats ( address validator ) external view returns (
uint256 totalKeys ,
uint256 activeKeys ,
uint256 whitelistCount
)
const stats = await nftPassport . methods . getValidatorStats ( validatorAddress ). call ();
console . log ({
totalKeysCreated: stats . totalKeys ,
activeKeys: stats . activeKeys ,
whitelistedDelegators: stats . whitelistCount
});
getWhitelistedBy
Get which validator whitelisted a delegator.
function getWhitelistedBy ( address delegator )
external view returns ( address validator )
const validator = await nftPassport . methods . getWhitelistedBy (
delegatorAddress
). call ();
console . log ( 'Whitelisted by:' , validator );
Helper Functions
hashReferralCode
Generate key hash from human-readable string.
function hashReferralCode ( string calldata code )
external pure returns ( bytes32 )
On-Chain Hash
Off-Chain Hash (Same Result)
const hash = await nftPassport . methods . hashReferralCode ( 'MY-CODE-2024' ). call ();
console . log ( 'Hash:' , hash );
generateKey
Generate deterministic key from validator address + nonce.
function generateKey ( address validator , uint256 nonce )
external pure returns ( bytes32 )
const nonce = Date . now ();
const key = await nftPassport . methods . generateKey (
validatorAddress ,
nonce
). call ();
console . log ( 'Generated key:' , key );
Events
event ReferralKeyCreated (
address indexed validator ,
bytes32 indexed keyHash ,
bool isMultiUse ,
uint256 maxUsage ,
uint256 expiresAt
)
event ReferralKeyUsed (
address indexed delegator ,
address indexed validator ,
bytes32 indexed keyHash
)
event ReferralKeyRevoked (
address indexed validator ,
bytes32 indexed keyHash
)
event DirectInvite (
address indexed validator ,
address indexed delegator
)
event WhitelistRevoked (
address indexed validator ,
address indexed delegator
)
event DelegatorExited (
address indexed delegator ,
address indexed validator
)
Integration Examples
Validator: Create Campaign
Delegator: Join with Code
List All Whitelisted Users
// Marketing campaign with multi-use promo code
const campaignCode = 'FENINE-LAUNCH-2024' ;
const key = web3 . utils . keccak256 ( campaignCode );
await nftPassport . methods . createMultiUseKey (
key ,
1000 , // 1000 uses
28800 * 30 // 30-day campaign
). send ({ from: validatorAddress });
console . log ( 'Campaign created! Share code:' , campaignCode );
// Monitor usage
setInterval ( async () => {
const info = await nftPassport . methods . getKeyInfo ( key ). call ();
const stats = await nftPassport . methods . getValidatorStats ( validatorAddress ). call ();
console . log ({
codeUsed: info . usageCount + '/1000' ,
totalWhitelisted: stats . whitelistCount ,
blocksRemaining: info . expiresAt - await web3 . eth . getBlockNumber ()
});
}, 60000 );
// Received promo code from validator
const promoCode = 'FENINE-LAUNCH-2024' ;
const validatorAddress = '0x1234...' ; // From validator's marketing
// Step 1: Hash the code
const key = web3 . utils . keccak256 ( promoCode );
// Step 2: Check if valid
const info = await nftPassport . methods . getKeyInfo ( key ). call ();
if ( ! info . isUsable ) {
console . log ( 'Code not valid:' , {
expired: info . isExpired ,
maxUsed: info . usageCount >= info . maxUsage
});
return ;
}
// Step 3: Use the key
await nftPassport . methods . useReferralKey ( key ). send ({
from: delegatorAddress
});
console . log ( '✓ Whitelisted to validator:' , validatorAddress );
// Step 4: Now can stake
const systemContract = new web3 . eth . Contract ( systemABI , systemAddress );
await systemContract . methods . stakeToValidator ( validatorAddress ). send ({
from: delegatorAddress ,
value: web3 . utils . toWei ( '1000' , 'ether' )
});
console . log ( '✓ Staked successfully!' );
const stats = await nftPassport . methods . getValidatorStats (
validatorAddress
). call ();
console . log ( 'Total whitelisted:' , stats . whitelistCount );
// Get delegator list from FenineSystem
const systemContract = new web3 . eth . Contract ( systemABI , systemAddress );
const stakers = await systemContract . methods . getValidatorStakers (
validatorAddress
). call ();
console . log ( 'Active stakers:' , stakers );
// Check each one
for ( const staker of stakers ) {
const isWhitelisted = await nftPassport . methods . isWhitelisted (
staker ,
validatorAddress
). call ();
const whitelistedBy = await nftPassport . methods . getWhitelistedBy (
staker
). call ();
console . log ({
delegator: staker ,
whitelisted: isWhitelisted ,
validator: whitelistedBy
});
}
Best Practices
Generate keys off-chain using keccak256 to save gas
Use descriptive codes for multi-use keys (SUMMER2024, not random hashes)
Store key-to-code mappings in your backend
Revoke compromised keys immediately
Set expiry on time-limited campaigns
Use one-time keys for VIP delegators
Use multi-use keys for public campaigns
Monitor key usage via getKeyInfo()
Track conversion: keys created → keys used → actual stakes
Revoke keys if abused
Save the original code, not the hash
Check key validity before using (getKeyInfo)
One delegator can be whitelisted to multiple validators
Exit cleanly: unstake first, then call exitFromValidator()
Keys are NOT secrets - they’re invitation tokens
Anyone with the code can use it (if multi-use or unused)
Validators should distribute keys via trusted channels
Monitor for unusual usage patterns
Admin Functions
These functions are restricted to contract admin for emergency use only.
setPaused
Pause/unpause the contract.
function setPaused ( bool _paused ) external onlyAdmin
Effects when paused:
Cannot create keys
Cannot use keys
Cannot direct invite
View functions still work
transferAdmin
Transfer admin role.
function transferAdmin ( address newAdmin ) external onlyAdmin
Next Steps
FenineSystem Contract Learn about staking and rewards
Tax Manager Reward taxation system