Skip to main content

System Contract Addresses

const addresses = {
  FenineSystem:      '0x0000000000000000000000000000000000001000',
  NFTPassport:       '0x0000000000000000000000000000000000001001',
  TaxManager:        '0x0000000000000000000000000000000000001002',
  RewardManager:     '0x0000000000000000000000000000000000001003'
};

Function Selectors

FenineSystem

FunctionSelectorParameters
getActiveValidators()0x9de70258-
getValidatorInfo(address)0xb5d89627address
getDelegatorInfo(address,address)0xa993683faddress, address
getValidatorStakers(address)-address
totalNetworkStake()0x635c8637-
getCurrentEpoch()--
getProximityConfig()--
getEstimatedDelegatorReward(address,address)-address, address
getNextEpochReward()--

NFTPassport

FunctionSelectorParameters
isWhitelisted(address,address)-address, address
getKeyInfo(bytes32)-bytes32
getValidatorKeys(address)-address
getValidatorStats(address)-address
hashReferralCode(string)-string

TaxManager

FunctionSelectorParameters
computeTax(address,uint256)-address, uint256
previewTax(address,uint256)-address, uint256
getConfig()--
getTaxRate()--
isTaxExempt(address)-address

Common Queries

curl -X POST https://rpc.fene.app \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x0000000000000000000000000000000000001000",
      "data": "0x9de70258"
    }, "latest"],
    "id": 1
  }'

Setup Contract Instances

const Web3 = require('web3');
const web3 = new Web3('https://rpc.fene.app');

// Get ABIs
const systemABI = JSON.parse(
  await web3.eth.extend({
    methods: [{ 
      name: 'getSystemContractABI',
      call: 'fenine_getSystemContractABI',
      params: 0
    }]
  }).getSystemContractABI()
);

const nftPassportABI = JSON.parse(
  await web3.eth.extend({
    methods: [{ 
      name: 'getNFTPassportABI',
      call: 'fenine_getNFTPassportABI',
      params: 0
    }]
  }).getNFTPassportABI()
);

// Create instances
const systemContract = new web3.eth.Contract(
  systemABI,
  '0x0000000000000000000000000000000000001000'
);

const nftPassport = new web3.eth.Contract(
  nftPassportABI,
  '0x0000000000000000000000000000000000001001'
);

// Use them
const validators = await systemContract.methods.getActiveValidators().call();
const isWhitelisted = await nftPassport.methods.isWhitelisted(
  delegatorAddress,
  validatorAddress
).call();

Conversion Helpers

// Wei ↔ FEN
const weiToFene = (wei) => web3.utils.fromWei(wei, 'ether');
const feneToWei = (fene) => web3.utils.toWei(fene, 'ether');

// Basis Points ↔ Percentage
const bpsToPercent = (bps) => bps / 100;
const percentToBps = (percent) => percent * 100;

// Block Time Estimation (3s average)
const blocksPerHour = 1200;
const blocksPerDay = 28800;
const blocksPerWeek = 201600;

// Epoch Calculation
const EPOCH_BLOCKS = 200;
const getCurrentEpoch = (blockNumber) => Math.floor(blockNumber / EPOCH_BLOCKS);
const blocksUntilNextEpoch = (blockNumber) => EPOCH_BLOCKS - (blockNumber % EPOCH_BLOCKS);
const estimateEpochTime = (blockNumber) => {
  const blocksLeft = blocksUntilNextEpoch(blockNumber);
  return blocksLeft * 3; // seconds
};

// Validator Status
const VA_STATUS = {
  0: 'NOT_EXIST',
  1: 'CREATED',
  2: 'STAKED',
  3: 'VALIDATED',
  4: 'UNSTAKED'
};

// Delegator Status
const DC_STATUS = {
  0: 'NOT_EXIST',
  1: 'ACTIVE',
  2: 'UNSTAKING'
};

// Usage
const currentBlock = await web3.eth.getBlockNumber();
console.log('Current epoch:', getCurrentEpoch(currentBlock));
console.log('Next epoch in:', estimateEpochTime(currentBlock), 'seconds');

Monitor Epoch Changes

async function monitorEpochs() {
  const systemContract = new web3.eth.Contract(systemABI, systemAddress);
  
  let lastEpoch = 0;
  
  setInterval(async () => {
    const currentBlock = await web3.eth.getBlockNumber();
    const currentEpoch = Math.floor(currentBlock / 200);
    
    if (currentEpoch > lastEpoch) {
      console.log('=== NEW EPOCH ===');
      console.log('Epoch:', currentEpoch);
      console.log('Block:', currentBlock);
      
      // Get updated validator set
      const validators = await systemContract.methods
        .getActiveValidators()
        .call();
      
      console.log('Active validators:', validators.length);
      
      // Get network stats
      const totalStake = await systemContract.methods
        .totalNetworkStake()
        .call();
      
      console.log('Total staked:', web3.utils.fromWei(totalStake, 'ether'), 'FEN');
      
      lastEpoch = currentEpoch;
    }
  }, 3000); // Check every 3 seconds (1 block)
}

monitorEpochs();

Complete Validator Setup

async function becomeValidator(validatorAddress, privateKey) {
  const account = web3.eth.accounts.privateKeyToAccount(privateKey);
  web3.eth.accounts.wallet.add(account);
  
  const systemContract = new web3.eth.Contract(systemABI, systemAddress);
  
  console.log('Step 1: Register');
  await systemContract.methods.registerValidator(
    validatorAddress,
    'MyValidator',
    '',
    'Professional validator service',
    500 // 5% commission
  ).send({ from: validatorAddress });
  
  console.log('Step 2: Stake 10,000 FEN');
  await systemContract.methods.stakeValidator().send({
    from: validatorAddress,
    value: web3.utils.toWei('10000', 'ether'),
    gas: 300000
  });
  
  console.log('Step 3: Wait for epoch activation');
  const currentBlock = await web3.eth.getBlockNumber();
  const blocksUntilEpoch = 200 - (currentBlock % 200);
  const estimatedTime = blocksUntilEpoch * 3;
  
  console.log(`Activation in ~${estimatedTime} seconds (${blocksUntilEpoch} blocks)`);
  
  // Wait for activation
  await new Promise(resolve => setTimeout(resolve, estimatedTime * 1000));
  
  // Verify
  const info = await systemContract.methods.getValidatorInfo(validatorAddress).call();
  console.log('Status:', VA_STATUS[info.status]);
  
  if (info.status == 3) {
    console.log('✓ Successfully activated as validator!');
  }
}

Complete Delegation Flow

async function delegateToValidator(delegatorAddress, validatorAddress, privateKey) {
  const account = web3.eth.accounts.privateKeyToAccount(privateKey);
  web3.eth.accounts.wallet.add(account);
  
  const systemContract = new web3.eth.Contract(systemABI, systemAddress);
  const nftPassport = new web3.eth.Contract(nftPassportABI, nftPassportAddress);
  
  // Step 1: Check whitelist
  console.log('Checking whitelist...');
  const isWhitelisted = await nftPassport.methods.isWhitelisted(
    delegatorAddress,
    validatorAddress
  ).call();
  
  if (!isWhitelisted) {
    console.log('❌ Not whitelisted. Need referral key from validator.');
    return;
  }
  
  // Step 2: Check validator status
  const vaInfo = await systemContract.methods.getValidatorInfo(validatorAddress).call();
  if (vaInfo.status != 3) {
    console.log('❌ Validator not active');
    return;
  }
  
  // Step 3: Stake
  console.log('Staking 1,000 FEN...');
  await systemContract.methods.stakeToValidator(validatorAddress).send({
    from: delegatorAddress,
    value: web3.utils.toWei('1000', 'ether'),
    gas: 300000
  });
  
  console.log('✓ Successfully staked!');
  
  // Step 4: Monitor rewards
  setInterval(async () => {
    const estimate = await systemContract.methods.getEstimatedDelegatorReward(
      delegatorAddress,
      validatorAddress
    ).call();
    
    console.log('Estimated rewards:', {
      pending: web3.utils.fromWei(estimate.pending, 'ether'),
      afterProximity: web3.utils.fromWei(estimate.afterProximity, 'ether'),
      afterTax: web3.utils.fromWei(estimate.afterTax, 'ether')
    });
  }, 60000); // Check every minute
}

Network Constants

const NETWORK = {
  CHAIN_ID: 'TBD',
  BLOCK_TIME: 3, // seconds
  EPOCH_LENGTH: 200, // blocks
  EPOCH_TIME: 600, // seconds (10 minutes)
  
  MAX_VALIDATORS: 101,
  MIN_VA_STAKE: '10000', // FEN
  MIN_DC_STAKE: '1000',  // FEN
  
  VA_LOCK_PERIOD: 50000,  // blocks (~3.5 days)
  DC_LOCK_PERIOD: 25000,  // blocks (~1.75 days)
  
  DEFAULT_COMMISSION: 500,  // 5%
  MAX_COMMISSION: 1000,     // 10%
  
  PROXIMITY_LEVELS: 8,
  PROXIMITY_TOTAL_BPS: 3000 // 30%
};

Error Handling

try {
  await systemContract.methods.stakeValidator().send({
    from: validatorAddress,
    value: web3.utils.toWei('10000', 'ether')
  });
} catch (error) {
  if (error.message.includes('Invalid status')) {
    console.log('Must register first');
  } else if (error.message.includes('Minimum 10k required')) {
    console.log('Need at least 10,000 FEN');
  } else if (error.message.includes('paused')) {
    console.log('Contract is paused');
  } else {
    console.error('Error:', error.message);
  }
}

Next Steps

RPC Methods

Detailed RPC endpoint documentation

Smart Contracts

Full contract reference

Web3 Examples

Complete integration examples

API Introduction

Back to API overview