Skip to main content

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

1

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 });
2

Delegator Uses Key

Delegator uses the key to get whitelisted
await nftPassport.methods.useReferralKey(key).send({ from: delegator });
3

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
key
bytes32
required
Unique key hash. Generate using keccak256 of a random string or nonce.
// 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
expiresInBlocks
uint256
required
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
maxUsage
uint256
required
Maximum number of uses (0 = unlimited)
expiresInBlocks
uint256
Blocks until expiration (0 = never expires)
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
// 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)
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

// 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);

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