 function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { makeMemoryDisklet } from 'disklet'
import { bridgifyObject, close, update, watchMethod } from 'yaob'











import { makePeriodicTask, } from '../../util/periodic-task'
import { snooze } from '../../util/snooze'
import { getMaxSpendableInner } from '../currency/wallet/max-spend'
import { makeLog } from '../log/log'
import { getCurrencyTools } from '../plugins/plugins-selectors'


let memoryWalletCount = 0

export const makeMemoryWalletInner = async (
  ai,
  config,
  walletType,
  opts = {}
) => {
  const { keys } = opts
  if (keys == null) throw new Error('No keys provided')

  const walletId = `memorywallet-${memoryWalletCount++}`
  const walletInfo = {
    id: walletId,
    type: walletType,
    keys
  }

  const tools = await getCurrencyTools(ai, config.currencyInfo.pluginId)
  const publicKeys = await tools.derivePublicKey(walletInfo)
  walletInfo.keys = { ...publicKeys, ...walletInfo.keys }

  const log = makeLog(ai.props.logBackend, `${walletId}-${walletType}`)
  let balanceMap = new Map()
  let detectedTokenIds = []
  let syncRatio = 0

  let needsUpdate = false
  const updateWallet = () => {
    if (needsUpdate) {
      update(out)
      needsUpdate = false
    }
  }
  const updater = makePeriodicTask(async () => {
    await snooze(1000) // one second
    updateWallet()
  }, 0)

  const plugin = ai.props.state.plugins.currency[config.currencyInfo.pluginId]
  const engine = await plugin.makeCurrencyEngine(walletInfo, {
    callbacks: {
      onAddressChanged: () => {},
      onAddressesChecked: (progressRatio) => {
        if (out.syncRatio === 1) return

        if (progressRatio === 1) {
          syncRatio = progressRatio
          needsUpdate = true
        }
      },
      onNewTokens: (tokenIds) => {
        const sortedTokenIds = tokenIds.sort((a, b) => a.localeCompare(b))

        if (detectedTokenIds.length !== sortedTokenIds.length) {
          detectedTokenIds = sortedTokenIds
          needsUpdate = true
          return
        }
        for (let i = 0; i < sortedTokenIds.length; i++) {
          if (detectedTokenIds[i] !== sortedTokenIds[i]) {
            detectedTokenIds = sortedTokenIds
            needsUpdate = true
            return
          }
        }
      },
      onSeenTxCheckpoint: () => {},
      onStakingStatusChanged: () => {},
      onSubscribeAddresses: () => {},
      onTokenBalanceChanged: (tokenId, balance) => {
        if (balanceMap.get(tokenId) === balance) return

        balanceMap = new Map(balanceMap)
        balanceMap.set(tokenId, balance)
        needsUpdate = true
      },
      onTransactions: () => {},
      onTransactionsChanged: () => {},
      onTxidsChanged: () => {},
      onUnactivatedTokenIdsChanged: () => {},
      onWcNewContractCall: () => {},
      onBlockHeightChanged: () => {},
      onBalanceChanged: () => {}
    },
    customTokens: { ...config.customTokens },
    enabledTokenIds: [...Object.keys(config.allTokens)],
    lightMode: true,
    log,
    userSettings: { ...(_nullishCoalesce(config.userSettings, () => ( {}))) },
    walletLocalDisklet: makeMemoryDisklet(),
    walletLocalEncryptedDisklet: makeMemoryDisklet()
  })

  const {
    unsafeBroadcastTx = false,
    unsafeMakeSpend = false,
    unsafeSyncNetwork = false
  } = plugin.currencyInfo

  const privateKeys = { ...keys }

  let syncNetworkTask
  // Setup syncNetwork routine if defined by the currency engine:
  if (engine.syncNetwork != null) {
    // Get the private keys if required by the engine:
    const doNetworkSync = async () => {
      if (engine.syncNetwork != null) {
        const delay = await engine.syncNetwork({
          privateKeys: unsafeSyncNetwork ? { privateKeys: keys } : undefined
        })
        syncNetworkTask.setDelay(delay)
      } else {
        syncNetworkTask.stop()
      }
    }
    syncNetworkTask = makePeriodicTask(doNetworkSync, 10000, {
      onError: error => {
        ai.props.log.error(error)
      }
    })
    syncNetworkTask.start({ wait: false })
  }

  const out = bridgifyObject({
    watch: watchMethod,
    get balanceMap() {
      return balanceMap
    },
    get detectedTokenIds() {
      return detectedTokenIds
    },
    get syncRatio() {
      return syncRatio
    },
    async changeEnabledTokenIds(tokenIds) {
      if (engine.changeEnabledTokenIds != null) {
        await engine.changeEnabledTokenIds(tokenIds)
      }
    },
    async startEngine() {
      await engine.startEngine()
      _optionalChain([syncNetworkTask, 'optionalAccess', _ => _.start, 'call', _2 => _2({ wait: false })])
    },
    async getMaxSpendable(spendInfo) {
      return await getMaxSpendableInner(
        spendInfo,
        plugin,
        engine,
        config.allTokens,
        walletInfo
      )
    },
    async makeSpend(spendInfo) {
      return await engine.makeSpend(
        spendInfo,
        unsafeMakeSpend ? privateKeys : undefined
      )
    },
    async signTx(tx) {
      return await engine.signTx(tx, privateKeys)
    },
    async broadcastTx(tx) {
      return await engine.broadcastTx(
        tx,
        unsafeBroadcastTx ? privateKeys : undefined
      )
    },
    async saveTx() {},

    async close() {
      log.warn('killing memory wallet')
      _optionalChain([syncNetworkTask, 'optionalAccess', _3 => _3.stop, 'call', _4 => _4()])
      close(out)
      await engine.killEngine()
    }
  })

  updater.start({ wait: false })
  return out
}
