CurrencyConverter.psm1

function Convert-CryptoCurrency {
    <#
    .SYNOPSIS
        Converts a decimal value between specified crypto and/or non-crypto currencies
 
    .DESCRIPTION
        Uses the Coin Base API (https://api.coinbase.com/v2/exchange-rates) to perform currency conversions.
        Caches the conversion rates for the specified currency for a number of seconds if -CacheSeconds is specified.
 
    .PARAMETER Value
        The amount you want to convert between currencies.
 
    .PARAMETER From
        The currency to convert from. Defaults to the value saved with Set-CurrencyDefault -From, if any.
 
    .PARAMETER To
        The currency to convert to. Defaults to the value saved with Set-CurrencyDefault -To, if any.
 
    .PARAMETER CacheSeconds
        Number of seconds to cache the result. Default: 0.
 
    .INPUTS
        A numerical value, can be provided via the pipeline.
 
    .EXAMPLE
        100 | Convert-CryptoCurrency -From BTC -To ETH
 
        Description
        -----------
        Converts the value 100 (provided via the pipeline) from BTC (Bitcount) to ETH (Ether), example result: 2149.6130696474600
 
    .EXAMPLE
        Convert-CryptoCurrency -Value 100 -From ETH -To GBP
 
        Description
        -----------
        Converts the value 100 from ETH (Ether) to GBP (British Pound), example result: 245122.500
 
    .EXAMPLE
        Convert-CryptoCurrency -Value 1 -From BTC -To ETH | Format-Currency
 
        Description
        -----------
        Converts 1 BTC to ETH and pipes the result to Format-Currency, which picks up the destination currency (ETH) from the pipeline, example result: Ξ21.4961307
 
    .EXAMPLE
        Set-CurrencyDefault -From BTC -To ETH
        Convert-CryptoCurrency -Value 1
 
        Description
        -----------
        Saves BTC/ETH as the default currencies to convert between, then converts 1 BTC to ETH without needing to specify -From/-To.
    #>

    [cmdletbinding()]
    [OutputType([decimal])]
    param(
        [parameter(ValueFromPipeline, Mandatory, Position = 0)]
        [decimal]
        $Value,

        [validateset('00', '1INCH', 'AAVE', 'ABT', 'ACH', 'ACS', 'ADA', 'AERGO', 'AERO', 'AGLD', 'AIOZ', 'AKT', 'ALCX', 'ALEPH', 'ALGO', 'ALICE', 'AMP', 'ANKR', 'ANT', 'APE', 'API3', 'APT', 'ARB', 'ARKM', 'ARPA', 'ASM', 'AST', 'ATA', 'ATOM', 'AUCTION', 'AUDIO', 'AURORA', 'AVAX', 'AVT', 'AXL', 'AXS', 'BADGER', 'BAL', 'BAND', 'BAT', 'BCH', 'BICO', 'BIGTIME', 'BIT', 'BLUR', 'BLZ', 'BNT', 'BOBA', 'BOND', 'BONK', 'BTC', 'BTRST', 'BUSD', 'C98', 'CBETH', 'CELR', 'CGLD', 'CHZ', 'CLV', 'COMP', 'COTI', 'COVAL', 'CRO', 'CRPT', 'CRV', 'CTSI', 'CTX', 'CVC', 'CVX', 'DAI', 'DAR', 'DASH', 'DDX', 'DESO', 'DEXT', 'DIA', 'DIMO', 'DNT', 'DOGE', 'DOT', 'DREP', 'DRIFT', 'DYP', 'EGLD', 'ELA', 'ENJ', 'ENS', 'EOS', 'ERN', 'ETC', 'ETH', 'EUR', 'EUROC', 'FARM', 'FET', 'FIDA', 'FIL', 'FIS', 'FLOW', 'FLR', 'FORT', 'FORTH', 'FOX', 'FX', 'GAL', 'GALA', 'GBP', 'GFI', 'GHST', 'GLM', 'GMT', 'GNO', 'GNT', 'GODS', 'GRT', 'GST', 'GTC', 'GUSD', 'GYEN', 'HBAR', 'HFT', 'HIGH', 'HNT', 'HONEY', 'HOPR', 'ICP', 'IDEX', 'ILV', 'IMX', 'INDEX', 'INJ', 'INV', 'IOTX', 'JASMY', 'JTO', 'JUP', 'KARRAT', 'KAVA', 'KEEP', 'KNC', 'KRL', 'KSM', 'LCX', 'LDO', 'LINK', 'LIT', 'LOKA', 'LOOM', 'LPT', 'LQTY', 'LRC', 'LSETH', 'LTC', 'MAGIC', 'MANA', 'MASK', 'MATH', 'MATIC', 'MCO2', 'MDT', 'MEDIA', 'METIS', 'MINA', 'MIR', 'MKR', 'MLN', 'MNDE', 'MOBILE', 'MONA', 'MPL', 'MSOL', 'MTL', 'MULTI', 'MUSD', 'MUSE', 'MXC', 'NCT', 'NEAR', 'NEON', 'NEST', 'NKN', 'NMR', 'NU', 'OCEAN', 'OGN', 'OMG', 'OMNI', 'ONDO', 'OOKI', 'OP', 'ORCA', 'ORN', 'OSMO', 'OXT', 'PAX', 'PERP', 'PLA', 'PLU', 'PNG', 'POLS', 'POLY', 'POND', 'POWR', 'PRIME', 'PRO', 'PRQ', 'PUNDIX', 'PYR', 'PYUSD', 'QI', 'QNT', 'QSP', 'QUICK', 'RAD', 'RAI', 'RARE', 'RARI', 'RBN', 'REN', 'RENDER', 'REP', 'REQ', 'RGT', 'RLC', 'RLY', 'RNDR', 'RONIN', 'ROSE', 'RPL', 'SAFE', 'SAND', 'SEAM', 'SEI', 'SHDW', 'SHIB', 'SHPING', 'SKL', 'SNT', 'SNX', 'SOL', 'SPA', 'SPELL', 'STG', 'STORJ', 'STRK', 'STX', 'SUI', 'SUKU', 'SUPER', 'SUSHI', 'SWFTC', 'SYLO', 'SYN', 'T', 'TBTC', 'TIA', 'TIME', 'TNSR', 'TONE', 'TRAC', 'TRB', 'TRIBE', 'TRU', 'TVK', 'UMA', 'UNFI', 'UNI', 'UPI', 'USD', 'USDC', 'USDT', 'UST', 'VARA', 'VELO', 'VET', 'VGX', 'VOXEL', 'VTHO', 'WAMPL', 'WAXL', 'WBTC', 'WCFG', 'WLUNA', 'XCN', 'XLM', 'XMON', 'XRP', 'XTZ', 'XYO', 'YFI', 'YFII', 'ZEC', 'ZEN', 'ZETA', 'ZRX')]
        [string]
        $From = (Get-CurrencyDefaultValue -Name 'From'),

        [validateset('00', '1INCH', 'AAVE', 'ABT', 'ACH', 'ACS', 'ADA', 'AERGO', 'AERO', 'AGLD', 'AIOZ', 'AKT', 'ALCX', 'ALEPH', 'ALGO', 'ALICE', 'AMP', 'ANKR', 'ANT', 'APE', 'API3', 'APT', 'ARB', 'ARKM', 'ARPA', 'ASM', 'AST', 'ATA', 'ATOM', 'AUCTION', 'AUDIO', 'AURORA', 'AVAX', 'AVT', 'AXL', 'AXS', 'BADGER', 'BAL', 'BAND', 'BAT', 'BCH', 'BICO', 'BIGTIME', 'BIT', 'BLUR', 'BLZ', 'BNT', 'BOBA', 'BOND', 'BONK', 'BTC', 'BTRST', 'BUSD', 'C98', 'CBETH', 'CELR', 'CGLD', 'CHZ', 'CLV', 'COMP', 'COTI', 'COVAL', 'CRO', 'CRPT', 'CRV', 'CTSI', 'CTX', 'CVC', 'CVX', 'DAI', 'DAR', 'DASH', 'DDX', 'DESO', 'DEXT', 'DIA', 'DIMO', 'DNT', 'DOGE', 'DOT', 'DREP', 'DRIFT', 'DYP', 'EGLD', 'ELA', 'ENJ', 'ENS', 'EOS', 'ERN', 'ETC', 'ETH', 'EUR', 'EUROC', 'FARM', 'FET', 'FIDA', 'FIL', 'FIS', 'FLOW', 'FLR', 'FORT', 'FORTH', 'FOX', 'FX', 'GAL', 'GALA', 'GBP', 'GFI', 'GHST', 'GLM', 'GMT', 'GNO', 'GNT', 'GODS', 'GRT', 'GST', 'GTC', 'GUSD', 'GYEN', 'HBAR', 'HFT', 'HIGH', 'HNT', 'HONEY', 'HOPR', 'ICP', 'IDEX', 'ILV', 'IMX', 'INDEX', 'INJ', 'INV', 'IOTX', 'JASMY', 'JTO', 'JUP', 'KARRAT', 'KAVA', 'KEEP', 'KNC', 'KRL', 'KSM', 'LCX', 'LDO', 'LINK', 'LIT', 'LOKA', 'LOOM', 'LPT', 'LQTY', 'LRC', 'LSETH', 'LTC', 'MAGIC', 'MANA', 'MASK', 'MATH', 'MATIC', 'MCO2', 'MDT', 'MEDIA', 'METIS', 'MINA', 'MIR', 'MKR', 'MLN', 'MNDE', 'MOBILE', 'MONA', 'MPL', 'MSOL', 'MTL', 'MULTI', 'MUSD', 'MUSE', 'MXC', 'NCT', 'NEAR', 'NEON', 'NEST', 'NKN', 'NMR', 'NU', 'OCEAN', 'OGN', 'OMG', 'OMNI', 'ONDO', 'OOKI', 'OP', 'ORCA', 'ORN', 'OSMO', 'OXT', 'PAX', 'PERP', 'PLA', 'PLU', 'PNG', 'POLS', 'POLY', 'POND', 'POWR', 'PRIME', 'PRO', 'PRQ', 'PUNDIX', 'PYR', 'PYUSD', 'QI', 'QNT', 'QSP', 'QUICK', 'RAD', 'RAI', 'RARE', 'RARI', 'RBN', 'REN', 'RENDER', 'REP', 'REQ', 'RGT', 'RLC', 'RLY', 'RNDR', 'RONIN', 'ROSE', 'RPL', 'SAFE', 'SAND', 'SEAM', 'SEI', 'SHDW', 'SHIB', 'SHPING', 'SKL', 'SNT', 'SNX', 'SOL', 'SPA', 'SPELL', 'STG', 'STORJ', 'STRK', 'STX', 'SUI', 'SUKU', 'SUPER', 'SUSHI', 'SWFTC', 'SYLO', 'SYN', 'T', 'TBTC', 'TIA', 'TIME', 'TNSR', 'TONE', 'TRAC', 'TRB', 'TRIBE', 'TRU', 'TVK', 'UMA', 'UNFI', 'UNI', 'UPI', 'USD', 'USDC', 'USDT', 'UST', 'VARA', 'VELO', 'VET', 'VGX', 'VOXEL', 'VTHO', 'WAMPL', 'WAXL', 'WBTC', 'WCFG', 'WLUNA', 'XCN', 'XLM', 'XMON', 'XRP', 'XTZ', 'XYO', 'YFI', 'YFII', 'ZEC', 'ZEN', 'ZETA', 'ZRX')]
        [string]
        $To = (Get-CurrencyDefaultValue -Name 'To'),

        [validaterange(0, [int]::MaxValue)]
        $CacheSeconds = 0
    )

    process {
        if (-not $From -or -not $To) {
            Write-Error "A -From and -To currency are required. Specify them explicitly, or save defaults with Set-CurrencyDefault."
            return
        }

        if ($From -eq $To) {
            $Value | Add-Member -NotePropertyName Currency -NotePropertyValue $To -PassThru
        }
        else {
            $Cached = Join-Path $PSScriptRoot "Crypto-${From}.json"
            $APIUrl = "https://api.coinbase.com/v2/exchange-rates?currency=${From}"
            $RateStr = 'rates'
        
            $isCached = Test-Path $Cached

            if ($isCached) {
                $ExchangeRates = Get-Content $Cached | ConvertFrom-Json
            }

            if (-not $isCached -or ((Get-Date) -gt ($ExchangeRates.Expires | Get-Date))) {
                try {
                    $ExchangeRates = (Invoke-RestMethod $APIUrl -ErrorAction Stop).data

                    if ($CacheSeconds -gt 0) {
                        $ExchangeRates | Add-Member -MemberType NoteProperty -Name Expires -Value (Get-Date).AddSeconds($CacheSeconds)
                        $ExchangeRates | ConvertTo-Json | Out-File $Cached -Force
                    }
                }
                catch {
                    Write-Error $_
                }
            }

            if ($ExchangeRates.$RateStr.$To) {
                $Value * $ExchangeRates.$RateStr.$To | Add-Member -NotePropertyName Currency -NotePropertyValue $To -PassThru
            }
            else {
                Write-Error "Could not convert crypto currency."
            }
        }
    }
}
function Convert-Currency {
    <#
    .SYNOPSIS
        Converts a decimal value between specified currencies
 
    .DESCRIPTION
        Uses the Open Exchange Rate API (https://www.exchangerate-api.com/docs/free) to perform currency conversions.
        Caches the conversion rates for the specified currency (valid for 24 hours).
 
    .PARAMETER Value
        The amount you want to convert between currencies.
 
    .PARAMETER From
        The currency to convert from. Defaults to the value saved with Set-CurrencyDefault -From, if any.
 
    .PARAMETER To
        The currency to convert to. Defaults to the value saved with Set-CurrencyDefault -To, if any.
 
    .PARAMETER APIKey
        Optional: To use the v6 API, provide your API key, as provided by https://www.exchangerate-api.com/
        Defaults to the value saved with Set-CurrencyDefault -APIKey, if any.
 
    .INPUTS
        A numerical value, can be provided via the pipeline.
 
    .EXAMPLE
        100 | Convert-Currency -From USD -To GBP
 
        Description
        -----------
        Converts the value 100 (provided via the pipeline) from USD (US Dollar) to GBP (British Pound), example result: 79.674700
 
    .EXAMPLE
        Convert-Currency -Value 100 -From AUD -To EUR
 
        Description
        -----------
        Converts the value 100 from USD (US Dollar) to AUD (Australian Dollar), example result: 154.211800
 
    .EXAMPLE
        Convert-Currency -Value 100 -From USD -To EUR | Format-Currency
 
        Description
        -----------
        Converts 100 USD to EUR and pipes the result to Format-Currency, which picks up the destination currency (EUR) from the pipeline, example result: €92.15
 
    .EXAMPLE
        Set-CurrencyDefault -From USD -To GBP
        Convert-Currency -Value 100
 
        Description
        -----------
        Saves USD/GBP as the default currencies to convert between, then converts 100 USD to GBP without needing to specify -From/-To.
    #>

    [cmdletbinding()]
    [OutputType([decimal])]
    param(
        [parameter(ValueFromPipeline, Mandatory, Position = 0)]
        [decimal]
        $Value,

        [validateset('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'FOK', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KID', 'KMF', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD', 'SSP', 'STN', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XDR', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW', 'ZWL')]
        [string]
        $From = (Get-CurrencyDefaultValue -Name 'From'),

        [validateset('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'FOK', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KID', 'KMF', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD', 'SSP', 'STN', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XDR', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW', 'ZWL')]
        [string]
        $To = (Get-CurrencyDefaultValue -Name 'To'),

        [string]
        $APIKey = (Get-CurrencyDefaultValue -Name 'APIKey')
    )

    process {
        if (-not $From -or -not $To) {
            Write-Error "A -From and -To currency are required. Specify them explicitly, or save defaults with Set-CurrencyDefault."
            return
        }

        if ($From -eq $To) {
            $Value | Add-Member -NotePropertyName Currency -NotePropertyValue $To -PassThru
        }
        else {
            if ($APIKey) {
                $Cached = Join-Path $PSScriptRoot "${From}v6.json"
                $APIUrl = "https://v6.exchangerate-api.com/v6/${APIKey}/latest/${From}"
                $RateStr = 'conversion_rates'
            }
            else {
                $Cached = Join-Path $PSScriptRoot "${From}.json"
                $APIUrl = "https://open.er-api.com/v6/latest/${From}"
                $RateStr = 'rates'
            }

            $isCached = Test-Path $Cached

            if ($isCached) {
                $ExchangeRates = Get-Content $Cached | ConvertFrom-Json
            }

            if (-not $isCached -or ((Get-Date) -gt ($ExchangeRates.time_next_update_unix | ConvertFrom-UnixTime))) {
                try {
                    $ExchangeRates = Invoke-RestMethod $APIUrl -ErrorAction Stop
                    $ExchangeRates | ConvertTo-Json | Out-File $Cached -Force
                }
                catch {
                    Write-Error $_
                }
            }

            if ($ExchangeRates.$RateStr.$To) {
                $Value * $ExchangeRates.$RateStr.$To | Add-Member -NotePropertyName Currency -NotePropertyValue $To -PassThru
            }
            else {
                Write-Error "Could not convert currency."
            }
        }
    }
}
function Format-Currency {
    <#
    .SYNOPSIS
        Formats a value as a string with the currency symbol for a specified country.
 
    .DESCRIPTION
        Outputs a value with the specified currency symbol. Can also be used to perform a currency conversion and then format as the converted currency.
 
    .PARAMETER Value
        The currency value.
 
    .PARAMETER Currency
        The currency represented by value. If not specified, this can be picked up from the pipeline by property name, e.g. from the output of Convert-Currency or Convert-CryptoCurrency.
 
    .PARAMETER Decimals
        The number of decimal places to round the value to. Default: 2
 
    .PARAMETER ConvertTo
        Perform a currency conversion and then format as that currency.
 
    .PARAMETER SymbolAtEnd
        Place the currency symbol after the value.
 
    .INPUTS
        A numerical value, can be provided via the pipeline. If piped from Convert-Currency or Convert-CryptoCurrency, the Currency parameter is automatically populated from the destination currency of that conversion.
 
    .EXAMPLE
        100 | Format-Currency -Currency USD
 
        Description
        -----------
        Formats the value (provided via the pipeline) as USD: $100.00.
 
    .EXAMPLE
        Convert-Currency -Value 100 -From USD -To EUR | Format-Currency
 
        Description
        -----------
        Converts 100 USD to EUR and formats the result as EUR, with the Currency parameter inferred from the pipeline, e.g.: €92.15
 
    .EXAMPLE
        100 | Format-Currency -Currency USD -ConvertTo GBP
 
        Description
        -----------
        Converts 100 from USD to GBP and formats as GBP, e.g.: £79.67
 
    .EXAMPLE
        Format-Currency -Value 100 -Currency USD
 
        Description
        -----------
        Formats the value as USD: $100.00.
 
    .EXAMPLE
        Format-Currency -Value 100.2345 -Currency USD -Decimals 3
 
        Description
        -----------
        Formats the value as USD: $100.235.
 
    .EXAMPLE
        Format-Currency -Value 100 -Currency USD -SymbolAtEnd
 
        Description
        -----------
        Formats the value as USD: 100.00$.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [parameter(ValueFromPipeline, Mandatory)]
        [decimal]
        $Value,

        [validateset('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'FOK', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KID', 'KMF', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD', 'SSP', 'STN', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XDR', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW', 'ZWL','BTC','ETH','USDT','ADA','XRP','SOL','DOT','DOGE','DAI','LTC','ALGO','BCH','OMI','ICP','ETC','XMR','XTZ','MIOTA','EOS','BSV','MKR','ZEC','DASH','XNO','REP','STEEM')]
        [parameter(ValueFromPipelineByPropertyName, Mandatory)]
        [string]
        $Currency,

        [int]
        $Decimals = 2,

        [validateset('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'FOK', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KID', 'KMF', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD', 'SSP', 'STN', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XDR', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW', 'ZWL', 'BTC','ETH','USDT','ADA','XRP','SOL','DOT','DOGE','DAI','LTC','ALGO','BCH','OMI','ICP','ETC','XMR','XTZ','MIOTA','EOS','BSV','MKR','ZEC','DASH','XNO','REP','STEEM')]
        [parameter(ValueFromPipelineByPropertyName)]
        [string]
        $ConvertTo,

        [switch]
        $SymbolAtEnd
    )

    process {
        $currencySymbols = @{
            'AED'   = 'د.إ'
            'AFN'   = '؋'
            'ALL'   = 'L'
            'AMD'   = 'Դ'
            'ANG'   = 'ƒ'
            'AOA'   = 'Kz'
            'ARS'   = '$'
            'AUD'   = '$'
            'AWG'   = 'ƒ'
            'AZN'   = 'ман'
            'BAM'   = 'KM'
            'BBD'   = '$'
            'BDT'   = '৳'
            'BGN'   = 'лв'
            'BHD'   = 'ب.د'
            'BIF'   = 'Fr'
            'BMD'   = '$'
            'BND'   = '$'
            'BOB'   = 'Bs.'
            'BRL'   = 'R$'
            'BSD'   = '$'
            'BTN'   = 'Nu.'
            'BWP'   = 'P'
            'BYN'   = 'Br'
            'BZD'   = '$'
            'CAD'   = '$'
            'CDF'   = 'Fr'
            'CHF'   = '₣'
            'CLP'   = '$'
            'CNY'   = '¥'
            'COP'   = '$'
            'CRC'   = '₡'
            'CUP'   = '₱'
            'CVE'   = '$'
            'CZK'   = 'Kč'
            'DJF'   = 'Fr'
            'DKK'   = 'kr'
            'DOP'   = 'RD$'
            'DZD'   = 'د.ج'
            'EGP'   = '£'
            'ERN'   = 'Nfk'
            'ETB'   = 'Br'
            'EUR'   = '€'
            'FJD'   = '$'
            'FKP'   = '£'
            'FOK'   = 'kr'
            'GBP'   = '£'
            'GEL'   = '₾'
            'GGP'   = '£'
            'GHS'   = '₵'
            'GIP'   = '£'
            'GMD'   = 'D'
            'GNF'   = 'Fr'
            'GTQ'   = 'Q'
            'GYD'   = '$'
            'HKD'   = '$'
            'HNL'   = 'L'
            'HRK'   = 'kn'
            'HTG'   = 'G'
            'HUF'   = 'Ft'
            'IDR'   = 'Rp'
            'ILS'   = '₪'
            'IMP'   = '£'
            'INR'   = '₹'
            'IQD'   = 'ع.د'
            'IRR'   = '﷼'
            'ISK'   = 'kr'
            'JEP'   = '£'
            'JMD'   = 'J$'
            'JOD'   = 'د.ا'
            'JPY'   = '¥'
            'KES'   = 'Sh'
            'KGS'   = 'с'
            'KHR'   = '៛'
            'KID'   = '$'
            'KMF'   = 'Fr'
            'KRW'   = '₩'
            'KWD'   = 'د.ك'
            'KYD'   = '$'
            'KZT'   = '₸'
            'LAK'   = '₭'
            'LBP'   = 'ل.ل'
            'LKR'   = 'Rs'
            'LRD'   = '$'
            'LSL'   = 'L'
            'LYD'   = 'ل.د'
            'MAD'   = 'د.م.'
            'MDL'   = 'L'
            'MGA'   = 'Ar'
            'MKD'   = 'ден'
            'MMK'   = 'Ks'
            'MNT'   = '₮'
            'MOP'   = 'P'
            'MRU'   = 'UM'
            'MUR'   = '₨'
            'MVR'   = 'Rf'
            'MWK'   = 'MK'
            'MXN'   = '$'
            'MYR'   = 'RM'
            'MZN'   = 'MT'
            'NAD'   = '$'
            'NGN'   = '₦'
            'NIO'   = 'C$'
            'NOK'   = 'kr'
            'NPR'   = '₨'
            'NZD'   = '$'
            'OMR'   = 'ر.ع.'
            'PAB'   = 'B/.'
            'PEN'   = 'S/'
            'PGK'   = 'K'
            'PHP'   = '₱'
            'PKR'   = '₨'
            'PLN'   = 'zł'
            'PYG'   = '₲'
            'QAR'   = 'ر.ق'
            'RON'   = 'lei'
            'RSD'   = 'дин'
            'RUB'   = '₽'
            'RWF'   = 'Fr'
            'SAR'   = 'ر.س'
            'SBD'   = '$'
            'SCR'   = '₨'
            'SDG'   = 'ج.س.'
            'SEK'   = 'kr'
            'SGD'   = '$'
            'SHP'   = '£'
            'SLE'   = 'Le'
            'SOS'   = 'Sh'
            'SRD'   = '$'
            'SSP'   = '£'
            'STN'   = 'Db'
            'SYP'   = '£'
            'SZL'   = 'L'
            'THB'   = '฿'
            'TJS'   = 'ЅМ'
            'TMT'   = 'm'
            'TND'   = 'د.ت'
            'TOP'   = 'T$'
            'TRY'   = '₺'
            'TTD'   = '$'
            'TVD'   = '$'
            'TWD'   = '$'
            'TZS'   = 'Sh'
            'UAH'   = '₴'
            'UGX'   = 'Sh'
            'USD'   = '$'
            'UYU'   = '$'
            'UZS'   = 'сўм'
            'VES'   = 'Bs.S'
            'VND'   = '₫'
            'VUV'   = 'Vt'
            'WST'   = 'T'
            'XAF'   = 'Fr'
            'XCD'   = '$'
            'XDR'   = 'SDR'
            'XOF'   = 'Fr'
            'XPF'   = 'Fr'
            'YER'   = '﷼'
            'ZAR'   = 'R'
            'ZMW'   = 'ZK'
            'ZWL'   = '$'
            # Cryto Currency
            'BTC'   = '₿'
            'ETH'   = 'Ξ'
            'USDT'  = '₮'
            'ADA'   = '₳'
            'XRP'   = '✕'
            'SOL'   = '◎'
            'DOT'   = '●'
            'DOGE'  = 'Ð'
            'DAI'   = '◈'
            'LTC'   = 'Ł'
            'ALGO'  = 'Ⱥ'
            'BCH'   = 'Ƀ'
            'OMI'   = 'Ο'
            'ICP'   = '∞'
            'ETC'   = 'ξ'
            'XMR'   = 'ɱ'
            'XTZ'   = 'ꜩ'
            'MIOTA' = 'ɨ'
            'EOS'   = 'ε'
            'BSV'   = 'Ɓ'
            'MKR'   = 'Μ'
            'ZEC'   = 'ⓩ'
            'DASH'  = 'Đ'
            'XNO'   = 'Ӿ'
            'REP'   = 'Ɍ'
            'STEEM' = 'ȿ'
        }

        if ($ConvertTo) {
            if ($ConvertTo -in 'BTC','ETH','USDT','ADA','XRP','SOL','DOT','DOGE','DAI','LTC','ALGO','BCH','OMI','ICP','ETC','XMR','XTZ','MIOTA','EOS','BSV','MKR','ZEC','DASH','XNO','REP','STEEM') {
                $Value = $Value | Convert-CryptoCurrency -From $Currency -To $ConvertTo
            }
            else {
                $Value = $Value | Convert-Currency -From $Currency -To $ConvertTo
            }
            
            $Currency = $ConvertTo
        }

        $CurrencySymbol = $CurrencySymbols[$Currency]
        
        if ($SymbolAtEnd) {
            ("{0:n$Decimals}" -f $Value) + $CurrencySymbol
        }
        else {
            $CurrencySymbol + ("{0:n$Decimals}" -f $Value)
        }
    }
}
function Get-CryptoCurrency {
    <#
    .SYNOPSIS
        Returns the list of supported crypto currency codes along with their currency name.
 
    .DESCRIPTION
        Use to list the crypto currencies that are supported by this module, and the CoinBaser API.
 
    .PARAMETER Currency
        The currency to return. Alias: Code.
 
    .PARAMETER Name
        The currency name to return.
 
    .INPUTS
        Currency codes can be provided via the pipeline.
 
    .EXAMPLE
        Get-CryptoCurrency
 
        Description
        -----------
        Returns the list of all supported currency codes, along with their name and type.
 
    .EXAMPLE
        Get-CryptoCurrency -Currency BTC
 
        Description
        -----------
        Returns the code and name for BTC.
 
    .EXAMPLE
        'BTC','ETH' | Get-CryptoCurrency
 
        Description
        -----------
        Returns the code and name details for BTC and ETH.
 
    .EXAMPLE
        Get-CryptoCurrency -Name 'Bitcoin'
 
        Description
        -----------
        Returns all currencies that include the word 'Bitcoin'
    #>

    [OutputType([pscustomobject])]
    [CmdletBinding(DefaultParameterSetName='Code')]
    param(
        [parameter(ValueFromPipeline,ParameterSetName='Code',Position=0)]
        [alias('Code')]
        [string]
        $Currency,
        
        [parameter(ParameterSetName='Name')]
        [string]
        $Name
    )

    process {
        $SupportedCurrency = Import-Csv "$PSScriptRoot\SupportedCryptoCurrency.csv"

        if ($Currency) {
            $SupportedCurrency | Where-Object { $_.Code -eq $Currency }
        }
        elseif ($Name) {
            $SupportedCurrency | Where-Object { $_.Name -match $Name }
        }
        else {
            $SupportedCurrency
        }
    }
}
function Get-CryptoExchangeRate {
    <#
    .SYNOPSIS
        Returns all crypto exchange rates for a specified currency, or a specified exchange rate between two currencies.
 
    .DESCRIPTION
        Uses the Coin Base API (https://api.coinbase.com/v2/exchange-rates) to perform currency conversions.
        Caches the conversion rates for the specified currency for a number of seconds if -CacheSeconds is specified.
 
    .PARAMETER Currency
        The currency to return exchange rates for. (Alias: From) Defaults to the value saved with
        Set-CurrencyDefault -From, if any.
 
    .PARAMETER To
        The currency to return the exchange rate to (from the currency specified as -Currency or -From).
        Defaults to the value saved with Set-CurrencyDefault -To, if any.
 
    .PARAMETER Rates
        Switch: Use to return just the exchange rates for a specified currency.
 
    .PARAMETER CacheSeconds
        Number of seconds to cache the result. Default: 0.
 
    .INPUTS
        Currency codes can be provided via the pipeline.
 
    .EXAMPLE
        Get-CryptoExchangeRate -Currency BTC
 
        Description
        -----------
        Returns the exchange rates for Bitcoin as a PowerShell object.
 
    .EXAMPLE
        Get-CryptoExchangeRate -Currency BTC -Rates
 
        Description
        -----------
        Returns all exchange rates for Bitcoin.
     
    .EXAMPLE
        Get-CryptoExchangeRate -From USD -To BTC
 
        Description
        -----------
        Returns the exchange rate from US Dollar to Bitcoin, example result: 0.0000149456903504
 
    .EXAMPLE
        'BTC','ETH' | Get-CryptoExchangeRate
 
        Description
        -----------
        Returns the exchange rates for Bitcoin and Ether as PowerShell objects.
 
    .EXAMPLE
        Set-CurrencyDefault -From USD -To BTC
        Get-CryptoExchangeRate
 
        Description
        -----------
        Saves USD/BTC as the default currencies, then returns the exchange rate from US Dollar to Bitcoin without needing to specify -Currency/-To.
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'ToRate')]
    [OutputType([pscustomobject])]
    [OutputType([pscustomobject], ParameterSetName = ('Rates'))]
    [OutputType([double], ParameterSetName = ('ToRate'))]
    param(
        [validateset('00', '1INCH', 'AAVE', 'ABT', 'ACH', 'ACS', 'ADA', 'AERGO', 'AERO', 'AGLD', 'AIOZ', 'AKT', 'ALCX', 'ALEPH', 'ALGO', 'ALICE', 'AMP', 'ANKR', 'ANT', 'APE', 'API3', 'APT', 'ARB', 'ARKM', 'ARPA', 'ASM', 'AST', 'ATA', 'ATOM', 'AUCTION', 'AUDIO', 'AURORA', 'AVAX', 'AVT', 'AXL', 'AXS', 'BADGER', 'BAL', 'BAND', 'BAT', 'BCH', 'BICO', 'BIGTIME', 'BIT', 'BLUR', 'BLZ', 'BNT', 'BOBA', 'BOND', 'BONK', 'BTC', 'BTRST', 'BUSD', 'C98', 'CBETH', 'CELR', 'CGLD', 'CHZ', 'CLV', 'COMP', 'COTI', 'COVAL', 'CRO', 'CRPT', 'CRV', 'CTSI', 'CTX', 'CVC', 'CVX', 'DAI', 'DAR', 'DASH', 'DDX', 'DESO', 'DEXT', 'DIA', 'DIMO', 'DNT', 'DOGE', 'DOT', 'DREP', 'DRIFT', 'DYP', 'EGLD', 'ELA', 'ENJ', 'ENS', 'EOS', 'ERN', 'ETC', 'ETH', 'EUR', 'EUROC', 'FARM', 'FET', 'FIDA', 'FIL', 'FIS', 'FLOW', 'FLR', 'FORT', 'FORTH', 'FOX', 'FX', 'GAL', 'GALA', 'GBP', 'GFI', 'GHST', 'GLM', 'GMT', 'GNO', 'GNT', 'GODS', 'GRT', 'GST', 'GTC', 'GUSD', 'GYEN', 'HBAR', 'HFT', 'HIGH', 'HNT', 'HONEY', 'HOPR', 'ICP', 'IDEX', 'ILV', 'IMX', 'INDEX', 'INJ', 'INV', 'IOTX', 'JASMY', 'JTO', 'JUP', 'KARRAT', 'KAVA', 'KEEP', 'KNC', 'KRL', 'KSM', 'LCX', 'LDO', 'LINK', 'LIT', 'LOKA', 'LOOM', 'LPT', 'LQTY', 'LRC', 'LSETH', 'LTC', 'MAGIC', 'MANA', 'MASK', 'MATH', 'MATIC', 'MCO2', 'MDT', 'MEDIA', 'METIS', 'MINA', 'MIR', 'MKR', 'MLN', 'MNDE', 'MOBILE', 'MONA', 'MPL', 'MSOL', 'MTL', 'MULTI', 'MUSD', 'MUSE', 'MXC', 'NCT', 'NEAR', 'NEON', 'NEST', 'NKN', 'NMR', 'NU', 'OCEAN', 'OGN', 'OMG', 'OMNI', 'ONDO', 'OOKI', 'OP', 'ORCA', 'ORN', 'OSMO', 'OXT', 'PAX', 'PERP', 'PLA', 'PLU', 'PNG', 'POLS', 'POLY', 'POND', 'POWR', 'PRIME', 'PRO', 'PRQ', 'PUNDIX', 'PYR', 'PYUSD', 'QI', 'QNT', 'QSP', 'QUICK', 'RAD', 'RAI', 'RARE', 'RARI', 'RBN', 'REN', 'RENDER', 'REP', 'REQ', 'RGT', 'RLC', 'RLY', 'RNDR', 'RONIN', 'ROSE', 'RPL', 'SAFE', 'SAND', 'SEAM', 'SEI', 'SHDW', 'SHIB', 'SHPING', 'SKL', 'SNT', 'SNX', 'SOL', 'SPA', 'SPELL', 'STG', 'STORJ', 'STRK', 'STX', 'SUI', 'SUKU', 'SUPER', 'SUSHI', 'SWFTC', 'SYLO', 'SYN', 'T', 'TBTC', 'TIA', 'TIME', 'TNSR', 'TONE', 'TRAC', 'TRB', 'TRIBE', 'TRU', 'TVK', 'UMA', 'UNFI', 'UNI', 'UPI', 'USD', 'USDC', 'USDT', 'UST', 'VARA', 'VELO', 'VET', 'VGX', 'VOXEL', 'VTHO', 'WAMPL', 'WAXL', 'WBTC', 'WCFG', 'WLUNA', 'XCN', 'XLM', 'XMON', 'XRP', 'XTZ', 'XYO', 'YFI', 'YFII', 'ZEC', 'ZEN', 'ZETA', 'ZRX')]
        [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [alias('From')]
        [string]
        $Currency = (Get-CurrencyDefaultValue -Name 'From'),

        [validateset('00', '1INCH', 'AAVE', 'ABT', 'ACH', 'ACS', 'ADA', 'AERGO', 'AERO', 'AGLD', 'AIOZ', 'AKT', 'ALCX', 'ALEPH', 'ALGO', 'ALICE', 'AMP', 'ANKR', 'ANT', 'APE', 'API3', 'APT', 'ARB', 'ARKM', 'ARPA', 'ASM', 'AST', 'ATA', 'ATOM', 'AUCTION', 'AUDIO', 'AURORA', 'AVAX', 'AVT', 'AXL', 'AXS', 'BADGER', 'BAL', 'BAND', 'BAT', 'BCH', 'BICO', 'BIGTIME', 'BIT', 'BLUR', 'BLZ', 'BNT', 'BOBA', 'BOND', 'BONK', 'BTC', 'BTRST', 'BUSD', 'C98', 'CBETH', 'CELR', 'CGLD', 'CHZ', 'CLV', 'COMP', 'COTI', 'COVAL', 'CRO', 'CRPT', 'CRV', 'CTSI', 'CTX', 'CVC', 'CVX', 'DAI', 'DAR', 'DASH', 'DDX', 'DESO', 'DEXT', 'DIA', 'DIMO', 'DNT', 'DOGE', 'DOT', 'DREP', 'DRIFT', 'DYP', 'EGLD', 'ELA', 'ENJ', 'ENS', 'EOS', 'ERN', 'ETC', 'ETH', 'EUR', 'EUROC', 'FARM', 'FET', 'FIDA', 'FIL', 'FIS', 'FLOW', 'FLR', 'FORT', 'FORTH', 'FOX', 'FX', 'GAL', 'GALA', 'GBP', 'GFI', 'GHST', 'GLM', 'GMT', 'GNO', 'GNT', 'GODS', 'GRT', 'GST', 'GTC', 'GUSD', 'GYEN', 'HBAR', 'HFT', 'HIGH', 'HNT', 'HONEY', 'HOPR', 'ICP', 'IDEX', 'ILV', 'IMX', 'INDEX', 'INJ', 'INV', 'IOTX', 'JASMY', 'JTO', 'JUP', 'KARRAT', 'KAVA', 'KEEP', 'KNC', 'KRL', 'KSM', 'LCX', 'LDO', 'LINK', 'LIT', 'LOKA', 'LOOM', 'LPT', 'LQTY', 'LRC', 'LSETH', 'LTC', 'MAGIC', 'MANA', 'MASK', 'MATH', 'MATIC', 'MCO2', 'MDT', 'MEDIA', 'METIS', 'MINA', 'MIR', 'MKR', 'MLN', 'MNDE', 'MOBILE', 'MONA', 'MPL', 'MSOL', 'MTL', 'MULTI', 'MUSD', 'MUSE', 'MXC', 'NCT', 'NEAR', 'NEON', 'NEST', 'NKN', 'NMR', 'NU', 'OCEAN', 'OGN', 'OMG', 'OMNI', 'ONDO', 'OOKI', 'OP', 'ORCA', 'ORN', 'OSMO', 'OXT', 'PAX', 'PERP', 'PLA', 'PLU', 'PNG', 'POLS', 'POLY', 'POND', 'POWR', 'PRIME', 'PRO', 'PRQ', 'PUNDIX', 'PYR', 'PYUSD', 'QI', 'QNT', 'QSP', 'QUICK', 'RAD', 'RAI', 'RARE', 'RARI', 'RBN', 'REN', 'RENDER', 'REP', 'REQ', 'RGT', 'RLC', 'RLY', 'RNDR', 'RONIN', 'ROSE', 'RPL', 'SAFE', 'SAND', 'SEAM', 'SEI', 'SHDW', 'SHIB', 'SHPING', 'SKL', 'SNT', 'SNX', 'SOL', 'SPA', 'SPELL', 'STG', 'STORJ', 'STRK', 'STX', 'SUI', 'SUKU', 'SUPER', 'SUSHI', 'SWFTC', 'SYLO', 'SYN', 'T', 'TBTC', 'TIA', 'TIME', 'TNSR', 'TONE', 'TRAC', 'TRB', 'TRIBE', 'TRU', 'TVK', 'UMA', 'UNFI', 'UNI', 'UPI', 'USD', 'USDC', 'USDT', 'UST', 'VARA', 'VELO', 'VET', 'VGX', 'VOXEL', 'VTHO', 'WAMPL', 'WAXL', 'WBTC', 'WCFG', 'WLUNA', 'XCN', 'XLM', 'XMON', 'XRP', 'XTZ', 'XYO', 'YFI', 'YFII', 'ZEC', 'ZEN', 'ZETA', 'ZRX')]
        [parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ToRate')]
        [string]
        $To = (Get-CurrencyDefaultValue -Name 'To'),

        [parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Rates')]
        [switch]
        $Rates,

        [validaterange(0, [int]::MaxValue)]
        $CacheSeconds = 0
    )
    process {
        if (-not $Currency) {
            Write-Error "A -Currency (or -From) is required. Specify it explicitly, or save a default with Set-CurrencyDefault -From."
            return
        }

        $Cached = Join-Path $PSScriptRoot "Crypto-${Currency}.json"
        $APIUrl = "https://api.coinbase.com/v2/exchange-rates?currency=${Currency}"
        $RateStr = 'rates'
    
        $isCached = Test-Path $Cached

        if ($isCached) {
            $ExchangeRates = Get-Content $Cached | ConvertFrom-Json
        }

        if (-not $isCached -or ((Get-Date) -gt ($ExchangeRates.Expires | Get-Date))) {
            try {
                $ExchangeRates = (Invoke-RestMethod $APIUrl -ErrorAction Stop).data

                if ($CacheSeconds -gt 0) {
                    $ExchangeRates | Add-Member -MemberType NoteProperty -Name Expires -Value (Get-Date).AddSeconds($CacheSeconds)
                    $ExchangeRates | ConvertTo-Json | Out-File $Cached -Force
                }
            }
            catch {
                Write-Error $_
            }
        }

        if ($ExchangeRates) {

            if ($Rates) {
                $ExchangeRates.$RateStr
            }
            elseif ($To) {
                $ExchangeRates.$RateStr.$To
            }
            else {
                $ExchangeRates
            }
        }
        else {
            Write-Error "Failed to retrieve rates for $Currency."
        }
    }
}
function Get-Currency {
    <#
    .SYNOPSIS
        Returns the list of supported currency codes along with their currency name and country code.
 
    .DESCRIPTION
        Use to list the countries and currencies that are supported by this module, and the Exchange Rate API.
 
    .PARAMETER Currency
        The currency to return. Alias: Code.
 
    .PARAMETER Name
        The currency name to return.
 
    .PARAMETER Country
        The country to return.
 
    .INPUTS
        Currency codes can be provided via the pipeline.
 
    .EXAMPLE
        Get-Currency
 
        Description
        -----------
        Returns the list of all supported currency codes, along with their name and country.
 
    .EXAMPLE
        Get-Currency -Currency GBP
 
        Description
        -----------
        Returns the code, name and country for GBP.
 
    .EXAMPLE
        'GBP','USD' | Get-Currency
 
        Description
        -----------
        Returns the code, name and country details for GBP and USD.
 
    .EXAMPLE
        Get-Currency -Country 'United Kingdom'
 
        Description
        -----------
        Returns the code, name and country for the United Kingdom.
 
    .EXAMPLE
        Get-Currency -Name 'Dollar'
 
        Description
        -----------
        Returns all currencies that include the word 'Dollar'
    #>

    [OutputType([pscustomobject])]
    [CmdletBinding(DefaultParameterSetName='Code')]
    param(
        [parameter(ValueFromPipeline,ParameterSetName='Code',Position=0)]
        [alias('Code')]
        [string]
        $Currency,
        
        [parameter(ParameterSetName='Name')]
        [string]
        $Name,

        [parameter(ParameterSetName='Country')]
        [string]
        $Country
    )

    process {
        $SupportedCurrency = Import-Csv "$PSScriptRoot\SupportedCurrency.csv"

        if ($Currency) {
            $SupportedCurrency | Where-Object { $_.Code -eq $Currency }
        }
        elseif ($Name) {
            $SupportedCurrency | Where-Object { $_.Name -match $Name }
        }
        elseif ($Country) {
            $SupportedCurrency | Where-Object { $_.Country -match $Country }
        }
        else {
            $SupportedCurrency
        }
    }
}
function Get-CurrencyDefault {
    <#
    .SYNOPSIS
        Gets the default preferences saved by Set-CurrencyDefault.
 
    .DESCRIPTION
        The Get-CurrencyDefault cmdlet returns the default preferences currently saved by
        Set-CurrencyDefault, such as the default -From/-To currency or -APIKey. Preferences that
        haven't been saved are omitted -- each cmdlet that honours a preference falls back to its own
        built-in behavior when one isn't saved here.
 
    .EXAMPLE
        Get-CurrencyDefault
 
        Description
        -----------
        Returns the currently saved default preferences.
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        # Path to the file used to store default preferences. By default this is in the user's per-user
        # data directory (~/.currencyconverter).
        [string]
        $DefaultsPath = (Get-CurrencyDefaultsPath)
    )

    if (Test-Path $DefaultsPath) {
        Get-Content $DefaultsPath | ConvertFrom-Json
    }
    else {
        [pscustomobject]@{}
    }
}
function Get-ExchangeRate {
    <#
    .SYNOPSIS
        Returns all exchange rates for a specified currency, or a specified exchange rate between two currencies.
 
    .DESCRIPTION
        Uses the Open Exchange Rate API (https://www.exchangerate-api.com/docs/free) to retrieve currency rates.
        Caches the conversion rates for the specified currency (valid for 24 hours).
 
    .PARAMETER Currency
        The currency to return exchange rates for. (Alias: From) Defaults to the value saved with
        Set-CurrencyDefault -From, if any.
 
    .PARAMETER To
        The currency to return the exchange rate to (from the currency specified as -Currency or -From).
        Defaults to the value saved with Set-CurrencyDefault -To, if any.
 
    .PARAMETER Rates
        Switch: Use to return just the exchange rates for a specified currency.
 
    .PARAMETER APIKey
        Optional: To use the v6 API, provide your API key, as provided by https://www.exchangerate-api.com/
        Defaults to the value saved with Set-CurrencyDefault -APIKey, if any.
 
    .INPUTS
        Currency codes can be provided via the pipeline.
 
    .EXAMPLE
        Get-ExchangeRate -Currency USD
 
        Description
        -----------
        Returns the exchange rates for US Dollar as a PowerShell object.
 
    .EXAMPLE
        Get-ExchangeRate -Currency USD -Rates
 
        Description
        -----------
        Returns all exchange rates for US Dollar.
     
    .EXAMPLE
        Get-ExchangeRate -From USD -To GBP
 
        Description
        -----------
        Returns the exchange rate from US Dollar to British Pound, example result: 0.792011
 
    .EXAMPLE
        'USD','GBP' | Get-ExchangeRate
 
        Description
        -----------
        Returns the exchange rates for US Dollar and British Pound as PowerShell objects.
 
    .EXAMPLE
        Set-CurrencyDefault -From USD -To GBP
        Get-ExchangeRate
 
        Description
        -----------
        Saves USD/GBP as the default currencies, then returns the exchange rate from US Dollar to British Pound without needing to specify -Currency/-To.
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'ToRate')]
    [OutputType([pscustomobject])]
    [OutputType([pscustomobject], ParameterSetName=('Rates'))]
    [OutputType([double], ParameterSetName=('ToRate'))]
    param(
        [validateset('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'FOK', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KID', 'KMF', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD', 'SSP', 'STN', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XDR', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW', 'ZWL')]
        [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [alias('From')]
        [string]
        $Currency = (Get-CurrencyDefaultValue -Name 'From'),

        [validateset('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'FOK', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KID', 'KMF', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD', 'SSP', 'STN', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XDR', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW', 'ZWL')]
        [parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ToRate')]
        [string]
        $To = (Get-CurrencyDefaultValue -Name 'To'),

        [parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Rates')]
        [switch]
        $Rates,

        [string]
        $APIKey = (Get-CurrencyDefaultValue -Name 'APIKey')
    )
    process {
        if (-not $Currency) {
            Write-Error "A -Currency (or -From) is required. Specify it explicitly, or save a default with Set-CurrencyDefault -From."
            return
        }

        if ($APIKey) {
            $Cached = Join-Path $PSScriptRoot "${Currency}v6.json"
            $APIUrl = "https://v6.exchangerate-api.com/v6/${APIKey}/latest/${Currency}"
            $RateStr = 'conversion_rates'
        }
        else {
            $Cached = Join-Path $PSScriptRoot "${Currency}.json"
            $APIUrl = "https://open.er-api.com/v6/latest/${Currency}"
            $RateStr = 'rates'
        }

        $isCached = Test-Path $Cached

        if ($isCached) {
            $ExchangeRates = Get-Content $Cached | ConvertFrom-Json
        }

        if (-not $isCached -or ((Get-Date) -gt ($ExchangeRates.time_next_update_unix | ConvertFrom-UnixTime))) {
            try {
                $ExchangeRates = Invoke-RestMethod $APIUrl -ErrorAction Stop
                $ExchangeRates | ConvertTo-Json | Out-File $Cached -Force
            }
            catch {
                Write-Error $_
            }
        }

        if ($ExchangeRates) {

            if ($Rates) {
                $ExchangeRates.$RateStr
            }
            elseif ($To) {
                $ExchangeRates.$RateStr.$To
            }
            else {
                $ExchangeRates
            }
        }
        else {
            Write-Error "Failed to retrieve rates for $Currency."
        }
    }
}
function Set-CurrencyDefault {
    <#
    .SYNOPSIS
        Sets default preferences used automatically by other cmdlets in this module, such as the currency
        to convert From, To, and your APIKey.
 
    .DESCRIPTION
        The Set-CurrencyDefault cmdlet saves default preferences that are then used automatically by
        other cmdlets in this module, so you don't need to specify the same parameters every time. Explicitly
        specifying a parameter on a cmdlet (e.g. Convert-Currency -From EUR) always overrides the saved default.
 
        Only the preferences you specify are changed -- any others already saved are left as they are. Use
        -Reset to remove all saved preferences and restore the module's built-in behavior.
 
        Supported preferences:
 
        -From Default for -From on Convert-Currency and Convert-CryptoCurrency, and -Currency (alias -From)
                 on Get-ExchangeRate and Get-CryptoExchangeRate.
        -To Default for -To on Convert-Currency, Convert-CryptoCurrency, Get-ExchangeRate and
                 Get-CryptoExchangeRate.
        -APIKey Default for -APIKey on Convert-Currency and Get-ExchangeRate.
 
    .EXAMPLE
        Set-CurrencyDefault -From USD -To GBP
 
        Description
        -----------
        Makes Convert-Currency, Convert-CryptoCurrency, Get-ExchangeRate and Get-CryptoExchangeRate use USD
        and GBP by default, so they can be called without specifying -From/-To.
 
    .EXAMPLE
        Set-CurrencyDefault -APIKey 12345abcdef
 
        Description
        -----------
        Makes Convert-Currency and Get-ExchangeRate use the specified API key by default.
 
    .EXAMPLE
        Set-CurrencyDefault -Reset
 
        Description
        -----------
        Removes all saved default preferences.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([pscustomobject])]
    param(
        # Sets the default for -From on Convert-Currency and Convert-CryptoCurrency, and -Currency (alias
        # -From) on Get-ExchangeRate and Get-CryptoExchangeRate.
        [string]
        $From,

        # Sets the default for -To on Convert-Currency, Convert-CryptoCurrency, Get-ExchangeRate and
        # Get-CryptoExchangeRate.
        [string]
        $To,

        # Sets the default for -APIKey on Convert-Currency and Get-ExchangeRate.
        [string]
        $APIKey,

        # Removes all saved default preferences, restoring the module's built-in behavior.
        [switch]
        $Reset,

        # Path to the file used to store default preferences. By default this is in the user's per-user
        # data directory (~/.currencyconverter).
        [string]
        $DefaultsPath = (Get-CurrencyDefaultsPath)
    )

    if ($Reset) {
        if ((Test-Path $DefaultsPath) -and $PSCmdlet.ShouldProcess($DefaultsPath, 'Remove all saved default preferences')) {
            Remove-Item $DefaultsPath -Force
        }
        return [pscustomobject]@{}
    }

    $Current = Get-CurrencyDefault -DefaultsPath $DefaultsPath

    if ($PSBoundParameters.ContainsKey('From')) { $Current | Add-Member -NotePropertyName From -NotePropertyValue $From -Force }
    if ($PSBoundParameters.ContainsKey('To')) { $Current | Add-Member -NotePropertyName To -NotePropertyValue $To -Force }
    if ($PSBoundParameters.ContainsKey('APIKey')) { $Current | Add-Member -NotePropertyName APIKey -NotePropertyValue $APIKey -Force }

    $AnyPreferenceSpecified = 'From', 'To', 'APIKey' | Where-Object { $PSBoundParameters.ContainsKey($_) }

    if ($AnyPreferenceSpecified -and $PSCmdlet.ShouldProcess($DefaultsPath, 'Save default preferences')) {
        $Current | ConvertTo-Json | Out-File $DefaultsPath -Force
    }

    $Current
}
function ConvertFrom-UnixTime {
    <#
    .SYNOPSIS
        Converts a unix timestamp to a datetime object.
    #>

    param(
        [parameter(ValueFromPipeline, Mandatory)]
        $UnixTime
    )
    begin {
        [datetime]$Origin = '1970-01-01 00:00:00'
    }
    process {
        $Origin.AddSeconds($UnixTime)
    }
}
function Get-CurrencyDefaultsPath {
    <#
    .SYNOPSIS
        Returns the path to the file used to store default preferences (set via Set-CurrencyDefault),
        creating its directory if it doesn't already exist.
    #>

    [CmdletBinding()]
    param()

    $Directory = Join-Path $HOME '.currencyconverter'

    if (-not (Test-Path $Directory)) {
        New-Item -ItemType Directory -Path $Directory -Force | Out-Null
    }

    Join-Path $Directory 'CurrencyConverter.defaults.json'
}
function Get-CurrencyDefaultValue {
    <#
    .SYNOPSIS
        Returns the saved default for the named preference (set via Set-CurrencyDefault), or the
        given fallback value if no default has been saved for it.
    #>

    [CmdletBinding()]
    param(
        # The name of the preference to look up, e.g. 'From', 'To' or 'APIKey'.
        [Parameter(Mandatory)]
        [string]
        $Name,

        # The value to use if no default has been saved for this preference.
        $Value
    )

    $Stored = (Get-CurrencyDefault).$Name

    if ($null -ne $Stored) { $Stored } else { $Value }
}