text: $(function() {
    consoleInit()
    start(main)
  })
  
  const ALPACA_CHEF_ABI = [
    {
      inputs: [
        {internalType: 'contract AlpacaToken', name: '_alpaca', type: 'address'},
        {internalType: 'address', name: '_devaddr', type: 'address'},
        {internalType: 'uint256', name: '_alpacaPerBlock', type: 'uint256'},
        {internalType: 'uint256', name: '_startBlock', type: 'uint256'},
        {internalType: 'uint256', name: '_bonusLockupBps', type: 'uint256'},
        {internalType: 'uint256', name: '_bonusEndBlock', type: 'uint256'},
      ],
      stateMutability: 'nonpayable',
      type: 'constructor',
    },
    {
      anonymous: false,
      inputs: [
        {indexed: true, internalType: 'address', name: 'user', type: 'address'},
        {indexed: true, internalType: 'uint256', name: 'pid', type: 'uint256'},
        {indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256'},
      ],
      name: 'Deposit',
      type: 'event',
    },
    {
      anonymous: false,
      inputs: [
        {indexed: true, internalType: 'address', name: 'user', type: 'address'},
        {indexed: true, internalType: 'uint256', name: 'pid', type: 'uint256'},
        {indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256'},
      ],
      name: 'EmergencyWithdraw',
      type: 'event',
    },
    {
      anonymous: false,
      inputs: [
        {indexed: true, internalType: 'address', name: 'previousOwner', type: 'address'},
        {indexed: true, internalType: 'address', name: 'newOwner', type: 'address'},
      ],
      name: 'OwnershipTransferred',
      type: 'event',
    },
    {
      anonymous: false,
      inputs: [
        {indexed: true, internalType: 'address', name: 'user', type: 'address'},
        {indexed: true, internalType: 'uint256', name: 'pid', type: 'uint256'},
        {indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256'},
      ],
      name: 'Withdraw',
      type: 'event',
    },
    {
      inputs: [
        {internalType: 'uint256', name: '_allocPoint', type: 'uint256'},
        {internalType: 'address', name: '_stakeToken', type: 'address'},
        {internalType: 'bool', name: '_withUpdate', type: 'bool'},
      ],
      name: 'addPool',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [],
      name: 'alpaca',
      outputs: [{internalType: 'contract AlpacaToken', name: '', type: 'address'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [],
      name: 'alpacaPerBlock',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [],
      name: 'bonusEndBlock',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [],
      name: 'bonusLockUpBps',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [],
      name: 'bonusMultiplier',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'address', name: '_for', type: 'address'},
        {internalType: 'uint256', name: '_pid', type: 'uint256'},
        {internalType: 'uint256', name: '_amount', type: 'uint256'},
      ],
      name: 'deposit',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [],
      name: 'devaddr',
      outputs: [{internalType: 'address', name: '', type: 'address'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [{internalType: 'uint256', name: '_pid', type: 'uint256'}],
      name: 'emergencyWithdraw',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'uint256', name: '_lastRewardBlock', type: 'uint256'},
        {internalType: 'uint256', name: '_currentBlock', type: 'uint256'},
      ],
      name: 'getMultiplier',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [{internalType: 'uint256', name: '_pid', type: 'uint256'}],
      name: 'harvest',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [{internalType: 'address', name: '_stakeToken', type: 'address'}],
      name: 'isDuplicatedPool',
      outputs: [{internalType: 'bool', name: '', type: 'bool'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'address', name: '_to', type: 'address'},
        {internalType: 'uint256', name: '_amount', type: 'uint256'},
      ],
      name: 'manualMint',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {inputs: [], name: 'massUpdatePools', outputs: [], stateMutability: 'nonpayable', type: 'function'},
    {
      inputs: [],
      name: 'owner',
      outputs: [{internalType: 'address', name: '', type: 'address'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'uint256', name: '_pid', type: 'uint256'},
        {internalType: 'address', name: '_user', type: 'address'},
      ],
      name: 'pendingAlpaca',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      name: 'poolInfo',
      outputs: [
        {internalType: 'address', name: 'stakeToken', type: 'address'},
        {internalType: 'uint256', name: 'allocPoint', type: 'uint256'},
        {internalType: 'uint256', name: 'lastRewardBlock', type: 'uint256'},
        {internalType: 'uint256', name: 'accAlpacaPerShare', type: 'uint256'},
        {internalType: 'uint256', name: 'accAlpacaPerShareTilBonusEnd', type: 'uint256'},
      ],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [],
      name: 'poolLength',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {inputs: [], name: 'renounceOwnership', outputs: [], stateMutability: 'nonpayable', type: 'function'},
    {
      inputs: [{internalType: 'uint256', name: '_alpacaPerBlock', type: 'uint256'}],
      name: 'setAlpacaPerBlock',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'uint256', name: '_bonusMultiplier', type: 'uint256'},
        {internalType: 'uint256', name: '_bonusEndBlock', type: 'uint256'},
        {internalType: 'uint256', name: '_bonusLockUpBps', type: 'uint256'},
      ],
      name: 'setBonus',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [{internalType: 'address', name: '_devaddr', type: 'address'}],
      name: 'setDev',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'uint256', name: '_pid', type: 'uint256'},
        {internalType: 'uint256', name: '_allocPoint', type: 'uint256'},
        {internalType: 'bool', name: '_withUpdate', type: 'bool'},
      ],
      name: 'setPool',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [],
      name: 'startBlock',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [],
      name: 'totalAllocPoint',
      outputs: [{internalType: 'uint256', name: '', type: 'uint256'}],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [{internalType: 'address', name: 'newOwner', type: 'address'}],
      name: 'transferOwnership',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [{internalType: 'uint256', name: '_pid', type: 'uint256'}],
      name: 'updatePool',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'uint256', name: '', type: 'uint256'},
        {internalType: 'address', name: '', type: 'address'},
      ],
      name: 'userInfo',
      outputs: [
        {internalType: 'uint256', name: 'amount', type: 'uint256'},
        {internalType: 'uint256', name: 'rewardDebt', type: 'uint256'},
        {internalType: 'uint256', name: 'bonusDebt', type: 'uint256'},
        {internalType: 'address', name: 'fundedBy', type: 'address'},
      ],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'address', name: '_for', type: 'address'},
        {internalType: 'uint256', name: '_pid', type: 'uint256'},
        {internalType: 'uint256', name: '_amount', type: 'uint256'},
      ],
      name: 'withdraw',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
    {
      inputs: [
        {internalType: 'address', name: '_for', type: 'address'},
        {internalType: 'uint256', name: '_pid', type: 'uint256'},
      ],
      name: 'withdrawAll',
      outputs: [],
      stateMutability: 'nonpayable',
      type: 'function',
    },
  ]
  
  async function getAlpacaPoolInfo(app, chefContract, chefAddress, poolIndex, pendingRewardsFunction) {
    const poolInfo = await chefContract.poolInfo(poolIndex)
    const poolToken = await getBscToken(app, poolInfo.stakeToken, chefAddress)
    const userInfo = await chefContract.userInfo(poolIndex, app.YOUR_ADDRESS)
    const pendingRewardTokens = await chefContract.callStatic[pendingRewardsFunction](poolIndex, app.YOUR_ADDRESS)
    const staked = userInfo.amount / 10 ** poolToken.decimals
    return {
      address: poolInfo.stakeToken,
      allocPoints: poolInfo.allocPoint ?? 1,
      poolToken: poolToken,
      userStaked: staked,
      pendingRewardTokens: pendingRewardTokens / 10 ** 18,
      lastRewardBlock: poolInfo.lastRewardBlock,
    }
  }
  
  async function loadAlpacaBscChefContract(
    App,
    tokens,
    prices,
    chef,
    chefAddress,
    chefAbi,
    rewardTokenTicker,
    rewardTokenFunction,
    rewardsPerBlockFunction,
    rewardsPerWeekFixed,
    pendingRewardsFunction,
    deathPoolIndices
  ) {
    const chefContract = chef ?? new ethers.Contract(chefAddress, chefAbi, App.provider)
  
    const poolCount = parseInt(await chefContract.poolLength(), 10)
    const totalAllocPoints = await chefContract.totalAllocPoint()
    _print(`<a href='https://bscscan.com/address/${chefAddress}' target='_blank'>Staking Contract</a>\n`)
  
    _print(`Found ${poolCount} pools.\n`)
  
    _print(`Showing incentivized pools only.\n`)
  
    var tokens = {}
  
    const rewardTokenAddress = await chefContract.callStatic[rewardTokenFunction]()
    const rewardToken = await getBscToken(App, rewardTokenAddress, chefAddress)
    const rewardsPerWeek =
      rewardsPerWeekFixed ??
      (((await chefContract.callStatic[rewardsPerBlockFunction]()) / 10 ** rewardToken.decimals) * 604800) / 3
  
    const poolInfos = await Promise.all(
      [...Array(poolCount).keys()].map(
        async x => await getAlpacaPoolInfo(App, chefContract, chefAddress, x, pendingRewardsFunction)
      )
    )
  
    var tokenAddresses = [].concat.apply(
      [],
      poolInfos.filter(x => x.poolToken).map(x => x.poolToken.tokens)
    )
  
    await Promise.all(
      tokenAddresses.map(async address => {
        tokens[address] = await getBscToken(App, address, chefAddress)
      })
    )
  
    if (deathPoolIndices) {
      //load prices for the deathpool assets
      deathPoolIndices
        .map(i => poolInfos[i])
        .map(poolInfo => (poolInfo.poolToken ? getPoolPrices(tokens, prices, poolInfo.poolToken, 'bsc') : undefined))
    }
    prices['0x6F695Bd5FFD25149176629f8491A5099426Ce7a7'] = getParameterCaseInsensitive(
      prices,
      '0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F'
    )
    const poolPrices = poolInfos.map(poolInfo =>
      poolInfo.poolToken ? getPoolPrices(tokens, prices, poolInfo.poolToken, 'bsc') : undefined
    )
  
    _print('Finished reading smart contracts.\n')
  
    let aprs = []
    for (i = 0; i < poolCount; i++) {
      if (poolPrices[i]) {
        const apr = printChefPool(
          App,
          chefAbi,
          chefAddress,
          prices,
          tokens,
          poolInfos[i],
          i,
          poolPrices[i],
          totalAllocPoints,
          rewardsPerWeek,
          rewardTokenTicker,
          rewardTokenAddress,
          pendingRewardsFunction,
          null,
          null,
          'bsc'
        )
        aprs.push(apr)
      }
    }
    let totalUserStaked = 0,
      totalStaked = 0,
      averageApr = 0
    for (const a of aprs) {
      if (a && !isNaN(a.totalStakedUsd)) {
        totalStaked += a.totalStakedUsd
      }
      if (a && a.userStakedUsd > 0) {
        totalUserStaked += a.userStakedUsd
        averageApr += (a.userStakedUsd * a.yearlyAPR) / 100
      }
    }
    averageApr = averageApr / totalUserStaked
    _print_bold(`Total Staked: $${formatMoney(totalStaked)}`)
    if (totalUserStaked > 0) {
      _print_bold(
        `\nYou are staking a total of $${formatMoney(totalUserStaked)} at an average APR of ${(averageApr * 100).toFixed(
          2
        )}%`
      )
      _print(
        `Estimated earnings:` +
          ` Day $${formatMoney((totalUserStaked * averageApr) / 365)}` +
          ` Week $${formatMoney((totalUserStaked * averageApr) / 52)}` +
          ` Year $${formatMoney(totalUserStaked * averageApr)}\n`
      )
    }
    return {prices, totalUserStaked, totalStaked, averageApr}
  }
  
  async function main() {
    const App = await init_ethers()
  
    _print(`Initialized ${App.YOUR_ADDRESS}\n`)
    _print('Reading smart contracts...\n')
  
    const ALPACA_CHEF_ADDR = '0xa625ab01b08ce023b2a342dbb12a16f2c8489a8f'
    const rewardTokenTicker = 'ALPACA'
    const ALPACA_CHEF = new ethers.Contract(ALPACA_CHEF_ADDR, ALPACA_CHEF_ABI, App.provider)
  
    const rewardsPerWeek = (((await ALPACA_CHEF.alpacaPerBlock()) / 1e18) * 604800) / 3 //bonus multiplier
  
    const tokens = {}
    const prices = await getBscPrices()
    prices['0xd7d069493685a581d27824fc46eda46b7efc0063'] = prices['0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'] ///ibBNB-wBNB
    prices['0x7C9e73d4C71dae564d41F78d56439bB4ba87592f'] = {usd: 1}
  
    await loadAlpacaBscChefContract(
      App,
      tokens,
      prices,
      ALPACA_CHEF,
      ALPACA_CHEF_ADDR,
      ALPACA_CHEF_ABI,
      rewardTokenTicker,
      'alpaca',
      null,
      rewardsPerWeek,
      'pendingAlpaca',
      [4]
    )
  
    hideLoading()
  }
  