From b1bc406a58e4c6ebed0f390be8481380e54faaff Mon Sep 17 00:00:00 2001 From: 4TT1L4 <2914096+4TT1L4@users.noreply.github.com> Date: Thu, 4 Jul 2024 18:19:12 +0200 Subject: [PATCH 1/5] Added Genius Yield --- js/ccxt.d.ts | 5 +- js/src/abstract/geniusyield.d.ts | 29 +++++++++++ js/src/geniusyield.d.ts | 86 ++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 js/src/abstract/geniusyield.d.ts create mode 100644 js/src/geniusyield.d.ts diff --git a/js/ccxt.d.ts b/js/ccxt.d.ts index c95b3a2961e7..c7f62540bea1 100644 --- a/js/ccxt.d.ts +++ b/js/ccxt.d.ts @@ -65,6 +65,7 @@ import fmfwio from './src/fmfwio.js'; import gate from './src/gate.js'; import gateio from './src/gateio.js'; import gemini from './src/gemini.js'; +import geniusyield from './src/geniusyield.js'; import hitbtc from './src/hitbtc.js'; import hitbtc3 from './src/hitbtc3.js'; import hollaex from './src/hollaex.js'; @@ -241,6 +242,7 @@ declare const exchanges: { gate: typeof gate; gateio: typeof gateio; gemini: typeof gemini; + geniusyield: typeof geniusyield; hitbtc: typeof hitbtc; hitbtc3: typeof hitbtc3; hollaex: typeof hollaex; @@ -494,6 +496,7 @@ declare const ccxt: { gate: typeof gate; gateio: typeof gateio; gemini: typeof gemini; + geniusyield: typeof geniusyield; hitbtc: typeof hitbtc; hitbtc3: typeof hitbtc3; hollaex: typeof hollaex; @@ -543,5 +546,5 @@ declare const ccxt: { zaif: typeof zaif; zonda: typeof zonda; } & typeof functions & typeof errors; -export { version, Exchange, exchanges, pro, Precise, functions, errors, BaseError, ExchangeError, AuthenticationError, PermissionDenied, AccountNotEnabled, AccountSuspended, ArgumentsRequired, BadRequest, BadSymbol, OperationRejected, NoChange, MarginModeAlreadySet, MarketClosed, BadResponse, NullResponse, InsufficientFunds, InvalidAddress, AddressPending, InvalidOrder, OrderNotFound, OrderNotCached, CancelPending, OrderImmediatelyFillable, OrderNotFillable, DuplicateOrderId, ContractUnavailable, NotSupported, ProxyError, ExchangeClosedByUser, OperationFailed, NetworkError, DDoSProtection, RateLimitExceeded, ExchangeNotAvailable, OnMaintenance, InvalidNonce, RequestTimeout, Int, int, Str, Strings, Num, Bool, IndexType, OrderSide, OrderType, MarketType, SubType, Dict, NullableDict, List, NullableList, Fee, OHLCV, OHLCVC, implicitReturnType, Market, Currency, Dictionary, MinMax, FeeInterface, TradingFeeInterface, MarketInterface, Trade, Order, OrderBook, Ticker, Transaction, Tickers, CurrencyInterface, Balance, BalanceAccount, Account, PartialBalances, Balances, DepositAddress, WithdrawalResponse, DepositAddressResponse, FundingRate, FundingRates, Position, BorrowInterest, LeverageTier, LedgerEntry, DepositWithdrawFeeNetwork, DepositWithdrawFee, TransferEntry, CrossBorrowRate, IsolatedBorrowRate, FundingRateHistory, OpenInterest, Liquidation, OrderRequest, CancellationRequest, FundingHistory, MarginMode, Greeks, Conversion, Option, LastPrice, Leverage, MarginModification, Leverages, LastPrices, Currencies, TradingFees, MarginModes, OptionChain, IsolatedBorrowRates, CrossBorrowRates, TransferEntries, LeverageTiers, ace, alpaca, ascendex, bequant, bigone, binance, binancecoinm, binanceus, binanceusdm, bingx, bit2c, bitbank, bitbay, bitbns, bitcoincom, bitfinex, bitfinex2, bitflyer, bitget, bithumb, bitmart, bitmex, bitopro, bitpanda, bitrue, bitso, bitstamp, bitteam, bitvavo, bl3p, blockchaincom, blofin, btcalpha, btcbox, btcmarkets, btcturk, bybit, cex, coinbase, coinbaseadvanced, coinbaseexchange, coinbaseinternational, coincheck, coinex, coinlist, coinmate, coinmetro, coinone, coinsph, coinspot, cryptocom, currencycom, delta, deribit, digifinex, exmo, fmfwio, gate, gateio, gemini, hitbtc, hitbtc3, hollaex, htx, huobi, huobijp, hyperliquid, idex, independentreserve, indodax, kraken, krakenfutures, kucoin, kucoinfutures, kuna, latoken, lbank, luno, lykke, mercado, mexc, ndax, novadax, oceanex, okcoin, okx, onetrading, oxfun, p2b, paymium, phemex, poloniex, poloniexfutures, probit, timex, tokocrypto, tradeogre, upbit, vertex, wavesexchange, wazirx, whitebit, woo, woofipro, xt, yobit, zaif, zonda, }; +export { version, Exchange, exchanges, pro, Precise, functions, errors, BaseError, ExchangeError, AuthenticationError, PermissionDenied, AccountNotEnabled, AccountSuspended, ArgumentsRequired, BadRequest, BadSymbol, OperationRejected, NoChange, MarginModeAlreadySet, MarketClosed, BadResponse, NullResponse, InsufficientFunds, InvalidAddress, AddressPending, InvalidOrder, OrderNotFound, OrderNotCached, CancelPending, OrderImmediatelyFillable, OrderNotFillable, DuplicateOrderId, ContractUnavailable, NotSupported, ProxyError, ExchangeClosedByUser, OperationFailed, NetworkError, DDoSProtection, RateLimitExceeded, ExchangeNotAvailable, OnMaintenance, InvalidNonce, RequestTimeout, Int, int, Str, Strings, Num, Bool, IndexType, OrderSide, OrderType, MarketType, SubType, Dict, NullableDict, List, NullableList, Fee, OHLCV, OHLCVC, implicitReturnType, Market, Currency, Dictionary, MinMax, FeeInterface, TradingFeeInterface, MarketInterface, Trade, Order, OrderBook, Ticker, Transaction, Tickers, CurrencyInterface, Balance, BalanceAccount, Account, PartialBalances, Balances, DepositAddress, WithdrawalResponse, DepositAddressResponse, FundingRate, FundingRates, Position, BorrowInterest, LeverageTier, LedgerEntry, DepositWithdrawFeeNetwork, DepositWithdrawFee, TransferEntry, CrossBorrowRate, IsolatedBorrowRate, FundingRateHistory, OpenInterest, Liquidation, OrderRequest, CancellationRequest, FundingHistory, MarginMode, Greeks, Conversion, Option, LastPrice, Leverage, MarginModification, Leverages, LastPrices, Currencies, TradingFees, MarginModes, OptionChain, IsolatedBorrowRates, CrossBorrowRates, TransferEntries, LeverageTiers, ace, alpaca, ascendex, bequant, bigone, binance, binancecoinm, binanceus, binanceusdm, bingx, bit2c, bitbank, bitbay, bitbns, bitcoincom, bitfinex, bitfinex2, bitflyer, bitget, bithumb, bitmart, bitmex, bitopro, bitpanda, bitrue, bitso, bitstamp, bitteam, bitvavo, bl3p, blockchaincom, blofin, btcalpha, btcbox, btcmarkets, btcturk, bybit, cex, coinbase, coinbaseadvanced, coinbaseexchange, coinbaseinternational, coincheck, coinex, coinlist, coinmate, coinmetro, coinone, coinsph, coinspot, cryptocom, currencycom, delta, deribit, digifinex, exmo, fmfwio, gate, gateio, gemini, geniusyield, hitbtc, hitbtc3, hollaex, htx, huobi, huobijp, hyperliquid, idex, independentreserve, indodax, kraken, krakenfutures, kucoin, kucoinfutures, kuna, latoken, lbank, luno, lykke, mercado, mexc, ndax, novadax, oceanex, okcoin, okx, onetrading, oxfun, p2b, paymium, phemex, poloniex, poloniexfutures, probit, timex, tokocrypto, tradeogre, upbit, vertex, wavesexchange, wazirx, whitebit, woo, woofipro, xt, yobit, zaif, zonda, }; export default ccxt; diff --git a/js/src/abstract/geniusyield.d.ts b/js/src/abstract/geniusyield.d.ts new file mode 100644 index 000000000000..1426ec02246e --- /dev/null +++ b/js/src/abstract/geniusyield.d.ts @@ -0,0 +1,29 @@ +import { implicitReturnType } from '../base/types.js'; +import { Exchange as _Exchange } from '../base/Exchange.js'; +interface Exchange { + publicGetPing(params?: {}): Promise; + publicGetTime(params?: {}): Promise; + publicGetExchange(params?: {}): Promise; + publicGetAssets(params?: {}): Promise; + publicGetMarkets(params?: {}): Promise; + publicGetTickers(params?: {}): Promise; + publicGetCandles(params?: {}): Promise; + publicGetTrades(params?: {}): Promise; + publicGetOrderbook(params?: {}): Promise; + privateGetUser(params?: {}): Promise; + privateGetWallets(params?: {}): Promise; + privateGetBalances(params?: {}): Promise; + privateGetOrders(params?: {}): Promise; + privateGetFills(params?: {}): Promise; + privateGetDeposits(params?: {}): Promise; + privateGetWithdrawals(params?: {}): Promise; + privateGetWsToken(params?: {}): Promise; + privatePostWallets(params?: {}): Promise; + privatePostOrders(params?: {}): Promise; + privatePostOrdersTest(params?: {}): Promise; + privatePostWithdrawals(params?: {}): Promise; + privateDeleteOrders(params?: {}): Promise; +} +declare abstract class Exchange extends _Exchange { +} +export default Exchange; diff --git a/js/src/geniusyield.d.ts b/js/src/geniusyield.d.ts new file mode 100644 index 000000000000..3bf22ea8e799 --- /dev/null +++ b/js/src/geniusyield.d.ts @@ -0,0 +1,86 @@ +import Exchange from './abstract/geniusyield.js'; +import type { Balances, Currencies, Currency, Dict, Int, Market, Num, OHLCV, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, int } from './base/types.js'; +/** + * @class geniusyield + * @augments Exchange + */ +export default class geniusyield extends Exchange { + describe(): any; + priceToPrecision(symbol: any, price: any): string; + fetchMarkets(params?: {}): Promise; + fetchTicker(symbol: string, params?: {}): Promise; + fetchTickers(symbols?: Strings, params?: {}): Promise; + parseTicker(ticker: Dict, market?: Market): Ticker; + fetchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise; + parseOHLCV(ohlcv: any, market?: Market): OHLCV; + fetchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise; + parseTrade(trade: Dict, market?: Market): Trade; + fetchTradingFees(params?: {}): Promise; + fetchOrderBook(symbol: string, limit?: Int, params?: {}): Promise; + parseSide(book: any, side: any): any[]; + fetchCurrencies(params?: {}): Promise; + parseBalance(response: any): Balances; + fetchBalance(params?: {}): Promise; + fetchMyTrades(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; + fetchOrder(id: string, symbol?: Str, params?: {}): Promise; + fetchOpenOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; + fetchClosedOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; + fetchOrdersHelper(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; + parseOrderStatus(status: Str): string; + parseOrder(order: Dict, market?: Market): Order; + associateWallet(walletAddress: any, params?: {}): Promise; + createOrder(symbol: string, type: OrderType, side: OrderSide, amount: number, price?: Num, params?: {}): Promise; + withdraw(code: string, amount: number, address: string, tag?: any, params?: {}): Promise; + cancelAllOrders(symbol?: Str, params?: {}): Promise; + cancelOrder(id: string, symbol?: Str, params?: {}): Promise; + handleErrors(code: int, reason: string, url: string, method: string, headers: Dict, body: string, response: any, requestHeaders: any, requestBody: any): any; + fetchDeposit(id: string, code?: Str, params?: {}): Promise; + fetchDeposits(code?: Str, since?: Int, limit?: Int, params?: {}): Promise; + fetchStatus(params?: {}): Promise<{ + status: string; + updated: any; + eta: any; + url: any; + info: any; + }>; + fetchTime(params?: {}): Promise; + fetchWithdrawal(id: string, code?: Str, params?: {}): Promise; + fetchWithdrawals(code?: Str, since?: Int, limit?: Int, params?: {}): Promise; + fetchTransactionsHelper(code?: Str, since?: Int, limit?: Int, params?: {}): Promise; + parseTransactionStatus(status: Str): string; + parseTransaction(transaction: Dict, currency?: Currency): Transaction; + calculateRateLimiterCost(api: any, method: any, path: any, params: any, config?: {}): any; + fetchDepositAddress(code?: Str, params?: {}): Promise<{ + info: any; + currency: any; + address: string; + tag: any; + network: string; + }>; + parseDepositAddress(depositAddress: any, currency?: Currency): { + info: any; + currency: any; + address: string; + tag: any; + network: string; + }; + sign(path: any, api?: string, method?: string, params?: {}, headers?: any, body?: any): { + url: string; + method: string; + body: any; + headers: any; + }; + remove0xPrefix(hexData: any): any; + hashMessage(message: any): string; + signHash(hash: any, privateKey: any): { + r: string; + s: string; + v: number; + }; + signMessage(message: any, privateKey: any): { + r: string; + s: string; + v: number; + }; + signMessageString(message: any, privateKey: any): string; +} From 11ec3586298a2b5a5b69262fe41d3ce82b61e5e6 Mon Sep 17 00:00:00 2001 From: 4TT1L4 <2914096+4TT1L4@users.noreply.github.com> Date: Thu, 4 Jul 2024 18:33:39 +0200 Subject: [PATCH 2/5] Added Genius Yield --- cs/ccxt/api/geniusyield.cs | 124 ++ js/src/abstract/geniusyield.js | 5 + js/src/geniusyield.js | 1879 ++++++++++++++++++++++++++ php/abstract/geniusyield.php | 142 ++ php/async/abstract/geniusyield.php | 142 ++ python/ccxt/abstract/geniusyield.py | 26 + ts/src/abstract/geniusyield.ts | 37 + ts/src/geniusyield.ts | 1911 +++++++++++++++++++++++++++ 8 files changed, 4266 insertions(+) create mode 100644 cs/ccxt/api/geniusyield.cs create mode 100644 js/src/abstract/geniusyield.js create mode 100644 js/src/geniusyield.js create mode 100644 php/abstract/geniusyield.php create mode 100644 php/async/abstract/geniusyield.php create mode 100644 python/ccxt/abstract/geniusyield.py create mode 100644 ts/src/abstract/geniusyield.ts create mode 100644 ts/src/geniusyield.ts diff --git a/cs/ccxt/api/geniusyield.cs b/cs/ccxt/api/geniusyield.cs new file mode 100644 index 000000000000..3bf78d508c58 --- /dev/null +++ b/cs/ccxt/api/geniusyield.cs @@ -0,0 +1,124 @@ +// ------------------------------------------------------------------------------- + +// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code + +// ------------------------------------------------------------------------------- + +namespace ccxt; + +public partial class geniusyield : Exchange +{ + public geniusyield (object args = null): base(args) {} + + public async Task publicGetPing (object parameters = null) + { + return await this.callAsync ("publicGetPing",parameters); + } + + public async Task publicGetTime (object parameters = null) + { + return await this.callAsync ("publicGetTime",parameters); + } + + public async Task publicGetExchange (object parameters = null) + { + return await this.callAsync ("publicGetExchange",parameters); + } + + public async Task publicGetAssets (object parameters = null) + { + return await this.callAsync ("publicGetAssets",parameters); + } + + public async Task publicGetMarkets (object parameters = null) + { + return await this.callAsync ("publicGetMarkets",parameters); + } + + public async Task publicGetTickers (object parameters = null) + { + return await this.callAsync ("publicGetTickers",parameters); + } + + public async Task publicGetCandles (object parameters = null) + { + return await this.callAsync ("publicGetCandles",parameters); + } + + public async Task publicGetTrades (object parameters = null) + { + return await this.callAsync ("publicGetTrades",parameters); + } + + public async Task publicGetOrderbook (object parameters = null) + { + return await this.callAsync ("publicGetOrderbook",parameters); + } + + public async Task privateGetUser (object parameters = null) + { + return await this.callAsync ("privateGetUser",parameters); + } + + public async Task privateGetWallets (object parameters = null) + { + return await this.callAsync ("privateGetWallets",parameters); + } + + public async Task privateGetBalances (object parameters = null) + { + return await this.callAsync ("privateGetBalances",parameters); + } + + public async Task privateGetOrders (object parameters = null) + { + return await this.callAsync ("privateGetOrders",parameters); + } + + public async Task privateGetFills (object parameters = null) + { + return await this.callAsync ("privateGetFills",parameters); + } + + public async Task privateGetDeposits (object parameters = null) + { + return await this.callAsync ("privateGetDeposits",parameters); + } + + public async Task privateGetWithdrawals (object parameters = null) + { + return await this.callAsync ("privateGetWithdrawals",parameters); + } + + public async Task privateGetWsToken (object parameters = null) + { + return await this.callAsync ("privateGetWsToken",parameters); + } + + public async Task privatePostWallets (object parameters = null) + { + return await this.callAsync ("privatePostWallets",parameters); + } + + public async Task privatePostOrders (object parameters = null) + { + return await this.callAsync ("privatePostOrders",parameters); + } + + public async Task privatePostOrdersTest (object parameters = null) + { + return await this.callAsync ("privatePostOrdersTest",parameters); + } + + public async Task privatePostWithdrawals (object parameters = null) + { + return await this.callAsync ("privatePostWithdrawals",parameters); + } + + public async Task privateDeleteOrders (object parameters = null) + { + return await this.callAsync ("privateDeleteOrders",parameters); + } + +} \ No newline at end of file diff --git a/js/src/abstract/geniusyield.js b/js/src/abstract/geniusyield.js new file mode 100644 index 000000000000..d3776174f72c --- /dev/null +++ b/js/src/abstract/geniusyield.js @@ -0,0 +1,5 @@ +// ------------------------------------------------------------------------------- +import { Exchange as _Exchange } from '../base/Exchange.js'; +class Exchange extends _Exchange { +} +export default Exchange; diff --git a/js/src/geniusyield.js b/js/src/geniusyield.js new file mode 100644 index 000000000000..8d63050fed17 --- /dev/null +++ b/js/src/geniusyield.js @@ -0,0 +1,1879 @@ +// --------------------------------------------------------------------------- +import Exchange from './abstract/geniusyield.js'; +import { TICK_SIZE, PAD_WITH_ZERO, ROUND, TRUNCATE, DECIMAL_PLACES } from './base/functions/number.js'; +import { InvalidOrder, InsufficientFunds, ExchangeError, ExchangeNotAvailable, DDoSProtection, BadRequest, NotSupported, InvalidAddress, AuthenticationError } from './base/errors.js'; +import { Precise } from './base/Precise.js'; +import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; +import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js'; +import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js'; +import { ecdsa } from './base/functions/crypto.js'; +// --------------------------------------------------------------------------- +/** + * @class geniusyield + * @augments Exchange + */ +export default class geniusyield extends Exchange { + describe() { + return this.deepExtend(super.describe(), { + 'id': 'geniusyield', + 'name': 'Genius Yield', + 'countries': ['CH'], + 'rateLimit': 1000, + 'version': 'v0', + 'pro': false, + 'dex': true, + 'certified': false, + 'requiresWeb3': true, + 'has': { + 'CORS': undefined, + 'spot': true, + 'margin': false, + 'swap': false, + 'future': false, + 'option': false, + 'addMargin': false, + 'cancelAllOrders': true, + 'cancelOrder': true, + 'cancelOrders': false, + 'closeAllPositions': false, + 'closePosition': false, + 'createDepositAddress': false, + 'createOrder': true, + 'createReduceOnlyOrder': false, + 'createStopLimitOrder': true, + 'createStopMarketOrder': true, + 'createStopOrder': true, + 'fetchBalance': true, + 'fetchBorrowRateHistories': false, + 'fetchBorrowRateHistory': false, + 'fetchClosedOrders': true, + 'fetchCrossBorrowRate': false, + 'fetchCrossBorrowRates': false, + 'fetchCurrencies': true, + 'fetchDeposit': true, + 'fetchDepositAddress': true, + 'fetchDepositAddresses': false, + 'fetchDepositAddressesByNetwork': false, + 'fetchDeposits': true, + 'fetchFundingHistory': false, + 'fetchFundingRate': false, + 'fetchFundingRateHistory': false, + 'fetchFundingRates': false, + 'fetchIndexOHLCV': false, + 'fetchIsolatedBorrowRate': false, + 'fetchIsolatedBorrowRates': false, + 'fetchLeverage': false, + 'fetchLeverageTiers': false, + 'fetchMarginMode': false, + 'fetchMarkets': true, + 'fetchMarkOHLCV': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenInterestHistory': false, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrders': false, + 'fetchPosition': false, + 'fetchPositionHistory': false, + 'fetchPositionMode': false, + 'fetchPositions': false, + 'fetchPositionsForSymbol': false, + 'fetchPositionsHistory': false, + 'fetchPositionsRisk': false, + 'fetchPremiumIndexOHLCV': false, + 'fetchStatus': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': true, + 'fetchTransactions': false, + 'fetchWithdrawal': true, + 'fetchWithdrawals': true, + 'reduceMargin': false, + 'sandbox': true, + 'setLeverage': false, + 'setMarginMode': false, + 'setPositionMode': false, + 'transfer': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'MATIC': 'https://api-sandbox-matic.geniusyield.io', + }, + 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api': { + 'MATIC': 'https://api-matic.geniusyield.io', + }, + 'www': 'https://geniusyield.io', + 'doc': [ + 'https://api-docs-v3.geniusyield.io/', + ], + }, + 'api': { + 'public': { + 'get': { + 'ping': 1, + 'time': 1, + 'exchange': 1, + 'assets': 1, + 'markets': 1, + 'tickers': 1, + 'candles': 1, + 'trades': 1, + 'orderbook': 1, + }, + }, + 'private': { + 'get': { + 'user': 1, + 'wallets': 1, + 'balances': 1, + 'orders': 0.1, + 'fills': 0.1, + 'deposits': 1, + 'withdrawals': 1, + 'wsToken': 1, + }, + 'post': { + 'wallets': 1, + 'orders': 0.1, + 'orders/test': 0.1, + 'withdrawals': 1, + }, + 'delete': { + 'orders': 0.1, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'gtc', + 'defaultSelfTradePrevention': 'cn', + 'network': 'MATIC', + }, + 'exceptions': { + 'exact': { + 'INVALID_ORDER_QUANTITY': InvalidOrder, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + 'SERVICE_UNAVAILABLE': ExchangeNotAvailable, + 'EXCEEDED_RATE_LIMIT': DDoSProtection, + 'INVALID_PARAMETER': BadRequest, + 'WALLET_NOT_ASSOCIATED': InvalidAddress, + 'INVALID_WALLET_SIGNATURE': AuthenticationError, + }, + }, + 'requiredCredentials': { + 'walletAddress': true, + 'privateKey': true, + 'apiKey': true, + 'secret': true, + }, + 'precisionMode': TICK_SIZE, + 'paddingMode': PAD_WITH_ZERO, + 'commonCurrencies': {}, + }); + } + priceToPrecision(symbol, price) { + // + // we override priceToPrecision to fix the following issue + // https://github.com/ccxt/ccxt/issues/13367 + // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} + // + const market = this.market(symbol); + const info = this.safeValue(market, 'info', {}); + const quoteAssetPrecision = this.safeInteger(info, 'quoteAssetPrecision'); + price = this.decimalToPrecision(price, ROUND, market['precision']['price'], this.precisionMode); + return this.decimalToPrecision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); + } + async fetchMarkets(params = {}) { + /** + * @method + * @name geniusyield#fetchMarkets + * @description retrieves data on all markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-markets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} an array of objects representing market data + */ + const response = await this.publicGetMarkets(params); + // + // [ + // { + // "market": "ETH-USDC", + // "type": "hybrid", + // "status": "activeHybrid", + // "baseAsset": "ETH", + // "baseAssetPrecision": "8", + // "quoteAsset": "USDC", + // "quoteAssetPrecision": "8", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.2500", + // "takerIdexFeeRate": "0.0500", + // "takerLiquidityProviderFeeRate": "0.2000", + // "tickSize": "0.01000000" + // }, + // ] + // + const response2 = await this.publicGetExchange(); + // + // { + // "timeZone": "UTC", + // "serverTime": "1654460599952", + // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + // "maticUsdPrice": "0.60", + // "gasPrice": "180", + // "volume24hUsd": "10015814.46", + // "totalVolumeUsd": "1589273533.28", + // "totalTrades": "1534904", + // "totalValueLockedUsd": "12041929.44", + // "geniusyieldStakingValueLockedUsd": "20133816.98", + // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", + // "geniusyieldUsdPrice": "0.07", + // "geniusyieldMarketCapUsd": "48012346.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020", + // "makerTradeMinimum": "10.00000000", + // "takerTradeMinimum": "1.00000000", + // "withdrawMinimum": "0.50000000", + // "liquidityAdditionMinimum": "0.50000000", + // "liquidityRemovalMinimum": "0.40000000", + // "blockConfirmationDelay": "64" + // } + // + const maker = this.safeNumber(response2, 'makerFeeRate'); + const taker = this.safeNumber(response2, 'takerFeeRate'); + const makerMin = this.safeString(response2, 'makerTradeMinimum'); + const takerMin = this.safeString(response2, 'takerTradeMinimum'); + const minCostETH = this.parseNumber(Precise.stringMin(makerMin, takerMin)); + const result = []; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const marketId = this.safeString(entry, 'market'); + const baseId = this.safeString(entry, 'baseAsset'); + const quoteId = this.safeString(entry, 'quoteAsset'); + const base = this.safeCurrencyCode(baseId); + const quote = this.safeCurrencyCode(quoteId); + const basePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'baseAssetPrecision'))); + const quotePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'quoteAssetPrecision'))); + const status = this.safeString(entry, 'status'); + let minCost = undefined; + if (quote === 'ETH') { + minCost = minCostETH; + } + result.push({ + 'id': marketId, + 'symbol': base + '/' + quote, + 'base': base, + 'quote': quote, + 'settle': undefined, + 'baseId': baseId, + 'quoteId': quoteId, + 'settleId': undefined, + 'type': 'spot', + 'spot': true, + 'margin': false, + 'swap': false, + 'future': false, + 'option': false, + 'active': (status !== 'inactive'), + 'contract': false, + 'linear': undefined, + 'inverse': undefined, + 'taker': taker, + 'maker': maker, + 'contractSize': undefined, + 'expiry': undefined, + 'expiryDatetime': undefined, + 'strike': undefined, + 'optionType': undefined, + 'precision': { + 'amount': basePrecision, + 'price': this.safeNumber(entry, 'tickSize'), + }, + 'limits': { + 'leverage': { + 'min': undefined, + 'max': undefined, + }, + 'amount': { + 'min': basePrecision, + 'max': undefined, + }, + 'price': { + 'min': quotePrecision, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'created': undefined, + 'info': entry, + }); + } + return result; + } + async fetchTicker(symbol, params = {}) { + /** + * @method + * @name geniusyield#fetchTicker + * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string} symbol unified symbol of the market to fetch the ticker for + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + }; + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + // ] + const response = await this.publicGetTickers(this.extend(request, params)); + const ticker = this.safeDict(response, 0); + return this.parseTicker(ticker, market); + } + async fetchTickers(symbols = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchTickers + * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + await this.loadMarkets(); + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // }, ... + // ] + const response = await this.publicGetTickers(params); + return this.parseTickers(response, symbols); + } + parseTicker(ticker, market = undefined) { + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + const marketId = this.safeString(ticker, 'market'); + market = this.safeMarket(marketId, market, '-'); + const symbol = market['symbol']; + const timestamp = this.safeInteger(ticker, 'time'); + const close = this.safeString(ticker, 'close'); + return this.safeTicker({ + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'high': this.safeString(ticker, 'high'), + 'low': this.safeString(ticker, 'low'), + 'bid': this.safeString(ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeString(ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeString(ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeString(ticker, 'percentChange'), + 'average': undefined, + 'baseVolume': this.safeString(ticker, 'baseVolume'), + 'quoteVolume': this.safeString(ticker, 'quoteVolume'), + 'info': ticker, + }, market); + } + async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOHLCV + * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + * @see https://api-docs-v3.geniusyield.io/#get-candles + * @param {string} symbol unified symbol of the market to fetch OHLCV data for + * @param {string} timeframe the length of time each candle represents + * @param {int} [since] timestamp in ms of the earliest candle to fetch + * @param {int} [limit] the maximum amount of candles to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + 'interval': timeframe, + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = Math.min(limit, 1000); + } + const response = await this.publicGetCandles(this.extend(request, params)); + if (Array.isArray(response)) { + // [ + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // }, ... + // ] + return this.parseOHLCVs(response, market, timeframe, since, limit); + } + else { + // {"nextTime":1595536440000} + return []; + } + } + parseOHLCV(ohlcv, market = undefined) { + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // } + const timestamp = this.safeInteger(ohlcv, 'start'); + const open = this.safeNumber(ohlcv, 'open'); + const high = this.safeNumber(ohlcv, 'high'); + const low = this.safeNumber(ohlcv, 'low'); + const close = this.safeNumber(ohlcv, 'close'); + const volume = this.safeNumber(ohlcv, 'volume'); + return [timestamp, open, high, low, close, volume]; + } + async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchTrades + * @description get the list of most recent trades for a particular symbol + * @see https://api-docs-v3.geniusyield.io/#get-trades + * @param {string} symbol unified symbol of the market to fetch trades for + * @param {int} [since] timestamp in ms of the earliest trade to fetch + * @param {int} [limit] the maximum amount of trades to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = Math.min(limit, 1000); + } + // [ + // { + // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", + // "price": "0.09771286", + // "quantity": "1.45340410", + // "quoteQuantity": "0.14201627", + // "time": 1598345638994, + // "makerSide": "buy", + // "sequence": 3853 + // }, ... + // ] + const response = await this.publicGetTrades(this.extend(request, params)); + return this.parseTrades(response, market, since, limit); + } + parseTrade(trade, market = undefined) { + // + // public trades + // { + // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + // "price":"0.20377008", + // "quantity":"47.58448728", + // "quoteQuantity":"9.69629509", + // "time":1642091300873, + // "makerSide":"buy", + // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" + // "sequence":31876 + // } + // + // private trades + // { + // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + // "price":"0.20717368", + // "quantity":"15.00000000", + // "quoteQuantity":"3.10760523", + // "orderBookQuantity":"0.00000003", + // "orderBookQuoteQuantity":"0.00000001", + // "poolQuantity":"14.99999997", + // "poolQuoteQuantity":"3.10760522", + // "time":1642083351215, + // "makerSide":"sell", + // "sequence":31795, + // "market":"Genius Yield-USDC", + // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + // "side":"buy", + // "fee":"0.03749989", + // "feeAsset":"Genius Yield", + // "gas":"0.40507261", + // "liquidity":"taker", + // "type":"hybrid", + // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + // "txStatus":"mined" + // } + // + const id = this.safeString(trade, 'fillId'); + const priceString = this.safeString(trade, 'price'); + const amountString = this.safeString(trade, 'quantity'); + const costString = this.safeString(trade, 'quoteQuantity'); + const timestamp = this.safeInteger(trade, 'time'); + const marketId = this.safeString(trade, 'market'); + const symbol = this.safeSymbol(marketId, market, '-'); + // this code handles the duality of public vs private trades + const makerSide = this.safeString(trade, 'makerSide'); + const oppositeSide = (makerSide === 'buy') ? 'sell' : 'buy'; + const side = this.safeString(trade, 'side', oppositeSide); + const takerOrMaker = this.safeString(trade, 'liquidity', 'taker'); + const feeCostString = this.safeString(trade, 'fee'); + let fee = undefined; + if (feeCostString !== undefined) { + const feeCurrencyId = this.safeString(trade, 'feeAsset'); + fee = { + 'cost': feeCostString, + 'currency': this.safeCurrencyCode(feeCurrencyId), + }; + } + const orderId = this.safeString(trade, 'orderId'); + return this.safeTrade({ + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': priceString, + 'amount': amountString, + 'cost': costString, + 'fee': fee, + }, market); + } + async fetchTradingFees(params = {}) { + /** + * @method + * @name geniusyield#fetchTradingFees + * @description fetch the trading fees for multiple markets + * @see https://api-docs-v3.geniusyield.io/#get-api-account + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + }; + let response = undefined; + response = await this.privateGetUser(this.extend(request, params)); + // + // { + // "depositEnabled": true, + // "orderEnabled": true, + // "cancelEnabled": true, + // "withdrawEnabled": true, + // "totalPortfolioValueUsd": "0.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020" + // } + // + const maker = this.safeNumber(response, 'makerFeeRate'); + const taker = this.safeNumber(response, 'takerFeeRate'); + const result = {}; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + result[symbol] = { + 'info': response, + 'symbol': symbol, + 'maker': maker, + 'taker': taker, + 'percentage': true, + 'tierBased': false, + }; + } + return result; + } + async fetchOrderBook(symbol, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOrderBook + * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + * @see https://api-docs-v3.geniusyield.io/#get-order-books + * @param {string} symbol unified symbol of the market to fetch the order book for + * @param {int} [limit] the maximum amount of order book entries to return + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + 'level': 2, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + // { + // "sequence": 36416753, + // "bids": [ + // [ '0.09672815', "8.22284267", 1 ], + // [ '0.09672814', "1.83685554", 1 ], + // [ '0.09672143', "4.10962617", 1 ], + // [ '0.09658884', "4.03863759", 1 ], + // [ '0.09653781', "3.35730684", 1 ], + // [ '0.09624660', "2.54163586", 1 ], + // [ '0.09617490', "1.93065030", 1 ] + // ], + // "asks": [ + // [ '0.09910476', "3.22840154", 1 ], + // [ '0.09940587', "3.39796593", 1 ], + // [ '0.09948189', "4.25088898", 1 ], + // [ '0.09958362', "2.42195784", 1 ], + // [ '0.09974393', "4.25234367", 1 ], + // [ '0.09995250', "3.40192141", 1 ] + // ] + // } + const response = await this.publicGetOrderbook(this.extend(request, params)); + const nonce = this.safeInteger(response, 'sequence'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'nonce': nonce, + 'bids': this.parseSide(response, 'bids'), + 'asks': this.parseSide(response, 'asks'), + }; + } + parseSide(book, side) { + const bookSide = this.safeValue(book, side, []); + const result = []; + for (let i = 0; i < bookSide.length; i++) { + const order = bookSide[i]; + const price = this.safeNumber(order, 0); + const amount = this.safeNumber(order, 1); + const orderCount = this.safeInteger(order, 2); + result.push([price, amount, orderCount]); + } + const descending = side === 'bids'; + return this.sortBy(result, 0, descending); + } + async fetchCurrencies(params = {}) { + /** + * @method + * @name geniusyield#fetchCurrencies + * @description fetches all available currencies on an exchange + * @see https://api-docs-v3.geniusyield.io/#get-assets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an associative dictionary of currencies + */ + const response = await this.publicGetAssets(params); + // + // [ + // { + // "name": "Ethereum", + // "symbol": "ETH", + // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + // "assetDecimals": "18", + // "exchangeDecimals": "8", + // "maticPrice": "3029.38503483" + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const name = this.safeString(entry, 'name'); + const currencyId = this.safeString(entry, 'symbol'); + const code = this.safeCurrencyCode(currencyId); + const precision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'exchangeDecimals'))); + result[code] = { + 'id': currencyId, + 'code': code, + 'info': entry, + 'type': undefined, + 'name': name, + 'active': undefined, + 'deposit': undefined, + 'withdraw': undefined, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { 'min': precision, 'max': undefined }, + 'withdraw': { 'min': precision, 'max': undefined }, + }, + }; + } + return result; + } + parseBalance(response) { + const result = { + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + }; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const currencyId = this.safeString(entry, 'asset'); + const code = this.safeCurrencyCode(currencyId); + const account = this.account(); + account['total'] = this.safeString(entry, 'quantity'); + account['free'] = this.safeString(entry, 'availableForTrade'); + account['used'] = this.safeString(entry, 'locked'); + result[code] = account; + } + return this.safeBalance(result); + } + async fetchBalance(params = {}) { + /** + * @method + * @name geniusyield#fetchBalance + * @description query for balance and get the amount of funds available for trading or funds locked in orders + * @see https://api-docs-v3.geniusyield.io/#get-balances + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + const nonce1 = this.uuidv1(); + const request = { + 'nonce': nonce1, + 'wallet': this.walletAddress, + }; + // [ + // { + // "asset": "DIL", + // "quantity": "0.00000000", + // "availableForTrade": "0.00000000", + // "locked": "0.00000000", + // "usdValue": null + // }, ... + // ] + const extendedRequest = this.extend(request, params); + if (extendedRequest['wallet'] === undefined) { + throw new BadRequest(this.id + ' fetchBalance() wallet is undefined, set this.walletAddress or "address" in params'); + } + let response = undefined; + try { + response = await this.privateGetBalances(extendedRequest); + } + catch (e) { + if (e instanceof InvalidAddress) { + const walletAddress = extendedRequest['wallet']; + await this.associateWallet(walletAddress); + response = await this.privateGetBalances(extendedRequest); + } + else { + throw e; + } + } + return this.parseBalance(response); + } + async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchMyTrades + * @description fetch all trades made by the user + * @see https://api-docs-v3.geniusyield.io/#get-fills + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch trades for + * @param {int} [limit] the maximum number of trades structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + let market = undefined; + const request = { + 'nonce': this.uuidv1(), + 'wallet': this.walletAddress, + }; + if (symbol !== undefined) { + market = this.market(symbol); + request['market'] = market['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + // [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478762, + // "makerSide": "sell", + // "sequence": 5053, + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "side": "buy", + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + const extendedRequest = this.extend(request, params); + if (extendedRequest['wallet'] === undefined) { + throw new BadRequest(this.id + ' fetchMyTrades() walletAddress is undefined, set this.walletAddress or "address" in params'); + } + let response = undefined; + try { + response = await this.privateGetFills(extendedRequest); + } + catch (e) { + if (e instanceof InvalidAddress) { + const walletAddress = extendedRequest['wallet']; + await this.associateWallet(walletAddress); + response = await this.privateGetFills(extendedRequest); + } + else { + throw e; + } + } + return this.parseTrades(response, market, since, limit); + } + async fetchOrder(id, symbol = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOrder + * @description fetches information on an order made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request = { + 'orderId': id, + }; + return await this.fetchOrdersHelper(symbol, undefined, undefined, this.extend(request, params)); + } + async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOpenOrders + * @description fetch all unfilled currently open orders + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch open orders for + * @param {int} [limit] the maximum number of open orders structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request = { + 'closed': false, + }; + return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); + } + async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchClosedOrders + * @description fetches information on multiple closed orders made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol of the market orders were made in + * @param {int} [since] the earliest time in ms to fetch orders for + * @param {int} [limit] the maximum number of order structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request = { + 'closed': true, + }; + return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); + } + async fetchOrdersHelper(symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets(); + const request = { + 'nonce': this.uuidv1(), + 'wallet': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market(symbol); + request['market'] = market['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders(this.extend(request, params)); + // fetchClosedOrders / fetchOpenOrders + // [ + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // ] + // fetchOrder + // { market: "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": + // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" } ] } + if (Array.isArray(response)) { + return this.parseOrders(response, market, since, limit); + } + else { + return this.parseOrder(response, market); + } + } + parseOrderStatus(status) { + // https://docs.geniusyield.io/#order-states-amp-lifecycle + const statuses = { + 'active': 'open', + 'partiallyFilled': 'open', + 'rejected': 'canceled', + 'filled': 'closed', + }; + return this.safeString(statuses, status, status); + } + parseOrder(order, market = undefined) { + // + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // + const timestamp = this.safeInteger(order, 'time'); + const fills = this.safeValue(order, 'fills', []); + const id = this.safeString(order, 'orderId'); + const clientOrderId = this.safeString(order, 'clientOrderId'); + const marketId = this.safeString(order, 'market'); + const side = this.safeString(order, 'side'); + const symbol = this.safeSymbol(marketId, market, '-'); + const type = this.safeString(order, 'type'); + const amount = this.safeString(order, 'originalQuantity'); + const filled = this.safeString(order, 'executedQuantity'); + const average = this.safeString(order, 'avgExecutionPrice'); + const price = this.safeString(order, 'price'); + const rawStatus = this.safeString(order, 'status'); + const timeInForce = this.safeStringUpper(order, 'timeInForce'); + const status = this.parseOrderStatus(rawStatus); + return this.safeOrder({ + 'info': order, + 'id': id, + 'clientOrderId': clientOrderId, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'timeInForce': timeInForce, + 'postOnly': undefined, + 'side': side, + 'price': price, + 'stopPrice': undefined, + 'triggerPrice': undefined, + 'amount': amount, + 'cost': undefined, + 'average': average, + 'filled': filled, + 'remaining': undefined, + 'status': status, + 'fee': undefined, + 'trades': fills, + }, market); + } + async associateWallet(walletAddress, params = {}) { + const nonce = this.uuidv1(); + const noPrefix = this.remove0xPrefix(walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(noPrefix), + ]; + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, keccak, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + // { + // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "totalPortfolioValueUsd": "0.00", + // "time": 1598468353626 + // } + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': walletAddress, + }, + 'signature': signature, + }; + const result = await this.privatePostWallets(request); + return result; + } + async createOrder(symbol, type, side, amount, price = undefined, params = {}) { + /** + * @method + * @name geniusyield#createOrder + * @description create a trade order, https://docs.geniusyield.io/#create-order + * @see https://api-docs-v3.geniusyield.io/#create-order + * @param {string} symbol unified symbol of the market to create an order in + * @param {string} type 'market' or 'limit' + * @param {string} side 'buy' or 'sell' + * @param {float} amount how much of currency you want to trade in units of base currency + * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated + * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + const testOrder = this.safeBool(params, 'test', false); + params = this.omit(params, 'test'); + const market = this.market(symbol); + const nonce = this.uuidv1(); + let typeEnum = undefined; + const stopLossTypeEnums = { + 'stopLoss': 3, + 'stopLossLimit': 4, + 'takeProfit': 5, + 'takeProfitLimit': 6, + }; + let stopPriceString = undefined; + if ((type === 'stopLossLimit') || (type === 'takeProfitLimit') || ('stopPrice' in params)) { + if (!('stopPrice' in params)) { + throw new BadRequest(this.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders'); + } + stopPriceString = this.priceToPrecision(symbol, params['stopPrice']); + } + const limitTypeEnums = { + 'limit': 1, + 'limitMaker': 2, + }; + let priceString = undefined; + const typeLower = type.toLowerCase(); + const limitOrder = typeLower.indexOf('limit') >= 0; + if (type in limitTypeEnums) { + typeEnum = limitTypeEnums[type]; + priceString = this.priceToPrecision(symbol, price); + } + else if (type in stopLossTypeEnums) { + typeEnum = stopLossTypeEnums[type]; + priceString = this.priceToPrecision(symbol, price); + } + else if (type === 'market') { + typeEnum = 0; + } + else { + throw new BadRequest(this.id + ' ' + type + ' is not a valid order type'); + } + let amountEnum = 0; // base quantity + if ('quoteOrderQuantity' in params) { + if (type !== 'market') { + throw new NotSupported(this.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders'); + } + amountEnum = 1; + amount = this.safeNumber(params, 'quoteOrderQuantity'); + } + const sideEnum = (side === 'buy') ? 0 : 1; + const walletBytes = this.remove0xPrefix(this.walletAddress); + const network = this.safeString(this.options, 'network', 'ETH'); + const orderVersion = this.getSupportedMapping(network, { + 'ETH': 1, + 'BSC': 2, + 'MATIC': 4, + }); + const amountString = this.amountToPrecision(symbol, amount); + // https://docs.geniusyield.io/#time-in-force + const timeInForceEnums = { + 'gtc': 0, + 'ioc': 2, + 'fok': 3, + }; + const defaultTimeInForce = this.safeString(this.options, 'defaultTimeInForce', 'gtc'); + const timeInForce = this.safeString(params, 'timeInForce', defaultTimeInForce); + let timeInForceEnum = undefined; + if (timeInForce in timeInForceEnums) { + timeInForceEnum = timeInForceEnums[timeInForce]; + } + else { + const allOptions = Object.keys(timeInForceEnums); + const asString = allOptions.join(', '); + throw new BadRequest(this.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString); + } + // https://docs.geniusyield.io/#self-trade-prevention + const selfTradePreventionEnums = { + 'dc': 0, + 'co': 1, + 'cn': 2, + 'cb': 3, + }; + const defaultSelfTradePrevention = this.safeString(this.options, 'defaultSelfTradePrevention', 'cn'); + const selfTradePrevention = this.safeString(params, 'selfTradePrevention', defaultSelfTradePrevention); + let selfTradePreventionEnum = undefined; + if (selfTradePrevention in selfTradePreventionEnums) { + selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]; + } + else { + const allOptions = Object.keys(selfTradePreventionEnums); + const asString = allOptions.join(', '); + throw new BadRequest(this.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString); + } + const byteArray = [ + this.numberToBE(orderVersion, 1), + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + this.encode(market['id']), + this.numberToBE(typeEnum, 1), + this.numberToBE(sideEnum, 1), + this.encode(amountString), + this.numberToBE(amountEnum, 1), + ]; + if (limitOrder) { + const encodedPrice = this.encode(priceString); + byteArray.push(encodedPrice); + } + if (type in stopLossTypeEnums) { + const encodedPrice = this.encode(stopPriceString || priceString); + byteArray.push(encodedPrice); + } + const clientOrderId = this.safeString(params, 'clientOrderId'); + if (clientOrderId !== undefined) { + byteArray.push(this.encode(clientOrderId)); + } + const after = [ + this.numberToBE(timeInForceEnum, 1), + this.numberToBE(selfTradePreventionEnum, 1), + this.numberToBE(0, 8), // unused + ]; + const allBytes = this.arrayConcat(byteArray, after); + const binary = this.binaryConcatArray(allBytes); + const hash = this.hash(binary, keccak, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + const request = { + 'parameters': { + 'nonce': nonce, + 'market': market['id'], + 'side': side, + 'type': type, + 'wallet': this.walletAddress, + 'selfTradePrevention': selfTradePrevention, + }, + 'signature': signature, + }; + if (type !== 'market') { + request['parameters']['timeInForce'] = timeInForce; + } + if (limitOrder) { + request['parameters']['price'] = priceString; + } + if (type in stopLossTypeEnums) { + request['parameters']['stopPrice'] = stopPriceString || priceString; + } + if (amountEnum === 0) { + request['parameters']['quantity'] = amountString; + } + else { + request['parameters']['quoteOrderQuantity'] = amountString; + } + if (clientOrderId !== undefined) { + request['parameters']['clientOrderId'] = clientOrderId; + } + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txStatus": "pending" + // } + // ], + // "avgExecutionPrice": "0.09905990" + // } + // we don't use extend here because it is a signed endpoint + let response = undefined; + if (testOrder) { + response = await this.privatePostOrdersTest(request); + } + else { + response = await this.privatePostOrders(request); + } + return this.parseOrder(response, market); + } + async withdraw(code, amount, address, tag = undefined, params = {}) { + /** + * @method + * @name geniusyield#withdraw + * @description make a withdrawal + * @see https://api-docs-v3.geniusyield.io/#withdraw-funds + * @param {string} code unified currency code + * @param {float} amount the amount to withdraw + * @param {string} address the address to withdraw to + * @param {string} tag + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + [tag, params] = this.handleWithdrawTagAndParams(tag, params); + this.checkRequiredCredentials(); + await this.loadMarkets(); + const nonce = this.uuidv1(); + const amountString = this.currencyToPrecision(code, amount); + const currency = this.currency(code); + const walletBytes = this.remove0xPrefix(this.walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + this.encode(currency['id']), + this.encode(amountString), + this.numberToBE(1, 1), // bool set to true + ]; + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, keccak, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': address, + 'asset': currency['id'], + 'quantity': amountString, + }, + 'signature': signature, + }; + const response = await this.privatePostWithdrawals(request); + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + return this.parseTransaction(response, currency); + } + async cancelAllOrders(symbol = undefined, params = {}) { + /** + * @method + * @name geniusyield#cancelAllOrders + * @description cancel all open orders + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + let market = undefined; + if (symbol !== undefined) { + market = this.market(symbol); + } + const nonce = this.uuidv1(); + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': this.walletAddress, + }, + }; + const walletBytes = this.remove0xPrefix(this.walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + ]; + if (market !== undefined) { + byteArray.push(this.encode(market['id'])); + request['parameters']['market'] = market['id']; + } + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, keccak, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + request['signature'] = signature; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + const response = await this.privateDeleteOrders(this.extend(request, params)); + return this.parseOrders(response, market); + } + async cancelOrder(id, symbol = undefined, params = {}) { + /** + * @method + * @name geniusyield#cancelOrder + * @description cancels an open order + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} id order id + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + let market = undefined; + if (symbol !== undefined) { + market = this.market(symbol); + } + const nonce = this.uuidv1(); + const walletBytes = this.remove0xPrefix(this.walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + this.encode(id), + ]; + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, keccak, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'orderId': id, + }, + 'signature': signature, + }; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + const response = await this.privateDeleteOrders(this.extend(request, params)); + const canceledOrder = this.safeDict(response, 0); + return this.parseOrder(canceledOrder, market); + } + handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + const errorCode = this.safeString(response, 'code'); + const message = this.safeString(response, 'message'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, message); + throw new ExchangeError(this.id + ' ' + message); + } + return undefined; + } + async fetchDeposit(id, code = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDeposit + * @description fetch information on a deposit + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} id deposit id + * @param {string} code not used by geniusyield fetchDeposit () + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'depositId': id, + }; + const response = await this.privateGetDeposits(this.extend(request, params)); + return this.parseTransaction(response); + } + async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDeposits + * @description fetch all deposits made to an account + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch deposits for + * @param {int} [limit] the maximum number of deposits structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + params = this.extend({ + 'method': 'privateGetDeposits', + }, params); + return await this.fetchTransactionsHelper(code, since, limit, params); + } + async fetchStatus(params = {}) { + /** + * @method + * @name geniusyield#fetchStatus + * @description the latest known information on the availability of the exchange API + * @see https://api-docs-v3.geniusyield.io/#get-ping + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} + */ + const response = await this.publicGetPing(params); + return { + 'status': 'ok', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + 'info': response, + }; + } + async fetchTime(params = {}) { + /** + * @method + * @name geniusyield#fetchTime + * @description fetches the current integer timestamp in milliseconds from the exchange server + * @see https://api-docs-v3.geniusyield.io/#get-time + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int} the current integer timestamp in milliseconds from the exchange server + */ + const response = await this.publicGetTime(params); + // + // { serverTime: "1655258263236" } + // + return this.safeInteger(response, 'serverTime'); + } + async fetchWithdrawal(id, code = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchWithdrawal + * @description fetch data on a currency withdrawal via the withdrawal id + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} id withdrawal id + * @param {string} code not used by geniusyield.fetchWithdrawal + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'withdrawalId': id, + }; + const response = await this.privateGetWithdrawals(this.extend(request, params)); + return this.parseTransaction(response); + } + async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchWithdrawals + * @description fetch all withdrawals made from an account + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch withdrawals for + * @param {int} [limit] the maximum number of withdrawals structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + params = this.extend({ + 'method': 'privateGetWithdrawals', + }, params); + return await this.fetchTransactionsHelper(code, since, limit, params); + } + async fetchTransactionsHelper(code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + 'wallet': this.walletAddress, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency(code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + // [ + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // ] + const method = params['method']; + params = this.omit(params, 'method'); + let response = undefined; + if (method === 'privateGetDeposits') { + response = await this.privateGetDeposits(this.extend(request, params)); + } + else if (method === 'privateGetWithdrawals') { + response = await this.privateGetWithdrawals(this.extend(request, params)); + } + else { + throw new NotSupported(this.id + ' fetchTransactionsHelper() not support this method'); + } + return this.parseTransactions(response, currency, since, limit); + } + parseTransactionStatus(status) { + const statuses = { + 'mined': 'ok', + }; + return this.safeString(statuses, status, status); + } + parseTransaction(transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // + // fetchWithdrwalas + // + // { + // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883288, + // "fee": "0.00024000", + // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + // "txStatus": "mined" + // } + // + // withdraw + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + let type = undefined; + if ('depositId' in transaction) { + type = 'deposit'; + } + else if (('withdrawId' in transaction) || ('withdrawalId' in transaction)) { + type = 'withdrawal'; + } + let id = this.safeString2(transaction, 'depositId', 'withdrawId'); + id = this.safeString(transaction, 'withdrawalId', id); + const code = this.safeCurrencyCode(this.safeString(transaction, 'asset'), currency); + const amount = this.safeNumber(transaction, 'quantity'); + const txid = this.safeString(transaction, 'txId'); + const timestamp = this.safeInteger2(transaction, 'txTime', 'time'); + let fee = undefined; + if ('fee' in transaction) { + fee = { + 'cost': this.safeNumber(transaction, 'fee'), + 'currency': 'ETH', + }; + } + const rawStatus = this.safeString(transaction, 'txStatus'); + const status = this.parseTransactionStatus(rawStatus); + const updated = this.safeInteger(transaction, 'confirmationTime'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'network': undefined, + 'address': undefined, + 'addressTo': undefined, + 'addressFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'tagFrom': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'comment': undefined, + 'internal': undefined, + 'fee': fee, + }; + } + calculateRateLimiterCost(api, method, path, params, config = {}) { + const hasApiKey = (this.apiKey !== undefined); + const hasSecret = (this.secret !== undefined); + const hasWalletAddress = (this.walletAddress !== undefined); + const hasPrivateKey = (this.privateKey !== undefined); + const defaultCost = this.safeValue(config, 'cost', 1); + const authenticated = hasApiKey && hasSecret && hasWalletAddress && hasPrivateKey; + return authenticated ? (defaultCost / 2) : defaultCost; + } + async fetchDepositAddress(code = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDepositAddress + * @description fetch the Polygon address of the wallet + * @see https://api-docs-v3.geniusyield.io/#get-wallets + * @param {string} code not used by geniusyield + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} + */ + const request = {}; + request['nonce'] = this.uuidv1(); + const response = await this.privateGetWallets(this.extend(request, params)); + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + return this.parseDepositAddress(response); + } + parseDepositAddress(depositAddress, currency = undefined) { + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + const length = depositAddress.length; + const entry = this.safeDict(depositAddress, length - 1); + const address = this.safeString(entry, 'address'); + this.checkAddress(address); + return { + 'info': depositAddress, + 'currency': undefined, + 'address': address, + 'tag': undefined, + 'network': 'MATIC', + }; + } + sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const network = this.safeString(this.options, 'network', 'ETH'); + const version = this.safeString(this.options, 'version', 'v1'); + let url = this.urls['api'][network] + '/' + version + '/' + path; + const keys = Object.keys(params); + const length = keys.length; + let query = undefined; + if (length > 0) { + if (method === 'GET') { + query = this.urlencode(params); + url = url + '?' + query; + } + else { + body = this.json(params); + } + } + headers = { + 'Content-Type': 'application/json', + }; + if (this.apiKey !== undefined) { + headers['Genius Yield-API-Key'] = this.apiKey; + } + if (api === 'private') { + let payload = undefined; + if (method === 'GET') { + payload = query; + } + else { + payload = body; + } + headers['Genius Yield-HMAC-Signature'] = this.hmac(this.encode(payload), this.encode(this.secret), sha256, 'hex'); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + remove0xPrefix(hexData) { + if (hexData.slice(0, 2) === '0x') { + return hexData.slice(2); + } + else { + return hexData; + } + } + hashMessage(message) { + // takes a hex encoded message + const binaryMessage = this.base16ToBinary(this.remove0xPrefix(message)); + const prefix = this.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength); + return '0x' + this.hash(this.binaryConcat(prefix, binaryMessage), keccak, 'hex'); + } + signHash(hash, privateKey) { + const signature = ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1, undefined); + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + }; + } + signMessage(message, privateKey) { + return this.signHash(this.hashMessage(message), privateKey.slice(-64)); + } + signMessageString(message, privateKey) { + // still takes the input as a hex string + // same as above but returns a string instead of an object + const signature = this.signMessage(message, privateKey); + return signature['r'] + this.remove0xPrefix(signature['s']) + this.binaryToBase16(this.numberToBE(signature['v'], 1)); + } +} diff --git a/php/abstract/geniusyield.php b/php/abstract/geniusyield.php new file mode 100644 index 000000000000..967e79679ec9 --- /dev/null +++ b/php/abstract/geniusyield.php @@ -0,0 +1,142 @@ +request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_time($params = array()) { + return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_exchange($params = array()) { + return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_assets($params = array()) { + return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_markets($params = array()) { + return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_tickers($params = array()) { + return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_candles($params = array()) { + return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_trades($params = array()) { + return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_orderbook($params = array()) { + return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_user($params = array()) { + return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_wallets($params = array()) { + return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_balances($params = array()) { + return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_orders($params = array()) { + return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function private_get_fills($params = array()) { + return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function private_get_deposits($params = array()) { + return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_withdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_wstoken($params = array()) { + return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_post_wallets($params = array()) { + return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function private_post_orders($params = array()) { + return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function private_post_orders_test($params = array()) { + return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function private_post_withdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function private_delete_orders($params = array()) { + return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); + } + public function publicGetPing($params = array()) { + return $this->request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetTime($params = array()) { + return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetExchange($params = array()) { + return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetAssets($params = array()) { + return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetMarkets($params = array()) { + return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetTickers($params = array()) { + return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetCandles($params = array()) { + return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetTrades($params = array()) { + return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetOrderbook($params = array()) { + return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetUser($params = array()) { + return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetWallets($params = array()) { + return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetBalances($params = array()) { + return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetOrders($params = array()) { + return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function privateGetFills($params = array()) { + return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function privateGetDeposits($params = array()) { + return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetWithdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetWsToken($params = array()) { + return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privatePostWallets($params = array()) { + return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function privatePostOrders($params = array()) { + return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function privatePostOrdersTest($params = array()) { + return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function privatePostWithdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function privateDeleteOrders($params = array()) { + return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); + } +} diff --git a/php/async/abstract/geniusyield.php b/php/async/abstract/geniusyield.php new file mode 100644 index 000000000000..1ad8b0cd7ceb --- /dev/null +++ b/php/async/abstract/geniusyield.php @@ -0,0 +1,142 @@ +request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_time($params = array()) { + return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_exchange($params = array()) { + return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_assets($params = array()) { + return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_markets($params = array()) { + return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_tickers($params = array()) { + return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_candles($params = array()) { + return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_trades($params = array()) { + return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function public_get_orderbook($params = array()) { + return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_user($params = array()) { + return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_wallets($params = array()) { + return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_balances($params = array()) { + return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_orders($params = array()) { + return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function private_get_fills($params = array()) { + return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function private_get_deposits($params = array()) { + return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_withdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_get_wstoken($params = array()) { + return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function private_post_wallets($params = array()) { + return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function private_post_orders($params = array()) { + return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function private_post_orders_test($params = array()) { + return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function private_post_withdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function private_delete_orders($params = array()) { + return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); + } + public function publicGetPing($params = array()) { + return $this->request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetTime($params = array()) { + return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetExchange($params = array()) { + return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetAssets($params = array()) { + return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetMarkets($params = array()) { + return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetTickers($params = array()) { + return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetCandles($params = array()) { + return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetTrades($params = array()) { + return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function publicGetOrderbook($params = array()) { + return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetUser($params = array()) { + return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetWallets($params = array()) { + return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetBalances($params = array()) { + return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetOrders($params = array()) { + return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function privateGetFills($params = array()) { + return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); + } + public function privateGetDeposits($params = array()) { + return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetWithdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privateGetWsToken($params = array()) { + return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); + } + public function privatePostWallets($params = array()) { + return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function privatePostOrders($params = array()) { + return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function privatePostOrdersTest($params = array()) { + return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); + } + public function privatePostWithdrawals($params = array()) { + return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); + } + public function privateDeleteOrders($params = array()) { + return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); + } +} diff --git a/python/ccxt/abstract/geniusyield.py b/python/ccxt/abstract/geniusyield.py new file mode 100644 index 000000000000..4392f685b1b6 --- /dev/null +++ b/python/ccxt/abstract/geniusyield.py @@ -0,0 +1,26 @@ +from ccxt.base.types import Entry + + +class ImplicitAPI: + public_get_ping = publicGetPing = Entry('ping', 'public', 'GET', {'cost': 1}) + public_get_time = publicGetTime = Entry('time', 'public', 'GET', {'cost': 1}) + public_get_exchange = publicGetExchange = Entry('exchange', 'public', 'GET', {'cost': 1}) + public_get_assets = publicGetAssets = Entry('assets', 'public', 'GET', {'cost': 1}) + public_get_markets = publicGetMarkets = Entry('markets', 'public', 'GET', {'cost': 1}) + public_get_tickers = publicGetTickers = Entry('tickers', 'public', 'GET', {'cost': 1}) + public_get_candles = publicGetCandles = Entry('candles', 'public', 'GET', {'cost': 1}) + public_get_trades = publicGetTrades = Entry('trades', 'public', 'GET', {'cost': 1}) + public_get_orderbook = publicGetOrderbook = Entry('orderbook', 'public', 'GET', {'cost': 1}) + private_get_user = privateGetUser = Entry('user', 'private', 'GET', {'cost': 1}) + private_get_wallets = privateGetWallets = Entry('wallets', 'private', 'GET', {'cost': 1}) + private_get_balances = privateGetBalances = Entry('balances', 'private', 'GET', {'cost': 1}) + private_get_orders = privateGetOrders = Entry('orders', 'private', 'GET', {'cost': 0.1}) + private_get_fills = privateGetFills = Entry('fills', 'private', 'GET', {'cost': 0.1}) + private_get_deposits = privateGetDeposits = Entry('deposits', 'private', 'GET', {'cost': 1}) + private_get_withdrawals = privateGetWithdrawals = Entry('withdrawals', 'private', 'GET', {'cost': 1}) + private_get_wstoken = privateGetWsToken = Entry('wsToken', 'private', 'GET', {'cost': 1}) + private_post_wallets = privatePostWallets = Entry('wallets', 'private', 'POST', {'cost': 1}) + private_post_orders = privatePostOrders = Entry('orders', 'private', 'POST', {'cost': 0.1}) + private_post_orders_test = privatePostOrdersTest = Entry('orders/test', 'private', 'POST', {'cost': 0.1}) + private_post_withdrawals = privatePostWithdrawals = Entry('withdrawals', 'private', 'POST', {'cost': 1}) + private_delete_orders = privateDeleteOrders = Entry('orders', 'private', 'DELETE', {'cost': 0.1}) diff --git a/ts/src/abstract/geniusyield.ts b/ts/src/abstract/geniusyield.ts new file mode 100644 index 000000000000..34a705e21503 --- /dev/null +++ b/ts/src/abstract/geniusyield.ts @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------- + +// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code + +// ------------------------------------------------------------------------------- + +import { implicitReturnType } from '../base/types.js'; +import { Exchange as _Exchange } from '../base/Exchange.js'; + +interface Exchange { + publicGetPing (params?: {}): Promise; + publicGetTime (params?: {}): Promise; + publicGetExchange (params?: {}): Promise; + publicGetAssets (params?: {}): Promise; + publicGetMarkets (params?: {}): Promise; + publicGetTickers (params?: {}): Promise; + publicGetCandles (params?: {}): Promise; + publicGetTrades (params?: {}): Promise; + publicGetOrderbook (params?: {}): Promise; + privateGetUser (params?: {}): Promise; + privateGetWallets (params?: {}): Promise; + privateGetBalances (params?: {}): Promise; + privateGetOrders (params?: {}): Promise; + privateGetFills (params?: {}): Promise; + privateGetDeposits (params?: {}): Promise; + privateGetWithdrawals (params?: {}): Promise; + privateGetWsToken (params?: {}): Promise; + privatePostWallets (params?: {}): Promise; + privatePostOrders (params?: {}): Promise; + privatePostOrdersTest (params?: {}): Promise; + privatePostWithdrawals (params?: {}): Promise; + privateDeleteOrders (params?: {}): Promise; +} +abstract class Exchange extends _Exchange {} + +export default Exchange diff --git a/ts/src/geniusyield.ts b/ts/src/geniusyield.ts new file mode 100644 index 000000000000..d72d0a064096 --- /dev/null +++ b/ts/src/geniusyield.ts @@ -0,0 +1,1911 @@ + +// --------------------------------------------------------------------------- + +import Exchange from './abstract/geniusyield.js'; +import { TICK_SIZE, PAD_WITH_ZERO, ROUND, TRUNCATE, DECIMAL_PLACES } from './base/functions/number.js'; +import { InvalidOrder, InsufficientFunds, ExchangeError, ExchangeNotAvailable, DDoSProtection, BadRequest, NotSupported, InvalidAddress, AuthenticationError } from './base/errors.js'; +import { Precise } from './base/Precise.js'; +import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; +import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js'; +import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js'; +import { ecdsa } from './base/functions/crypto.js'; +import type { Balances, Currencies, Currency, Dict, Int, Market, Num, OHLCV, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, int } from './base/types.js'; + +// --------------------------------------------------------------------------- + +/** + * @class geniusyield + * @augments Exchange + */ +export default class geniusyield extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'geniusyield', + 'name': 'Genius Yield', + 'countries': [ 'CH' ], + 'rateLimit': 1000, + 'version': 'v0', + 'pro': false, + 'dex': true, + 'certified': false, + 'requiresWeb3': true, + 'has': { + 'CORS': undefined, + 'spot': true, + 'margin': false, + 'swap': false, + 'future': false, + 'option': false, + 'addMargin': false, + 'cancelAllOrders': true, + 'cancelOrder': true, + 'cancelOrders': false, + 'closeAllPositions': false, + 'closePosition': false, + 'createDepositAddress': false, + 'createOrder': true, + 'createReduceOnlyOrder': false, + 'createStopLimitOrder': true, + 'createStopMarketOrder': true, + 'createStopOrder': true, + 'fetchBalance': true, + 'fetchBorrowRateHistories': false, + 'fetchBorrowRateHistory': false, + 'fetchClosedOrders': true, + 'fetchCrossBorrowRate': false, + 'fetchCrossBorrowRates': false, + 'fetchCurrencies': true, + 'fetchDeposit': true, + 'fetchDepositAddress': true, + 'fetchDepositAddresses': false, + 'fetchDepositAddressesByNetwork': false, + 'fetchDeposits': true, + 'fetchFundingHistory': false, + 'fetchFundingRate': false, + 'fetchFundingRateHistory': false, + 'fetchFundingRates': false, + 'fetchIndexOHLCV': false, + 'fetchIsolatedBorrowRate': false, + 'fetchIsolatedBorrowRates': false, + 'fetchLeverage': false, + 'fetchLeverageTiers': false, + 'fetchMarginMode': false, + 'fetchMarkets': true, + 'fetchMarkOHLCV': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenInterestHistory': false, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrders': false, + 'fetchPosition': false, + 'fetchPositionHistory': false, + 'fetchPositionMode': false, + 'fetchPositions': false, + 'fetchPositionsForSymbol': false, + 'fetchPositionsHistory': false, + 'fetchPositionsRisk': false, + 'fetchPremiumIndexOHLCV': false, + 'fetchStatus': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': true, + 'fetchTransactions': false, + 'fetchWithdrawal': true, + 'fetchWithdrawals': true, + 'reduceMargin': false, + 'sandbox': true, + 'setLeverage': false, + 'setMarginMode': false, + 'setPositionMode': false, + 'transfer': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'MATIC': 'https://api-sandbox-matic.geniusyield.io', + }, + 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api': { + 'MATIC': 'https://api-matic.geniusyield.io', + }, + 'www': 'https://geniusyield.io', + 'doc': [ + 'https://api-docs-v3.geniusyield.io/', + ], + }, + 'api': { + 'public': { + 'get': { + 'ping': 1, + 'time': 1, + 'exchange': 1, + 'assets': 1, + 'markets': 1, + 'tickers': 1, + 'candles': 1, + 'trades': 1, + 'orderbook': 1, + }, + }, + 'private': { + 'get': { + 'user': 1, + 'wallets': 1, + 'balances': 1, + 'orders': 0.1, + 'fills': 0.1, + 'deposits': 1, + 'withdrawals': 1, + 'wsToken': 1, + }, + 'post': { + 'wallets': 1, + 'orders': 0.1, + 'orders/test': 0.1, + 'withdrawals': 1, + }, + 'delete': { + 'orders': 0.1, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'gtc', + 'defaultSelfTradePrevention': 'cn', + 'network': 'MATIC', + }, + 'exceptions': { + 'exact': { + 'INVALID_ORDER_QUANTITY': InvalidOrder, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + 'SERVICE_UNAVAILABLE': ExchangeNotAvailable, + 'EXCEEDED_RATE_LIMIT': DDoSProtection, + 'INVALID_PARAMETER': BadRequest, + 'WALLET_NOT_ASSOCIATED': InvalidAddress, + 'INVALID_WALLET_SIGNATURE': AuthenticationError, + }, + }, + 'requiredCredentials': { + 'walletAddress': true, + 'privateKey': true, + 'apiKey': true, + 'secret': true, + }, + 'precisionMode': TICK_SIZE, + 'paddingMode': PAD_WITH_ZERO, + 'commonCurrencies': {}, + }); + } + + priceToPrecision (symbol, price) { + // + // we override priceToPrecision to fix the following issue + // https://github.com/ccxt/ccxt/issues/13367 + // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} + // + const market = this.market (symbol); + const info = this.safeValue (market, 'info', {}); + const quoteAssetPrecision = this.safeInteger (info, 'quoteAssetPrecision'); + price = this.decimalToPrecision (price, ROUND, market['precision']['price'], this.precisionMode); + return this.decimalToPrecision (price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); + } + + async fetchMarkets (params = {}): Promise { + /** + * @method + * @name geniusyield#fetchMarkets + * @description retrieves data on all markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-markets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} an array of objects representing market data + */ + const response = await this.publicGetMarkets (params); + // + // [ + // { + // "market": "ETH-USDC", + // "type": "hybrid", + // "status": "activeHybrid", + // "baseAsset": "ETH", + // "baseAssetPrecision": "8", + // "quoteAsset": "USDC", + // "quoteAssetPrecision": "8", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.2500", + // "takerIdexFeeRate": "0.0500", + // "takerLiquidityProviderFeeRate": "0.2000", + // "tickSize": "0.01000000" + // }, + // ] + // + const response2 = await this.publicGetExchange (); + // + // { + // "timeZone": "UTC", + // "serverTime": "1654460599952", + // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + // "maticUsdPrice": "0.60", + // "gasPrice": "180", + // "volume24hUsd": "10015814.46", + // "totalVolumeUsd": "1589273533.28", + // "totalTrades": "1534904", + // "totalValueLockedUsd": "12041929.44", + // "geniusyieldStakingValueLockedUsd": "20133816.98", + // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", + // "geniusyieldUsdPrice": "0.07", + // "geniusyieldMarketCapUsd": "48012346.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020", + // "makerTradeMinimum": "10.00000000", + // "takerTradeMinimum": "1.00000000", + // "withdrawMinimum": "0.50000000", + // "liquidityAdditionMinimum": "0.50000000", + // "liquidityRemovalMinimum": "0.40000000", + // "blockConfirmationDelay": "64" + // } + // + const maker = this.safeNumber (response2, 'makerFeeRate'); + const taker = this.safeNumber (response2, 'takerFeeRate'); + const makerMin = this.safeString (response2, 'makerTradeMinimum'); + const takerMin = this.safeString (response2, 'takerTradeMinimum'); + const minCostETH = this.parseNumber (Precise.stringMin (makerMin, takerMin)); + const result = []; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const marketId = this.safeString (entry, 'market'); + const baseId = this.safeString (entry, 'baseAsset'); + const quoteId = this.safeString (entry, 'quoteAsset'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const basePrecision = this.parseNumber (this.parsePrecision (this.safeString (entry, 'baseAssetPrecision'))); + const quotePrecision = this.parseNumber (this.parsePrecision (this.safeString (entry, 'quoteAssetPrecision'))); + const status = this.safeString (entry, 'status'); + let minCost = undefined; + if (quote === 'ETH') { + minCost = minCostETH; + } + result.push ({ + 'id': marketId, + 'symbol': base + '/' + quote, + 'base': base, + 'quote': quote, + 'settle': undefined, + 'baseId': baseId, + 'quoteId': quoteId, + 'settleId': undefined, + 'type': 'spot', + 'spot': true, + 'margin': false, + 'swap': false, + 'future': false, + 'option': false, + 'active': (status !== 'inactive'), + 'contract': false, + 'linear': undefined, + 'inverse': undefined, + 'taker': taker, + 'maker': maker, + 'contractSize': undefined, + 'expiry': undefined, + 'expiryDatetime': undefined, + 'strike': undefined, + 'optionType': undefined, + 'precision': { + 'amount': basePrecision, + 'price': this.safeNumber (entry, 'tickSize'), + }, + 'limits': { + 'leverage': { + 'min': undefined, + 'max': undefined, + }, + 'amount': { + 'min': basePrecision, + 'max': undefined, + }, + 'price': { + 'min': quotePrecision, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'created': undefined, + 'info': entry, + }); + } + return result; + } + + async fetchTicker (symbol: string, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchTicker + * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string} symbol unified symbol of the market to fetch the ticker for + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + await this.loadMarkets (); + const market = this.market (symbol); + const request: Dict = { + 'market': market['id'], + }; + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + // ] + const response = await this.publicGetTickers (this.extend (request, params)); + const ticker = this.safeDict (response, 0); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols: Strings = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchTickers + * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + await this.loadMarkets (); + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // }, ... + // ] + const response = await this.publicGetTickers (params); + return this.parseTickers (response, symbols); + } + + parseTicker (ticker: Dict, market: Market = undefined): Ticker { + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + const marketId = this.safeString (ticker, 'market'); + market = this.safeMarket (marketId, market, '-'); + const symbol = market['symbol']; + const timestamp = this.safeInteger (ticker, 'time'); + const close = this.safeString (ticker, 'close'); + return this.safeTicker ({ + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeString (ticker, 'high'), + 'low': this.safeString (ticker, 'low'), + 'bid': this.safeString (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeString (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeString (ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeString (ticker, 'percentChange'), + 'average': undefined, + 'baseVolume': this.safeString (ticker, 'baseVolume'), + 'quoteVolume': this.safeString (ticker, 'quoteVolume'), + 'info': ticker, + }, market); + } + + async fetchOHLCV (symbol: string, timeframe = '1m', since: Int = undefined, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchOHLCV + * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + * @see https://api-docs-v3.geniusyield.io/#get-candles + * @param {string} symbol unified symbol of the market to fetch OHLCV data for + * @param {string} timeframe the length of time each candle represents + * @param {int} [since] timestamp in ms of the earliest candle to fetch + * @param {int} [limit] the maximum amount of candles to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume + */ + await this.loadMarkets (); + const market = this.market (symbol); + const request: Dict = { + 'market': market['id'], + 'interval': timeframe, + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = Math.min (limit, 1000); + } + const response = await this.publicGetCandles (this.extend (request, params)); + if (Array.isArray (response)) { + // [ + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // }, ... + // ] + return this.parseOHLCVs (response, market, timeframe, since, limit); + } else { + // {"nextTime":1595536440000} + return []; + } + } + + parseOHLCV (ohlcv, market: Market = undefined): OHLCV { + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // } + const timestamp = this.safeInteger (ohlcv, 'start'); + const open = this.safeNumber (ohlcv, 'open'); + const high = this.safeNumber (ohlcv, 'high'); + const low = this.safeNumber (ohlcv, 'low'); + const close = this.safeNumber (ohlcv, 'close'); + const volume = this.safeNumber (ohlcv, 'volume'); + return [ timestamp, open, high, low, close, volume ]; + } + + async fetchTrades (symbol: string, since: Int = undefined, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchTrades + * @description get the list of most recent trades for a particular symbol + * @see https://api-docs-v3.geniusyield.io/#get-trades + * @param {string} symbol unified symbol of the market to fetch trades for + * @param {int} [since] timestamp in ms of the earliest trade to fetch + * @param {int} [limit] the maximum amount of trades to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} + */ + await this.loadMarkets (); + const market = this.market (symbol); + const request: Dict = { + 'market': market['id'], + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = Math.min (limit, 1000); + } + // [ + // { + // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", + // "price": "0.09771286", + // "quantity": "1.45340410", + // "quoteQuantity": "0.14201627", + // "time": 1598345638994, + // "makerSide": "buy", + // "sequence": 3853 + // }, ... + // ] + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseTrade (trade: Dict, market: Market = undefined): Trade { + // + // public trades + // { + // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + // "price":"0.20377008", + // "quantity":"47.58448728", + // "quoteQuantity":"9.69629509", + // "time":1642091300873, + // "makerSide":"buy", + // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" + // "sequence":31876 + // } + // + // private trades + // { + // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + // "price":"0.20717368", + // "quantity":"15.00000000", + // "quoteQuantity":"3.10760523", + // "orderBookQuantity":"0.00000003", + // "orderBookQuoteQuantity":"0.00000001", + // "poolQuantity":"14.99999997", + // "poolQuoteQuantity":"3.10760522", + // "time":1642083351215, + // "makerSide":"sell", + // "sequence":31795, + // "market":"Genius Yield-USDC", + // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + // "side":"buy", + // "fee":"0.03749989", + // "feeAsset":"Genius Yield", + // "gas":"0.40507261", + // "liquidity":"taker", + // "type":"hybrid", + // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + // "txStatus":"mined" + // } + // + const id = this.safeString (trade, 'fillId'); + const priceString = this.safeString (trade, 'price'); + const amountString = this.safeString (trade, 'quantity'); + const costString = this.safeString (trade, 'quoteQuantity'); + const timestamp = this.safeInteger (trade, 'time'); + const marketId = this.safeString (trade, 'market'); + const symbol = this.safeSymbol (marketId, market, '-'); + // this code handles the duality of public vs private trades + const makerSide = this.safeString (trade, 'makerSide'); + const oppositeSide = (makerSide === 'buy') ? 'sell' : 'buy'; + const side = this.safeString (trade, 'side', oppositeSide); + const takerOrMaker = this.safeString (trade, 'liquidity', 'taker'); + const feeCostString = this.safeString (trade, 'fee'); + let fee = undefined; + if (feeCostString !== undefined) { + const feeCurrencyId = this.safeString (trade, 'feeAsset'); + fee = { + 'cost': feeCostString, + 'currency': this.safeCurrencyCode (feeCurrencyId), + }; + } + const orderId = this.safeString (trade, 'orderId'); + return this.safeTrade ({ + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': priceString, + 'amount': amountString, + 'cost': costString, + 'fee': fee, + }, market); + } + + async fetchTradingFees (params = {}): Promise { + /** + * @method + * @name geniusyield#fetchTradingFees + * @description fetch the trading fees for multiple markets + * @see https://api-docs-v3.geniusyield.io/#get-api-account + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols + */ + this.checkRequiredCredentials (); + await this.loadMarkets (); + const nonce = this.uuidv1 (); + const request: Dict = { + 'nonce': nonce, + }; + let response = undefined; + response = await this.privateGetUser (this.extend (request, params)); + // + // { + // "depositEnabled": true, + // "orderEnabled": true, + // "cancelEnabled": true, + // "withdrawEnabled": true, + // "totalPortfolioValueUsd": "0.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020" + // } + // + const maker = this.safeNumber (response, 'makerFeeRate'); + const taker = this.safeNumber (response, 'takerFeeRate'); + const result: Dict = {}; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + result[symbol] = { + 'info': response, + 'symbol': symbol, + 'maker': maker, + 'taker': taker, + 'percentage': true, + 'tierBased': false, + }; + } + return result; + } + + async fetchOrderBook (symbol: string, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchOrderBook + * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + * @see https://api-docs-v3.geniusyield.io/#get-order-books + * @param {string} symbol unified symbol of the market to fetch the order book for + * @param {int} [limit] the maximum amount of order book entries to return + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols + */ + await this.loadMarkets (); + const market = this.market (symbol); + const request: Dict = { + 'market': market['id'], + 'level': 2, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + // { + // "sequence": 36416753, + // "bids": [ + // [ '0.09672815', "8.22284267", 1 ], + // [ '0.09672814', "1.83685554", 1 ], + // [ '0.09672143', "4.10962617", 1 ], + // [ '0.09658884', "4.03863759", 1 ], + // [ '0.09653781', "3.35730684", 1 ], + // [ '0.09624660', "2.54163586", 1 ], + // [ '0.09617490', "1.93065030", 1 ] + // ], + // "asks": [ + // [ '0.09910476', "3.22840154", 1 ], + // [ '0.09940587', "3.39796593", 1 ], + // [ '0.09948189', "4.25088898", 1 ], + // [ '0.09958362', "2.42195784", 1 ], + // [ '0.09974393', "4.25234367", 1 ], + // [ '0.09995250', "3.40192141", 1 ] + // ] + // } + const response = await this.publicGetOrderbook (this.extend (request, params)); + const nonce = this.safeInteger (response, 'sequence'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'nonce': nonce, + 'bids': this.parseSide (response, 'bids'), + 'asks': this.parseSide (response, 'asks'), + } as OrderBook; + } + + parseSide (book, side) { + const bookSide = this.safeValue (book, side, []); + const result = []; + for (let i = 0; i < bookSide.length; i++) { + const order = bookSide[i]; + const price = this.safeNumber (order, 0); + const amount = this.safeNumber (order, 1); + const orderCount = this.safeInteger (order, 2); + result.push ([ price, amount, orderCount ]); + } + const descending = side === 'bids'; + return this.sortBy (result, 0, descending); + } + + async fetchCurrencies (params = {}): Promise { + /** + * @method + * @name geniusyield#fetchCurrencies + * @description fetches all available currencies on an exchange + * @see https://api-docs-v3.geniusyield.io/#get-assets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an associative dictionary of currencies + */ + const response = await this.publicGetAssets (params); + // + // [ + // { + // "name": "Ethereum", + // "symbol": "ETH", + // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + // "assetDecimals": "18", + // "exchangeDecimals": "8", + // "maticPrice": "3029.38503483" + // }, + // ] + // + const result: Dict = {}; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const name = this.safeString (entry, 'name'); + const currencyId = this.safeString (entry, 'symbol'); + const code = this.safeCurrencyCode (currencyId); + const precision = this.parseNumber (this.parsePrecision (this.safeString (entry, 'exchangeDecimals'))); + result[code] = { + 'id': currencyId, + 'code': code, + 'info': entry, + 'type': undefined, + 'name': name, + 'active': undefined, + 'deposit': undefined, + 'withdraw': undefined, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { 'min': precision, 'max': undefined }, + 'withdraw': { 'min': precision, 'max': undefined }, + }, + }; + } + return result; + } + + parseBalance (response): Balances { + const result: Dict = { + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + }; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const currencyId = this.safeString (entry, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeString (entry, 'quantity'); + account['free'] = this.safeString (entry, 'availableForTrade'); + account['used'] = this.safeString (entry, 'locked'); + result[code] = account; + } + return this.safeBalance (result); + } + + async fetchBalance (params = {}): Promise { + /** + * @method + * @name geniusyield#fetchBalance + * @description query for balance and get the amount of funds available for trading or funds locked in orders + * @see https://api-docs-v3.geniusyield.io/#get-balances + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} + */ + this.checkRequiredCredentials (); + await this.loadMarkets (); + const nonce1 = this.uuidv1 (); + const request: Dict = { + 'nonce': nonce1, + 'wallet': this.walletAddress, + }; + // [ + // { + // "asset": "DIL", + // "quantity": "0.00000000", + // "availableForTrade": "0.00000000", + // "locked": "0.00000000", + // "usdValue": null + // }, ... + // ] + const extendedRequest = this.extend (request, params); + if (extendedRequest['wallet'] === undefined) { + throw new BadRequest (this.id + ' fetchBalance() wallet is undefined, set this.walletAddress or "address" in params'); + } + let response = undefined; + try { + response = await this.privateGetBalances (extendedRequest); + } catch (e) { + if (e instanceof InvalidAddress) { + const walletAddress = extendedRequest['wallet']; + await this.associateWallet (walletAddress); + response = await this.privateGetBalances (extendedRequest); + } else { + throw e; + } + } + return this.parseBalance (response); + } + + async fetchMyTrades (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchMyTrades + * @description fetch all trades made by the user + * @see https://api-docs-v3.geniusyield.io/#get-fills + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch trades for + * @param {int} [limit] the maximum number of trades structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} + */ + this.checkRequiredCredentials (); + await this.loadMarkets (); + let market = undefined; + const request: Dict = { + 'nonce': this.uuidv1 (), + 'wallet': this.walletAddress, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + // [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478762, + // "makerSide": "sell", + // "sequence": 5053, + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "side": "buy", + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + const extendedRequest = this.extend (request, params); + if (extendedRequest['wallet'] === undefined) { + throw new BadRequest (this.id + ' fetchMyTrades() walletAddress is undefined, set this.walletAddress or "address" in params'); + } + let response = undefined; + try { + response = await this.privateGetFills (extendedRequest); + } catch (e) { + if (e instanceof InvalidAddress) { + const walletAddress = extendedRequest['wallet']; + await this.associateWallet (walletAddress); + response = await this.privateGetFills (extendedRequest); + } else { + throw e; + } + } + return this.parseTrades (response, market, since, limit); + } + + async fetchOrder (id: string, symbol: Str = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOrder + * @description fetches information on an order made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request: Dict = { + 'orderId': id, + }; + return await this.fetchOrdersHelper (symbol, undefined, undefined, this.extend (request, params)) as Order; + } + + async fetchOpenOrders (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchOpenOrders + * @description fetch all unfilled currently open orders + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch open orders for + * @param {int} [limit] the maximum number of open orders structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request: Dict = { + 'closed': false, + }; + return await this.fetchOrdersHelper (symbol, since, limit, this.extend (request, params)) as Order[]; + } + + async fetchClosedOrders (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchClosedOrders + * @description fetches information on multiple closed orders made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol of the market orders were made in + * @param {int} [since] the earliest time in ms to fetch orders for + * @param {int} [limit] the maximum number of order structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request: Dict = { + 'closed': true, + }; + return await this.fetchOrdersHelper (symbol, since, limit, this.extend (request, params)) as Order[]; + } + + async fetchOrdersHelper (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}) { + await this.loadMarkets (); + const request: Dict = { + 'nonce': this.uuidv1 (), + 'wallet': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // fetchClosedOrders / fetchOpenOrders + // [ + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // ] + // fetchOrder + // { market: "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": + // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" } ] } + if (Array.isArray (response)) { + return this.parseOrders (response, market, since, limit) as any; + } else { + return this.parseOrder (response, market); + } + } + + parseOrderStatus (status: Str) { + // https://docs.geniusyield.io/#order-states-amp-lifecycle + const statuses: Dict = { + 'active': 'open', + 'partiallyFilled': 'open', + 'rejected': 'canceled', + 'filled': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order: Dict, market: Market = undefined): Order { + // + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // + const timestamp = this.safeInteger (order, 'time'); + const fills = this.safeValue (order, 'fills', []); + const id = this.safeString (order, 'orderId'); + const clientOrderId = this.safeString (order, 'clientOrderId'); + const marketId = this.safeString (order, 'market'); + const side = this.safeString (order, 'side'); + const symbol = this.safeSymbol (marketId, market, '-'); + const type = this.safeString (order, 'type'); + const amount = this.safeString (order, 'originalQuantity'); + const filled = this.safeString (order, 'executedQuantity'); + const average = this.safeString (order, 'avgExecutionPrice'); + const price = this.safeString (order, 'price'); + const rawStatus = this.safeString (order, 'status'); + const timeInForce = this.safeStringUpper (order, 'timeInForce'); + const status = this.parseOrderStatus (rawStatus); + return this.safeOrder ({ + 'info': order, + 'id': id, + 'clientOrderId': clientOrderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'timeInForce': timeInForce, + 'postOnly': undefined, + 'side': side, + 'price': price, + 'stopPrice': undefined, + 'triggerPrice': undefined, + 'amount': amount, + 'cost': undefined, + 'average': average, + 'filled': filled, + 'remaining': undefined, + 'status': status, + 'fee': undefined, + 'trades': fills, + }, market); + } + + async associateWallet (walletAddress, params = {}) { + const nonce = this.uuidv1 (); + const noPrefix = this.remove0xPrefix (walletAddress); + const byteArray = [ + this.base16ToBinary (nonce), + this.base16ToBinary (noPrefix), + ]; + const binary = this.binaryConcatArray (byteArray); + const hash = this.hash (binary, keccak, 'hex'); + const signature = this.signMessageString (hash, this.privateKey); + // { + // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "totalPortfolioValueUsd": "0.00", + // "time": 1598468353626 + // } + const request: Dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': walletAddress, + }, + 'signature': signature, + }; + const result = await this.privatePostWallets (request); + return result; + } + + async createOrder (symbol: string, type: OrderType, side: OrderSide, amount: number, price: Num = undefined, params = {}) { + /** + * @method + * @name geniusyield#createOrder + * @description create a trade order, https://docs.geniusyield.io/#create-order + * @see https://api-docs-v3.geniusyield.io/#create-order + * @param {string} symbol unified symbol of the market to create an order in + * @param {string} type 'market' or 'limit' + * @param {string} side 'buy' or 'sell' + * @param {float} amount how much of currency you want to trade in units of base currency + * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated + * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials (); + await this.loadMarkets (); + const testOrder = this.safeBool (params, 'test', false); + params = this.omit (params, 'test'); + const market = this.market (symbol); + const nonce = this.uuidv1 (); + let typeEnum = undefined; + const stopLossTypeEnums: Dict = { + 'stopLoss': 3, + 'stopLossLimit': 4, + 'takeProfit': 5, + 'takeProfitLimit': 6, + }; + let stopPriceString = undefined; + if ((type === 'stopLossLimit') || (type === 'takeProfitLimit') || ('stopPrice' in params)) { + if (!('stopPrice' in params)) { + throw new BadRequest (this.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders'); + } + stopPriceString = this.priceToPrecision (symbol, params['stopPrice']); + } + const limitTypeEnums: Dict = { + 'limit': 1, + 'limitMaker': 2, + }; + let priceString = undefined; + const typeLower = type.toLowerCase (); + const limitOrder = typeLower.indexOf ('limit') >= 0; + if (type in limitTypeEnums) { + typeEnum = limitTypeEnums[type]; + priceString = this.priceToPrecision (symbol, price); + } else if (type in stopLossTypeEnums) { + typeEnum = stopLossTypeEnums[type]; + priceString = this.priceToPrecision (symbol, price); + } else if (type === 'market') { + typeEnum = 0; + } else { + throw new BadRequest (this.id + ' ' + type + ' is not a valid order type'); + } + let amountEnum = 0; // base quantity + if ('quoteOrderQuantity' in params) { + if (type !== 'market') { + throw new NotSupported (this.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders'); + } + amountEnum = 1; + amount = this.safeNumber (params, 'quoteOrderQuantity'); + } + const sideEnum = (side === 'buy') ? 0 : 1; + const walletBytes = this.remove0xPrefix (this.walletAddress); + const network = this.safeString (this.options, 'network', 'ETH'); + const orderVersion = this.getSupportedMapping (network, { + 'ETH': 1, + 'BSC': 2, + 'MATIC': 4, + }); + const amountString = this.amountToPrecision (symbol, amount); + // https://docs.geniusyield.io/#time-in-force + const timeInForceEnums: Dict = { + 'gtc': 0, + 'ioc': 2, + 'fok': 3, + }; + const defaultTimeInForce = this.safeString (this.options, 'defaultTimeInForce', 'gtc'); + const timeInForce = this.safeString (params, 'timeInForce', defaultTimeInForce); + let timeInForceEnum = undefined; + if (timeInForce in timeInForceEnums) { + timeInForceEnum = timeInForceEnums[timeInForce]; + } else { + const allOptions = Object.keys (timeInForceEnums); + const asString = allOptions.join (', '); + throw new BadRequest (this.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString); + } + // https://docs.geniusyield.io/#self-trade-prevention + const selfTradePreventionEnums: Dict = { + 'dc': 0, + 'co': 1, + 'cn': 2, + 'cb': 3, + }; + const defaultSelfTradePrevention = this.safeString (this.options, 'defaultSelfTradePrevention', 'cn'); + const selfTradePrevention = this.safeString (params, 'selfTradePrevention', defaultSelfTradePrevention); + let selfTradePreventionEnum = undefined; + if (selfTradePrevention in selfTradePreventionEnums) { + selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]; + } else { + const allOptions = Object.keys (selfTradePreventionEnums); + const asString = allOptions.join (', '); + throw new BadRequest (this.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString); + } + const byteArray = [ + this.numberToBE (orderVersion, 1), + this.base16ToBinary (nonce), + this.base16ToBinary (walletBytes), + this.encode (market['id']), + this.numberToBE (typeEnum, 1), + this.numberToBE (sideEnum, 1), + this.encode (amountString), + this.numberToBE (amountEnum, 1), + ]; + if (limitOrder) { + const encodedPrice = this.encode (priceString); + byteArray.push (encodedPrice); + } + if (type in stopLossTypeEnums) { + const encodedPrice = this.encode (stopPriceString || priceString); + byteArray.push (encodedPrice); + } + const clientOrderId = this.safeString (params, 'clientOrderId'); + if (clientOrderId !== undefined) { + byteArray.push (this.encode (clientOrderId)); + } + const after = [ + this.numberToBE (timeInForceEnum, 1), + this.numberToBE (selfTradePreventionEnum, 1), + this.numberToBE (0, 8), // unused + ]; + const allBytes = this.arrayConcat (byteArray, after); + const binary = this.binaryConcatArray (allBytes); + const hash = this.hash (binary, keccak, 'hex'); + const signature = this.signMessageString (hash, this.privateKey); + const request: Dict = { + 'parameters': { + 'nonce': nonce, + 'market': market['id'], + 'side': side, + 'type': type, + 'wallet': this.walletAddress, + 'selfTradePrevention': selfTradePrevention, + }, + 'signature': signature, + }; + if (type !== 'market') { + request['parameters']['timeInForce'] = timeInForce; + } + if (limitOrder) { + request['parameters']['price'] = priceString; + } + if (type in stopLossTypeEnums) { + request['parameters']['stopPrice'] = stopPriceString || priceString; + } + if (amountEnum === 0) { + request['parameters']['quantity'] = amountString; + } else { + request['parameters']['quoteOrderQuantity'] = amountString; + } + if (clientOrderId !== undefined) { + request['parameters']['clientOrderId'] = clientOrderId; + } + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txStatus": "pending" + // } + // ], + // "avgExecutionPrice": "0.09905990" + // } + // we don't use extend here because it is a signed endpoint + let response = undefined; + if (testOrder) { + response = await this.privatePostOrdersTest (request); + } else { + response = await this.privatePostOrders (request); + } + return this.parseOrder (response, market); + } + + async withdraw (code: string, amount: number, address: string, tag = undefined, params = {}) { + /** + * @method + * @name geniusyield#withdraw + * @description make a withdrawal + * @see https://api-docs-v3.geniusyield.io/#withdraw-funds + * @param {string} code unified currency code + * @param {float} amount the amount to withdraw + * @param {string} address the address to withdraw to + * @param {string} tag + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + [ tag, params ] = this.handleWithdrawTagAndParams (tag, params); + this.checkRequiredCredentials (); + await this.loadMarkets (); + const nonce = this.uuidv1 (); + const amountString = this.currencyToPrecision (code, amount); + const currency = this.currency (code); + const walletBytes = this.remove0xPrefix (this.walletAddress); + const byteArray = [ + this.base16ToBinary (nonce), + this.base16ToBinary (walletBytes), + this.encode (currency['id']), + this.encode (amountString), + this.numberToBE (1, 1), // bool set to true + ]; + const binary = this.binaryConcatArray (byteArray); + const hash = this.hash (binary, keccak, 'hex'); + const signature = this.signMessageString (hash, this.privateKey); + const request: Dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': address, + 'asset': currency['id'], + 'quantity': amountString, + }, + 'signature': signature, + }; + const response = await this.privatePostWithdrawals (request); + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + return this.parseTransaction (response, currency); + } + + async cancelAllOrders (symbol: Str = undefined, params = {}) { + /** + * @method + * @name geniusyield#cancelAllOrders + * @description cancel all open orders + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials (); + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const nonce = this.uuidv1 (); + const request: Dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': this.walletAddress, + }, + }; + const walletBytes = this.remove0xPrefix (this.walletAddress); + const byteArray = [ + this.base16ToBinary (nonce), + this.base16ToBinary (walletBytes), + ]; + if (market !== undefined) { + byteArray.push (this.encode (market['id'])); + request['parameters']['market'] = market['id']; + } + const binary = this.binaryConcatArray (byteArray); + const hash = this.hash (binary, keccak, 'hex'); + const signature = this.signMessageString (hash, this.privateKey); + request['signature'] = signature; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + const response = await this.privateDeleteOrders (this.extend (request, params)); + return this.parseOrders (response, market); + } + + async cancelOrder (id: string, symbol: Str = undefined, params = {}) { + /** + * @method + * @name geniusyield#cancelOrder + * @description cancels an open order + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} id order id + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials (); + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const nonce = this.uuidv1 (); + const walletBytes = this.remove0xPrefix (this.walletAddress); + const byteArray = [ + this.base16ToBinary (nonce), + this.base16ToBinary (walletBytes), + this.encode (id), + ]; + const binary = this.binaryConcatArray (byteArray); + const hash = this.hash (binary, keccak, 'hex'); + const signature = this.signMessageString (hash, this.privateKey); + const request: Dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'orderId': id, + }, + 'signature': signature, + }; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + const response = await this.privateDeleteOrders (this.extend (request, params)); + const canceledOrder = this.safeDict (response, 0); + return this.parseOrder (canceledOrder, market); + } + + handleErrors (code: int, reason: string, url: string, method: string, headers: Dict, body: string, response, requestHeaders, requestBody) { + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, message); + throw new ExchangeError (this.id + ' ' + message); + } + return undefined; + } + + async fetchDeposit (id: string, code: Str = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDeposit + * @description fetch information on a deposit + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} id deposit id + * @param {string} code not used by geniusyield fetchDeposit () + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + await this.loadMarkets (); + const nonce = this.uuidv1 (); + const request: Dict = { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'depositId': id, + }; + const response = await this.privateGetDeposits (this.extend (request, params)); + return this.parseTransaction (response); + } + + async fetchDeposits (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchDeposits + * @description fetch all deposits made to an account + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch deposits for + * @param {int} [limit] the maximum number of deposits structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + params = this.extend ({ + 'method': 'privateGetDeposits', + }, params); + return await this.fetchTransactionsHelper (code, since, limit, params); + } + + async fetchStatus (params = {}) { + /** + * @method + * @name geniusyield#fetchStatus + * @description the latest known information on the availability of the exchange API + * @see https://api-docs-v3.geniusyield.io/#get-ping + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} + */ + const response = await this.publicGetPing (params); + return { + 'status': 'ok', // if there's no Errors, status = 'ok' + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + 'info': response, + }; + } + + async fetchTime (params = {}) { + /** + * @method + * @name geniusyield#fetchTime + * @description fetches the current integer timestamp in milliseconds from the exchange server + * @see https://api-docs-v3.geniusyield.io/#get-time + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int} the current integer timestamp in milliseconds from the exchange server + */ + const response = await this.publicGetTime (params); + // + // { serverTime: "1655258263236" } + // + return this.safeInteger (response, 'serverTime'); + } + + async fetchWithdrawal (id: string, code: Str = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchWithdrawal + * @description fetch data on a currency withdrawal via the withdrawal id + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} id withdrawal id + * @param {string} code not used by geniusyield.fetchWithdrawal + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + await this.loadMarkets (); + const nonce = this.uuidv1 (); + const request: Dict = { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'withdrawalId': id, + }; + const response = await this.privateGetWithdrawals (this.extend (request, params)); + return this.parseTransaction (response); + } + + async fetchWithdrawals (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { + /** + * @method + * @name geniusyield#fetchWithdrawals + * @description fetch all withdrawals made from an account + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch withdrawals for + * @param {int} [limit] the maximum number of withdrawals structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + params = this.extend ({ + 'method': 'privateGetWithdrawals', + }, params); + return await this.fetchTransactionsHelper (code, since, limit, params); + } + + async fetchTransactionsHelper (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}) { + await this.loadMarkets (); + const nonce = this.uuidv1 (); + const request: Dict = { + 'nonce': nonce, + 'wallet': this.walletAddress, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + // [ + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // ] + const method = params['method']; + params = this.omit (params, 'method'); + let response = undefined; + if (method === 'privateGetDeposits') { + response = await this.privateGetDeposits (this.extend (request, params)); + } else if (method === 'privateGetWithdrawals') { + response = await this.privateGetWithdrawals (this.extend (request, params)); + } else { + throw new NotSupported (this.id + ' fetchTransactionsHelper() not support this method'); + } + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (status: Str) { + const statuses: Dict = { + 'mined': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction: Dict, currency: Currency = undefined): Transaction { + // + // fetchDeposits + // + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // + // fetchWithdrwalas + // + // { + // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883288, + // "fee": "0.00024000", + // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + // "txStatus": "mined" + // } + // + // withdraw + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + let type = undefined; + if ('depositId' in transaction) { + type = 'deposit'; + } else if (('withdrawId' in transaction) || ('withdrawalId' in transaction)) { + type = 'withdrawal'; + } + let id = this.safeString2 (transaction, 'depositId', 'withdrawId'); + id = this.safeString (transaction, 'withdrawalId', id); + const code = this.safeCurrencyCode (this.safeString (transaction, 'asset'), currency); + const amount = this.safeNumber (transaction, 'quantity'); + const txid = this.safeString (transaction, 'txId'); + const timestamp = this.safeInteger2 (transaction, 'txTime', 'time'); + let fee = undefined; + if ('fee' in transaction) { + fee = { + 'cost': this.safeNumber (transaction, 'fee'), + 'currency': 'ETH', + }; + } + const rawStatus = this.safeString (transaction, 'txStatus'); + const status = this.parseTransactionStatus (rawStatus); + const updated = this.safeInteger (transaction, 'confirmationTime'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'network': undefined, + 'address': undefined, + 'addressTo': undefined, + 'addressFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'tagFrom': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'comment': undefined, + 'internal': undefined, + 'fee': fee, + }; + } + + calculateRateLimiterCost (api, method, path, params, config = {}) { + const hasApiKey = (this.apiKey !== undefined); + const hasSecret = (this.secret !== undefined); + const hasWalletAddress = (this.walletAddress !== undefined); + const hasPrivateKey = (this.privateKey !== undefined); + const defaultCost = this.safeValue (config, 'cost', 1); + const authenticated = hasApiKey && hasSecret && hasWalletAddress && hasPrivateKey; + return authenticated ? (defaultCost / 2) : defaultCost; + } + + async fetchDepositAddress (code: Str = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDepositAddress + * @description fetch the Polygon address of the wallet + * @see https://api-docs-v3.geniusyield.io/#get-wallets + * @param {string} code not used by geniusyield + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} + */ + const request: Dict = {}; + request['nonce'] = this.uuidv1 (); + const response = await this.privateGetWallets (this.extend (request, params)); + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + return this.parseDepositAddress (response); + } + + parseDepositAddress (depositAddress, currency: Currency = undefined) { + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + const length = depositAddress.length; + const entry = this.safeDict (depositAddress, length - 1); + const address = this.safeString (entry, 'address'); + this.checkAddress (address); + return { + 'info': depositAddress, + 'currency': undefined, + 'address': address, + 'tag': undefined, + 'network': 'MATIC', + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const network = this.safeString (this.options, 'network', 'ETH'); + const version = this.safeString (this.options, 'version', 'v1'); + let url = this.urls['api'][network] + '/' + version + '/' + path; + const keys = Object.keys (params); + const length = keys.length; + let query = undefined; + if (length > 0) { + if (method === 'GET') { + query = this.urlencode (params); + url = url + '?' + query; + } else { + body = this.json (params); + } + } + headers = { + 'Content-Type': 'application/json', + }; + if (this.apiKey !== undefined) { + headers['Genius Yield-API-Key'] = this.apiKey; + } + if (api === 'private') { + let payload = undefined; + if (method === 'GET') { + payload = query; + } else { + payload = body; + } + headers['Genius Yield-HMAC-Signature'] = this.hmac (this.encode (payload), this.encode (this.secret), sha256, 'hex'); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + remove0xPrefix (hexData) { + if (hexData.slice (0, 2) === '0x') { + return hexData.slice (2); + } else { + return hexData; + } + } + + hashMessage (message) { + // takes a hex encoded message + const binaryMessage = this.base16ToBinary (this.remove0xPrefix (message)); + const prefix = this.encode ('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength); + return '0x' + this.hash (this.binaryConcat (prefix, binaryMessage), keccak, 'hex'); + } + + signHash (hash, privateKey) { + const signature = ecdsa (hash.slice (-64), privateKey.slice (-64), secp256k1, undefined); + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + }; + } + + signMessage (message, privateKey) { + return this.signHash (this.hashMessage (message), privateKey.slice (-64)); + } + + signMessageString (message, privateKey) { + // still takes the input as a hex string + // same as above but returns a string instead of an object + const signature = this.signMessage (message, privateKey); + return signature['r'] + this.remove0xPrefix (signature['s']) + this.binaryToBase16 (this.numberToBE (signature['v'], 1)); + } +} From d32779f03b0b53fed55e3ed549036778f3b9fcf9 Mon Sep 17 00:00:00 2001 From: 4TT1L4 <2914096+4TT1L4@users.noreply.github.com> Date: Wed, 24 Jul 2024 14:43:28 +0200 Subject: [PATCH 3/5] Added Genius Yield --- cs/ccxt/api/geniusyield.cs | 108 +- cs/ccxt/exchanges/geniusyield.cs | 2036 ++++++++++++++++++++++ cs/ccxt/wrappers/geniusyield.cs | 601 +++++++ dist/cjs/src/abstract/geniusyield.js | 9 + dist/cjs/src/geniusyield.js | 1884 ++++++++++++++++++++ js/src/abstract/geniusyield.d.ts | 24 +- js/src/geniusyield.d.ts | 78 +- js/src/geniusyield.js | 1759 +------------------ php/abstract/geniusyield.php | 136 +- php/async/abstract/geniusyield.php | 136 +- php/async/geniusyield.php | 1916 ++++++++++++++++++++ php/geniusyield.php | 1855 ++++++++++++++++++++ python/ccxt/abstract/geniusyield.py | 24 +- python/ccxt/async_support/geniusyield.py | 1766 +++++++++++++++++++ python/ccxt/geniusyield.py | 1766 +++++++++++++++++++ ts/src/abstract/geniusyield.ts | 24 +- ts/src/geniusyield.ts | 1797 +------------------ wiki/exchanges/geniusyield.md | 556 ++++++ 18 files changed, 12549 insertions(+), 3926 deletions(-) create mode 100644 cs/ccxt/exchanges/geniusyield.cs create mode 100644 cs/ccxt/wrappers/geniusyield.cs create mode 100644 dist/cjs/src/abstract/geniusyield.js create mode 100644 dist/cjs/src/geniusyield.js create mode 100644 php/async/geniusyield.php create mode 100644 php/geniusyield.php create mode 100644 python/ccxt/async_support/geniusyield.py create mode 100644 python/ccxt/geniusyield.py create mode 100644 wiki/exchanges/geniusyield.md diff --git a/cs/ccxt/api/geniusyield.cs b/cs/ccxt/api/geniusyield.cs index 3bf78d508c58..15b7260e72d6 100644 --- a/cs/ccxt/api/geniusyield.cs +++ b/cs/ccxt/api/geniusyield.cs @@ -11,114 +11,14 @@ public partial class geniusyield : Exchange { public geniusyield (object args = null): base(args) {} - public async Task publicGetPing (object parameters = null) + public async Task privateGetMarkets (object parameters = null) { - return await this.callAsync ("publicGetPing",parameters); + return await this.callAsync ("privateGetMarkets",parameters); } - public async Task publicGetTime (object parameters = null) + public async Task privateGetTradingFees (object parameters = null) { - return await this.callAsync ("publicGetTime",parameters); - } - - public async Task publicGetExchange (object parameters = null) - { - return await this.callAsync ("publicGetExchange",parameters); - } - - public async Task publicGetAssets (object parameters = null) - { - return await this.callAsync ("publicGetAssets",parameters); - } - - public async Task publicGetMarkets (object parameters = null) - { - return await this.callAsync ("publicGetMarkets",parameters); - } - - public async Task publicGetTickers (object parameters = null) - { - return await this.callAsync ("publicGetTickers",parameters); - } - - public async Task publicGetCandles (object parameters = null) - { - return await this.callAsync ("publicGetCandles",parameters); - } - - public async Task publicGetTrades (object parameters = null) - { - return await this.callAsync ("publicGetTrades",parameters); - } - - public async Task publicGetOrderbook (object parameters = null) - { - return await this.callAsync ("publicGetOrderbook",parameters); - } - - public async Task privateGetUser (object parameters = null) - { - return await this.callAsync ("privateGetUser",parameters); - } - - public async Task privateGetWallets (object parameters = null) - { - return await this.callAsync ("privateGetWallets",parameters); - } - - public async Task privateGetBalances (object parameters = null) - { - return await this.callAsync ("privateGetBalances",parameters); - } - - public async Task privateGetOrders (object parameters = null) - { - return await this.callAsync ("privateGetOrders",parameters); - } - - public async Task privateGetFills (object parameters = null) - { - return await this.callAsync ("privateGetFills",parameters); - } - - public async Task privateGetDeposits (object parameters = null) - { - return await this.callAsync ("privateGetDeposits",parameters); - } - - public async Task privateGetWithdrawals (object parameters = null) - { - return await this.callAsync ("privateGetWithdrawals",parameters); - } - - public async Task privateGetWsToken (object parameters = null) - { - return await this.callAsync ("privateGetWsToken",parameters); - } - - public async Task privatePostWallets (object parameters = null) - { - return await this.callAsync ("privatePostWallets",parameters); - } - - public async Task privatePostOrders (object parameters = null) - { - return await this.callAsync ("privatePostOrders",parameters); - } - - public async Task privatePostOrdersTest (object parameters = null) - { - return await this.callAsync ("privatePostOrdersTest",parameters); - } - - public async Task privatePostWithdrawals (object parameters = null) - { - return await this.callAsync ("privatePostWithdrawals",parameters); - } - - public async Task privateDeleteOrders (object parameters = null) - { - return await this.callAsync ("privateDeleteOrders",parameters); + return await this.callAsync ("privateGetTradingFees",parameters); } } \ No newline at end of file diff --git a/cs/ccxt/exchanges/geniusyield.cs b/cs/ccxt/exchanges/geniusyield.cs new file mode 100644 index 000000000000..500187b7c110 --- /dev/null +++ b/cs/ccxt/exchanges/geniusyield.cs @@ -0,0 +1,2036 @@ +namespace ccxt; + +// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code + +public partial class geniusyield : Exchange +{ + public override object describe() + { + return this.deepExtend(base.describe(), new Dictionary() { + { "id", "geniusyield" }, + { "name", "Genius Yield" }, + { "countries", new List() {"CH"} }, + { "rateLimit", 1000 }, + { "version", "v0" }, + { "pro", false }, + { "dex", true }, + { "certified", false }, + { "requiresWeb3", true }, + { "has", new Dictionary() { + { "CORS", null }, + { "spot", true }, + { "margin", false }, + { "swap", false }, + { "future", false }, + { "option", false }, + { "addMargin", false }, + { "cancelAllOrders", true }, + { "cancelOrder", true }, + { "cancelOrders", false }, + { "closeAllPositions", false }, + { "closePosition", false }, + { "createDepositAddress", false }, + { "createOrder", true }, + { "createReduceOnlyOrder", false }, + { "createStopLimitOrder", true }, + { "createStopMarketOrder", true }, + { "createStopOrder", true }, + { "fetchBalance", true }, + { "fetchBorrowRateHistories", false }, + { "fetchBorrowRateHistory", false }, + { "fetchClosedOrders", true }, + { "fetchCrossBorrowRate", false }, + { "fetchCrossBorrowRates", false }, + { "fetchCurrencies", true }, + { "fetchDeposit", true }, + { "fetchDepositAddress", true }, + { "fetchDepositAddresses", false }, + { "fetchDepositAddressesByNetwork", false }, + { "fetchDeposits", true }, + { "fetchFundingHistory", false }, + { "fetchFundingRate", false }, + { "fetchFundingRateHistory", false }, + { "fetchFundingRates", false }, + { "fetchIndexOHLCV", false }, + { "fetchIsolatedBorrowRate", false }, + { "fetchIsolatedBorrowRates", false }, + { "fetchLeverage", false }, + { "fetchLeverageTiers", false }, + { "fetchMarginMode", false }, + { "fetchMarkets", true }, + { "fetchMarkOHLCV", false }, + { "fetchMyTrades", true }, + { "fetchOHLCV", true }, + { "fetchOpenInterestHistory", false }, + { "fetchOpenOrders", true }, + { "fetchOrder", true }, + { "fetchOrderBook", true }, + { "fetchOrders", false }, + { "fetchPosition", false }, + { "fetchPositionHistory", false }, + { "fetchPositionMode", false }, + { "fetchPositions", false }, + { "fetchPositionsForSymbol", false }, + { "fetchPositionsHistory", false }, + { "fetchPositionsRisk", false }, + { "fetchPremiumIndexOHLCV", false }, + { "fetchStatus", true }, + { "fetchTicker", true }, + { "fetchTickers", true }, + { "fetchTime", true }, + { "fetchTrades", true }, + { "fetchTradingFee", false }, + { "fetchTradingFees", true }, + { "fetchTransactions", false }, + { "fetchWithdrawal", true }, + { "fetchWithdrawals", true }, + { "reduceMargin", false }, + { "sandbox", true }, + { "setLeverage", false }, + { "setMarginMode", false }, + { "setPositionMode", false }, + { "transfer", false }, + { "withdraw", true }, + } }, + { "timeframes", new Dictionary() { + { "1m", "1m" }, + { "5m", "5m" }, + { "15m", "15m" }, + { "30m", "30m" }, + { "1h", "1h" }, + { "6h", "6h" }, + { "1d", "1d" }, + } }, + { "urls", new Dictionary() { + { "test", new Dictionary() { + { "MATIC", "https://api-sandbox-matic.geniusyield.io" }, + } }, + { "logo", "https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg" }, + { "api", new Dictionary() { + { "MATIC", "https://api-matic.geniusyield.io" }, + } }, + { "www", "https://geniusyield.io" }, + { "doc", new List() {"https://api-docs-v3.geniusyield.io/"} }, + } }, + { "api", new Dictionary() { + { "public", new Dictionary() { + { "get", new Dictionary() { + { "ping", 1 }, + { "time", 1 }, + { "exchange", 1 }, + { "assets", 1 }, + { "markets", 1 }, + { "tickers", 1 }, + { "candles", 1 }, + { "trades", 1 }, + { "orderbook", 1 }, + } }, + } }, + { "private", new Dictionary() { + { "get", new Dictionary() { + { "user", 1 }, + { "wallets", 1 }, + { "balances", 1 }, + { "orders", 0.1 }, + { "fills", 0.1 }, + { "deposits", 1 }, + { "withdrawals", 1 }, + { "wsToken", 1 }, + } }, + { "post", new Dictionary() { + { "wallets", 1 }, + { "orders", 0.1 }, + { "orders/test", 0.1 }, + { "withdrawals", 1 }, + } }, + { "delete", new Dictionary() { + { "orders", 0.1 }, + } }, + } }, + } }, + { "options", new Dictionary() { + { "defaultTimeInForce", "gtc" }, + { "defaultSelfTradePrevention", "cn" }, + { "network", "MATIC" }, + } }, + { "exceptions", new Dictionary() { + { "exact", new Dictionary() { + { "INVALID_ORDER_QUANTITY", typeof(InvalidOrder) }, + { "INSUFFICIENT_FUNDS", typeof(InsufficientFunds) }, + { "SERVICE_UNAVAILABLE", typeof(ExchangeNotAvailable) }, + { "EXCEEDED_RATE_LIMIT", typeof(DDoSProtection) }, + { "INVALID_PARAMETER", typeof(BadRequest) }, + { "WALLET_NOT_ASSOCIATED", typeof(InvalidAddress) }, + { "INVALID_WALLET_SIGNATURE", typeof(AuthenticationError) }, + } }, + } }, + { "requiredCredentials", new Dictionary() { + { "walletAddress", true }, + { "privateKey", true }, + { "apiKey", true }, + { "secret", true }, + } }, + { "precisionMode", TICK_SIZE }, + { "paddingMode", PAD_WITH_ZERO }, + { "commonCurrencies", new Dictionary() {} }, + }); + } + + public override object priceToPrecision(object symbol, object price) + { + // + // we override priceToPrecision to fix the following issue + // https://github.com/ccxt/ccxt/issues/13367 + // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} + // + object market = this.market(symbol); + object info = this.safeValue(market, "info", new Dictionary() {}); + object quoteAssetPrecision = this.safeInteger(info, "quoteAssetPrecision"); + price = this.decimalToPrecision(price, ROUND, getValue(getValue(market, "precision"), "price"), this.precisionMode); + return this.decimalToPrecision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); + } + + public async override Task fetchMarkets(object parameters = null) + { + /** + * @method + * @name geniusyield#fetchMarkets + * @description retrieves data on all markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-markets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} an array of objects representing market data + */ + parameters ??= new Dictionary(); + object response = await this.publicGetMarkets(parameters); + // + // [ + // { + // "market": "ETH-USDC", + // "type": "hybrid", + // "status": "activeHybrid", + // "baseAsset": "ETH", + // "baseAssetPrecision": "8", + // "quoteAsset": "USDC", + // "quoteAssetPrecision": "8", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.2500", + // "takerIdexFeeRate": "0.0500", + // "takerLiquidityProviderFeeRate": "0.2000", + // "tickSize": "0.01000000" + // }, + // ] + // + object response2 = await this.publicGetExchange(); + // + // { + // "timeZone": "UTC", + // "serverTime": "1654460599952", + // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + // "maticUsdPrice": "0.60", + // "gasPrice": "180", + // "volume24hUsd": "10015814.46", + // "totalVolumeUsd": "1589273533.28", + // "totalTrades": "1534904", + // "totalValueLockedUsd": "12041929.44", + // "geniusyieldStakingValueLockedUsd": "20133816.98", + // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", + // "geniusyieldUsdPrice": "0.07", + // "geniusyieldMarketCapUsd": "48012346.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020", + // "makerTradeMinimum": "10.00000000", + // "takerTradeMinimum": "1.00000000", + // "withdrawMinimum": "0.50000000", + // "liquidityAdditionMinimum": "0.50000000", + // "liquidityRemovalMinimum": "0.40000000", + // "blockConfirmationDelay": "64" + // } + // + object maker = this.safeNumber(response2, "makerFeeRate"); + object taker = this.safeNumber(response2, "takerFeeRate"); + object makerMin = this.safeString(response2, "makerTradeMinimum"); + object takerMin = this.safeString(response2, "takerTradeMinimum"); + object minCostETH = this.parseNumber(Precise.stringMin(makerMin, takerMin)); + object result = new List() {}; + for (object i = 0; isLessThan(i, getArrayLength(response)); postFixIncrement(ref i)) + { + object entry = getValue(response, i); + object marketId = this.safeString(entry, "market"); + object baseId = this.safeString(entry, "baseAsset"); + object quoteId = this.safeString(entry, "quoteAsset"); + object bs = this.safeCurrencyCode(baseId); + object quote = this.safeCurrencyCode(quoteId); + object basePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, "baseAssetPrecision"))); + object quotePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, "quoteAssetPrecision"))); + object status = this.safeString(entry, "status"); + object minCost = null; + if (isTrue(isEqual(quote, "ETH"))) + { + minCost = minCostETH; + } + ((IList)result).Add(new Dictionary() { + { "id", marketId }, + { "symbol", add(add(bs, "/"), quote) }, + { "base", bs }, + { "quote", quote }, + { "settle", null }, + { "baseId", baseId }, + { "quoteId", quoteId }, + { "settleId", null }, + { "type", "spot" }, + { "spot", true }, + { "margin", false }, + { "swap", false }, + { "future", false }, + { "option", false }, + { "active", (!isEqual(status, "inactive")) }, + { "contract", false }, + { "linear", null }, + { "inverse", null }, + { "taker", taker }, + { "maker", maker }, + { "contractSize", null }, + { "expiry", null }, + { "expiryDatetime", null }, + { "strike", null }, + { "optionType", null }, + { "precision", new Dictionary() { + { "amount", basePrecision }, + { "price", this.safeNumber(entry, "tickSize") }, + } }, + { "limits", new Dictionary() { + { "leverage", new Dictionary() { + { "min", null }, + { "max", null }, + } }, + { "amount", new Dictionary() { + { "min", basePrecision }, + { "max", null }, + } }, + { "price", new Dictionary() { + { "min", quotePrecision }, + { "max", null }, + } }, + { "cost", new Dictionary() { + { "min", minCost }, + { "max", null }, + } }, + } }, + { "created", null }, + { "info", entry }, + }); + } + return result; + } + + public async override Task fetchTicker(object symbol, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchTicker + * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string} symbol unified symbol of the market to fetch the ticker for + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + parameters ??= new Dictionary(); + await this.loadMarkets(); + object market = this.market(symbol); + object request = new Dictionary() { + { "market", getValue(market, "id") }, + }; + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + // ] + object response = await this.publicGetTickers(this.extend(request, parameters)); + object ticker = this.safeDict(response, 0); + return this.parseTicker(ticker, market); + } + + public async override Task fetchTickers(object symbols = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchTickers + * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + parameters ??= new Dictionary(); + await this.loadMarkets(); + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // }, ... + // ] + object response = await this.publicGetTickers(parameters); + return this.parseTickers(response, symbols); + } + + public override object parseTicker(object ticker, object market = null) + { + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + object marketId = this.safeString(ticker, "market"); + market = this.safeMarket(marketId, market, "-"); + object symbol = getValue(market, "symbol"); + object timestamp = this.safeInteger(ticker, "time"); + object close = this.safeString(ticker, "close"); + return this.safeTicker(new Dictionary() { + { "symbol", symbol }, + { "timestamp", timestamp }, + { "datetime", this.iso8601(timestamp) }, + { "high", this.safeString(ticker, "high") }, + { "low", this.safeString(ticker, "low") }, + { "bid", this.safeString(ticker, "bid") }, + { "bidVolume", null }, + { "ask", this.safeString(ticker, "ask") }, + { "askVolume", null }, + { "vwap", null }, + { "open", this.safeString(ticker, "open") }, + { "close", close }, + { "last", close }, + { "previousClose", null }, + { "change", null }, + { "percentage", this.safeString(ticker, "percentChange") }, + { "average", null }, + { "baseVolume", this.safeString(ticker, "baseVolume") }, + { "quoteVolume", this.safeString(ticker, "quoteVolume") }, + { "info", ticker }, + }, market); + } + + public async override Task fetchOHLCV(object symbol, object timeframe = null, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchOHLCV + * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + * @see https://api-docs-v3.geniusyield.io/#get-candles + * @param {string} symbol unified symbol of the market to fetch OHLCV data for + * @param {string} timeframe the length of time each candle represents + * @param {int} [since] timestamp in ms of the earliest candle to fetch + * @param {int} [limit] the maximum amount of candles to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume + */ + timeframe ??= "1m"; + parameters ??= new Dictionary(); + await this.loadMarkets(); + object market = this.market(symbol); + object request = new Dictionary() { + { "market", getValue(market, "id") }, + { "interval", timeframe }, + }; + if (isTrue(!isEqual(since, null))) + { + ((IDictionary)request)["start"] = since; + } + if (isTrue(!isEqual(limit, null))) + { + ((IDictionary)request)["limit"] = mathMin(limit, 1000); + } + object response = await this.publicGetCandles(this.extend(request, parameters)); + if (isTrue(((response is IList) || (response.GetType().IsGenericType && response.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>)))))) + { + // [ + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // }, ... + // ] + return this.parseOHLCVs(response, market, timeframe, since, limit); + } else + { + // {"nextTime":1595536440000} + return new List() {}; + } + } + + public override object parseOHLCV(object ohlcv, object market = null) + { + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // } + object timestamp = this.safeInteger(ohlcv, "start"); + object open = this.safeNumber(ohlcv, "open"); + object high = this.safeNumber(ohlcv, "high"); + object low = this.safeNumber(ohlcv, "low"); + object close = this.safeNumber(ohlcv, "close"); + object volume = this.safeNumber(ohlcv, "volume"); + return new List() {timestamp, open, high, low, close, volume}; + } + + public async override Task fetchTrades(object symbol, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchTrades + * @description get the list of most recent trades for a particular symbol + * @see https://api-docs-v3.geniusyield.io/#get-trades + * @param {string} symbol unified symbol of the market to fetch trades for + * @param {int} [since] timestamp in ms of the earliest trade to fetch + * @param {int} [limit] the maximum amount of trades to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} + */ + parameters ??= new Dictionary(); + await this.loadMarkets(); + object market = this.market(symbol); + object request = new Dictionary() { + { "market", getValue(market, "id") }, + }; + if (isTrue(!isEqual(since, null))) + { + ((IDictionary)request)["start"] = since; + } + if (isTrue(!isEqual(limit, null))) + { + ((IDictionary)request)["limit"] = mathMin(limit, 1000); + } + // [ + // { + // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", + // "price": "0.09771286", + // "quantity": "1.45340410", + // "quoteQuantity": "0.14201627", + // "time": 1598345638994, + // "makerSide": "buy", + // "sequence": 3853 + // }, ... + // ] + object response = await this.publicGetTrades(this.extend(request, parameters)); + return this.parseTrades(response, market, since, limit); + } + + public override object parseTrade(object trade, object market = null) + { + // + // public trades + // { + // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + // "price":"0.20377008", + // "quantity":"47.58448728", + // "quoteQuantity":"9.69629509", + // "time":1642091300873, + // "makerSide":"buy", + // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" + // "sequence":31876 + // } + // + // private trades + // { + // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + // "price":"0.20717368", + // "quantity":"15.00000000", + // "quoteQuantity":"3.10760523", + // "orderBookQuantity":"0.00000003", + // "orderBookQuoteQuantity":"0.00000001", + // "poolQuantity":"14.99999997", + // "poolQuoteQuantity":"3.10760522", + // "time":1642083351215, + // "makerSide":"sell", + // "sequence":31795, + // "market":"Genius Yield-USDC", + // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + // "side":"buy", + // "fee":"0.03749989", + // "feeAsset":"Genius Yield", + // "gas":"0.40507261", + // "liquidity":"taker", + // "type":"hybrid", + // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + // "txStatus":"mined" + // } + // + object id = this.safeString(trade, "fillId"); + object priceString = this.safeString(trade, "price"); + object amountString = this.safeString(trade, "quantity"); + object costString = this.safeString(trade, "quoteQuantity"); + object timestamp = this.safeInteger(trade, "time"); + object marketId = this.safeString(trade, "market"); + object symbol = this.safeSymbol(marketId, market, "-"); + // this code handles the duality of public vs private trades + object makerSide = this.safeString(trade, "makerSide"); + object oppositeSide = ((bool) isTrue((isEqual(makerSide, "buy")))) ? "sell" : "buy"; + object side = this.safeString(trade, "side", oppositeSide); + object takerOrMaker = this.safeString(trade, "liquidity", "taker"); + object feeCostString = this.safeString(trade, "fee"); + object fee = null; + if (isTrue(!isEqual(feeCostString, null))) + { + object feeCurrencyId = this.safeString(trade, "feeAsset"); + fee = new Dictionary() { + { "cost", feeCostString }, + { "currency", this.safeCurrencyCode(feeCurrencyId) }, + }; + } + object orderId = this.safeString(trade, "orderId"); + return this.safeTrade(new Dictionary() { + { "info", trade }, + { "timestamp", timestamp }, + { "datetime", this.iso8601(timestamp) }, + { "symbol", symbol }, + { "id", id }, + { "order", orderId }, + { "type", "limit" }, + { "side", side }, + { "takerOrMaker", takerOrMaker }, + { "price", priceString }, + { "amount", amountString }, + { "cost", costString }, + { "fee", fee }, + }, market); + } + + public async override Task fetchTradingFees(object parameters = null) + { + /** + * @method + * @name geniusyield#fetchTradingFees + * @description fetch the trading fees for multiple markets + * @see https://api-docs-v3.geniusyield.io/#get-api-account + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols + */ + parameters ??= new Dictionary(); + this.checkRequiredCredentials(); + await this.loadMarkets(); + object nonce = this.uuidv1(); + object request = new Dictionary() { + { "nonce", nonce }, + }; + object response = null; + response = await this.privateGetUser(this.extend(request, parameters)); + // + // { + // "depositEnabled": true, + // "orderEnabled": true, + // "cancelEnabled": true, + // "withdrawEnabled": true, + // "totalPortfolioValueUsd": "0.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020" + // } + // + object maker = this.safeNumber(response, "makerFeeRate"); + object taker = this.safeNumber(response, "takerFeeRate"); + object result = new Dictionary() {}; + for (object i = 0; isLessThan(i, getArrayLength(this.symbols)); postFixIncrement(ref i)) + { + object symbol = getValue(this.symbols, i); + ((IDictionary)result)[(string)symbol] = new Dictionary() { + { "info", response }, + { "symbol", symbol }, + { "maker", maker }, + { "taker", taker }, + { "percentage", true }, + { "tierBased", false }, + }; + } + return result; + } + + public async override Task fetchOrderBook(object symbol, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchOrderBook + * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + * @see https://api-docs-v3.geniusyield.io/#get-order-books + * @param {string} symbol unified symbol of the market to fetch the order book for + * @param {int} [limit] the maximum amount of order book entries to return + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols + */ + parameters ??= new Dictionary(); + await this.loadMarkets(); + object market = this.market(symbol); + object request = new Dictionary() { + { "market", getValue(market, "id") }, + { "level", 2 }, + }; + if (isTrue(!isEqual(limit, null))) + { + ((IDictionary)request)["limit"] = limit; + } + // { + // "sequence": 36416753, + // "bids": [ + // [ '0.09672815', "8.22284267", 1 ], + // [ '0.09672814', "1.83685554", 1 ], + // [ '0.09672143', "4.10962617", 1 ], + // [ '0.09658884', "4.03863759", 1 ], + // [ '0.09653781', "3.35730684", 1 ], + // [ '0.09624660', "2.54163586", 1 ], + // [ '0.09617490', "1.93065030", 1 ] + // ], + // "asks": [ + // [ '0.09910476', "3.22840154", 1 ], + // [ '0.09940587', "3.39796593", 1 ], + // [ '0.09948189', "4.25088898", 1 ], + // [ '0.09958362', "2.42195784", 1 ], + // [ '0.09974393', "4.25234367", 1 ], + // [ '0.09995250', "3.40192141", 1 ] + // ] + // } + object response = await this.publicGetOrderbook(this.extend(request, parameters)); + object nonce = this.safeInteger(response, "sequence"); + return new Dictionary() { + { "symbol", symbol }, + { "timestamp", null }, + { "datetime", null }, + { "nonce", nonce }, + { "bids", this.parseSide(response, "bids") }, + { "asks", this.parseSide(response, "asks") }, + }; + } + + public virtual object parseSide(object book, object side) + { + object bookSide = this.safeValue(book, side, new List() {}); + object result = new List() {}; + for (object i = 0; isLessThan(i, getArrayLength(bookSide)); postFixIncrement(ref i)) + { + object order = getValue(bookSide, i); + object price = this.safeNumber(order, 0); + object amount = this.safeNumber(order, 1); + object orderCount = this.safeInteger(order, 2); + ((IList)result).Add(new List() {price, amount, orderCount}); + } + object descending = isEqual(side, "bids"); + return this.sortBy(result, 0, descending); + } + + public async override Task fetchCurrencies(object parameters = null) + { + /** + * @method + * @name geniusyield#fetchCurrencies + * @description fetches all available currencies on an exchange + * @see https://api-docs-v3.geniusyield.io/#get-assets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an associative dictionary of currencies + */ + parameters ??= new Dictionary(); + object response = await this.publicGetAssets(parameters); + // + // [ + // { + // "name": "Ethereum", + // "symbol": "ETH", + // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + // "assetDecimals": "18", + // "exchangeDecimals": "8", + // "maticPrice": "3029.38503483" + // }, + // ] + // + object result = new Dictionary() {}; + for (object i = 0; isLessThan(i, getArrayLength(response)); postFixIncrement(ref i)) + { + object entry = getValue(response, i); + object name = this.safeString(entry, "name"); + object currencyId = this.safeString(entry, "symbol"); + object code = this.safeCurrencyCode(currencyId); + object precision = this.parseNumber(this.parsePrecision(this.safeString(entry, "exchangeDecimals"))); + ((IDictionary)result)[(string)code] = new Dictionary() { + { "id", currencyId }, + { "code", code }, + { "info", entry }, + { "type", null }, + { "name", name }, + { "active", null }, + { "deposit", null }, + { "withdraw", null }, + { "fee", null }, + { "precision", precision }, + { "limits", new Dictionary() { + { "amount", new Dictionary() { + { "min", precision }, + { "max", null }, + } }, + { "withdraw", new Dictionary() { + { "min", precision }, + { "max", null }, + } }, + } }, + }; + } + return result; + } + + public override object parseBalance(object response) + { + object result = new Dictionary() { + { "info", response }, + { "timestamp", null }, + { "datetime", null }, + }; + for (object i = 0; isLessThan(i, getArrayLength(response)); postFixIncrement(ref i)) + { + object entry = getValue(response, i); + object currencyId = this.safeString(entry, "asset"); + object code = this.safeCurrencyCode(currencyId); + object account = this.account(); + ((IDictionary)account)["total"] = this.safeString(entry, "quantity"); + ((IDictionary)account)["free"] = this.safeString(entry, "availableForTrade"); + ((IDictionary)account)["used"] = this.safeString(entry, "locked"); + ((IDictionary)result)[(string)code] = account; + } + return this.safeBalance(result); + } + + public async override Task fetchBalance(object parameters = null) + { + /** + * @method + * @name geniusyield#fetchBalance + * @description query for balance and get the amount of funds available for trading or funds locked in orders + * @see https://api-docs-v3.geniusyield.io/#get-balances + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} + */ + parameters ??= new Dictionary(); + this.checkRequiredCredentials(); + await this.loadMarkets(); + object nonce1 = this.uuidv1(); + object request = new Dictionary() { + { "nonce", nonce1 }, + { "wallet", this.walletAddress }, + }; + // [ + // { + // "asset": "DIL", + // "quantity": "0.00000000", + // "availableForTrade": "0.00000000", + // "locked": "0.00000000", + // "usdValue": null + // }, ... + // ] + object extendedRequest = this.extend(request, parameters); + if (isTrue(isEqual(getValue(extendedRequest, "wallet"), null))) + { + throw new BadRequest ((string)add(this.id, " fetchBalance() wallet is undefined, set this.walletAddress or \"address\" in params")) ; + } + object response = null; + try + { + response = await this.privateGetBalances(extendedRequest); + } catch(Exception e) + { + if (isTrue(e is InvalidAddress)) + { + object walletAddress = getValue(extendedRequest, "wallet"); + await this.associateWallet(walletAddress); + response = await this.privateGetBalances(extendedRequest); + } else + { + throw e; + } + } + return this.parseBalance(response); + } + + public async override Task fetchMyTrades(object symbol = null, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchMyTrades + * @description fetch all trades made by the user + * @see https://api-docs-v3.geniusyield.io/#get-fills + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch trades for + * @param {int} [limit] the maximum number of trades structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} + */ + parameters ??= new Dictionary(); + this.checkRequiredCredentials(); + await this.loadMarkets(); + object market = null; + object request = new Dictionary() { + { "nonce", this.uuidv1() }, + { "wallet", this.walletAddress }, + }; + if (isTrue(!isEqual(symbol, null))) + { + market = this.market(symbol); + ((IDictionary)request)["market"] = getValue(market, "id"); + } + if (isTrue(!isEqual(since, null))) + { + ((IDictionary)request)["start"] = since; + } + if (isTrue(!isEqual(limit, null))) + { + ((IDictionary)request)["limit"] = limit; + } + // [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478762, + // "makerSide": "sell", + // "sequence": 5053, + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "side": "buy", + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + object extendedRequest = this.extend(request, parameters); + if (isTrue(isEqual(getValue(extendedRequest, "wallet"), null))) + { + throw new BadRequest ((string)add(this.id, " fetchMyTrades() walletAddress is undefined, set this.walletAddress or \"address\" in params")) ; + } + object response = null; + try + { + response = await this.privateGetFills(extendedRequest); + } catch(Exception e) + { + if (isTrue(e is InvalidAddress)) + { + object walletAddress = getValue(extendedRequest, "wallet"); + await this.associateWallet(walletAddress); + response = await this.privateGetFills(extendedRequest); + } else + { + throw e; + } + } + return this.parseTrades(response, market, since, limit); + } + + public async override Task fetchOrder(object id, object symbol = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchOrder + * @description fetches information on an order made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + parameters ??= new Dictionary(); + object request = new Dictionary() { + { "orderId", id }, + }; + return await this.fetchOrdersHelper(symbol, null, null, this.extend(request, parameters)); + } + + public async override Task fetchOpenOrders(object symbol = null, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchOpenOrders + * @description fetch all unfilled currently open orders + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch open orders for + * @param {int} [limit] the maximum number of open orders structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + parameters ??= new Dictionary(); + object request = new Dictionary() { + { "closed", false }, + }; + return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, parameters)); + } + + public async override Task fetchClosedOrders(object symbol = null, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchClosedOrders + * @description fetches information on multiple closed orders made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol of the market orders were made in + * @param {int} [since] the earliest time in ms to fetch orders for + * @param {int} [limit] the maximum number of order structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + parameters ??= new Dictionary(); + object request = new Dictionary() { + { "closed", true }, + }; + return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, parameters)); + } + + public async virtual Task fetchOrdersHelper(object symbol = null, object since = null, object limit = null, object parameters = null) + { + parameters ??= new Dictionary(); + await this.loadMarkets(); + object request = new Dictionary() { + { "nonce", this.uuidv1() }, + { "wallet", this.walletAddress }, + }; + object market = null; + if (isTrue(!isEqual(symbol, null))) + { + market = this.market(symbol); + ((IDictionary)request)["market"] = getValue(market, "id"); + } + if (isTrue(!isEqual(since, null))) + { + ((IDictionary)request)["start"] = since; + } + if (isTrue(!isEqual(limit, null))) + { + ((IDictionary)request)["limit"] = limit; + } + object response = await this.privateGetOrders(this.extend(request, parameters)); + // fetchClosedOrders / fetchOpenOrders + // [ + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // ] + // fetchOrder + // { market: "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": + // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" } ] } + if (isTrue(((response is IList) || (response.GetType().IsGenericType && response.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>)))))) + { + return ((object)this.parseOrders(response, market, since, limit)); + } else + { + return this.parseOrder(response, market); + } + } + + public virtual object parseOrderStatus(object status) + { + // https://docs.geniusyield.io/#order-states-amp-lifecycle + object statuses = new Dictionary() { + { "active", "open" }, + { "partiallyFilled", "open" }, + { "rejected", "canceled" }, + { "filled", "closed" }, + }; + return this.safeString(statuses, status, status); + } + + public override object parseOrder(object order, object market = null) + { + // + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // + object timestamp = this.safeInteger(order, "time"); + object fills = this.safeValue(order, "fills", new List() {}); + object id = this.safeString(order, "orderId"); + object clientOrderId = this.safeString(order, "clientOrderId"); + object marketId = this.safeString(order, "market"); + object side = this.safeString(order, "side"); + object symbol = this.safeSymbol(marketId, market, "-"); + object type = this.safeString(order, "type"); + object amount = this.safeString(order, "originalQuantity"); + object filled = this.safeString(order, "executedQuantity"); + object average = this.safeString(order, "avgExecutionPrice"); + object price = this.safeString(order, "price"); + object rawStatus = this.safeString(order, "status"); + object timeInForce = this.safeStringUpper(order, "timeInForce"); + object status = this.parseOrderStatus(rawStatus); + return this.safeOrder(new Dictionary() { + { "info", order }, + { "id", id }, + { "clientOrderId", clientOrderId }, + { "timestamp", timestamp }, + { "datetime", this.iso8601(timestamp) }, + { "lastTradeTimestamp", null }, + { "symbol", symbol }, + { "type", type }, + { "timeInForce", timeInForce }, + { "postOnly", null }, + { "side", side }, + { "price", price }, + { "stopPrice", null }, + { "triggerPrice", null }, + { "amount", amount }, + { "cost", null }, + { "average", average }, + { "filled", filled }, + { "remaining", null }, + { "status", status }, + { "fee", null }, + { "trades", fills }, + }, market); + } + + public async virtual Task associateWallet(object walletAddress, object parameters = null) + { + parameters ??= new Dictionary(); + object nonce = this.uuidv1(); + object noPrefix = this.remove0xPrefix(walletAddress); + object byteArray = new List {this.base16ToBinary(nonce), this.base16ToBinary(noPrefix)}; + object binary = this.binaryConcatArray(byteArray); + object hash = this.hash(binary, keccak, "hex"); + object signature = this.signMessageString(hash, this.privateKey); + // { + // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "totalPortfolioValueUsd": "0.00", + // "time": 1598468353626 + // } + object request = new Dictionary() { + { "parameters", new Dictionary() { + { "nonce", nonce }, + { "wallet", walletAddress }, + } }, + { "signature", signature }, + }; + object result = await this.privatePostWallets(request); + return result; + } + + public async override Task createOrder(object symbol, object type, object side, object amount, object price = null, object parameters = null) + { + /** + * @method + * @name geniusyield#createOrder + * @description create a trade order, https://docs.geniusyield.io/#create-order + * @see https://api-docs-v3.geniusyield.io/#create-order + * @param {string} symbol unified symbol of the market to create an order in + * @param {string} type 'market' or 'limit' + * @param {string} side 'buy' or 'sell' + * @param {float} amount how much of currency you want to trade in units of base currency + * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated + * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + parameters ??= new Dictionary(); + this.checkRequiredCredentials(); + await this.loadMarkets(); + object testOrder = this.safeBool(parameters, "test", false); + parameters = this.omit(parameters, "test"); + object market = this.market(symbol); + object nonce = this.uuidv1(); + object typeEnum = null; + object stopLossTypeEnums = new Dictionary() { + { "stopLoss", 3 }, + { "stopLossLimit", 4 }, + { "takeProfit", 5 }, + { "takeProfitLimit", 6 }, + }; + object stopPriceString = null; + if (isTrue(isTrue(isTrue((isEqual(type, "stopLossLimit"))) || isTrue((isEqual(type, "takeProfitLimit")))) || isTrue((inOp(parameters, "stopPrice"))))) + { + if (!isTrue((inOp(parameters, "stopPrice")))) + { + throw new BadRequest ((string)add(add(add(this.id, " createOrder() stopPrice is a required parameter for "), type), "orders")) ; + } + stopPriceString = this.priceToPrecision(symbol, getValue(parameters, "stopPrice")); + } + object limitTypeEnums = new Dictionary() { + { "limit", 1 }, + { "limitMaker", 2 }, + }; + object priceString = null; + object typeLower = ((string)type).ToLower(); + object limitOrder = isGreaterThanOrEqual(getIndexOf(typeLower, "limit"), 0); + if (isTrue(inOp(limitTypeEnums, type))) + { + typeEnum = getValue(limitTypeEnums, type); + priceString = this.priceToPrecision(symbol, price); + } else if (isTrue(inOp(stopLossTypeEnums, type))) + { + typeEnum = getValue(stopLossTypeEnums, type); + priceString = this.priceToPrecision(symbol, price); + } else if (isTrue(isEqual(type, "market"))) + { + typeEnum = 0; + } else + { + throw new BadRequest ((string)add(add(add(this.id, " "), type), " is not a valid order type")) ; + } + object amountEnum = 0; // base quantity + if (isTrue(inOp(parameters, "quoteOrderQuantity"))) + { + if (isTrue(!isEqual(type, "market"))) + { + throw new NotSupported ((string)add(add(add(this.id, " createOrder() quoteOrderQuantity is not supported for "), type), " orders, only supported for market orders")) ; + } + amountEnum = 1; + amount = this.safeNumber(parameters, "quoteOrderQuantity"); + } + object sideEnum = ((bool) isTrue((isEqual(side, "buy")))) ? 0 : 1; + object walletBytes = this.remove0xPrefix(this.walletAddress); + object network = this.safeString(this.options, "network", "ETH"); + object orderVersion = this.getSupportedMapping(network, new Dictionary() { + { "ETH", 1 }, + { "BSC", 2 }, + { "MATIC", 4 }, + }); + object amountString = this.amountToPrecision(symbol, amount); + // https://docs.geniusyield.io/#time-in-force + object timeInForceEnums = new Dictionary() { + { "gtc", 0 }, + { "ioc", 2 }, + { "fok", 3 }, + }; + object defaultTimeInForce = this.safeString(this.options, "defaultTimeInForce", "gtc"); + object timeInForce = this.safeString(parameters, "timeInForce", defaultTimeInForce); + object timeInForceEnum = null; + if (isTrue(inOp(timeInForceEnums, timeInForce))) + { + timeInForceEnum = getValue(timeInForceEnums, timeInForce); + } else + { + object allOptions = new List(((IDictionary)timeInForceEnums).Keys); + object asString = String.Join(", ", ((IList)allOptions).ToArray()); + throw new BadRequest ((string)add(add(add(add(this.id, " "), timeInForce), " is not a valid timeInForce, please choose one of "), asString)) ; + } + // https://docs.geniusyield.io/#self-trade-prevention + object selfTradePreventionEnums = new Dictionary() { + { "dc", 0 }, + { "co", 1 }, + { "cn", 2 }, + { "cb", 3 }, + }; + object defaultSelfTradePrevention = this.safeString(this.options, "defaultSelfTradePrevention", "cn"); + object selfTradePrevention = this.safeString(parameters, "selfTradePrevention", defaultSelfTradePrevention); + object selfTradePreventionEnum = null; + if (isTrue(inOp(selfTradePreventionEnums, selfTradePrevention))) + { + selfTradePreventionEnum = getValue(selfTradePreventionEnums, selfTradePrevention); + } else + { + object allOptions = new List(((IDictionary)selfTradePreventionEnums).Keys); + object asString = String.Join(", ", ((IList)allOptions).ToArray()); + throw new BadRequest ((string)add(add(add(add(this.id, " "), selfTradePrevention), " is not a valid selfTradePrevention, please choose one of "), asString)) ; + } + object byteArray = new List {this.numberToBE(orderVersion, 1), this.base16ToBinary(nonce), this.base16ToBinary(walletBytes), this.encode(getValue(market, "id")), this.numberToBE(typeEnum, 1), this.numberToBE(sideEnum, 1), this.encode(amountString), this.numberToBE(amountEnum, 1)}; + if (isTrue(limitOrder)) + { + object encodedPrice = this.encode(priceString); + ((IList)byteArray).Add(encodedPrice); + } + if (isTrue(inOp(stopLossTypeEnums, type))) + { + object encodedPrice = this.encode(isTrue(stopPriceString) || isTrue(priceString)); + ((IList)byteArray).Add(encodedPrice); + } + object clientOrderId = this.safeString(parameters, "clientOrderId"); + if (isTrue(!isEqual(clientOrderId, null))) + { + ((IList)byteArray).Add(this.encode(clientOrderId)); + } + object after = new List {this.numberToBE(timeInForceEnum, 1), this.numberToBE(selfTradePreventionEnum, 1), this.numberToBE(0, 8)}; + object allBytes = this.arrayConcat(byteArray, after); + object binary = this.binaryConcatArray(allBytes); + object hash = this.hash(binary, keccak, "hex"); + object signature = this.signMessageString(hash, this.privateKey); + object request = new Dictionary() { + { "parameters", new Dictionary() { + { "nonce", nonce }, + { "market", getValue(market, "id") }, + { "side", side }, + { "type", type }, + { "wallet", this.walletAddress }, + { "selfTradePrevention", selfTradePrevention }, + } }, + { "signature", signature }, + }; + if (isTrue(!isEqual(type, "market"))) + { + ((IDictionary)getValue(request, "parameters"))["timeInForce"] = timeInForce; + } + if (isTrue(limitOrder)) + { + ((IDictionary)getValue(request, "parameters"))["price"] = priceString; + } + if (isTrue(inOp(stopLossTypeEnums, type))) + { + ((IDictionary)getValue(request, "parameters"))["stopPrice"] = isTrue(stopPriceString) || isTrue(priceString); + } + if (isTrue(isEqual(amountEnum, 0))) + { + ((IDictionary)getValue(request, "parameters"))["quantity"] = amountString; + } else + { + ((IDictionary)getValue(request, "parameters"))["quoteOrderQuantity"] = amountString; + } + if (isTrue(!isEqual(clientOrderId, null))) + { + ((IDictionary)getValue(request, "parameters"))["clientOrderId"] = clientOrderId; + } + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txStatus": "pending" + // } + // ], + // "avgExecutionPrice": "0.09905990" + // } + // we don't use extend here because it is a signed endpoint + object response = null; + if (isTrue(testOrder)) + { + response = await this.privatePostOrdersTest(request); + } else + { + response = await this.privatePostOrders(request); + } + return this.parseOrder(response, market); + } + + public async override Task withdraw(object code, object amount, object address, object tag = null, object parameters = null) + { + /** + * @method + * @name geniusyield#withdraw + * @description make a withdrawal + * @see https://api-docs-v3.geniusyield.io/#withdraw-funds + * @param {string} code unified currency code + * @param {float} amount the amount to withdraw + * @param {string} address the address to withdraw to + * @param {string} tag + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + parameters ??= new Dictionary(); + var tagparametersVariable = this.handleWithdrawTagAndParams(tag, parameters); + tag = ((IList)tagparametersVariable)[0]; + parameters = ((IList)tagparametersVariable)[1]; + this.checkRequiredCredentials(); + await this.loadMarkets(); + object nonce = this.uuidv1(); + object amountString = this.currencyToPrecision(code, amount); + object currency = this.currency(code); + object walletBytes = this.remove0xPrefix(this.walletAddress); + object byteArray = new List {this.base16ToBinary(nonce), this.base16ToBinary(walletBytes), this.encode(getValue(currency, "id")), this.encode(amountString), this.numberToBE(1, 1)}; + object binary = this.binaryConcatArray(byteArray); + object hash = this.hash(binary, keccak, "hex"); + object signature = this.signMessageString(hash, this.privateKey); + object request = new Dictionary() { + { "parameters", new Dictionary() { + { "nonce", nonce }, + { "wallet", address }, + { "asset", getValue(currency, "id") }, + { "quantity", amountString }, + } }, + { "signature", signature }, + }; + object response = await this.privatePostWithdrawals(request); + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + return this.parseTransaction(response, currency); + } + + public async override Task cancelAllOrders(object symbol = null, object parameters = null) + { + /** + * @method + * @name geniusyield#cancelAllOrders + * @description cancel all open orders + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + parameters ??= new Dictionary(); + this.checkRequiredCredentials(); + await this.loadMarkets(); + object market = null; + if (isTrue(!isEqual(symbol, null))) + { + market = this.market(symbol); + } + object nonce = this.uuidv1(); + object request = new Dictionary() { + { "parameters", new Dictionary() { + { "nonce", nonce }, + { "wallet", this.walletAddress }, + } }, + }; + object walletBytes = this.remove0xPrefix(this.walletAddress); + object byteArray = new List {this.base16ToBinary(nonce), this.base16ToBinary(walletBytes)}; + if (isTrue(!isEqual(market, null))) + { + ((IList)byteArray).Add(this.encode(getValue(market, "id"))); + ((IDictionary)getValue(request, "parameters"))["market"] = getValue(market, "id"); + } + object binary = this.binaryConcatArray(byteArray); + object hash = this.hash(binary, keccak, "hex"); + object signature = this.signMessageString(hash, this.privateKey); + ((IDictionary)request)["signature"] = signature; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + object response = await this.privateDeleteOrders(this.extend(request, parameters)); + return this.parseOrders(response, market); + } + + public async override Task cancelOrder(object id, object symbol = null, object parameters = null) + { + /** + * @method + * @name geniusyield#cancelOrder + * @description cancels an open order + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} id order id + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + parameters ??= new Dictionary(); + this.checkRequiredCredentials(); + await this.loadMarkets(); + object market = null; + if (isTrue(!isEqual(symbol, null))) + { + market = this.market(symbol); + } + object nonce = this.uuidv1(); + object walletBytes = this.remove0xPrefix(this.walletAddress); + object byteArray = new List {this.base16ToBinary(nonce), this.base16ToBinary(walletBytes), this.encode(id)}; + object binary = this.binaryConcatArray(byteArray); + object hash = this.hash(binary, keccak, "hex"); + object signature = this.signMessageString(hash, this.privateKey); + object request = new Dictionary() { + { "parameters", new Dictionary() { + { "nonce", nonce }, + { "wallet", this.walletAddress }, + { "orderId", id }, + } }, + { "signature", signature }, + }; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + object response = await this.privateDeleteOrders(this.extend(request, parameters)); + object canceledOrder = this.safeDict(response, 0); + return this.parseOrder(canceledOrder, market); + } + + public override object handleErrors(object code, object reason, object url, object method, object headers, object body, object response, object requestHeaders, object requestBody) + { + object errorCode = this.safeString(response, "code"); + object message = this.safeString(response, "message"); + if (isTrue(!isEqual(errorCode, null))) + { + this.throwExactlyMatchedException(getValue(this.exceptions, "exact"), errorCode, message); + throw new ExchangeError ((string)add(add(this.id, " "), message)) ; + } + return null; + } + + public async virtual Task fetchDeposit(object id, object code = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchDeposit + * @description fetch information on a deposit + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} id deposit id + * @param {string} code not used by geniusyield fetchDeposit () + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + parameters ??= new Dictionary(); + await this.loadMarkets(); + object nonce = this.uuidv1(); + object request = new Dictionary() { + { "nonce", nonce }, + { "wallet", this.walletAddress }, + { "depositId", id }, + }; + object response = await this.privateGetDeposits(this.extend(request, parameters)); + return this.parseTransaction(response); + } + + public async override Task fetchDeposits(object code = null, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchDeposits + * @description fetch all deposits made to an account + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch deposits for + * @param {int} [limit] the maximum number of deposits structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + parameters ??= new Dictionary(); + parameters = this.extend(new Dictionary() { + { "method", "privateGetDeposits" }, + }, parameters); + return await this.fetchTransactionsHelper(code, since, limit, parameters); + } + + public async override Task fetchStatus(object parameters = null) + { + /** + * @method + * @name geniusyield#fetchStatus + * @description the latest known information on the availability of the exchange API + * @see https://api-docs-v3.geniusyield.io/#get-ping + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} + */ + parameters ??= new Dictionary(); + object response = await this.publicGetPing(parameters); + return new Dictionary() { + { "status", "ok" }, + { "updated", null }, + { "eta", null }, + { "url", null }, + { "info", response }, + }; + } + + public async override Task fetchTime(object parameters = null) + { + /** + * @method + * @name geniusyield#fetchTime + * @description fetches the current integer timestamp in milliseconds from the exchange server + * @see https://api-docs-v3.geniusyield.io/#get-time + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int} the current integer timestamp in milliseconds from the exchange server + */ + parameters ??= new Dictionary(); + object response = await this.publicGetTime(parameters); + // + // { serverTime: "1655258263236" } + // + return this.safeInteger(response, "serverTime"); + } + + public async virtual Task fetchWithdrawal(object id, object code = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchWithdrawal + * @description fetch data on a currency withdrawal via the withdrawal id + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} id withdrawal id + * @param {string} code not used by geniusyield.fetchWithdrawal + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + parameters ??= new Dictionary(); + await this.loadMarkets(); + object nonce = this.uuidv1(); + object request = new Dictionary() { + { "nonce", nonce }, + { "wallet", this.walletAddress }, + { "withdrawalId", id }, + }; + object response = await this.privateGetWithdrawals(this.extend(request, parameters)); + return this.parseTransaction(response); + } + + public async override Task fetchWithdrawals(object code = null, object since = null, object limit = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchWithdrawals + * @description fetch all withdrawals made from an account + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch withdrawals for + * @param {int} [limit] the maximum number of withdrawals structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + parameters ??= new Dictionary(); + parameters = this.extend(new Dictionary() { + { "method", "privateGetWithdrawals" }, + }, parameters); + return await this.fetchTransactionsHelper(code, since, limit, parameters); + } + + public async virtual Task fetchTransactionsHelper(object code = null, object since = null, object limit = null, object parameters = null) + { + parameters ??= new Dictionary(); + await this.loadMarkets(); + object nonce = this.uuidv1(); + object request = new Dictionary() { + { "nonce", nonce }, + { "wallet", this.walletAddress }, + }; + object currency = null; + if (isTrue(!isEqual(code, null))) + { + currency = this.currency(code); + ((IDictionary)request)["asset"] = getValue(currency, "id"); + } + if (isTrue(!isEqual(since, null))) + { + ((IDictionary)request)["start"] = since; + } + if (isTrue(!isEqual(limit, null))) + { + ((IDictionary)request)["limit"] = limit; + } + // [ + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // ] + object method = getValue(parameters, "method"); + parameters = this.omit(parameters, "method"); + object response = null; + if (isTrue(isEqual(method, "privateGetDeposits"))) + { + response = await this.privateGetDeposits(this.extend(request, parameters)); + } else if (isTrue(isEqual(method, "privateGetWithdrawals"))) + { + response = await this.privateGetWithdrawals(this.extend(request, parameters)); + } else + { + throw new NotSupported ((string)add(this.id, " fetchTransactionsHelper() not support this method")) ; + } + return this.parseTransactions(response, currency, since, limit); + } + + public virtual object parseTransactionStatus(object status) + { + object statuses = new Dictionary() { + { "mined", "ok" }, + }; + return this.safeString(statuses, status, status); + } + + public override object parseTransaction(object transaction, object currency = null) + { + // + // fetchDeposits + // + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // + // fetchWithdrwalas + // + // { + // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883288, + // "fee": "0.00024000", + // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + // "txStatus": "mined" + // } + // + // withdraw + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + object type = null; + if (isTrue(inOp(transaction, "depositId"))) + { + type = "deposit"; + } else if (isTrue(isTrue((inOp(transaction, "withdrawId"))) || isTrue((inOp(transaction, "withdrawalId"))))) + { + type = "withdrawal"; + } + object id = this.safeString2(transaction, "depositId", "withdrawId"); + id = this.safeString(transaction, "withdrawalId", id); + object code = this.safeCurrencyCode(this.safeString(transaction, "asset"), currency); + object amount = this.safeNumber(transaction, "quantity"); + object txid = this.safeString(transaction, "txId"); + object timestamp = this.safeInteger2(transaction, "txTime", "time"); + object fee = null; + if (isTrue(inOp(transaction, "fee"))) + { + fee = new Dictionary() { + { "cost", this.safeNumber(transaction, "fee") }, + { "currency", "ETH" }, + }; + } + object rawStatus = this.safeString(transaction, "txStatus"); + object status = this.parseTransactionStatus(rawStatus); + object updated = this.safeInteger(transaction, "confirmationTime"); + return new Dictionary() { + { "info", transaction }, + { "id", id }, + { "txid", txid }, + { "timestamp", timestamp }, + { "datetime", this.iso8601(timestamp) }, + { "network", null }, + { "address", null }, + { "addressTo", null }, + { "addressFrom", null }, + { "tag", null }, + { "tagTo", null }, + { "tagFrom", null }, + { "type", type }, + { "amount", amount }, + { "currency", code }, + { "status", status }, + { "updated", updated }, + { "comment", null }, + { "internal", null }, + { "fee", fee }, + }; + } + + public override object calculateRateLimiterCost(object api, object method, object path, object parameters, object config = null) + { + config ??= new Dictionary(); + object hasApiKey = (!isEqual(this.apiKey, null)); + object hasSecret = (!isEqual(this.secret, null)); + object hasWalletAddress = (!isEqual(this.walletAddress, null)); + object hasPrivateKey = (!isEqual(this.privateKey, null)); + object defaultCost = this.safeValue(config, "cost", 1); + object authenticated = isTrue(isTrue(isTrue(hasApiKey) && isTrue(hasSecret)) && isTrue(hasWalletAddress)) && isTrue(hasPrivateKey); + return ((bool) isTrue(authenticated)) ? (divide(defaultCost, 2)) : defaultCost; + } + + public async override Task fetchDepositAddress(object code = null, object parameters = null) + { + /** + * @method + * @name geniusyield#fetchDepositAddress + * @description fetch the Polygon address of the wallet + * @see https://api-docs-v3.geniusyield.io/#get-wallets + * @param {string} code not used by geniusyield + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} + */ + parameters ??= new Dictionary(); + object request = new Dictionary() {}; + ((IDictionary)request)["nonce"] = this.uuidv1(); + object response = await this.privateGetWallets(this.extend(request, parameters)); + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + return this.parseDepositAddress(response); + } + + public override object parseDepositAddress(object depositAddress, object currency = null) + { + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + object length = getArrayLength(depositAddress); + object entry = this.safeDict(depositAddress, subtract(length, 1)); + object address = this.safeString(entry, "address"); + this.checkAddress(address); + return new Dictionary() { + { "info", depositAddress }, + { "currency", null }, + { "address", address }, + { "tag", null }, + { "network", "MATIC" }, + }; + } + + public override object sign(object path, object api = null, object method = null, object parameters = null, object headers = null, object body = null) + { + api ??= "public"; + method ??= "GET"; + parameters ??= new Dictionary(); + object network = this.safeString(this.options, "network", "ETH"); + object version = this.safeString(this.options, "version", "v1"); + object url = add(add(add(add(getValue(getValue(this.urls, "api"), network), "/"), version), "/"), path); + object keys = new List(((IDictionary)parameters).Keys); + object length = getArrayLength(keys); + object query = null; + if (isTrue(isGreaterThan(length, 0))) + { + if (isTrue(isEqual(method, "GET"))) + { + query = this.urlencode(parameters); + url = add(add(url, "?"), query); + } else + { + body = this.json(parameters); + } + } + headers = new Dictionary() { + { "Content-Type", "application/json" }, + }; + if (isTrue(!isEqual(this.apiKey, null))) + { + ((IDictionary)headers)["Genius Yield-API-Key"] = this.apiKey; + } + if (isTrue(isEqual(api, "private"))) + { + object payload = null; + if (isTrue(isEqual(method, "GET"))) + { + payload = query; + } else + { + payload = body; + } + ((IDictionary)headers)["Genius Yield-HMAC-Signature"] = this.hmac(this.encode(payload), this.encode(this.secret), sha256, "hex"); + } + return new Dictionary() { + { "url", url }, + { "method", method }, + { "body", body }, + { "headers", headers }, + }; + } + + public override object remove0xPrefix(object hexData) + { + if (isTrue(isEqual(slice(hexData, 0, 2), "0x"))) + { + return slice(hexData, 2, null); + } else + { + return hexData; + } + } + + public virtual object hashMessage(object message) + { + // takes a hex encoded message + object binaryMessage = this.base16ToBinary(this.remove0xPrefix(message)); + object prefix = this.encode(add("Ethereum Signed Message:\n", getValue(binaryMessage, "byteLength"))); + return add("0x", this.hash(this.binaryConcat(prefix, binaryMessage), keccak, "hex")); + } + + public virtual object signHash(object hash, object privateKey) + { + object signature = ecdsa(slice(hash, -64, null), slice(privateKey, -64, null), secp256k1, null); + return new Dictionary() { + { "r", add("0x", getValue(signature, "r")) }, + { "s", add("0x", getValue(signature, "s")) }, + { "v", add(27, getValue(signature, "v")) }, + }; + } + + public virtual object signMessage(object message, object privateKey) + { + return this.signHash(this.hashMessage(message), slice(privateKey, -64, null)); + } + + public virtual object signMessageString(object message, object privateKey) + { + // still takes the input as a hex string + // same as above but returns a string instead of an object + object signature = this.signMessage(message, privateKey); + return add(add(getValue(signature, "r"), this.remove0xPrefix(getValue(signature, "s"))), this.binaryToBase16(this.numberToBE(getValue(signature, "v"), 1))); + } +} diff --git a/cs/ccxt/wrappers/geniusyield.cs b/cs/ccxt/wrappers/geniusyield.cs new file mode 100644 index 000000000000..53e2bb88cf7a --- /dev/null +++ b/cs/ccxt/wrappers/geniusyield.cs @@ -0,0 +1,601 @@ +namespace ccxt; + +// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code + + +public partial class geniusyield +{ + /// + /// retrieves data on all markets for geniusyield + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object[] an array of objects representing market data. + public async Task> FetchMarkets(Dictionary parameters = null) + { + var res = await this.fetchMarkets(parameters); + return ((IList)res).Select(item => new MarketInterface(item)).ToList(); + } + /// + /// fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}. + public async Task FetchTicker(string symbol, Dictionary parameters = null) + { + var res = await this.fetchTicker(symbol, parameters); + return new Ticker(res); + } + /// + /// fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}. + public async Task FetchTickers(List symbols = null, Dictionary parameters = null) + { + var res = await this.fetchTickers(symbols, parameters); + return new Tickers(res); + } + /// + /// fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : timestamp in ms of the earliest candle to fetch + /// + /// + /// + /// limit + /// + /// int : the maximum amount of candles to fetch + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// int[][] A list of candles ordered as timestamp, open, high, low, close, volume. + public async Task> FetchOHLCV(string symbol, string timeframe = "1m", Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchOHLCV(symbol, timeframe, since, limit, parameters); + return ((IList)res).Select(item => new OHLCV(item)).ToList(); + } + /// + /// get the list of most recent trades for a particular symbol + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : timestamp in ms of the earliest trade to fetch + /// + /// + /// + /// limit + /// + /// int : the maximum amount of trades to fetch + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// Trade[] a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}. + public async Task> FetchTrades(string symbol, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchTrades(symbol, since, limit, parameters); + return ((IList)res).Select(item => new Trade(item)).ToList(); + } + /// + /// fetch the trading fees for multiple markets + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols. + public async Task FetchTradingFees(Dictionary parameters = null) + { + var res = await this.fetchTradingFees(parameters); + return new TradingFees(res); + } + /// + /// fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + /// + /// + /// See
+ /// + /// + /// limit + /// + /// int : the maximum amount of order book entries to return + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols. + public async Task FetchOrderBook(string symbol, Int64? limit2 = 0, Dictionary parameters = null) + { + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchOrderBook(symbol, limit, parameters); + return new OrderBook(res); + } + /// + /// query for balance and get the amount of funds available for trading or funds locked in orders + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}. + public async Task FetchBalance(Dictionary parameters = null) + { + var res = await this.fetchBalance(parameters); + return new Balances(res); + } + /// + /// fetch all trades made by the user + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : the earliest time in ms to fetch trades for + /// + /// + /// + /// limit + /// + /// int : the maximum number of trades structures to retrieve + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// Trade[] a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}. + public async Task> FetchMyTrades(string symbol = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchMyTrades(symbol, since, limit, parameters); + return ((IList)res).Select(item => new Trade(item)).ToList(); + } + /// + /// fetches information on an order made by the user + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}. + public async Task FetchOrder(string id, string symbol = null, Dictionary parameters = null) + { + var res = await this.fetchOrder(id, symbol, parameters); + return new Order(res); + } + /// + /// fetch all unfilled currently open orders + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : the earliest time in ms to fetch open orders for + /// + /// + /// + /// limit + /// + /// int : the maximum number of open orders structures to retrieve + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// Order[] a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}. + public async Task> FetchOpenOrders(string symbol = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchOpenOrders(symbol, since, limit, parameters); + return ((IList)res).Select(item => new Order(item)).ToList(); + } + /// + /// fetches information on multiple closed orders made by the user + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : the earliest time in ms to fetch orders for + /// + /// + /// + /// limit + /// + /// int : the maximum number of order structures to retrieve + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// Order[] a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}. + public async Task> FetchClosedOrders(string symbol = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchClosedOrders(symbol, since, limit, parameters); + return ((IList)res).Select(item => new Order(item)).ToList(); + } + public async Task> FetchOrdersHelper(string symbol = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchOrdersHelper(symbol, since, limit, parameters); + return ((Dictionary)res); + } + /// + /// create a trade order, https://docs.geniusyield.io/#create-order + /// + /// + /// See
+ /// + /// + /// price + /// + /// float : the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + /// params.test + /// + /// bool : set to true to test an order, no order will be created but the request will be validated + /// + /// + /// + ///
+ /// object an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}. + public async Task CreateOrder(string symbol, string type, string side, double amount, double? price2 = 0, Dictionary parameters = null) + { + var price = price2 == 0 ? null : (object)price2; + var res = await this.createOrder(symbol, type, side, amount, price, parameters); + return new Order(res); + } + /// + /// make a withdrawal + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}. + public async Task Withdraw(string code, double amount, string address, object tag = null, Dictionary parameters = null) + { + var res = await this.withdraw(code, amount, address, tag, parameters); + return new Transaction(res); + } + /// + /// cancel all open orders + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object[] a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}. + public async Task> CancelAllOrders(string symbol = null, Dictionary parameters = null) + { + var res = await this.cancelAllOrders(symbol, parameters); + return ((IList)res).Select(item => new Order(item)).ToList(); + } + /// + /// cancels an open order + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}. + public async Task CancelOrder(string id, string symbol = null, Dictionary parameters = null) + { + var res = await this.cancelOrder(id, symbol, parameters); + return new Order(res); + } + /// + /// fetch information on a deposit + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}. + public async Task FetchDeposit(string id, string code = null, Dictionary parameters = null) + { + var res = await this.fetchDeposit(id, code, parameters); + return new Transaction(res); + } + /// + /// fetch all deposits made to an account + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : the earliest time in ms to fetch deposits for + /// + /// + /// + /// limit + /// + /// int : the maximum number of deposits structures to retrieve + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object[] a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}. + public async Task> FetchDeposits(string code = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchDeposits(code, since, limit, parameters); + return ((IList)res).Select(item => new Transaction(item)).ToList(); + } + /// + /// the latest known information on the availability of the exchange API + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure}. + public async Task> FetchStatus(Dictionary parameters = null) + { + var res = await this.fetchStatus(parameters); + return ((Dictionary)res); + } + /// + /// fetches the current integer timestamp in milliseconds from the exchange server + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// int the current integer timestamp in milliseconds from the exchange server. + public async Task FetchTime(Dictionary parameters = null) + { + var res = await this.fetchTime(parameters); + return (Int64)res; + } + /// + /// fetch data on a currency withdrawal via the withdrawal id + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}. + public async Task FetchWithdrawal(string id, string code = null, Dictionary parameters = null) + { + var res = await this.fetchWithdrawal(id, code, parameters); + return new Transaction(res); + } + /// + /// fetch all withdrawals made from an account + /// + /// + /// See
+ /// + /// + /// since + /// + /// int : the earliest time in ms to fetch withdrawals for + /// + /// + /// + /// limit + /// + /// int : the maximum number of withdrawals structures to retrieve + /// + /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object[] a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}. + public async Task> FetchWithdrawals(string code = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchWithdrawals(code, since, limit, parameters); + return ((IList)res).Select(item => new Transaction(item)).ToList(); + } + public async Task> FetchTransactionsHelper(string code = null, Int64? since2 = 0, Int64? limit2 = 0, Dictionary parameters = null) + { + var since = since2 == 0 ? null : (object)since2; + var limit = limit2 == 0 ? null : (object)limit2; + var res = await this.fetchTransactionsHelper(code, since, limit, parameters); + return ((IList)res).Select(item => new Transaction(item)).ToList(); + } + /// + /// fetch the Polygon address of the wallet + /// + /// + /// See
+ /// + /// + /// params + /// + /// object : extra parameters specific to the exchange API endpoint + /// + /// + /// + ///
+ /// object an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}. + public async Task> FetchDepositAddress(string code = null, Dictionary parameters = null) + { + var res = await this.fetchDepositAddress(code, parameters); + return ((Dictionary)res); + } +} diff --git a/dist/cjs/src/abstract/geniusyield.js b/dist/cjs/src/abstract/geniusyield.js new file mode 100644 index 000000000000..7e4804edc24a --- /dev/null +++ b/dist/cjs/src/abstract/geniusyield.js @@ -0,0 +1,9 @@ +'use strict'; + +var Exchange$1 = require('../base/Exchange.js'); + +// ------------------------------------------------------------------------------- +class Exchange extends Exchange$1["default"] { +} + +module.exports = Exchange; diff --git a/dist/cjs/src/geniusyield.js b/dist/cjs/src/geniusyield.js new file mode 100644 index 000000000000..93cdcfd1cf1b --- /dev/null +++ b/dist/cjs/src/geniusyield.js @@ -0,0 +1,1884 @@ +'use strict'; + +var geniusyield$1 = require('./abstract/geniusyield.js'); +var number = require('./base/functions/number.js'); +var errors = require('./base/errors.js'); +var Precise = require('./base/Precise.js'); +var sha256 = require('./static_dependencies/noble-hashes/sha256.js'); +var sha3 = require('./static_dependencies/noble-hashes/sha3.js'); +var secp256k1 = require('./static_dependencies/noble-curves/secp256k1.js'); +var crypto = require('./base/functions/crypto.js'); + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +/** + * @class geniusyield + * @augments Exchange + */ +class geniusyield extends geniusyield$1 { + describe() { + return this.deepExtend(super.describe(), { + 'id': 'geniusyield', + 'name': 'Genius Yield', + 'countries': ['CH'], + 'rateLimit': 1000, + 'version': 'v0', + 'pro': false, + 'dex': true, + 'certified': false, + 'requiresWeb3': true, + 'has': { + 'CORS': undefined, + 'spot': true, + 'margin': false, + 'swap': false, + 'future': false, + 'option': false, + 'addMargin': false, + 'cancelAllOrders': true, + 'cancelOrder': true, + 'cancelOrders': false, + 'closeAllPositions': false, + 'closePosition': false, + 'createDepositAddress': false, + 'createOrder': true, + 'createReduceOnlyOrder': false, + 'createStopLimitOrder': true, + 'createStopMarketOrder': true, + 'createStopOrder': true, + 'fetchBalance': true, + 'fetchBorrowRateHistories': false, + 'fetchBorrowRateHistory': false, + 'fetchClosedOrders': true, + 'fetchCrossBorrowRate': false, + 'fetchCrossBorrowRates': false, + 'fetchCurrencies': true, + 'fetchDeposit': true, + 'fetchDepositAddress': true, + 'fetchDepositAddresses': false, + 'fetchDepositAddressesByNetwork': false, + 'fetchDeposits': true, + 'fetchFundingHistory': false, + 'fetchFundingRate': false, + 'fetchFundingRateHistory': false, + 'fetchFundingRates': false, + 'fetchIndexOHLCV': false, + 'fetchIsolatedBorrowRate': false, + 'fetchIsolatedBorrowRates': false, + 'fetchLeverage': false, + 'fetchLeverageTiers': false, + 'fetchMarginMode': false, + 'fetchMarkets': true, + 'fetchMarkOHLCV': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenInterestHistory': false, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrders': false, + 'fetchPosition': false, + 'fetchPositionHistory': false, + 'fetchPositionMode': false, + 'fetchPositions': false, + 'fetchPositionsForSymbol': false, + 'fetchPositionsHistory': false, + 'fetchPositionsRisk': false, + 'fetchPremiumIndexOHLCV': false, + 'fetchStatus': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': true, + 'fetchTransactions': false, + 'fetchWithdrawal': true, + 'fetchWithdrawals': true, + 'reduceMargin': false, + 'sandbox': true, + 'setLeverage': false, + 'setMarginMode': false, + 'setPositionMode': false, + 'transfer': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'MATIC': 'https://api-sandbox-matic.geniusyield.io', + }, + 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api': { + 'MATIC': 'https://api-matic.geniusyield.io', + }, + 'www': 'https://geniusyield.io', + 'doc': [ + 'https://api-docs-v3.geniusyield.io/', + ], + }, + 'api': { + 'public': { + 'get': { + 'ping': 1, + 'time': 1, + 'exchange': 1, + 'assets': 1, + 'markets': 1, + 'tickers': 1, + 'candles': 1, + 'trades': 1, + 'orderbook': 1, + }, + }, + 'private': { + 'get': { + 'user': 1, + 'wallets': 1, + 'balances': 1, + 'orders': 0.1, + 'fills': 0.1, + 'deposits': 1, + 'withdrawals': 1, + 'wsToken': 1, + }, + 'post': { + 'wallets': 1, + 'orders': 0.1, + 'orders/test': 0.1, + 'withdrawals': 1, + }, + 'delete': { + 'orders': 0.1, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'gtc', + 'defaultSelfTradePrevention': 'cn', + 'network': 'MATIC', + }, + 'exceptions': { + 'exact': { + 'INVALID_ORDER_QUANTITY': errors.InvalidOrder, + 'INSUFFICIENT_FUNDS': errors.InsufficientFunds, + 'SERVICE_UNAVAILABLE': errors.ExchangeNotAvailable, + 'EXCEEDED_RATE_LIMIT': errors.DDoSProtection, + 'INVALID_PARAMETER': errors.BadRequest, + 'WALLET_NOT_ASSOCIATED': errors.InvalidAddress, + 'INVALID_WALLET_SIGNATURE': errors.AuthenticationError, + }, + }, + 'requiredCredentials': { + 'walletAddress': true, + 'privateKey': true, + 'apiKey': true, + 'secret': true, + }, + 'precisionMode': number.TICK_SIZE, + 'paddingMode': number.PAD_WITH_ZERO, + 'commonCurrencies': {}, + }); + } + priceToPrecision(symbol, price) { + // + // we override priceToPrecision to fix the following issue + // https://github.com/ccxt/ccxt/issues/13367 + // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} + // + const market = this.market(symbol); + const info = this.safeValue(market, 'info', {}); + const quoteAssetPrecision = this.safeInteger(info, 'quoteAssetPrecision'); + price = this.decimalToPrecision(price, number.ROUND, market['precision']['price'], this.precisionMode); + return this.decimalToPrecision(price, number.TRUNCATE, quoteAssetPrecision, number.DECIMAL_PLACES, number.PAD_WITH_ZERO); + } + async fetchMarkets(params = {}) { + /** + * @method + * @name geniusyield#fetchMarkets + * @description retrieves data on all markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-markets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} an array of objects representing market data + */ + const response = await this.publicGetMarkets(params); + // + // [ + // { + // "market": "ETH-USDC", + // "type": "hybrid", + // "status": "activeHybrid", + // "baseAsset": "ETH", + // "baseAssetPrecision": "8", + // "quoteAsset": "USDC", + // "quoteAssetPrecision": "8", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.2500", + // "takerIdexFeeRate": "0.0500", + // "takerLiquidityProviderFeeRate": "0.2000", + // "tickSize": "0.01000000" + // }, + // ] + // + const response2 = await this.publicGetExchange(); + // + // { + // "timeZone": "UTC", + // "serverTime": "1654460599952", + // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + // "maticUsdPrice": "0.60", + // "gasPrice": "180", + // "volume24hUsd": "10015814.46", + // "totalVolumeUsd": "1589273533.28", + // "totalTrades": "1534904", + // "totalValueLockedUsd": "12041929.44", + // "geniusyieldStakingValueLockedUsd": "20133816.98", + // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", + // "geniusyieldUsdPrice": "0.07", + // "geniusyieldMarketCapUsd": "48012346.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020", + // "makerTradeMinimum": "10.00000000", + // "takerTradeMinimum": "1.00000000", + // "withdrawMinimum": "0.50000000", + // "liquidityAdditionMinimum": "0.50000000", + // "liquidityRemovalMinimum": "0.40000000", + // "blockConfirmationDelay": "64" + // } + // + const maker = this.safeNumber(response2, 'makerFeeRate'); + const taker = this.safeNumber(response2, 'takerFeeRate'); + const makerMin = this.safeString(response2, 'makerTradeMinimum'); + const takerMin = this.safeString(response2, 'takerTradeMinimum'); + const minCostETH = this.parseNumber(Precise["default"].stringMin(makerMin, takerMin)); + const result = []; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const marketId = this.safeString(entry, 'market'); + const baseId = this.safeString(entry, 'baseAsset'); + const quoteId = this.safeString(entry, 'quoteAsset'); + const base = this.safeCurrencyCode(baseId); + const quote = this.safeCurrencyCode(quoteId); + const basePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'baseAssetPrecision'))); + const quotePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'quoteAssetPrecision'))); + const status = this.safeString(entry, 'status'); + let minCost = undefined; + if (quote === 'ETH') { + minCost = minCostETH; + } + result.push({ + 'id': marketId, + 'symbol': base + '/' + quote, + 'base': base, + 'quote': quote, + 'settle': undefined, + 'baseId': baseId, + 'quoteId': quoteId, + 'settleId': undefined, + 'type': 'spot', + 'spot': true, + 'margin': false, + 'swap': false, + 'future': false, + 'option': false, + 'active': (status !== 'inactive'), + 'contract': false, + 'linear': undefined, + 'inverse': undefined, + 'taker': taker, + 'maker': maker, + 'contractSize': undefined, + 'expiry': undefined, + 'expiryDatetime': undefined, + 'strike': undefined, + 'optionType': undefined, + 'precision': { + 'amount': basePrecision, + 'price': this.safeNumber(entry, 'tickSize'), + }, + 'limits': { + 'leverage': { + 'min': undefined, + 'max': undefined, + }, + 'amount': { + 'min': basePrecision, + 'max': undefined, + }, + 'price': { + 'min': quotePrecision, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'created': undefined, + 'info': entry, + }); + } + return result; + } + async fetchTicker(symbol, params = {}) { + /** + * @method + * @name geniusyield#fetchTicker + * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string} symbol unified symbol of the market to fetch the ticker for + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + }; + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + // ] + const response = await this.publicGetTickers(this.extend(request, params)); + const ticker = this.safeDict(response, 0); + return this.parseTicker(ticker, market); + } + async fetchTickers(symbols = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchTickers + * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} + */ + await this.loadMarkets(); + // [ + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // }, ... + // ] + const response = await this.publicGetTickers(params); + return this.parseTickers(response, symbols); + } + parseTicker(ticker, market = undefined) { + // { + // "market": "DIL-ETH", + // "time": 1598367493008, + // "open": "0.09695361", + // "high": "0.10245881", + // "low": "0.09572507", + // "close": "0.09917079", + // "closeQuantity": "0.71320950", + // "baseVolume": "309.17380612", + // "quoteVolume": "30.57633981", + // "percentChange": "2.28", + // "numTrades": 205, + // "ask": "0.09910476", + // "bid": "0.09688340", + // "sequence": 3902 + // } + const marketId = this.safeString(ticker, 'market'); + market = this.safeMarket(marketId, market, '-'); + const symbol = market['symbol']; + const timestamp = this.safeInteger(ticker, 'time'); + const close = this.safeString(ticker, 'close'); + return this.safeTicker({ + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'high': this.safeString(ticker, 'high'), + 'low': this.safeString(ticker, 'low'), + 'bid': this.safeString(ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeString(ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeString(ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeString(ticker, 'percentChange'), + 'average': undefined, + 'baseVolume': this.safeString(ticker, 'baseVolume'), + 'quoteVolume': this.safeString(ticker, 'quoteVolume'), + 'info': ticker, + }, market); + } + async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOHLCV + * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + * @see https://api-docs-v3.geniusyield.io/#get-candles + * @param {string} symbol unified symbol of the market to fetch OHLCV data for + * @param {string} timeframe the length of time each candle represents + * @param {int} [since] timestamp in ms of the earliest candle to fetch + * @param {int} [limit] the maximum amount of candles to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + 'interval': timeframe, + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = Math.min(limit, 1000); + } + const response = await this.publicGetCandles(this.extend(request, params)); + if (Array.isArray(response)) { + // [ + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // }, ... + // ] + return this.parseOHLCVs(response, market, timeframe, since, limit); + } + else { + // {"nextTime":1595536440000} + return []; + } + } + parseOHLCV(ohlcv, market = undefined) { + // { + // "start": 1598345580000, + // "open": "0.09771286", + // "high": "0.09771286", + // "low": "0.09771286", + // "close": "0.09771286", + // "volume": "1.45340410", + // "sequence": 3853 + // } + const timestamp = this.safeInteger(ohlcv, 'start'); + const open = this.safeNumber(ohlcv, 'open'); + const high = this.safeNumber(ohlcv, 'high'); + const low = this.safeNumber(ohlcv, 'low'); + const close = this.safeNumber(ohlcv, 'close'); + const volume = this.safeNumber(ohlcv, 'volume'); + return [timestamp, open, high, low, close, volume]; + } + async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchTrades + * @description get the list of most recent trades for a particular symbol + * @see https://api-docs-v3.geniusyield.io/#get-trades + * @param {string} symbol unified symbol of the market to fetch trades for + * @param {int} [since] timestamp in ms of the earliest trade to fetch + * @param {int} [limit] the maximum amount of trades to fetch + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = Math.min(limit, 1000); + } + // [ + // { + // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", + // "price": "0.09771286", + // "quantity": "1.45340410", + // "quoteQuantity": "0.14201627", + // "time": 1598345638994, + // "makerSide": "buy", + // "sequence": 3853 + // }, ... + // ] + const response = await this.publicGetTrades(this.extend(request, params)); + return this.parseTrades(response, market, since, limit); + } + parseTrade(trade, market = undefined) { + // + // public trades + // { + // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + // "price":"0.20377008", + // "quantity":"47.58448728", + // "quoteQuantity":"9.69629509", + // "time":1642091300873, + // "makerSide":"buy", + // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" + // "sequence":31876 + // } + // + // private trades + // { + // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + // "price":"0.20717368", + // "quantity":"15.00000000", + // "quoteQuantity":"3.10760523", + // "orderBookQuantity":"0.00000003", + // "orderBookQuoteQuantity":"0.00000001", + // "poolQuantity":"14.99999997", + // "poolQuoteQuantity":"3.10760522", + // "time":1642083351215, + // "makerSide":"sell", + // "sequence":31795, + // "market":"Genius Yield-USDC", + // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + // "side":"buy", + // "fee":"0.03749989", + // "feeAsset":"Genius Yield", + // "gas":"0.40507261", + // "liquidity":"taker", + // "type":"hybrid", + // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + // "txStatus":"mined" + // } + // + const id = this.safeString(trade, 'fillId'); + const priceString = this.safeString(trade, 'price'); + const amountString = this.safeString(trade, 'quantity'); + const costString = this.safeString(trade, 'quoteQuantity'); + const timestamp = this.safeInteger(trade, 'time'); + const marketId = this.safeString(trade, 'market'); + const symbol = this.safeSymbol(marketId, market, '-'); + // this code handles the duality of public vs private trades + const makerSide = this.safeString(trade, 'makerSide'); + const oppositeSide = (makerSide === 'buy') ? 'sell' : 'buy'; + const side = this.safeString(trade, 'side', oppositeSide); + const takerOrMaker = this.safeString(trade, 'liquidity', 'taker'); + const feeCostString = this.safeString(trade, 'fee'); + let fee = undefined; + if (feeCostString !== undefined) { + const feeCurrencyId = this.safeString(trade, 'feeAsset'); + fee = { + 'cost': feeCostString, + 'currency': this.safeCurrencyCode(feeCurrencyId), + }; + } + const orderId = this.safeString(trade, 'orderId'); + return this.safeTrade({ + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': priceString, + 'amount': amountString, + 'cost': costString, + 'fee': fee, + }, market); + } + async fetchTradingFees(params = {}) { + /** + * @method + * @name geniusyield#fetchTradingFees + * @description fetch the trading fees for multiple markets + * @see https://api-docs-v3.geniusyield.io/#get-api-account + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + }; + let response = undefined; + response = await this.privateGetUser(this.extend(request, params)); + // + // { + // "depositEnabled": true, + // "orderEnabled": true, + // "cancelEnabled": true, + // "withdrawEnabled": true, + // "totalPortfolioValueUsd": "0.00", + // "makerFeeRate": "0.0000", + // "takerFeeRate": "0.0025", + // "takerIdexFeeRate": "0.0005", + // "takerLiquidityProviderFeeRate": "0.0020" + // } + // + const maker = this.safeNumber(response, 'makerFeeRate'); + const taker = this.safeNumber(response, 'takerFeeRate'); + const result = {}; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + result[symbol] = { + 'info': response, + 'symbol': symbol, + 'maker': maker, + 'taker': taker, + 'percentage': true, + 'tierBased': false, + }; + } + return result; + } + async fetchOrderBook(symbol, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOrderBook + * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + * @see https://api-docs-v3.geniusyield.io/#get-order-books + * @param {string} symbol unified symbol of the market to fetch the order book for + * @param {int} [limit] the maximum amount of order book entries to return + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols + */ + await this.loadMarkets(); + const market = this.market(symbol); + const request = { + 'market': market['id'], + 'level': 2, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + // { + // "sequence": 36416753, + // "bids": [ + // [ '0.09672815', "8.22284267", 1 ], + // [ '0.09672814', "1.83685554", 1 ], + // [ '0.09672143', "4.10962617", 1 ], + // [ '0.09658884', "4.03863759", 1 ], + // [ '0.09653781', "3.35730684", 1 ], + // [ '0.09624660', "2.54163586", 1 ], + // [ '0.09617490', "1.93065030", 1 ] + // ], + // "asks": [ + // [ '0.09910476', "3.22840154", 1 ], + // [ '0.09940587', "3.39796593", 1 ], + // [ '0.09948189', "4.25088898", 1 ], + // [ '0.09958362', "2.42195784", 1 ], + // [ '0.09974393', "4.25234367", 1 ], + // [ '0.09995250', "3.40192141", 1 ] + // ] + // } + const response = await this.publicGetOrderbook(this.extend(request, params)); + const nonce = this.safeInteger(response, 'sequence'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'nonce': nonce, + 'bids': this.parseSide(response, 'bids'), + 'asks': this.parseSide(response, 'asks'), + }; + } + parseSide(book, side) { + const bookSide = this.safeValue(book, side, []); + const result = []; + for (let i = 0; i < bookSide.length; i++) { + const order = bookSide[i]; + const price = this.safeNumber(order, 0); + const amount = this.safeNumber(order, 1); + const orderCount = this.safeInteger(order, 2); + result.push([price, amount, orderCount]); + } + const descending = side === 'bids'; + return this.sortBy(result, 0, descending); + } + async fetchCurrencies(params = {}) { + /** + * @method + * @name geniusyield#fetchCurrencies + * @description fetches all available currencies on an exchange + * @see https://api-docs-v3.geniusyield.io/#get-assets + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an associative dictionary of currencies + */ + const response = await this.publicGetAssets(params); + // + // [ + // { + // "name": "Ethereum", + // "symbol": "ETH", + // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + // "assetDecimals": "18", + // "exchangeDecimals": "8", + // "maticPrice": "3029.38503483" + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const name = this.safeString(entry, 'name'); + const currencyId = this.safeString(entry, 'symbol'); + const code = this.safeCurrencyCode(currencyId); + const precision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'exchangeDecimals'))); + result[code] = { + 'id': currencyId, + 'code': code, + 'info': entry, + 'type': undefined, + 'name': name, + 'active': undefined, + 'deposit': undefined, + 'withdraw': undefined, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { 'min': precision, 'max': undefined }, + 'withdraw': { 'min': precision, 'max': undefined }, + }, + }; + } + return result; + } + parseBalance(response) { + const result = { + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + }; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const currencyId = this.safeString(entry, 'asset'); + const code = this.safeCurrencyCode(currencyId); + const account = this.account(); + account['total'] = this.safeString(entry, 'quantity'); + account['free'] = this.safeString(entry, 'availableForTrade'); + account['used'] = this.safeString(entry, 'locked'); + result[code] = account; + } + return this.safeBalance(result); + } + async fetchBalance(params = {}) { + /** + * @method + * @name geniusyield#fetchBalance + * @description query for balance and get the amount of funds available for trading or funds locked in orders + * @see https://api-docs-v3.geniusyield.io/#get-balances + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + const nonce1 = this.uuidv1(); + const request = { + 'nonce': nonce1, + 'wallet': this.walletAddress, + }; + // [ + // { + // "asset": "DIL", + // "quantity": "0.00000000", + // "availableForTrade": "0.00000000", + // "locked": "0.00000000", + // "usdValue": null + // }, ... + // ] + const extendedRequest = this.extend(request, params); + if (extendedRequest['wallet'] === undefined) { + throw new errors.BadRequest(this.id + ' fetchBalance() wallet is undefined, set this.walletAddress or "address" in params'); + } + let response = undefined; + try { + response = await this.privateGetBalances(extendedRequest); + } + catch (e) { + if (e instanceof errors.InvalidAddress) { + const walletAddress = extendedRequest['wallet']; + await this.associateWallet(walletAddress); + response = await this.privateGetBalances(extendedRequest); + } + else { + throw e; + } + } + return this.parseBalance(response); + } + async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchMyTrades + * @description fetch all trades made by the user + * @see https://api-docs-v3.geniusyield.io/#get-fills + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch trades for + * @param {int} [limit] the maximum number of trades structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + let market = undefined; + const request = { + 'nonce': this.uuidv1(), + 'wallet': this.walletAddress, + }; + if (symbol !== undefined) { + market = this.market(symbol); + request['market'] = market['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + // [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478762, + // "makerSide": "sell", + // "sequence": 5053, + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "side": "buy", + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + const extendedRequest = this.extend(request, params); + if (extendedRequest['wallet'] === undefined) { + throw new errors.BadRequest(this.id + ' fetchMyTrades() walletAddress is undefined, set this.walletAddress or "address" in params'); + } + let response = undefined; + try { + response = await this.privateGetFills(extendedRequest); + } + catch (e) { + if (e instanceof errors.InvalidAddress) { + const walletAddress = extendedRequest['wallet']; + await this.associateWallet(walletAddress); + response = await this.privateGetFills(extendedRequest); + } + else { + throw e; + } + } + return this.parseTrades(response, market, since, limit); + } + async fetchOrder(id, symbol = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOrder + * @description fetches information on an order made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request = { + 'orderId': id, + }; + return await this.fetchOrdersHelper(symbol, undefined, undefined, this.extend(request, params)); + } + async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchOpenOrders + * @description fetch all unfilled currently open orders + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol + * @param {int} [since] the earliest time in ms to fetch open orders for + * @param {int} [limit] the maximum number of open orders structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request = { + 'closed': false, + }; + return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); + } + async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchClosedOrders + * @description fetches information on multiple closed orders made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} symbol unified market symbol of the market orders were made in + * @param {int} [since] the earliest time in ms to fetch orders for + * @param {int} [limit] the maximum number of order structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + const request = { + 'closed': true, + }; + return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); + } + async fetchOrdersHelper(symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets(); + const request = { + 'nonce': this.uuidv1(), + 'wallet': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market(symbol); + request['market'] = market['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders(this.extend(request, params)); + // fetchClosedOrders / fetchOpenOrders + // [ + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // ] + // fetchOrder + // { market: "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": + // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" } ] } + if (Array.isArray(response)) { + return this.parseOrders(response, market, since, limit); + } + else { + return this.parseOrder(response, market); + } + } + parseOrderStatus(status) { + // https://docs.geniusyield.io/#order-states-amp-lifecycle + const statuses = { + 'active': 'open', + 'partiallyFilled': 'open', + 'rejected': 'canceled', + 'filled': 'closed', + }; + return this.safeString(statuses, status, status); + } + parseOrder(order, market = undefined) { + // + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "avgExecutionPrice": "0.09905990", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus": "mined" + // } + // ] + // } + // + const timestamp = this.safeInteger(order, 'time'); + const fills = this.safeValue(order, 'fills', []); + const id = this.safeString(order, 'orderId'); + const clientOrderId = this.safeString(order, 'clientOrderId'); + const marketId = this.safeString(order, 'market'); + const side = this.safeString(order, 'side'); + const symbol = this.safeSymbol(marketId, market, '-'); + const type = this.safeString(order, 'type'); + const amount = this.safeString(order, 'originalQuantity'); + const filled = this.safeString(order, 'executedQuantity'); + const average = this.safeString(order, 'avgExecutionPrice'); + const price = this.safeString(order, 'price'); + const rawStatus = this.safeString(order, 'status'); + const timeInForce = this.safeStringUpper(order, 'timeInForce'); + const status = this.parseOrderStatus(rawStatus); + return this.safeOrder({ + 'info': order, + 'id': id, + 'clientOrderId': clientOrderId, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'timeInForce': timeInForce, + 'postOnly': undefined, + 'side': side, + 'price': price, + 'stopPrice': undefined, + 'triggerPrice': undefined, + 'amount': amount, + 'cost': undefined, + 'average': average, + 'filled': filled, + 'remaining': undefined, + 'status': status, + 'fee': undefined, + 'trades': fills, + }, market); + } + async associateWallet(walletAddress, params = {}) { + const nonce = this.uuidv1(); + const noPrefix = this.remove0xPrefix(walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(noPrefix), + ]; + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, sha3.keccak_256, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + // { + // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "totalPortfolioValueUsd": "0.00", + // "time": 1598468353626 + // } + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': walletAddress, + }, + 'signature': signature, + }; + const result = await this.privatePostWallets(request); + return result; + } + async createOrder(symbol, type, side, amount, price = undefined, params = {}) { + /** + * @method + * @name geniusyield#createOrder + * @description create a trade order, https://docs.geniusyield.io/#create-order + * @see https://api-docs-v3.geniusyield.io/#create-order + * @param {string} symbol unified symbol of the market to create an order in + * @param {string} type 'market' or 'limit' + * @param {string} side 'buy' or 'sell' + * @param {float} amount how much of currency you want to trade in units of base currency + * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated + * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + const testOrder = this.safeBool(params, 'test', false); + params = this.omit(params, 'test'); + const market = this.market(symbol); + const nonce = this.uuidv1(); + let typeEnum = undefined; + const stopLossTypeEnums = { + 'stopLoss': 3, + 'stopLossLimit': 4, + 'takeProfit': 5, + 'takeProfitLimit': 6, + }; + let stopPriceString = undefined; + if ((type === 'stopLossLimit') || (type === 'takeProfitLimit') || ('stopPrice' in params)) { + if (!('stopPrice' in params)) { + throw new errors.BadRequest(this.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders'); + } + stopPriceString = this.priceToPrecision(symbol, params['stopPrice']); + } + const limitTypeEnums = { + 'limit': 1, + 'limitMaker': 2, + }; + let priceString = undefined; + const typeLower = type.toLowerCase(); + const limitOrder = typeLower.indexOf('limit') >= 0; + if (type in limitTypeEnums) { + typeEnum = limitTypeEnums[type]; + priceString = this.priceToPrecision(symbol, price); + } + else if (type in stopLossTypeEnums) { + typeEnum = stopLossTypeEnums[type]; + priceString = this.priceToPrecision(symbol, price); + } + else if (type === 'market') { + typeEnum = 0; + } + else { + throw new errors.BadRequest(this.id + ' ' + type + ' is not a valid order type'); + } + let amountEnum = 0; // base quantity + if ('quoteOrderQuantity' in params) { + if (type !== 'market') { + throw new errors.NotSupported(this.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders'); + } + amountEnum = 1; + amount = this.safeNumber(params, 'quoteOrderQuantity'); + } + const sideEnum = (side === 'buy') ? 0 : 1; + const walletBytes = this.remove0xPrefix(this.walletAddress); + const network = this.safeString(this.options, 'network', 'ETH'); + const orderVersion = this.getSupportedMapping(network, { + 'ETH': 1, + 'BSC': 2, + 'MATIC': 4, + }); + const amountString = this.amountToPrecision(symbol, amount); + // https://docs.geniusyield.io/#time-in-force + const timeInForceEnums = { + 'gtc': 0, + 'ioc': 2, + 'fok': 3, + }; + const defaultTimeInForce = this.safeString(this.options, 'defaultTimeInForce', 'gtc'); + const timeInForce = this.safeString(params, 'timeInForce', defaultTimeInForce); + let timeInForceEnum = undefined; + if (timeInForce in timeInForceEnums) { + timeInForceEnum = timeInForceEnums[timeInForce]; + } + else { + const allOptions = Object.keys(timeInForceEnums); + const asString = allOptions.join(', '); + throw new errors.BadRequest(this.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString); + } + // https://docs.geniusyield.io/#self-trade-prevention + const selfTradePreventionEnums = { + 'dc': 0, + 'co': 1, + 'cn': 2, + 'cb': 3, + }; + const defaultSelfTradePrevention = this.safeString(this.options, 'defaultSelfTradePrevention', 'cn'); + const selfTradePrevention = this.safeString(params, 'selfTradePrevention', defaultSelfTradePrevention); + let selfTradePreventionEnum = undefined; + if (selfTradePrevention in selfTradePreventionEnums) { + selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]; + } + else { + const allOptions = Object.keys(selfTradePreventionEnums); + const asString = allOptions.join(', '); + throw new errors.BadRequest(this.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString); + } + const byteArray = [ + this.numberToBE(orderVersion, 1), + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + this.encode(market['id']), + this.numberToBE(typeEnum, 1), + this.numberToBE(sideEnum, 1), + this.encode(amountString), + this.numberToBE(amountEnum, 1), + ]; + if (limitOrder) { + const encodedPrice = this.encode(priceString); + byteArray.push(encodedPrice); + } + if (type in stopLossTypeEnums) { + const encodedPrice = this.encode(stopPriceString || priceString); + byteArray.push(encodedPrice); + } + const clientOrderId = this.safeString(params, 'clientOrderId'); + if (clientOrderId !== undefined) { + byteArray.push(this.encode(clientOrderId)); + } + const after = [ + this.numberToBE(timeInForceEnum, 1), + this.numberToBE(selfTradePreventionEnum, 1), + this.numberToBE(0, 8), // unused + ]; + const allBytes = this.arrayConcat(byteArray, after); + const binary = this.binaryConcatArray(allBytes); + const hash = this.hash(binary, sha3.keccak_256, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + const request = { + 'parameters': { + 'nonce': nonce, + 'market': market['id'], + 'side': side, + 'type': type, + 'wallet': this.walletAddress, + 'selfTradePrevention': selfTradePrevention, + }, + 'signature': signature, + }; + if (type !== 'market') { + request['parameters']['timeInForce'] = timeInForce; + } + if (limitOrder) { + request['parameters']['price'] = priceString; + } + if (type in stopLossTypeEnums) { + request['parameters']['stopPrice'] = stopPriceString || priceString; + } + if (amountEnum === 0) { + request['parameters']['quantity'] = amountString; + } + else { + request['parameters']['quoteOrderQuantity'] = amountString; + } + if (clientOrderId !== undefined) { + request['parameters']['clientOrderId'] = clientOrderId; + } + // { + // "market": "DIL-ETH", + // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time": 1598873478650, + // "status": "filled", + // "type": "limit", + // "side": "buy", + // "originalQuantity": "0.40000000", + // "executedQuantity": "0.40000000", + // "cumulativeQuoteQuantity": "0.03962396", + // "price": "1.00000000", + // "fills": [ + // { + // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price": "0.09905990", + // "quantity": "0.40000000", + // "quoteQuantity": "0.03962396", + // "time": 1598873478650, + // "makerSide": "sell", + // "sequence": 5053, + // "fee": "0.00080000", + // "feeAsset": "DIL", + // "gas": "0.00857497", + // "liquidity": "taker", + // "txStatus": "pending" + // } + // ], + // "avgExecutionPrice": "0.09905990" + // } + // we don't use extend here because it is a signed endpoint + let response = undefined; + if (testOrder) { + response = await this.privatePostOrdersTest(request); + } + else { + response = await this.privatePostOrders(request); + } + return this.parseOrder(response, market); + } + async withdraw(code, amount, address, tag = undefined, params = {}) { + /** + * @method + * @name geniusyield#withdraw + * @description make a withdrawal + * @see https://api-docs-v3.geniusyield.io/#withdraw-funds + * @param {string} code unified currency code + * @param {float} amount the amount to withdraw + * @param {string} address the address to withdraw to + * @param {string} tag + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + [tag, params] = this.handleWithdrawTagAndParams(tag, params); + this.checkRequiredCredentials(); + await this.loadMarkets(); + const nonce = this.uuidv1(); + const amountString = this.currencyToPrecision(code, amount); + const currency = this.currency(code); + const walletBytes = this.remove0xPrefix(this.walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + this.encode(currency['id']), + this.encode(amountString), + this.numberToBE(1, 1), // bool set to true + ]; + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, sha3.keccak_256, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': address, + 'asset': currency['id'], + 'quantity': amountString, + }, + 'signature': signature, + }; + const response = await this.privatePostWithdrawals(request); + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + return this.parseTransaction(response, currency); + } + async cancelAllOrders(symbol = undefined, params = {}) { + /** + * @method + * @name geniusyield#cancelAllOrders + * @description cancel all open orders + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + let market = undefined; + if (symbol !== undefined) { + market = this.market(symbol); + } + const nonce = this.uuidv1(); + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': this.walletAddress, + }, + }; + const walletBytes = this.remove0xPrefix(this.walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + ]; + if (market !== undefined) { + byteArray.push(this.encode(market['id'])); + request['parameters']['market'] = market['id']; + } + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, sha3.keccak_256, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + request['signature'] = signature; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + const response = await this.privateDeleteOrders(this.extend(request, params)); + return this.parseOrders(response, market); + } + async cancelOrder(id, symbol = undefined, params = {}) { + /** + * @method + * @name geniusyield#cancelOrder + * @description cancels an open order + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} id order id + * @param {string} symbol unified symbol of the market the order was made in + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} + */ + this.checkRequiredCredentials(); + await this.loadMarkets(); + let market = undefined; + if (symbol !== undefined) { + market = this.market(symbol); + } + const nonce = this.uuidv1(); + const walletBytes = this.remove0xPrefix(this.walletAddress); + const byteArray = [ + this.base16ToBinary(nonce), + this.base16ToBinary(walletBytes), + this.encode(id), + ]; + const binary = this.binaryConcatArray(byteArray); + const hash = this.hash(binary, sha3.keccak_256, 'hex'); + const signature = this.signMessageString(hash, this.privateKey); + const request = { + 'parameters': { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'orderId': id, + }, + 'signature': signature, + }; + // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] + const response = await this.privateDeleteOrders(this.extend(request, params)); + const canceledOrder = this.safeDict(response, 0); + return this.parseOrder(canceledOrder, market); + } + handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + const errorCode = this.safeString(response, 'code'); + const message = this.safeString(response, 'message'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, message); + throw new errors.ExchangeError(this.id + ' ' + message); + } + return undefined; + } + async fetchDeposit(id, code = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDeposit + * @description fetch information on a deposit + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} id deposit id + * @param {string} code not used by geniusyield fetchDeposit () + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'depositId': id, + }; + const response = await this.privateGetDeposits(this.extend(request, params)); + return this.parseTransaction(response); + } + async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDeposits + * @description fetch all deposits made to an account + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch deposits for + * @param {int} [limit] the maximum number of deposits structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + params = this.extend({ + 'method': 'privateGetDeposits', + }, params); + return await this.fetchTransactionsHelper(code, since, limit, params); + } + async fetchStatus(params = {}) { + /** + * @method + * @name geniusyield#fetchStatus + * @description the latest known information on the availability of the exchange API + * @see https://api-docs-v3.geniusyield.io/#get-ping + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} + */ + const response = await this.publicGetPing(params); + return { + 'status': 'ok', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + 'info': response, + }; + } + async fetchTime(params = {}) { + /** + * @method + * @name geniusyield#fetchTime + * @description fetches the current integer timestamp in milliseconds from the exchange server + * @see https://api-docs-v3.geniusyield.io/#get-time + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {int} the current integer timestamp in milliseconds from the exchange server + */ + const response = await this.publicGetTime(params); + // + // { serverTime: "1655258263236" } + // + return this.safeInteger(response, 'serverTime'); + } + async fetchWithdrawal(id, code = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchWithdrawal + * @description fetch data on a currency withdrawal via the withdrawal id + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} id withdrawal id + * @param {string} code not used by geniusyield.fetchWithdrawal + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + 'wallet': this.walletAddress, + 'withdrawalId': id, + }; + const response = await this.privateGetWithdrawals(this.extend(request, params)); + return this.parseTransaction(response); + } + async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchWithdrawals + * @description fetch all withdrawals made from an account + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} code unified currency code + * @param {int} [since] the earliest time in ms to fetch withdrawals for + * @param {int} [limit] the maximum number of withdrawals structures to retrieve + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} + */ + params = this.extend({ + 'method': 'privateGetWithdrawals', + }, params); + return await this.fetchTransactionsHelper(code, since, limit, params); + } + async fetchTransactionsHelper(code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets(); + const nonce = this.uuidv1(); + const request = { + 'nonce': nonce, + 'wallet': this.walletAddress, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency(code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + // [ + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // ] + const method = params['method']; + params = this.omit(params, 'method'); + let response = undefined; + if (method === 'privateGetDeposits') { + response = await this.privateGetDeposits(this.extend(request, params)); + } + else if (method === 'privateGetWithdrawals') { + response = await this.privateGetWithdrawals(this.extend(request, params)); + } + else { + throw new errors.NotSupported(this.id + ' fetchTransactionsHelper() not support this method'); + } + return this.parseTransactions(response, currency, since, limit); + } + parseTransactionStatus(status) { + const statuses = { + 'mined': 'ok', + }; + return this.safeString(statuses, status, status); + } + parseTransaction(transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + // "asset": "ETH", + // "quantity": "1.00000000", + // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime": 1598865853000, + // "confirmationTime": 1598865930231 + // } + // + // fetchWithdrwalas + // + // { + // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883288, + // "fee": "0.00024000", + // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + // "txStatus": "mined" + // } + // + // withdraw + // + // { + // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset": "ETH", + // "assetContractAddress": "0x0000000000000000000000000000000000000000", + // "quantity": "0.20000000", + // "time": 1598962883190, + // "fee": "0.00024000", + // "txStatus": "pending", + // "txId": null + // } + // + let type = undefined; + if ('depositId' in transaction) { + type = 'deposit'; + } + else if (('withdrawId' in transaction) || ('withdrawalId' in transaction)) { + type = 'withdrawal'; + } + let id = this.safeString2(transaction, 'depositId', 'withdrawId'); + id = this.safeString(transaction, 'withdrawalId', id); + const code = this.safeCurrencyCode(this.safeString(transaction, 'asset'), currency); + const amount = this.safeNumber(transaction, 'quantity'); + const txid = this.safeString(transaction, 'txId'); + const timestamp = this.safeInteger2(transaction, 'txTime', 'time'); + let fee = undefined; + if ('fee' in transaction) { + fee = { + 'cost': this.safeNumber(transaction, 'fee'), + 'currency': 'ETH', + }; + } + const rawStatus = this.safeString(transaction, 'txStatus'); + const status = this.parseTransactionStatus(rawStatus); + const updated = this.safeInteger(transaction, 'confirmationTime'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601(timestamp), + 'network': undefined, + 'address': undefined, + 'addressTo': undefined, + 'addressFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'tagFrom': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'comment': undefined, + 'internal': undefined, + 'fee': fee, + }; + } + calculateRateLimiterCost(api, method, path, params, config = {}) { + const hasApiKey = (this.apiKey !== undefined); + const hasSecret = (this.secret !== undefined); + const hasWalletAddress = (this.walletAddress !== undefined); + const hasPrivateKey = (this.privateKey !== undefined); + const defaultCost = this.safeValue(config, 'cost', 1); + const authenticated = hasApiKey && hasSecret && hasWalletAddress && hasPrivateKey; + return authenticated ? (defaultCost / 2) : defaultCost; + } + async fetchDepositAddress(code = undefined, params = {}) { + /** + * @method + * @name geniusyield#fetchDepositAddress + * @description fetch the Polygon address of the wallet + * @see https://api-docs-v3.geniusyield.io/#get-wallets + * @param {string} code not used by geniusyield + * @param {object} [params] extra parameters specific to the exchange API endpoint + * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} + */ + const request = {}; + request['nonce'] = this.uuidv1(); + const response = await this.privateGetWallets(this.extend(request, params)); + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + return this.parseDepositAddress(response); + } + parseDepositAddress(depositAddress, currency = undefined) { + // + // [ + // { + // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd: "0.00", + // time: "1678342148086" + // }, + // { + // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd: "15.90", + // time: "1691697811659" + // } + // ] + // + const length = depositAddress.length; + const entry = this.safeDict(depositAddress, length - 1); + const address = this.safeString(entry, 'address'); + this.checkAddress(address); + return { + 'info': depositAddress, + 'currency': undefined, + 'address': address, + 'tag': undefined, + 'network': 'MATIC', + }; + } + sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const network = this.safeString(this.options, 'network', 'ETH'); + const version = this.safeString(this.options, 'version', 'v1'); + let url = this.urls['api'][network] + '/' + version + '/' + path; + const keys = Object.keys(params); + const length = keys.length; + let query = undefined; + if (length > 0) { + if (method === 'GET') { + query = this.urlencode(params); + url = url + '?' + query; + } + else { + body = this.json(params); + } + } + headers = { + 'Content-Type': 'application/json', + }; + if (this.apiKey !== undefined) { + headers['Genius Yield-API-Key'] = this.apiKey; + } + if (api === 'private') { + let payload = undefined; + if (method === 'GET') { + payload = query; + } + else { + payload = body; + } + headers['Genius Yield-HMAC-Signature'] = this.hmac(this.encode(payload), this.encode(this.secret), sha256.sha256, 'hex'); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + remove0xPrefix(hexData) { + if (hexData.slice(0, 2) === '0x') { + return hexData.slice(2); + } + else { + return hexData; + } + } + hashMessage(message) { + // takes a hex encoded message + const binaryMessage = this.base16ToBinary(this.remove0xPrefix(message)); + const prefix = this.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength); + return '0x' + this.hash(this.binaryConcat(prefix, binaryMessage), sha3.keccak_256, 'hex'); + } + signHash(hash, privateKey) { + const signature = crypto.ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1.secp256k1, undefined); + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + }; + } + signMessage(message, privateKey) { + return this.signHash(this.hashMessage(message), privateKey.slice(-64)); + } + signMessageString(message, privateKey) { + // still takes the input as a hex string + // same as above but returns a string instead of an object + const signature = this.signMessage(message, privateKey); + return signature['r'] + this.remove0xPrefix(signature['s']) + this.binaryToBase16(this.numberToBE(signature['v'], 1)); + } +} + +module.exports = geniusyield; diff --git a/js/src/abstract/geniusyield.d.ts b/js/src/abstract/geniusyield.d.ts index 1426ec02246e..fed72de5d6de 100644 --- a/js/src/abstract/geniusyield.d.ts +++ b/js/src/abstract/geniusyield.d.ts @@ -1,28 +1,8 @@ import { implicitReturnType } from '../base/types.js'; import { Exchange as _Exchange } from '../base/Exchange.js'; interface Exchange { - publicGetPing(params?: {}): Promise; - publicGetTime(params?: {}): Promise; - publicGetExchange(params?: {}): Promise; - publicGetAssets(params?: {}): Promise; - publicGetMarkets(params?: {}): Promise; - publicGetTickers(params?: {}): Promise; - publicGetCandles(params?: {}): Promise; - publicGetTrades(params?: {}): Promise; - publicGetOrderbook(params?: {}): Promise; - privateGetUser(params?: {}): Promise; - privateGetWallets(params?: {}): Promise; - privateGetBalances(params?: {}): Promise; - privateGetOrders(params?: {}): Promise; - privateGetFills(params?: {}): Promise; - privateGetDeposits(params?: {}): Promise; - privateGetWithdrawals(params?: {}): Promise; - privateGetWsToken(params?: {}): Promise; - privatePostWallets(params?: {}): Promise; - privatePostOrders(params?: {}): Promise; - privatePostOrdersTest(params?: {}): Promise; - privatePostWithdrawals(params?: {}): Promise; - privateDeleteOrders(params?: {}): Promise; + privateGetMarkets(params?: {}): Promise; + privateGetTradingFees(params?: {}): Promise; } declare abstract class Exchange extends _Exchange { } diff --git a/js/src/geniusyield.d.ts b/js/src/geniusyield.d.ts index 3bf22ea8e799..401cc8ef37f9 100644 --- a/js/src/geniusyield.d.ts +++ b/js/src/geniusyield.d.ts @@ -1,86 +1,10 @@ import Exchange from './abstract/geniusyield.js'; -import type { Balances, Currencies, Currency, Dict, Int, Market, Num, OHLCV, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, int } from './base/types.js'; +import type { Market } from './base/types.js'; /** * @class geniusyield * @augments Exchange */ export default class geniusyield extends Exchange { describe(): any; - priceToPrecision(symbol: any, price: any): string; fetchMarkets(params?: {}): Promise; - fetchTicker(symbol: string, params?: {}): Promise; - fetchTickers(symbols?: Strings, params?: {}): Promise; - parseTicker(ticker: Dict, market?: Market): Ticker; - fetchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise; - parseOHLCV(ohlcv: any, market?: Market): OHLCV; - fetchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise; - parseTrade(trade: Dict, market?: Market): Trade; - fetchTradingFees(params?: {}): Promise; - fetchOrderBook(symbol: string, limit?: Int, params?: {}): Promise; - parseSide(book: any, side: any): any[]; - fetchCurrencies(params?: {}): Promise; - parseBalance(response: any): Balances; - fetchBalance(params?: {}): Promise; - fetchMyTrades(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; - fetchOrder(id: string, symbol?: Str, params?: {}): Promise; - fetchOpenOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; - fetchClosedOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; - fetchOrdersHelper(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise; - parseOrderStatus(status: Str): string; - parseOrder(order: Dict, market?: Market): Order; - associateWallet(walletAddress: any, params?: {}): Promise; - createOrder(symbol: string, type: OrderType, side: OrderSide, amount: number, price?: Num, params?: {}): Promise; - withdraw(code: string, amount: number, address: string, tag?: any, params?: {}): Promise; - cancelAllOrders(symbol?: Str, params?: {}): Promise; - cancelOrder(id: string, symbol?: Str, params?: {}): Promise; - handleErrors(code: int, reason: string, url: string, method: string, headers: Dict, body: string, response: any, requestHeaders: any, requestBody: any): any; - fetchDeposit(id: string, code?: Str, params?: {}): Promise; - fetchDeposits(code?: Str, since?: Int, limit?: Int, params?: {}): Promise; - fetchStatus(params?: {}): Promise<{ - status: string; - updated: any; - eta: any; - url: any; - info: any; - }>; - fetchTime(params?: {}): Promise; - fetchWithdrawal(id: string, code?: Str, params?: {}): Promise; - fetchWithdrawals(code?: Str, since?: Int, limit?: Int, params?: {}): Promise; - fetchTransactionsHelper(code?: Str, since?: Int, limit?: Int, params?: {}): Promise; - parseTransactionStatus(status: Str): string; - parseTransaction(transaction: Dict, currency?: Currency): Transaction; - calculateRateLimiterCost(api: any, method: any, path: any, params: any, config?: {}): any; - fetchDepositAddress(code?: Str, params?: {}): Promise<{ - info: any; - currency: any; - address: string; - tag: any; - network: string; - }>; - parseDepositAddress(depositAddress: any, currency?: Currency): { - info: any; - currency: any; - address: string; - tag: any; - network: string; - }; - sign(path: any, api?: string, method?: string, params?: {}, headers?: any, body?: any): { - url: string; - method: string; - body: any; - headers: any; - }; - remove0xPrefix(hexData: any): any; - hashMessage(message: any): string; - signHash(hash: any, privateKey: any): { - r: string; - s: string; - v: number; - }; - signMessage(message: any, privateKey: any): { - r: string; - s: string; - v: number; - }; - signMessageString(message: any, privateKey: any): string; } diff --git a/js/src/geniusyield.js b/js/src/geniusyield.js index 8d63050fed17..9e06f1293015 100644 --- a/js/src/geniusyield.js +++ b/js/src/geniusyield.js @@ -1,12 +1,8 @@ // --------------------------------------------------------------------------- import Exchange from './abstract/geniusyield.js'; -import { TICK_SIZE, PAD_WITH_ZERO, ROUND, TRUNCATE, DECIMAL_PLACES } from './base/functions/number.js'; -import { InvalidOrder, InsufficientFunds, ExchangeError, ExchangeNotAvailable, DDoSProtection, BadRequest, NotSupported, InvalidAddress, AuthenticationError } from './base/errors.js'; +import { TICK_SIZE, PAD_WITH_ZERO } from './base/functions/number.js'; +import { InvalidOrder, InsufficientFunds, ExchangeNotAvailable, DDoSProtection, BadRequest, InvalidAddress, AuthenticationError } from './base/errors.js'; import { Precise } from './base/Precise.js'; -import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; -import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js'; -import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js'; -import { ecdsa } from './base/functions/crypto.js'; // --------------------------------------------------------------------------- /** * @class geniusyield @@ -32,29 +28,29 @@ export default class geniusyield extends Exchange { 'future': false, 'option': false, 'addMargin': false, - 'cancelAllOrders': true, - 'cancelOrder': true, + 'cancelAllOrders': false, + 'cancelOrder': false, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': true, + 'createOrder': false, 'createReduceOnlyOrder': false, - 'createStopLimitOrder': true, - 'createStopMarketOrder': true, - 'createStopOrder': true, - 'fetchBalance': true, + 'createStopLimitOrder': false, + 'createStopMarketOrder': false, + 'createStopOrder': false, + 'fetchBalance': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, - 'fetchClosedOrders': true, + 'fetchClosedOrders': false, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, - 'fetchCurrencies': true, - 'fetchDeposit': true, - 'fetchDepositAddress': true, + 'fetchCurrencies': false, + 'fetchDeposit': false, + 'fetchDepositAddress': false, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, - 'fetchDeposits': true, + 'fetchDeposits': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, @@ -67,12 +63,12 @@ export default class geniusyield extends Exchange { 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, - 'fetchMyTrades': true, - 'fetchOHLCV': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, 'fetchOpenInterestHistory': false, - 'fetchOpenOrders': true, - 'fetchOrder': true, - 'fetchOrderBook': true, + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': false, 'fetchOrders': false, 'fetchPosition': false, 'fetchPositionHistory': false, @@ -82,23 +78,23 @@ export default class geniusyield extends Exchange { 'fetchPositionsHistory': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, - 'fetchStatus': true, - 'fetchTicker': true, - 'fetchTickers': true, - 'fetchTime': true, - 'fetchTrades': true, + 'fetchStatus': false, + 'fetchTicker': false, + 'fetchTickers': false, + 'fetchTime': false, + 'fetchTrades': false, 'fetchTradingFee': false, - 'fetchTradingFees': true, + 'fetchTradingFees': false, 'fetchTransactions': false, - 'fetchWithdrawal': true, - 'fetchWithdrawals': true, + 'fetchWithdrawal': false, + 'fetchWithdrawals': false, 'reduceMargin': false, - 'sandbox': true, + 'sandbox': false, 'setLeverage': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, - 'withdraw': true, + 'withdraw': false, }, 'timeframes': { '1m': '1m', @@ -110,58 +106,30 @@ export default class geniusyield extends Exchange { '1d': '1d', }, 'urls': { - 'test': { - 'MATIC': 'https://api-sandbox-matic.geniusyield.io', - }, - 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api': { - 'MATIC': 'https://api-matic.geniusyield.io', + 'preprod': 'https://localhost:8082/v0/', + 'mainnet': 'https://localhost:8082/v0/', }, - 'www': 'https://geniusyield.io', + 'www': 'https://www.geniusyield.co/', 'doc': [ - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ], }, 'api': { 'public': { - 'get': { - 'ping': 1, - 'time': 1, - 'exchange': 1, - 'assets': 1, - 'markets': 1, - 'tickers': 1, - 'candles': 1, - 'trades': 1, - 'orderbook': 1, - }, + 'get': {}, }, 'private': { 'get': { - 'user': 1, - 'wallets': 1, - 'balances': 1, - 'orders': 0.1, - 'fills': 0.1, - 'deposits': 1, - 'withdrawals': 1, - 'wsToken': 1, - }, - 'post': { - 'wallets': 1, - 'orders': 0.1, - 'orders/test': 0.1, - 'withdrawals': 1, - }, - 'delete': { - 'orders': 0.1, - }, + 'markets': 0, + 'trading-fees': 0, + } }, }, 'options': { - 'defaultTimeInForce': 'gtc', + 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', - 'network': 'MATIC', + 'network': 'mainnet', }, 'exceptions': { 'exact': { @@ -175,28 +143,16 @@ export default class geniusyield extends Exchange { }, }, 'requiredCredentials': { - 'walletAddress': true, - 'privateKey': true, + 'walletAddress': false, + 'privateKey': false, 'apiKey': true, - 'secret': true, + 'secret': false, }, 'precisionMode': TICK_SIZE, 'paddingMode': PAD_WITH_ZERO, 'commonCurrencies': {}, }); } - priceToPrecision(symbol, price) { - // - // we override priceToPrecision to fix the following issue - // https://github.com/ccxt/ccxt/issues/13367 - // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} - // - const market = this.market(symbol); - const info = this.safeValue(market, 'info', {}); - const quoteAssetPrecision = this.safeInteger(info, 'quoteAssetPrecision'); - price = this.decimalToPrecision(price, ROUND, market['precision']['price'], this.precisionMode); - return this.decimalToPrecision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); - } async fetchMarkets(params = {}) { /** * @method @@ -206,74 +162,37 @@ export default class geniusyield extends Exchange { * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ - const response = await this.publicGetMarkets(params); - // - // [ - // { - // "market": "ETH-USDC", - // "type": "hybrid", - // "status": "activeHybrid", - // "baseAsset": "ETH", - // "baseAssetPrecision": "8", - // "quoteAsset": "USDC", - // "quoteAssetPrecision": "8", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.2500", - // "takerIdexFeeRate": "0.0500", - // "takerLiquidityProviderFeeRate": "0.2000", - // "tickSize": "0.01000000" - // }, - // ] - // - const response2 = await this.publicGetExchange(); - // + const markets = await this.privateGetMarkets(params); + // [ // { - // "timeZone": "UTC", - // "serverTime": "1654460599952", - // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - // "maticUsdPrice": "0.60", - // "gasPrice": "180", - // "volume24hUsd": "10015814.46", - // "totalVolumeUsd": "1589273533.28", - // "totalTrades": "1534904", - // "totalValueLockedUsd": "12041929.44", - // "geniusyieldStakingValueLockedUsd": "20133816.98", - // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", - // "geniusyieldUsdPrice": "0.07", - // "geniusyieldMarketCapUsd": "48012346.00", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.0025", - // "takerIdexFeeRate": "0.0005", - // "takerLiquidityProviderFeeRate": "0.0020", - // "makerTradeMinimum": "10.00000000", - // "takerTradeMinimum": "1.00000000", - // "withdrawMinimum": "0.50000000", - // "liquidityAdditionMinimum": "0.50000000", - // "liquidityRemovalMinimum": "0.40000000", - // "blockConfirmationDelay": "64" + // "market_id": "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + // "base_asset": "lovelace", + // "target_asset": "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" // } - // - const maker = this.safeNumber(response2, 'makerFeeRate'); - const taker = this.safeNumber(response2, 'takerFeeRate'); - const makerMin = this.safeString(response2, 'makerTradeMinimum'); - const takerMin = this.safeString(response2, 'takerTradeMinimum'); - const minCostETH = this.parseNumber(Precise.stringMin(makerMin, takerMin)); + // ] + const fees = await this.privateGetTradingFees(); + // { + // "flat_maker_fee": "1000000", + // "flat_taker_fee": "1000000", + // "percentage_maker_fee": "0.3", + // "percentage_taker_fee": "0.3" + // } + const maker = this.safeNumber(fees, 'makerFeeRate'); + const taker = this.safeNumber(fees, 'takerFeeRate'); + const makerMin = this.safeString(fees, 'makerTradeMinimum'); + const takerMin = this.safeString(fees, 'takerTradeMinimum'); + const minCost = this.parseNumber(Precise.stringMin(makerMin, takerMin)); const result = []; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const marketId = this.safeString(entry, 'market'); - const baseId = this.safeString(entry, 'baseAsset'); - const quoteId = this.safeString(entry, 'quoteAsset'); + for (let i = 0; i < markets.length; i++) { + const entry = markets[i]; + const marketId = this.safeString(entry, 'market_id'); + const baseId = this.safeString(entry, 'base_asset'); + const quoteId = this.safeString(entry, 'target_asset'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const basePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'baseAssetPrecision'))); const quotePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'quoteAssetPrecision'))); const status = this.safeString(entry, 'status'); - let minCost = undefined; - if (quote === 'ETH') { - minCost = minCostETH; - } result.push({ 'id': marketId, 'symbol': base + '/' + quote, @@ -328,1552 +247,4 @@ export default class geniusyield extends Exchange { } return result; } - async fetchTicker(symbol, params = {}) { - /** - * @method - * @name geniusyield#fetchTicker - * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string} symbol unified symbol of the market to fetch the ticker for - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - }; - // [ - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // } - // ] - const response = await this.publicGetTickers(this.extend(request, params)); - const ticker = this.safeDict(response, 0); - return this.parseTicker(ticker, market); - } - async fetchTickers(symbols = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchTickers - * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} - */ - await this.loadMarkets(); - // [ - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // }, ... - // ] - const response = await this.publicGetTickers(params); - return this.parseTickers(response, symbols); - } - parseTicker(ticker, market = undefined) { - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // } - const marketId = this.safeString(ticker, 'market'); - market = this.safeMarket(marketId, market, '-'); - const symbol = market['symbol']; - const timestamp = this.safeInteger(ticker, 'time'); - const close = this.safeString(ticker, 'close'); - return this.safeTicker({ - 'symbol': symbol, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'high': this.safeString(ticker, 'high'), - 'low': this.safeString(ticker, 'low'), - 'bid': this.safeString(ticker, 'bid'), - 'bidVolume': undefined, - 'ask': this.safeString(ticker, 'ask'), - 'askVolume': undefined, - 'vwap': undefined, - 'open': this.safeString(ticker, 'open'), - 'close': close, - 'last': close, - 'previousClose': undefined, - 'change': undefined, - 'percentage': this.safeString(ticker, 'percentChange'), - 'average': undefined, - 'baseVolume': this.safeString(ticker, 'baseVolume'), - 'quoteVolume': this.safeString(ticker, 'quoteVolume'), - 'info': ticker, - }, market); - } - async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOHLCV - * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market - * @see https://api-docs-v3.geniusyield.io/#get-candles - * @param {string} symbol unified symbol of the market to fetch OHLCV data for - * @param {string} timeframe the length of time each candle represents - * @param {int} [since] timestamp in ms of the earliest candle to fetch - * @param {int} [limit] the maximum amount of candles to fetch - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - 'interval': timeframe, - }; - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = Math.min(limit, 1000); - } - const response = await this.publicGetCandles(this.extend(request, params)); - if (Array.isArray(response)) { - // [ - // { - // "start": 1598345580000, - // "open": "0.09771286", - // "high": "0.09771286", - // "low": "0.09771286", - // "close": "0.09771286", - // "volume": "1.45340410", - // "sequence": 3853 - // }, ... - // ] - return this.parseOHLCVs(response, market, timeframe, since, limit); - } - else { - // {"nextTime":1595536440000} - return []; - } - } - parseOHLCV(ohlcv, market = undefined) { - // { - // "start": 1598345580000, - // "open": "0.09771286", - // "high": "0.09771286", - // "low": "0.09771286", - // "close": "0.09771286", - // "volume": "1.45340410", - // "sequence": 3853 - // } - const timestamp = this.safeInteger(ohlcv, 'start'); - const open = this.safeNumber(ohlcv, 'open'); - const high = this.safeNumber(ohlcv, 'high'); - const low = this.safeNumber(ohlcv, 'low'); - const close = this.safeNumber(ohlcv, 'close'); - const volume = this.safeNumber(ohlcv, 'volume'); - return [timestamp, open, high, low, close, volume]; - } - async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchTrades - * @description get the list of most recent trades for a particular symbol - * @see https://api-docs-v3.geniusyield.io/#get-trades - * @param {string} symbol unified symbol of the market to fetch trades for - * @param {int} [since] timestamp in ms of the earliest trade to fetch - * @param {int} [limit] the maximum amount of trades to fetch - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - }; - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = Math.min(limit, 1000); - } - // [ - // { - // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", - // "price": "0.09771286", - // "quantity": "1.45340410", - // "quoteQuantity": "0.14201627", - // "time": 1598345638994, - // "makerSide": "buy", - // "sequence": 3853 - // }, ... - // ] - const response = await this.publicGetTrades(this.extend(request, params)); - return this.parseTrades(response, market, since, limit); - } - parseTrade(trade, market = undefined) { - // - // public trades - // { - // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - // "price":"0.20377008", - // "quantity":"47.58448728", - // "quoteQuantity":"9.69629509", - // "time":1642091300873, - // "makerSide":"buy", - // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" - // "sequence":31876 - // } - // - // private trades - // { - // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - // "price":"0.20717368", - // "quantity":"15.00000000", - // "quoteQuantity":"3.10760523", - // "orderBookQuantity":"0.00000003", - // "orderBookQuoteQuantity":"0.00000001", - // "poolQuantity":"14.99999997", - // "poolQuoteQuantity":"3.10760522", - // "time":1642083351215, - // "makerSide":"sell", - // "sequence":31795, - // "market":"Genius Yield-USDC", - // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - // "side":"buy", - // "fee":"0.03749989", - // "feeAsset":"Genius Yield", - // "gas":"0.40507261", - // "liquidity":"taker", - // "type":"hybrid", - // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - // "txStatus":"mined" - // } - // - const id = this.safeString(trade, 'fillId'); - const priceString = this.safeString(trade, 'price'); - const amountString = this.safeString(trade, 'quantity'); - const costString = this.safeString(trade, 'quoteQuantity'); - const timestamp = this.safeInteger(trade, 'time'); - const marketId = this.safeString(trade, 'market'); - const symbol = this.safeSymbol(marketId, market, '-'); - // this code handles the duality of public vs private trades - const makerSide = this.safeString(trade, 'makerSide'); - const oppositeSide = (makerSide === 'buy') ? 'sell' : 'buy'; - const side = this.safeString(trade, 'side', oppositeSide); - const takerOrMaker = this.safeString(trade, 'liquidity', 'taker'); - const feeCostString = this.safeString(trade, 'fee'); - let fee = undefined; - if (feeCostString !== undefined) { - const feeCurrencyId = this.safeString(trade, 'feeAsset'); - fee = { - 'cost': feeCostString, - 'currency': this.safeCurrencyCode(feeCurrencyId), - }; - } - const orderId = this.safeString(trade, 'orderId'); - return this.safeTrade({ - 'info': trade, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'symbol': symbol, - 'id': id, - 'order': orderId, - 'type': 'limit', - 'side': side, - 'takerOrMaker': takerOrMaker, - 'price': priceString, - 'amount': amountString, - 'cost': costString, - 'fee': fee, - }, market); - } - async fetchTradingFees(params = {}) { - /** - * @method - * @name geniusyield#fetchTradingFees - * @description fetch the trading fees for multiple markets - * @see https://api-docs-v3.geniusyield.io/#get-api-account - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - }; - let response = undefined; - response = await this.privateGetUser(this.extend(request, params)); - // - // { - // "depositEnabled": true, - // "orderEnabled": true, - // "cancelEnabled": true, - // "withdrawEnabled": true, - // "totalPortfolioValueUsd": "0.00", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.0025", - // "takerIdexFeeRate": "0.0005", - // "takerLiquidityProviderFeeRate": "0.0020" - // } - // - const maker = this.safeNumber(response, 'makerFeeRate'); - const taker = this.safeNumber(response, 'takerFeeRate'); - const result = {}; - for (let i = 0; i < this.symbols.length; i++) { - const symbol = this.symbols[i]; - result[symbol] = { - 'info': response, - 'symbol': symbol, - 'maker': maker, - 'taker': taker, - 'percentage': true, - 'tierBased': false, - }; - } - return result; - } - async fetchOrderBook(symbol, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOrderBook - * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data - * @see https://api-docs-v3.geniusyield.io/#get-order-books - * @param {string} symbol unified symbol of the market to fetch the order book for - * @param {int} [limit] the maximum amount of order book entries to return - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - 'level': 2, - }; - if (limit !== undefined) { - request['limit'] = limit; - } - // { - // "sequence": 36416753, - // "bids": [ - // [ '0.09672815', "8.22284267", 1 ], - // [ '0.09672814', "1.83685554", 1 ], - // [ '0.09672143', "4.10962617", 1 ], - // [ '0.09658884', "4.03863759", 1 ], - // [ '0.09653781', "3.35730684", 1 ], - // [ '0.09624660', "2.54163586", 1 ], - // [ '0.09617490', "1.93065030", 1 ] - // ], - // "asks": [ - // [ '0.09910476', "3.22840154", 1 ], - // [ '0.09940587', "3.39796593", 1 ], - // [ '0.09948189', "4.25088898", 1 ], - // [ '0.09958362', "2.42195784", 1 ], - // [ '0.09974393', "4.25234367", 1 ], - // [ '0.09995250', "3.40192141", 1 ] - // ] - // } - const response = await this.publicGetOrderbook(this.extend(request, params)); - const nonce = this.safeInteger(response, 'sequence'); - return { - 'symbol': symbol, - 'timestamp': undefined, - 'datetime': undefined, - 'nonce': nonce, - 'bids': this.parseSide(response, 'bids'), - 'asks': this.parseSide(response, 'asks'), - }; - } - parseSide(book, side) { - const bookSide = this.safeValue(book, side, []); - const result = []; - for (let i = 0; i < bookSide.length; i++) { - const order = bookSide[i]; - const price = this.safeNumber(order, 0); - const amount = this.safeNumber(order, 1); - const orderCount = this.safeInteger(order, 2); - result.push([price, amount, orderCount]); - } - const descending = side === 'bids'; - return this.sortBy(result, 0, descending); - } - async fetchCurrencies(params = {}) { - /** - * @method - * @name geniusyield#fetchCurrencies - * @description fetches all available currencies on an exchange - * @see https://api-docs-v3.geniusyield.io/#get-assets - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} an associative dictionary of currencies - */ - const response = await this.publicGetAssets(params); - // - // [ - // { - // "name": "Ethereum", - // "symbol": "ETH", - // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - // "assetDecimals": "18", - // "exchangeDecimals": "8", - // "maticPrice": "3029.38503483" - // }, - // ] - // - const result = {}; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const name = this.safeString(entry, 'name'); - const currencyId = this.safeString(entry, 'symbol'); - const code = this.safeCurrencyCode(currencyId); - const precision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'exchangeDecimals'))); - result[code] = { - 'id': currencyId, - 'code': code, - 'info': entry, - 'type': undefined, - 'name': name, - 'active': undefined, - 'deposit': undefined, - 'withdraw': undefined, - 'fee': undefined, - 'precision': precision, - 'limits': { - 'amount': { 'min': precision, 'max': undefined }, - 'withdraw': { 'min': precision, 'max': undefined }, - }, - }; - } - return result; - } - parseBalance(response) { - const result = { - 'info': response, - 'timestamp': undefined, - 'datetime': undefined, - }; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const currencyId = this.safeString(entry, 'asset'); - const code = this.safeCurrencyCode(currencyId); - const account = this.account(); - account['total'] = this.safeString(entry, 'quantity'); - account['free'] = this.safeString(entry, 'availableForTrade'); - account['used'] = this.safeString(entry, 'locked'); - result[code] = account; - } - return this.safeBalance(result); - } - async fetchBalance(params = {}) { - /** - * @method - * @name geniusyield#fetchBalance - * @description query for balance and get the amount of funds available for trading or funds locked in orders - * @see https://api-docs-v3.geniusyield.io/#get-balances - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - const nonce1 = this.uuidv1(); - const request = { - 'nonce': nonce1, - 'wallet': this.walletAddress, - }; - // [ - // { - // "asset": "DIL", - // "quantity": "0.00000000", - // "availableForTrade": "0.00000000", - // "locked": "0.00000000", - // "usdValue": null - // }, ... - // ] - const extendedRequest = this.extend(request, params); - if (extendedRequest['wallet'] === undefined) { - throw new BadRequest(this.id + ' fetchBalance() wallet is undefined, set this.walletAddress or "address" in params'); - } - let response = undefined; - try { - response = await this.privateGetBalances(extendedRequest); - } - catch (e) { - if (e instanceof InvalidAddress) { - const walletAddress = extendedRequest['wallet']; - await this.associateWallet(walletAddress); - response = await this.privateGetBalances(extendedRequest); - } - else { - throw e; - } - } - return this.parseBalance(response); - } - async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchMyTrades - * @description fetch all trades made by the user - * @see https://api-docs-v3.geniusyield.io/#get-fills - * @param {string} symbol unified market symbol - * @param {int} [since] the earliest time in ms to fetch trades for - * @param {int} [limit] the maximum number of trades structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - let market = undefined; - const request = { - 'nonce': this.uuidv1(), - 'wallet': this.walletAddress, - }; - if (symbol !== undefined) { - market = this.market(symbol); - request['market'] = market['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - // [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478762, - // "makerSide": "sell", - // "sequence": 5053, - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "side": "buy", - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - const extendedRequest = this.extend(request, params); - if (extendedRequest['wallet'] === undefined) { - throw new BadRequest(this.id + ' fetchMyTrades() walletAddress is undefined, set this.walletAddress or "address" in params'); - } - let response = undefined; - try { - response = await this.privateGetFills(extendedRequest); - } - catch (e) { - if (e instanceof InvalidAddress) { - const walletAddress = extendedRequest['wallet']; - await this.associateWallet(walletAddress); - response = await this.privateGetFills(extendedRequest); - } - else { - throw e; - } - } - return this.parseTrades(response, market, since, limit); - } - async fetchOrder(id, symbol = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOrder - * @description fetches information on an order made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified symbol of the market the order was made in - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request = { - 'orderId': id, - }; - return await this.fetchOrdersHelper(symbol, undefined, undefined, this.extend(request, params)); - } - async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOpenOrders - * @description fetch all unfilled currently open orders - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified market symbol - * @param {int} [since] the earliest time in ms to fetch open orders for - * @param {int} [limit] the maximum number of open orders structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request = { - 'closed': false, - }; - return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); - } - async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchClosedOrders - * @description fetches information on multiple closed orders made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified market symbol of the market orders were made in - * @param {int} [since] the earliest time in ms to fetch orders for - * @param {int} [limit] the maximum number of order structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request = { - 'closed': true, - }; - return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); - } - async fetchOrdersHelper(symbol = undefined, since = undefined, limit = undefined, params = {}) { - await this.loadMarkets(); - const request = { - 'nonce': this.uuidv1(), - 'wallet': this.walletAddress, - }; - let market = undefined; - if (symbol !== undefined) { - market = this.market(symbol); - request['market'] = market['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - const response = await this.privateGetOrders(this.extend(request, params)); - // fetchClosedOrders / fetchOpenOrders - // [ - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - // } - // ] - // fetchOrder - // { market: "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": - // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" } ] } - if (Array.isArray(response)) { - return this.parseOrders(response, market, since, limit); - } - else { - return this.parseOrder(response, market); - } - } - parseOrderStatus(status) { - // https://docs.geniusyield.io/#order-states-amp-lifecycle - const statuses = { - 'active': 'open', - 'partiallyFilled': 'open', - 'rejected': 'canceled', - 'filled': 'closed', - }; - return this.safeString(statuses, status, status); - } - parseOrder(order, market = undefined) { - // - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - // } - // - const timestamp = this.safeInteger(order, 'time'); - const fills = this.safeValue(order, 'fills', []); - const id = this.safeString(order, 'orderId'); - const clientOrderId = this.safeString(order, 'clientOrderId'); - const marketId = this.safeString(order, 'market'); - const side = this.safeString(order, 'side'); - const symbol = this.safeSymbol(marketId, market, '-'); - const type = this.safeString(order, 'type'); - const amount = this.safeString(order, 'originalQuantity'); - const filled = this.safeString(order, 'executedQuantity'); - const average = this.safeString(order, 'avgExecutionPrice'); - const price = this.safeString(order, 'price'); - const rawStatus = this.safeString(order, 'status'); - const timeInForce = this.safeStringUpper(order, 'timeInForce'); - const status = this.parseOrderStatus(rawStatus); - return this.safeOrder({ - 'info': order, - 'id': id, - 'clientOrderId': clientOrderId, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'lastTradeTimestamp': undefined, - 'symbol': symbol, - 'type': type, - 'timeInForce': timeInForce, - 'postOnly': undefined, - 'side': side, - 'price': price, - 'stopPrice': undefined, - 'triggerPrice': undefined, - 'amount': amount, - 'cost': undefined, - 'average': average, - 'filled': filled, - 'remaining': undefined, - 'status': status, - 'fee': undefined, - 'trades': fills, - }, market); - } - async associateWallet(walletAddress, params = {}) { - const nonce = this.uuidv1(); - const noPrefix = this.remove0xPrefix(walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(noPrefix), - ]; - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, keccak, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - // { - // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "totalPortfolioValueUsd": "0.00", - // "time": 1598468353626 - // } - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': walletAddress, - }, - 'signature': signature, - }; - const result = await this.privatePostWallets(request); - return result; - } - async createOrder(symbol, type, side, amount, price = undefined, params = {}) { - /** - * @method - * @name geniusyield#createOrder - * @description create a trade order, https://docs.geniusyield.io/#create-order - * @see https://api-docs-v3.geniusyield.io/#create-order - * @param {string} symbol unified symbol of the market to create an order in - * @param {string} type 'market' or 'limit' - * @param {string} side 'buy' or 'sell' - * @param {float} amount how much of currency you want to trade in units of base currency - * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated - * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - const testOrder = this.safeBool(params, 'test', false); - params = this.omit(params, 'test'); - const market = this.market(symbol); - const nonce = this.uuidv1(); - let typeEnum = undefined; - const stopLossTypeEnums = { - 'stopLoss': 3, - 'stopLossLimit': 4, - 'takeProfit': 5, - 'takeProfitLimit': 6, - }; - let stopPriceString = undefined; - if ((type === 'stopLossLimit') || (type === 'takeProfitLimit') || ('stopPrice' in params)) { - if (!('stopPrice' in params)) { - throw new BadRequest(this.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders'); - } - stopPriceString = this.priceToPrecision(symbol, params['stopPrice']); - } - const limitTypeEnums = { - 'limit': 1, - 'limitMaker': 2, - }; - let priceString = undefined; - const typeLower = type.toLowerCase(); - const limitOrder = typeLower.indexOf('limit') >= 0; - if (type in limitTypeEnums) { - typeEnum = limitTypeEnums[type]; - priceString = this.priceToPrecision(symbol, price); - } - else if (type in stopLossTypeEnums) { - typeEnum = stopLossTypeEnums[type]; - priceString = this.priceToPrecision(symbol, price); - } - else if (type === 'market') { - typeEnum = 0; - } - else { - throw new BadRequest(this.id + ' ' + type + ' is not a valid order type'); - } - let amountEnum = 0; // base quantity - if ('quoteOrderQuantity' in params) { - if (type !== 'market') { - throw new NotSupported(this.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders'); - } - amountEnum = 1; - amount = this.safeNumber(params, 'quoteOrderQuantity'); - } - const sideEnum = (side === 'buy') ? 0 : 1; - const walletBytes = this.remove0xPrefix(this.walletAddress); - const network = this.safeString(this.options, 'network', 'ETH'); - const orderVersion = this.getSupportedMapping(network, { - 'ETH': 1, - 'BSC': 2, - 'MATIC': 4, - }); - const amountString = this.amountToPrecision(symbol, amount); - // https://docs.geniusyield.io/#time-in-force - const timeInForceEnums = { - 'gtc': 0, - 'ioc': 2, - 'fok': 3, - }; - const defaultTimeInForce = this.safeString(this.options, 'defaultTimeInForce', 'gtc'); - const timeInForce = this.safeString(params, 'timeInForce', defaultTimeInForce); - let timeInForceEnum = undefined; - if (timeInForce in timeInForceEnums) { - timeInForceEnum = timeInForceEnums[timeInForce]; - } - else { - const allOptions = Object.keys(timeInForceEnums); - const asString = allOptions.join(', '); - throw new BadRequest(this.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString); - } - // https://docs.geniusyield.io/#self-trade-prevention - const selfTradePreventionEnums = { - 'dc': 0, - 'co': 1, - 'cn': 2, - 'cb': 3, - }; - const defaultSelfTradePrevention = this.safeString(this.options, 'defaultSelfTradePrevention', 'cn'); - const selfTradePrevention = this.safeString(params, 'selfTradePrevention', defaultSelfTradePrevention); - let selfTradePreventionEnum = undefined; - if (selfTradePrevention in selfTradePreventionEnums) { - selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]; - } - else { - const allOptions = Object.keys(selfTradePreventionEnums); - const asString = allOptions.join(', '); - throw new BadRequest(this.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString); - } - const byteArray = [ - this.numberToBE(orderVersion, 1), - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - this.encode(market['id']), - this.numberToBE(typeEnum, 1), - this.numberToBE(sideEnum, 1), - this.encode(amountString), - this.numberToBE(amountEnum, 1), - ]; - if (limitOrder) { - const encodedPrice = this.encode(priceString); - byteArray.push(encodedPrice); - } - if (type in stopLossTypeEnums) { - const encodedPrice = this.encode(stopPriceString || priceString); - byteArray.push(encodedPrice); - } - const clientOrderId = this.safeString(params, 'clientOrderId'); - if (clientOrderId !== undefined) { - byteArray.push(this.encode(clientOrderId)); - } - const after = [ - this.numberToBE(timeInForceEnum, 1), - this.numberToBE(selfTradePreventionEnum, 1), - this.numberToBE(0, 8), // unused - ]; - const allBytes = this.arrayConcat(byteArray, after); - const binary = this.binaryConcatArray(allBytes); - const hash = this.hash(binary, keccak, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - const request = { - 'parameters': { - 'nonce': nonce, - 'market': market['id'], - 'side': side, - 'type': type, - 'wallet': this.walletAddress, - 'selfTradePrevention': selfTradePrevention, - }, - 'signature': signature, - }; - if (type !== 'market') { - request['parameters']['timeInForce'] = timeInForce; - } - if (limitOrder) { - request['parameters']['price'] = priceString; - } - if (type in stopLossTypeEnums) { - request['parameters']['stopPrice'] = stopPriceString || priceString; - } - if (amountEnum === 0) { - request['parameters']['quantity'] = amountString; - } - else { - request['parameters']['quoteOrderQuantity'] = amountString; - } - if (clientOrderId !== undefined) { - request['parameters']['clientOrderId'] = clientOrderId; - } - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txStatus": "pending" - // } - // ], - // "avgExecutionPrice": "0.09905990" - // } - // we don't use extend here because it is a signed endpoint - let response = undefined; - if (testOrder) { - response = await this.privatePostOrdersTest(request); - } - else { - response = await this.privatePostOrders(request); - } - return this.parseOrder(response, market); - } - async withdraw(code, amount, address, tag = undefined, params = {}) { - /** - * @method - * @name geniusyield#withdraw - * @description make a withdrawal - * @see https://api-docs-v3.geniusyield.io/#withdraw-funds - * @param {string} code unified currency code - * @param {float} amount the amount to withdraw - * @param {string} address the address to withdraw to - * @param {string} tag - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - [tag, params] = this.handleWithdrawTagAndParams(tag, params); - this.checkRequiredCredentials(); - await this.loadMarkets(); - const nonce = this.uuidv1(); - const amountString = this.currencyToPrecision(code, amount); - const currency = this.currency(code); - const walletBytes = this.remove0xPrefix(this.walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - this.encode(currency['id']), - this.encode(amountString), - this.numberToBE(1, 1), // bool set to true - ]; - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, keccak, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': address, - 'asset': currency['id'], - 'quantity': amountString, - }, - 'signature': signature, - }; - const response = await this.privatePostWithdrawals(request); - // - // { - // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883190, - // "fee": "0.00024000", - // "txStatus": "pending", - // "txId": null - // } - // - return this.parseTransaction(response, currency); - } - async cancelAllOrders(symbol = undefined, params = {}) { - /** - * @method - * @name geniusyield#cancelAllOrders - * @description cancel all open orders - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - let market = undefined; - if (symbol !== undefined) { - market = this.market(symbol); - } - const nonce = this.uuidv1(); - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': this.walletAddress, - }, - }; - const walletBytes = this.remove0xPrefix(this.walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - ]; - if (market !== undefined) { - byteArray.push(this.encode(market['id'])); - request['parameters']['market'] = market['id']; - } - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, keccak, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - request['signature'] = signature; - // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] - const response = await this.privateDeleteOrders(this.extend(request, params)); - return this.parseOrders(response, market); - } - async cancelOrder(id, symbol = undefined, params = {}) { - /** - * @method - * @name geniusyield#cancelOrder - * @description cancels an open order - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} id order id - * @param {string} symbol unified symbol of the market the order was made in - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - let market = undefined; - if (symbol !== undefined) { - market = this.market(symbol); - } - const nonce = this.uuidv1(); - const walletBytes = this.remove0xPrefix(this.walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - this.encode(id), - ]; - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, keccak, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'orderId': id, - }, - 'signature': signature, - }; - // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] - const response = await this.privateDeleteOrders(this.extend(request, params)); - const canceledOrder = this.safeDict(response, 0); - return this.parseOrder(canceledOrder, market); - } - handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) { - const errorCode = this.safeString(response, 'code'); - const message = this.safeString(response, 'message'); - if (errorCode !== undefined) { - this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, message); - throw new ExchangeError(this.id + ' ' + message); - } - return undefined; - } - async fetchDeposit(id, code = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDeposit - * @description fetch information on a deposit - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} id deposit id - * @param {string} code not used by geniusyield fetchDeposit () - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'depositId': id, - }; - const response = await this.privateGetDeposits(this.extend(request, params)); - return this.parseTransaction(response); - } - async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDeposits - * @description fetch all deposits made to an account - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} code unified currency code - * @param {int} [since] the earliest time in ms to fetch deposits for - * @param {int} [limit] the maximum number of deposits structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - params = this.extend({ - 'method': 'privateGetDeposits', - }, params); - return await this.fetchTransactionsHelper(code, since, limit, params); - } - async fetchStatus(params = {}) { - /** - * @method - * @name geniusyield#fetchStatus - * @description the latest known information on the availability of the exchange API - * @see https://api-docs-v3.geniusyield.io/#get-ping - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} - */ - const response = await this.publicGetPing(params); - return { - 'status': 'ok', - 'updated': undefined, - 'eta': undefined, - 'url': undefined, - 'info': response, - }; - } - async fetchTime(params = {}) { - /** - * @method - * @name geniusyield#fetchTime - * @description fetches the current integer timestamp in milliseconds from the exchange server - * @see https://api-docs-v3.geniusyield.io/#get-time - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {int} the current integer timestamp in milliseconds from the exchange server - */ - const response = await this.publicGetTime(params); - // - // { serverTime: "1655258263236" } - // - return this.safeInteger(response, 'serverTime'); - } - async fetchWithdrawal(id, code = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchWithdrawal - * @description fetch data on a currency withdrawal via the withdrawal id - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} id withdrawal id - * @param {string} code not used by geniusyield.fetchWithdrawal - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'withdrawalId': id, - }; - const response = await this.privateGetWithdrawals(this.extend(request, params)); - return this.parseTransaction(response); - } - async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchWithdrawals - * @description fetch all withdrawals made from an account - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} code unified currency code - * @param {int} [since] the earliest time in ms to fetch withdrawals for - * @param {int} [limit] the maximum number of withdrawals structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - params = this.extend({ - 'method': 'privateGetWithdrawals', - }, params); - return await this.fetchTransactionsHelper(code, since, limit, params); - } - async fetchTransactionsHelper(code = undefined, since = undefined, limit = undefined, params = {}) { - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - 'wallet': this.walletAddress, - }; - let currency = undefined; - if (code !== undefined) { - currency = this.currency(code); - request['asset'] = currency['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - // [ - // { - // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - // "asset": "ETH", - // "quantity": "1.00000000", - // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime": 1598865853000, - // "confirmationTime": 1598865930231 - // } - // ] - const method = params['method']; - params = this.omit(params, 'method'); - let response = undefined; - if (method === 'privateGetDeposits') { - response = await this.privateGetDeposits(this.extend(request, params)); - } - else if (method === 'privateGetWithdrawals') { - response = await this.privateGetWithdrawals(this.extend(request, params)); - } - else { - throw new NotSupported(this.id + ' fetchTransactionsHelper() not support this method'); - } - return this.parseTransactions(response, currency, since, limit); - } - parseTransactionStatus(status) { - const statuses = { - 'mined': 'ok', - }; - return this.safeString(statuses, status, status); - } - parseTransaction(transaction, currency = undefined) { - // - // fetchDeposits - // - // { - // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - // "asset": "ETH", - // "quantity": "1.00000000", - // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime": 1598865853000, - // "confirmationTime": 1598865930231 - // } - // - // fetchWithdrwalas - // - // { - // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883288, - // "fee": "0.00024000", - // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - // "txStatus": "mined" - // } - // - // withdraw - // - // { - // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883190, - // "fee": "0.00024000", - // "txStatus": "pending", - // "txId": null - // } - // - let type = undefined; - if ('depositId' in transaction) { - type = 'deposit'; - } - else if (('withdrawId' in transaction) || ('withdrawalId' in transaction)) { - type = 'withdrawal'; - } - let id = this.safeString2(transaction, 'depositId', 'withdrawId'); - id = this.safeString(transaction, 'withdrawalId', id); - const code = this.safeCurrencyCode(this.safeString(transaction, 'asset'), currency); - const amount = this.safeNumber(transaction, 'quantity'); - const txid = this.safeString(transaction, 'txId'); - const timestamp = this.safeInteger2(transaction, 'txTime', 'time'); - let fee = undefined; - if ('fee' in transaction) { - fee = { - 'cost': this.safeNumber(transaction, 'fee'), - 'currency': 'ETH', - }; - } - const rawStatus = this.safeString(transaction, 'txStatus'); - const status = this.parseTransactionStatus(rawStatus); - const updated = this.safeInteger(transaction, 'confirmationTime'); - return { - 'info': transaction, - 'id': id, - 'txid': txid, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'network': undefined, - 'address': undefined, - 'addressTo': undefined, - 'addressFrom': undefined, - 'tag': undefined, - 'tagTo': undefined, - 'tagFrom': undefined, - 'type': type, - 'amount': amount, - 'currency': code, - 'status': status, - 'updated': updated, - 'comment': undefined, - 'internal': undefined, - 'fee': fee, - }; - } - calculateRateLimiterCost(api, method, path, params, config = {}) { - const hasApiKey = (this.apiKey !== undefined); - const hasSecret = (this.secret !== undefined); - const hasWalletAddress = (this.walletAddress !== undefined); - const hasPrivateKey = (this.privateKey !== undefined); - const defaultCost = this.safeValue(config, 'cost', 1); - const authenticated = hasApiKey && hasSecret && hasWalletAddress && hasPrivateKey; - return authenticated ? (defaultCost / 2) : defaultCost; - } - async fetchDepositAddress(code = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDepositAddress - * @description fetch the Polygon address of the wallet - * @see https://api-docs-v3.geniusyield.io/#get-wallets - * @param {string} code not used by geniusyield - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} - */ - const request = {}; - request['nonce'] = this.uuidv1(); - const response = await this.privateGetWallets(this.extend(request, params)); - // - // [ - // { - // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd: "0.00", - // time: "1678342148086" - // }, - // { - // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd: "15.90", - // time: "1691697811659" - // } - // ] - // - return this.parseDepositAddress(response); - } - parseDepositAddress(depositAddress, currency = undefined) { - // - // [ - // { - // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd: "0.00", - // time: "1678342148086" - // }, - // { - // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd: "15.90", - // time: "1691697811659" - // } - // ] - // - const length = depositAddress.length; - const entry = this.safeDict(depositAddress, length - 1); - const address = this.safeString(entry, 'address'); - this.checkAddress(address); - return { - 'info': depositAddress, - 'currency': undefined, - 'address': address, - 'tag': undefined, - 'network': 'MATIC', - }; - } - sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { - const network = this.safeString(this.options, 'network', 'ETH'); - const version = this.safeString(this.options, 'version', 'v1'); - let url = this.urls['api'][network] + '/' + version + '/' + path; - const keys = Object.keys(params); - const length = keys.length; - let query = undefined; - if (length > 0) { - if (method === 'GET') { - query = this.urlencode(params); - url = url + '?' + query; - } - else { - body = this.json(params); - } - } - headers = { - 'Content-Type': 'application/json', - }; - if (this.apiKey !== undefined) { - headers['Genius Yield-API-Key'] = this.apiKey; - } - if (api === 'private') { - let payload = undefined; - if (method === 'GET') { - payload = query; - } - else { - payload = body; - } - headers['Genius Yield-HMAC-Signature'] = this.hmac(this.encode(payload), this.encode(this.secret), sha256, 'hex'); - } - return { 'url': url, 'method': method, 'body': body, 'headers': headers }; - } - remove0xPrefix(hexData) { - if (hexData.slice(0, 2) === '0x') { - return hexData.slice(2); - } - else { - return hexData; - } - } - hashMessage(message) { - // takes a hex encoded message - const binaryMessage = this.base16ToBinary(this.remove0xPrefix(message)); - const prefix = this.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength); - return '0x' + this.hash(this.binaryConcat(prefix, binaryMessage), keccak, 'hex'); - } - signHash(hash, privateKey) { - const signature = ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1, undefined); - return { - 'r': '0x' + signature['r'], - 's': '0x' + signature['s'], - 'v': 27 + signature['v'], - }; - } - signMessage(message, privateKey) { - return this.signHash(this.hashMessage(message), privateKey.slice(-64)); - } - signMessageString(message, privateKey) { - // still takes the input as a hex string - // same as above but returns a string instead of an object - const signature = this.signMessage(message, privateKey); - return signature['r'] + this.remove0xPrefix(signature['s']) + this.binaryToBase16(this.numberToBE(signature['v'], 1)); - } } diff --git a/php/abstract/geniusyield.php b/php/abstract/geniusyield.php index 967e79679ec9..ce2ab30e3683 100644 --- a/php/abstract/geniusyield.php +++ b/php/abstract/geniusyield.php @@ -7,136 +7,16 @@ abstract class geniusyield extends \ccxt\Exchange { - public function public_get_ping($params = array()) { - return $this->request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); + public function private_get_markets($params = array()) { + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); } - public function public_get_time($params = array()) { - return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); + public function private_get_trading_fees($params = array()) { + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); } - public function public_get_exchange($params = array()) { - return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); + public function privateGetMarkets($params = array()) { + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); } - public function public_get_assets($params = array()) { - return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_markets($params = array()) { - return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_tickers($params = array()) { - return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_candles($params = array()) { - return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_trades($params = array()) { - return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_orderbook($params = array()) { - return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_user($params = array()) { - return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_wallets($params = array()) { - return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_balances($params = array()) { - return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_orders($params = array()) { - return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function private_get_fills($params = array()) { - return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function private_get_deposits($params = array()) { - return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_withdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_wstoken($params = array()) { - return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_post_wallets($params = array()) { - return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function private_post_orders($params = array()) { - return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function private_post_orders_test($params = array()) { - return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function private_post_withdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function private_delete_orders($params = array()) { - return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); - } - public function publicGetPing($params = array()) { - return $this->request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetTime($params = array()) { - return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetExchange($params = array()) { - return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetAssets($params = array()) { - return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetMarkets($params = array()) { - return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetTickers($params = array()) { - return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetCandles($params = array()) { - return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetTrades($params = array()) { - return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetOrderbook($params = array()) { - return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetUser($params = array()) { - return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetWallets($params = array()) { - return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetBalances($params = array()) { - return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetOrders($params = array()) { - return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function privateGetFills($params = array()) { - return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function privateGetDeposits($params = array()) { - return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetWithdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetWsToken($params = array()) { - return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privatePostWallets($params = array()) { - return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function privatePostOrders($params = array()) { - return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function privatePostOrdersTest($params = array()) { - return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function privatePostWithdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function privateDeleteOrders($params = array()) { - return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); + public function privateGetTradingFees($params = array()) { + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); } } diff --git a/php/async/abstract/geniusyield.php b/php/async/abstract/geniusyield.php index 1ad8b0cd7ceb..f8c05fa0f57b 100644 --- a/php/async/abstract/geniusyield.php +++ b/php/async/abstract/geniusyield.php @@ -7,136 +7,16 @@ abstract class geniusyield extends \ccxt\async\Exchange { - public function public_get_ping($params = array()) { - return $this->request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); + public function private_get_markets($params = array()) { + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); } - public function public_get_time($params = array()) { - return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); + public function private_get_trading_fees($params = array()) { + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); } - public function public_get_exchange($params = array()) { - return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); + public function privateGetMarkets($params = array()) { + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); } - public function public_get_assets($params = array()) { - return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_markets($params = array()) { - return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_tickers($params = array()) { - return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_candles($params = array()) { - return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_trades($params = array()) { - return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function public_get_orderbook($params = array()) { - return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_user($params = array()) { - return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_wallets($params = array()) { - return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_balances($params = array()) { - return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_orders($params = array()) { - return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function private_get_fills($params = array()) { - return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function private_get_deposits($params = array()) { - return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_withdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_get_wstoken($params = array()) { - return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function private_post_wallets($params = array()) { - return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function private_post_orders($params = array()) { - return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function private_post_orders_test($params = array()) { - return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function private_post_withdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function private_delete_orders($params = array()) { - return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); - } - public function publicGetPing($params = array()) { - return $this->request('ping', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetTime($params = array()) { - return $this->request('time', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetExchange($params = array()) { - return $this->request('exchange', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetAssets($params = array()) { - return $this->request('assets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetMarkets($params = array()) { - return $this->request('markets', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetTickers($params = array()) { - return $this->request('tickers', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetCandles($params = array()) { - return $this->request('candles', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetTrades($params = array()) { - return $this->request('trades', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function publicGetOrderbook($params = array()) { - return $this->request('orderbook', 'public', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetUser($params = array()) { - return $this->request('user', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetWallets($params = array()) { - return $this->request('wallets', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetBalances($params = array()) { - return $this->request('balances', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetOrders($params = array()) { - return $this->request('orders', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function privateGetFills($params = array()) { - return $this->request('fills', 'private', 'GET', $params, null, null, array("cost" => 0.1)); - } - public function privateGetDeposits($params = array()) { - return $this->request('deposits', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetWithdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privateGetWsToken($params = array()) { - return $this->request('wsToken', 'private', 'GET', $params, null, null, array("cost" => 1)); - } - public function privatePostWallets($params = array()) { - return $this->request('wallets', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function privatePostOrders($params = array()) { - return $this->request('orders', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function privatePostOrdersTest($params = array()) { - return $this->request('orders/test', 'private', 'POST', $params, null, null, array("cost" => 0.1)); - } - public function privatePostWithdrawals($params = array()) { - return $this->request('withdrawals', 'private', 'POST', $params, null, null, array("cost" => 1)); - } - public function privateDeleteOrders($params = array()) { - return $this->request('orders', 'private', 'DELETE', $params, null, null, array("cost" => 0.1)); + public function privateGetTradingFees($params = array()) { + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); } } diff --git a/php/async/geniusyield.php b/php/async/geniusyield.php new file mode 100644 index 000000000000..737ad25ebe69 --- /dev/null +++ b/php/async/geniusyield.php @@ -0,0 +1,1916 @@ +deep_extend(parent::describe(), array( + 'id' => 'geniusyield', + 'name' => 'Genius Yield', + 'countries' => array( 'CH' ), + 'rateLimit' => 1000, + 'version' => 'v0', + 'pro' => false, + 'dex' => true, + 'certified' => false, + 'requiresWeb3' => true, + 'has' => array( + 'CORS' => null, + 'spot' => true, + 'margin' => false, + 'swap' => false, + 'future' => false, + 'option' => false, + 'addMargin' => false, + 'cancelAllOrders' => true, + 'cancelOrder' => true, + 'cancelOrders' => false, + 'closeAllPositions' => false, + 'closePosition' => false, + 'createDepositAddress' => false, + 'createOrder' => true, + 'createReduceOnlyOrder' => false, + 'createStopLimitOrder' => true, + 'createStopMarketOrder' => true, + 'createStopOrder' => true, + 'fetchBalance' => true, + 'fetchBorrowRateHistories' => false, + 'fetchBorrowRateHistory' => false, + 'fetchClosedOrders' => true, + 'fetchCrossBorrowRate' => false, + 'fetchCrossBorrowRates' => false, + 'fetchCurrencies' => true, + 'fetchDeposit' => true, + 'fetchDepositAddress' => true, + 'fetchDepositAddresses' => false, + 'fetchDepositAddressesByNetwork' => false, + 'fetchDeposits' => true, + 'fetchFundingHistory' => false, + 'fetchFundingRate' => false, + 'fetchFundingRateHistory' => false, + 'fetchFundingRates' => false, + 'fetchIndexOHLCV' => false, + 'fetchIsolatedBorrowRate' => false, + 'fetchIsolatedBorrowRates' => false, + 'fetchLeverage' => false, + 'fetchLeverageTiers' => false, + 'fetchMarginMode' => false, + 'fetchMarkets' => true, + 'fetchMarkOHLCV' => false, + 'fetchMyTrades' => true, + 'fetchOHLCV' => true, + 'fetchOpenInterestHistory' => false, + 'fetchOpenOrders' => true, + 'fetchOrder' => true, + 'fetchOrderBook' => true, + 'fetchOrders' => false, + 'fetchPosition' => false, + 'fetchPositionHistory' => false, + 'fetchPositionMode' => false, + 'fetchPositions' => false, + 'fetchPositionsForSymbol' => false, + 'fetchPositionsHistory' => false, + 'fetchPositionsRisk' => false, + 'fetchPremiumIndexOHLCV' => false, + 'fetchStatus' => true, + 'fetchTicker' => true, + 'fetchTickers' => true, + 'fetchTime' => true, + 'fetchTrades' => true, + 'fetchTradingFee' => false, + 'fetchTradingFees' => true, + 'fetchTransactions' => false, + 'fetchWithdrawal' => true, + 'fetchWithdrawals' => true, + 'reduceMargin' => false, + 'sandbox' => true, + 'setLeverage' => false, + 'setMarginMode' => false, + 'setPositionMode' => false, + 'transfer' => false, + 'withdraw' => true, + ), + 'timeframes' => array( + '1m' => '1m', + '5m' => '5m', + '15m' => '15m', + '30m' => '30m', + '1h' => '1h', + '6h' => '6h', + '1d' => '1d', + ), + 'urls' => array( + 'test' => array( + 'MATIC' => 'https://api-sandbox-matic.geniusyield.io', + ), + 'logo' => 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api' => array( + 'MATIC' => 'https://api-matic.geniusyield.io', + ), + 'www' => 'https://geniusyield.io', + 'doc' => array( + 'https://api-docs-v3.geniusyield.io/', + ), + ), + 'api' => array( + 'public' => array( + 'get' => array( + 'ping' => 1, + 'time' => 1, + 'exchange' => 1, + 'assets' => 1, + 'markets' => 1, + 'tickers' => 1, + 'candles' => 1, + 'trades' => 1, + 'orderbook' => 1, + ), + ), + 'private' => array( + 'get' => array( + 'user' => 1, + 'wallets' => 1, + 'balances' => 1, + 'orders' => 0.1, + 'fills' => 0.1, + 'deposits' => 1, + 'withdrawals' => 1, + 'wsToken' => 1, + ), + 'post' => array( + 'wallets' => 1, + 'orders' => 0.1, + 'orders/test' => 0.1, + 'withdrawals' => 1, + ), + 'delete' => array( + 'orders' => 0.1, + ), + ), + ), + 'options' => array( + 'defaultTimeInForce' => 'gtc', + 'defaultSelfTradePrevention' => 'cn', + 'network' => 'MATIC', + ), + 'exceptions' => array( + 'exact' => array( + 'INVALID_ORDER_QUANTITY' => '\\ccxt\\InvalidOrder', + 'INSUFFICIENT_FUNDS' => '\\ccxt\\InsufficientFunds', + 'SERVICE_UNAVAILABLE' => '\\ccxt\\ExchangeNotAvailable', + 'EXCEEDED_RATE_LIMIT' => '\\ccxt\\DDoSProtection', + 'INVALID_PARAMETER' => '\\ccxt\\BadRequest', + 'WALLET_NOT_ASSOCIATED' => '\\ccxt\\InvalidAddress', + 'INVALID_WALLET_SIGNATURE' => '\\ccxt\\AuthenticationError', + ), + ), + 'requiredCredentials' => array( + 'walletAddress' => true, + 'privateKey' => true, + 'apiKey' => true, + 'secret' => true, + ), + 'precisionMode' => TICK_SIZE, + 'paddingMode' => PAD_WITH_ZERO, + 'commonCurrencies' => array(), + )); + } + + public function price_to_precision($symbol, $price) { + // + // we override priceToPrecision to fix the following issue + // https://github.com/ccxt/ccxt/issues/13367 + // array("code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\" => all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes") + // + $market = $this->market($symbol); + $info = $this->safe_value($market, 'info', array()); + $quoteAssetPrecision = $this->safe_integer($info, 'quoteAssetPrecision'); + $price = $this->decimal_to_precision($price, ROUND, $market['precision']['price'], $this->precisionMode); + return $this->decimal_to_precision($price, TRUNCATE, $quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); + } + + public function fetch_markets($params = array ()): PromiseInterface { + return Async\async(function () use ($params) { + /** + * retrieves data on all markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-markets + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} an array of objects representing market data + */ + $response = Async\await($this->publicGetMarkets ($params)); + // + // array( + // array( + // "market" => "ETH-USDC", + // "type" => "hybrid", + // "status" => "activeHybrid", + // "baseAsset" => "ETH", + // "baseAssetPrecision" => "8", + // "quoteAsset" => "USDC", + // "quoteAssetPrecision" => "8", + // "makerFeeRate" => "0.0000", + // "takerFeeRate" => "0.2500", + // "takerIdexFeeRate" => "0.0500", + // "takerLiquidityProviderFeeRate" => "0.2000", + // "tickSize" => "0.01000000" + // ), + // ) + // + $response2 = Async\await($this->publicGetExchange ()); + // + // { + // "timeZone" => "UTC", + // "serverTime" => "1654460599952", + // "maticDepositContractAddress" => "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + // "maticCustodyContractAddress" => "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + // "maticUsdPrice" => "0.60", + // "gasPrice" => "180", + // "volume24hUsd" => "10015814.46", + // "totalVolumeUsd" => "1589273533.28", + // "totalTrades" => "1534904", + // "totalValueLockedUsd" => "12041929.44", + // "geniusyieldStakingValueLockedUsd" => "20133816.98", + // "geniusyieldTokenAddress" => "0x9Cb74C8032b007466865f060ad2c46145d45553D", + // "geniusyieldUsdPrice" => "0.07", + // "geniusyieldMarketCapUsd" => "48012346.00", + // "makerFeeRate" => "0.0000", + // "takerFeeRate" => "0.0025", + // "takerIdexFeeRate" => "0.0005", + // "takerLiquidityProviderFeeRate" => "0.0020", + // "makerTradeMinimum" => "10.00000000", + // "takerTradeMinimum" => "1.00000000", + // "withdrawMinimum" => "0.50000000", + // "liquidityAdditionMinimum" => "0.50000000", + // "liquidityRemovalMinimum" => "0.40000000", + // "blockConfirmationDelay" => "64" + // } + // + $maker = $this->safe_number($response2, 'makerFeeRate'); + $taker = $this->safe_number($response2, 'takerFeeRate'); + $makerMin = $this->safe_string($response2, 'makerTradeMinimum'); + $takerMin = $this->safe_string($response2, 'takerTradeMinimum'); + $minCostETH = $this->parse_number(Precise::string_min($makerMin, $takerMin)); + $result = array(); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $marketId = $this->safe_string($entry, 'market'); + $baseId = $this->safe_string($entry, 'baseAsset'); + $quoteId = $this->safe_string($entry, 'quoteAsset'); + $base = $this->safe_currency_code($baseId); + $quote = $this->safe_currency_code($quoteId); + $basePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'baseAssetPrecision'))); + $quotePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'quoteAssetPrecision'))); + $status = $this->safe_string($entry, 'status'); + $minCost = null; + if ($quote === 'ETH') { + $minCost = $minCostETH; + } + $result[] = array( + 'id' => $marketId, + 'symbol' => $base . '/' . $quote, + 'base' => $base, + 'quote' => $quote, + 'settle' => null, + 'baseId' => $baseId, + 'quoteId' => $quoteId, + 'settleId' => null, + 'type' => 'spot', + 'spot' => true, + 'margin' => false, + 'swap' => false, + 'future' => false, + 'option' => false, + 'active' => ($status !== 'inactive'), + 'contract' => false, + 'linear' => null, + 'inverse' => null, + 'taker' => $taker, + 'maker' => $maker, + 'contractSize' => null, + 'expiry' => null, + 'expiryDatetime' => null, + 'strike' => null, + 'optionType' => null, + 'precision' => array( + 'amount' => $basePrecision, + 'price' => $this->safe_number($entry, 'tickSize'), + ), + 'limits' => array( + 'leverage' => array( + 'min' => null, + 'max' => null, + ), + 'amount' => array( + 'min' => $basePrecision, + 'max' => null, + ), + 'price' => array( + 'min' => $quotePrecision, + 'max' => null, + ), + 'cost' => array( + 'min' => $minCost, + 'max' => null, + ), + ), + 'created' => null, + 'info' => $entry, + ); + } + return $result; + }) (); + } + + public function fetch_ticker(string $symbol, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbol, $params) { + /** + * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string} $symbol unified $symbol of the $market to fetch the $ticker for + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=$ticker-structure $ticker structure~ + */ + Async\await($this->load_markets()); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + ); + // array( + // { + // "market" => "DIL-ETH", + // "time" => 1598367493008, + // "open" => "0.09695361", + // "high" => "0.10245881", + // "low" => "0.09572507", + // "close" => "0.09917079", + // "closeQuantity" => "0.71320950", + // "baseVolume" => "309.17380612", + // "quoteVolume" => "30.57633981", + // "percentChange" => "2.28", + // "numTrades" => 205, + // "ask" => "0.09910476", + // "bid" => "0.09688340", + // "sequence" => 3902 + // } + // ) + $response = Async\await($this->publicGetTickers ($this->extend($request, $params))); + $ticker = $this->safe_dict($response, 0); + return $this->parse_ticker($ticker, $market); + }) (); + } + + public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbols, $params) { + /** + * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~ + */ + Async\await($this->load_markets()); + // array( + // array( + // "market" => "DIL-ETH", + // "time" => 1598367493008, + // "open" => "0.09695361", + // "high" => "0.10245881", + // "low" => "0.09572507", + // "close" => "0.09917079", + // "closeQuantity" => "0.71320950", + // "baseVolume" => "309.17380612", + // "quoteVolume" => "30.57633981", + // "percentChange" => "2.28", + // "numTrades" => 205, + // "ask" => "0.09910476", + // "bid" => "0.09688340", + // "sequence" => 3902 + // ), ... + // ) + $response = Async\await($this->publicGetTickers ($params)); + return $this->parse_tickers($response, $symbols); + }) (); + } + + public function parse_ticker(array $ticker, ?array $market = null): array { + // { + // "market" => "DIL-ETH", + // "time" => 1598367493008, + // "open" => "0.09695361", + // "high" => "0.10245881", + // "low" => "0.09572507", + // "close" => "0.09917079", + // "closeQuantity" => "0.71320950", + // "baseVolume" => "309.17380612", + // "quoteVolume" => "30.57633981", + // "percentChange" => "2.28", + // "numTrades" => 205, + // "ask" => "0.09910476", + // "bid" => "0.09688340", + // "sequence" => 3902 + // } + $marketId = $this->safe_string($ticker, 'market'); + $market = $this->safe_market($marketId, $market, '-'); + $symbol = $market['symbol']; + $timestamp = $this->safe_integer($ticker, 'time'); + $close = $this->safe_string($ticker, 'close'); + return $this->safe_ticker(array( + 'symbol' => $symbol, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'high' => $this->safe_string($ticker, 'high'), + 'low' => $this->safe_string($ticker, 'low'), + 'bid' => $this->safe_string($ticker, 'bid'), + 'bidVolume' => null, + 'ask' => $this->safe_string($ticker, 'ask'), + 'askVolume' => null, + 'vwap' => null, + 'open' => $this->safe_string($ticker, 'open'), + 'close' => $close, + 'last' => $close, + 'previousClose' => null, + 'change' => null, + 'percentage' => $this->safe_string($ticker, 'percentChange'), + 'average' => null, + 'baseVolume' => $this->safe_string($ticker, 'baseVolume'), + 'quoteVolume' => $this->safe_string($ticker, 'quoteVolume'), + 'info' => $ticker, + ), $market); + } + + public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) { + /** + * fetches historical candlestick data containing the open, high, low, and close price, and the volume of a $market + * @see https://api-docs-v3.geniusyield.io/#get-candles + * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for + * @param {string} $timeframe the length of time each candle represents + * @param {int} [$since] timestamp in ms of the earliest candle to fetch + * @param {int} [$limit] the maximum amount of candles to fetch + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {int[][]} A list of candles ordered, open, high, low, close, volume + */ + Async\await($this->load_markets()); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + 'interval' => $timeframe, + ); + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = min ($limit, 1000); + } + $response = Async\await($this->publicGetCandles ($this->extend($request, $params))); + if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { + // array( + // array( + // "start" => 1598345580000, + // "open" => "0.09771286", + // "high" => "0.09771286", + // "low" => "0.09771286", + // "close" => "0.09771286", + // "volume" => "1.45340410", + // "sequence" => 3853 + // ), ... + // ) + return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit); + } else { + // array("nextTime":1595536440000) + return array(); + } + }) (); + } + + public function parse_ohlcv($ohlcv, ?array $market = null): array { + // { + // "start" => 1598345580000, + // "open" => "0.09771286", + // "high" => "0.09771286", + // "low" => "0.09771286", + // "close" => "0.09771286", + // "volume" => "1.45340410", + // "sequence" => 3853 + // } + $timestamp = $this->safe_integer($ohlcv, 'start'); + $open = $this->safe_number($ohlcv, 'open'); + $high = $this->safe_number($ohlcv, 'high'); + $low = $this->safe_number($ohlcv, 'low'); + $close = $this->safe_number($ohlcv, 'close'); + $volume = $this->safe_number($ohlcv, 'volume'); + return array( $timestamp, $open, $high, $low, $close, $volume ); + } + + public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbol, $since, $limit, $params) { + /** + * get the list of most recent trades for a particular $symbol + * @see https://api-docs-v3.geniusyield.io/#get-trades + * @param {string} $symbol unified $symbol of the $market to fetch trades for + * @param {int} [$since] timestamp in ms of the earliest trade to fetch + * @param {int} [$limit] the maximum amount of trades to fetch + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~ + */ + Async\await($this->load_markets()); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + ); + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = min ($limit, 1000); + } + // array( + // array( + // "fillId" => "b5467d00-b13e-3fa9-8216-dd66735550fc", + // "price" => "0.09771286", + // "quantity" => "1.45340410", + // "quoteQuantity" => "0.14201627", + // "time" => 1598345638994, + // "makerSide" => "buy", + // "sequence" => 3853 + // ), ... + // ) + $response = Async\await($this->publicGetTrades ($this->extend($request, $params))); + return $this->parse_trades($response, $market, $since, $limit); + }) (); + } + + public function parse_trade(array $trade, ?array $market = null): array { + // + // public trades + // { + // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + // "price":"0.20377008", + // "quantity":"47.58448728", + // "quoteQuantity":"9.69629509", + // "time":1642091300873, + // "makerSide":"buy", + // "type":"hybrid", // one of either => "orderBook", "hybrid", or "pool" + // "sequence":31876 + // } + // + // private trades + // { + // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + // "price":"0.20717368", + // "quantity":"15.00000000", + // "quoteQuantity":"3.10760523", + // "orderBookQuantity":"0.00000003", + // "orderBookQuoteQuantity":"0.00000001", + // "poolQuantity":"14.99999997", + // "poolQuoteQuantity":"3.10760522", + // "time":1642083351215, + // "makerSide":"sell", + // "sequence":31795, + // "market":"Genius Yield-USDC", + // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + // "side":"buy", + // "fee":"0.03749989", + // "feeAsset":"Genius Yield", + // "gas":"0.40507261", + // "liquidity":"taker", + // "type":"hybrid", + // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + // "txStatus":"mined" + // } + // + $id = $this->safe_string($trade, 'fillId'); + $priceString = $this->safe_string($trade, 'price'); + $amountString = $this->safe_string($trade, 'quantity'); + $costString = $this->safe_string($trade, 'quoteQuantity'); + $timestamp = $this->safe_integer($trade, 'time'); + $marketId = $this->safe_string($trade, 'market'); + $symbol = $this->safe_symbol($marketId, $market, '-'); + // this code handles the duality of public vs private trades + $makerSide = $this->safe_string($trade, 'makerSide'); + $oppositeSide = ($makerSide === 'buy') ? 'sell' : 'buy'; + $side = $this->safe_string($trade, 'side', $oppositeSide); + $takerOrMaker = $this->safe_string($trade, 'liquidity', 'taker'); + $feeCostString = $this->safe_string($trade, 'fee'); + $fee = null; + if ($feeCostString !== null) { + $feeCurrencyId = $this->safe_string($trade, 'feeAsset'); + $fee = array( + 'cost' => $feeCostString, + 'currency' => $this->safe_currency_code($feeCurrencyId), + ); + } + $orderId = $this->safe_string($trade, 'orderId'); + return $this->safe_trade(array( + 'info' => $trade, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'symbol' => $symbol, + 'id' => $id, + 'order' => $orderId, + 'type' => 'limit', + 'side' => $side, + 'takerOrMaker' => $takerOrMaker, + 'price' => $priceString, + 'amount' => $amountString, + 'cost' => $costString, + 'fee' => $fee, + ), $market); + } + + public function fetch_trading_fees($params = array ()): PromiseInterface { + return Async\async(function () use ($params) { + /** + * fetch the trading fees for multiple markets + * @see https://api-docs-v3.geniusyield.io/#get-api-account + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~ indexed by market symbols + */ + $this->check_required_credentials(); + Async\await($this->load_markets()); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + ); + $response = null; + $response = Async\await($this->privateGetUser ($this->extend($request, $params))); + // + // { + // "depositEnabled" => true, + // "orderEnabled" => true, + // "cancelEnabled" => true, + // "withdrawEnabled" => true, + // "totalPortfolioValueUsd" => "0.00", + // "makerFeeRate" => "0.0000", + // "takerFeeRate" => "0.0025", + // "takerIdexFeeRate" => "0.0005", + // "takerLiquidityProviderFeeRate" => "0.0020" + // } + // + $maker = $this->safe_number($response, 'makerFeeRate'); + $taker = $this->safe_number($response, 'takerFeeRate'); + $result = array(); + for ($i = 0; $i < count($this->symbols); $i++) { + $symbol = $this->symbols[$i]; + $result[$symbol] = array( + 'info' => $response, + 'symbol' => $symbol, + 'maker' => $maker, + 'taker' => $taker, + 'percentage' => true, + 'tierBased' => false, + ); + } + return $result; + }) (); + } + + public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbol, $limit, $params) { + /** + * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + * @see https://api-docs-v3.geniusyield.io/#get-order-books + * @param {string} $symbol unified $symbol of the $market to fetch the order book for + * @param {int} [$limit] the maximum amount of order book entries to return + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols + */ + Async\await($this->load_markets()); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + 'level' => 2, + ); + if ($limit !== null) { + $request['limit'] = $limit; + } + // { + // "sequence" => 36416753, + // "bids" => array( + // array( '0.09672815', "8.22284267", 1 ), + // array( '0.09672814', "1.83685554", 1 ), + // array( '0.09672143', "4.10962617", 1 ), + // array( '0.09658884', "4.03863759", 1 ), + // array( '0.09653781', "3.35730684", 1 ), + // array( '0.09624660', "2.54163586", 1 ), + // array( '0.09617490', "1.93065030", 1 ) + // ), + // "asks" => array( + // array( '0.09910476', "3.22840154", 1 ), + // array( '0.09940587', "3.39796593", 1 ), + // array( '0.09948189', "4.25088898", 1 ), + // array( '0.09958362', "2.42195784", 1 ), + // array( '0.09974393', "4.25234367", 1 ), + // array( '0.09995250', "3.40192141", 1 ) + // ) + // } + $response = Async\await($this->publicGetOrderbook ($this->extend($request, $params))); + $nonce = $this->safe_integer($response, 'sequence'); + return array( + 'symbol' => $symbol, + 'timestamp' => null, + 'datetime' => null, + 'nonce' => $nonce, + 'bids' => $this->parse_side($response, 'bids'), + 'asks' => $this->parse_side($response, 'asks'), + ); + }) (); + } + + public function parse_side($book, $side) { + $bookSide = $this->safe_value($book, $side, array()); + $result = array(); + for ($i = 0; $i < count($bookSide); $i++) { + $order = $bookSide[$i]; + $price = $this->safe_number($order, 0); + $amount = $this->safe_number($order, 1); + $orderCount = $this->safe_integer($order, 2); + $result[] = array( $price, $amount, $orderCount ); + } + $descending = $side === 'bids'; + return $this->sort_by($result, 0, $descending); + } + + public function fetch_currencies($params = array ()): PromiseInterface { + return Async\async(function () use ($params) { + /** + * fetches all available currencies on an exchange + * @see https://api-docs-v3.geniusyield.io/#get-assets + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} an associative dictionary of currencies + */ + $response = Async\await($this->publicGetAssets ($params)); + // + // array( + // array( + // "name" => "Ethereum", + // "symbol" => "ETH", + // "contractAddress" => "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + // "assetDecimals" => "18", + // "exchangeDecimals" => "8", + // "maticPrice" => "3029.38503483" + // ), + // ) + // + $result = array(); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $name = $this->safe_string($entry, 'name'); + $currencyId = $this->safe_string($entry, 'symbol'); + $code = $this->safe_currency_code($currencyId); + $precision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'exchangeDecimals'))); + $result[$code] = array( + 'id' => $currencyId, + 'code' => $code, + 'info' => $entry, + 'type' => null, + 'name' => $name, + 'active' => null, + 'deposit' => null, + 'withdraw' => null, + 'fee' => null, + 'precision' => $precision, + 'limits' => array( + 'amount' => array( 'min' => $precision, 'max' => null ), + 'withdraw' => array( 'min' => $precision, 'max' => null ), + ), + ); + } + return $result; + }) (); + } + + public function parse_balance($response): array { + $result = array( + 'info' => $response, + 'timestamp' => null, + 'datetime' => null, + ); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $currencyId = $this->safe_string($entry, 'asset'); + $code = $this->safe_currency_code($currencyId); + $account = $this->account(); + $account['total'] = $this->safe_string($entry, 'quantity'); + $account['free'] = $this->safe_string($entry, 'availableForTrade'); + $account['used'] = $this->safe_string($entry, 'locked'); + $result[$code] = $account; + } + return $this->safe_balance($result); + } + + public function fetch_balance($params = array ()): PromiseInterface { + return Async\async(function () use ($params) { + /** + * query for balance and get the amount of funds available for trading or funds locked in orders + * @see https://api-docs-v3.geniusyield.io/#get-balances + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~ + */ + $this->check_required_credentials(); + Async\await($this->load_markets()); + $nonce1 = $this->uuidv1(); + $request = array( + 'nonce' => $nonce1, + 'wallet' => $this->walletAddress, + ); + // array( + // array( + // "asset" => "DIL", + // "quantity" => "0.00000000", + // "availableForTrade" => "0.00000000", + // "locked" => "0.00000000", + // "usdValue" => null + // ), ... + // ) + $extendedRequest = $this->extend($request, $params); + if ($extendedRequest['wallet'] === null) { + throw new BadRequest($this->id . ' fetchBalance() wallet is null, set $this->walletAddress or "address" in params'); + } + $response = null; + try { + $response = Async\await($this->privateGetBalances ($extendedRequest)); + } catch (Exception $e) { + if ($e instanceof InvalidAddress) { + $walletAddress = $extendedRequest['wallet']; + Async\await($this->associate_wallet($walletAddress)); + $response = Async\await($this->privateGetBalances ($extendedRequest)); + } else { + throw $e; + } + } + return $this->parse_balance($response); + }) (); + } + + public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { + return Async\async(function () use ($symbol, $since, $limit, $params) { + /** + * fetch all trades made by the user + * @see https://api-docs-v3.geniusyield.io/#get-fills + * @param {string} $symbol unified $market $symbol + * @param {int} [$since] the earliest time in ms to fetch trades for + * @param {int} [$limit] the maximum number of trades structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~ + */ + $this->check_required_credentials(); + Async\await($this->load_markets()); + $market = null; + $request = array( + 'nonce' => $this->uuidv1(), + 'wallet' => $this->walletAddress, + ); + if ($symbol !== null) { + $market = $this->market($symbol); + $request['market'] = $market['id']; + } + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = $limit; + } + // array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478762, + // "makerSide" => "sell", + // "sequence" => 5053, + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "side" => "buy", + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" + // } + // ) + $extendedRequest = $this->extend($request, $params); + if ($extendedRequest['wallet'] === null) { + throw new BadRequest($this->id . ' fetchMyTrades() $walletAddress is null, set $this->walletAddress or "address" in params'); + } + $response = null; + try { + $response = Async\await($this->privateGetFills ($extendedRequest)); + } catch (Exception $e) { + if ($e instanceof InvalidAddress) { + $walletAddress = $extendedRequest['wallet']; + Async\await($this->associate_wallet($walletAddress)); + $response = Async\await($this->privateGetFills ($extendedRequest)); + } else { + throw $e; + } + } + return $this->parse_trades($response, $market, $since, $limit); + }) (); + } + + public function fetch_order(string $id, ?string $symbol = null, $params = array ()) { + return Async\async(function () use ($id, $symbol, $params) { + /** + * fetches information on an order made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} $symbol unified $symbol of the market the order was made in + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ + */ + $request = array( + 'orderId' => $id, + ); + return Async\await($this->fetch_orders_helper($symbol, null, null, $this->extend($request, $params))); + }) (); + } + + public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbol, $since, $limit, $params) { + /** + * fetch all unfilled currently open orders + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} $symbol unified market $symbol + * @param {int} [$since] the earliest time in ms to fetch open orders for + * @param {int} [$limit] the maximum number of open orders structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ + */ + $request = array( + 'closed' => false, + ); + return Async\await($this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params))); + }) (); + } + + public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($symbol, $since, $limit, $params) { + /** + * fetches information on multiple closed orders made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} $symbol unified market $symbol of the market orders were made in + * @param {int} [$since] the earliest time in ms to fetch orders for + * @param {int} [$limit] the maximum number of order structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ + */ + $request = array( + 'closed' => true, + ); + return Async\await($this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params))); + }) (); + } + + public function fetch_orders_helper(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { + return Async\async(function () use ($symbol, $since, $limit, $params) { + Async\await($this->load_markets()); + $request = array( + 'nonce' => $this->uuidv1(), + 'wallet' => $this->walletAddress, + ); + $market = null; + if ($symbol !== null) { + $market = $this->market($symbol); + $request['market'] = $market['id']; + } + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = $limit; + } + $response = Async\await($this->privateGetOrders ($this->extend($request, $params))); + // fetchClosedOrders / fetchOpenOrders + // array( + // { + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "avgExecutionPrice" => "0.09905990", + // "price" => "1.00000000", + // "fills" => array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" + // } + // ) + // } + // ) + // fetchOrder + // { $market => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "avgExecutionPrice" => "0.09905990", + // "price" => "1.00000000", + // "fills": + // array( { fillId => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" } ) } + if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { + return $this->parse_orders($response, $market, $since, $limit); + } else { + return $this->parse_order($response, $market); + } + }) (); + } + + public function parse_order_status(?string $status) { + // https://docs.geniusyield.io/#order-states-amp-lifecycle + $statuses = array( + 'active' => 'open', + 'partiallyFilled' => 'open', + 'rejected' => 'canceled', + 'filled' => 'closed', + ); + return $this->safe_string($statuses, $status, $status); + } + + public function parse_order(array $order, ?array $market = null): array { + // + // { + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "avgExecutionPrice" => "0.09905990", + // "price" => "1.00000000", + // "fills" => array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" + // } + // ) + // } + // + $timestamp = $this->safe_integer($order, 'time'); + $fills = $this->safe_value($order, 'fills', array()); + $id = $this->safe_string($order, 'orderId'); + $clientOrderId = $this->safe_string($order, 'clientOrderId'); + $marketId = $this->safe_string($order, 'market'); + $side = $this->safe_string($order, 'side'); + $symbol = $this->safe_symbol($marketId, $market, '-'); + $type = $this->safe_string($order, 'type'); + $amount = $this->safe_string($order, 'originalQuantity'); + $filled = $this->safe_string($order, 'executedQuantity'); + $average = $this->safe_string($order, 'avgExecutionPrice'); + $price = $this->safe_string($order, 'price'); + $rawStatus = $this->safe_string($order, 'status'); + $timeInForce = $this->safe_string_upper($order, 'timeInForce'); + $status = $this->parse_order_status($rawStatus); + return $this->safe_order(array( + 'info' => $order, + 'id' => $id, + 'clientOrderId' => $clientOrderId, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'lastTradeTimestamp' => null, + 'symbol' => $symbol, + 'type' => $type, + 'timeInForce' => $timeInForce, + 'postOnly' => null, + 'side' => $side, + 'price' => $price, + 'stopPrice' => null, + 'triggerPrice' => null, + 'amount' => $amount, + 'cost' => null, + 'average' => $average, + 'filled' => $filled, + 'remaining' => null, + 'status' => $status, + 'fee' => null, + 'trades' => $fills, + ), $market); + } + + public function associate_wallet($walletAddress, $params = array ()) { + return Async\async(function () use ($walletAddress, $params) { + $nonce = $this->uuidv1(); + $noPrefix = $this->remove0x_prefix($walletAddress); + $byteArray = array( + $this->base16_to_binary($nonce), + $this->base16_to_binary($noPrefix), + ); + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + // { + // "address" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "totalPortfolioValueUsd" => "0.00", + // "time" => 1598468353626 + // } + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $walletAddress, + ), + 'signature' => $signature, + ); + $result = Async\await($this->privatePostWallets ($request)); + return $result; + }) (); + } + + public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) { + return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) { + /** + * create a trade order, https://docs.geniusyield.io/#create-order + * @see https://api-docs-v3.geniusyield.io/#create-order + * @param {string} $symbol unified $symbol of the $market to create an order in + * @param {string} $type 'market' or 'limit' + * @param {string} $side 'buy' or 'sell' + * @param {float} $amount how much of currency you want to trade in units of base currency + * @param {float} [$price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in $market orders + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @param {bool} [$params->test] set to true to test an order, no order will be created but the $request will be validated + * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~ + */ + $this->check_required_credentials(); + Async\await($this->load_markets()); + $testOrder = $this->safe_bool($params, 'test', false); + $params = $this->omit($params, 'test'); + $market = $this->market($symbol); + $nonce = $this->uuidv1(); + $typeEnum = null; + $stopLossTypeEnums = array( + 'stopLoss' => 3, + 'stopLossLimit' => 4, + 'takeProfit' => 5, + 'takeProfitLimit' => 6, + ); + $stopPriceString = null; + if (($type === 'stopLossLimit') || ($type === 'takeProfitLimit') || (is_array($params) && array_key_exists('stopPrice', $params))) { + if (!(is_array($params) && array_key_exists('stopPrice', $params))) { + throw new BadRequest($this->id . ' createOrder() stopPrice is a required parameter for ' . $type . 'orders'); + } + $stopPriceString = $this->price_to_precision($symbol, $params['stopPrice']); + } + $limitTypeEnums = array( + 'limit' => 1, + 'limitMaker' => 2, + ); + $priceString = null; + $typeLower = strtolower($type); + $limitOrder = mb_strpos($typeLower, 'limit') !== false; + if (is_array($limitTypeEnums) && array_key_exists($type, $limitTypeEnums)) { + $typeEnum = $limitTypeEnums[$type]; + $priceString = $this->price_to_precision($symbol, $price); + } elseif (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { + $typeEnum = $stopLossTypeEnums[$type]; + $priceString = $this->price_to_precision($symbol, $price); + } elseif ($type === 'market') { + $typeEnum = 0; + } else { + throw new BadRequest($this->id . ' ' . $type . ' is not a valid order type'); + } + $amountEnum = 0; // base quantity + if (is_array($params) && array_key_exists('quoteOrderQuantity', $params)) { + if ($type !== 'market') { + throw new NotSupported($this->id . ' createOrder() quoteOrderQuantity is not supported for ' . $type . ' orders, only supported for $market orders'); + } + $amountEnum = 1; + $amount = $this->safe_number($params, 'quoteOrderQuantity'); + } + $sideEnum = ($side === 'buy') ? 0 : 1; + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $network = $this->safe_string($this->options, 'network', 'ETH'); + $orderVersion = $this->get_supported_mapping($network, array( + 'ETH' => 1, + 'BSC' => 2, + 'MATIC' => 4, + )); + $amountString = $this->amount_to_precision($symbol, $amount); + // https://docs.geniusyield.io/#time-in-force + $timeInForceEnums = array( + 'gtc' => 0, + 'ioc' => 2, + 'fok' => 3, + ); + $defaultTimeInForce = $this->safe_string($this->options, 'defaultTimeInForce', 'gtc'); + $timeInForce = $this->safe_string($params, 'timeInForce', $defaultTimeInForce); + $timeInForceEnum = null; + if (is_array($timeInForceEnums) && array_key_exists($timeInForce, $timeInForceEnums)) { + $timeInForceEnum = $timeInForceEnums[$timeInForce]; + } else { + $allOptions = is_array($timeInForceEnums) ? array_keys($timeInForceEnums) : array(); + $asString = implode(', ', $allOptions); + throw new BadRequest($this->id . ' ' . $timeInForce . ' is not a valid $timeInForce, please choose one of ' . $asString); + } + // https://docs.geniusyield.io/#self-trade-prevention + $selfTradePreventionEnums = array( + 'dc' => 0, + 'co' => 1, + 'cn' => 2, + 'cb' => 3, + ); + $defaultSelfTradePrevention = $this->safe_string($this->options, 'defaultSelfTradePrevention', 'cn'); + $selfTradePrevention = $this->safe_string($params, 'selfTradePrevention', $defaultSelfTradePrevention); + $selfTradePreventionEnum = null; + if (is_array($selfTradePreventionEnums) && array_key_exists($selfTradePrevention, $selfTradePreventionEnums)) { + $selfTradePreventionEnum = $selfTradePreventionEnums[$selfTradePrevention]; + } else { + $allOptions = is_array($selfTradePreventionEnums) ? array_keys($selfTradePreventionEnums) : array(); + $asString = implode(', ', $allOptions); + throw new BadRequest($this->id . ' ' . $selfTradePrevention . ' is not a valid $selfTradePrevention, please choose one of ' . $asString); + } + $byteArray = [ + $this->number_to_be($orderVersion, 1), + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + $this->encode($market['id']), + $this->number_to_be($typeEnum, 1), + $this->number_to_be($sideEnum, 1), + $this->encode($amountString), + $this->number_to_be($amountEnum, 1), + ]; + if ($limitOrder) { + $encodedPrice = $this->encode($priceString); + $byteArray[] = $encodedPrice; + } + if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { + $encodedPrice = $this->encode($stopPriceString || $priceString); + $byteArray[] = $encodedPrice; + } + $clientOrderId = $this->safe_string($params, 'clientOrderId'); + if ($clientOrderId !== null) { + $byteArray[] = $this->encode($clientOrderId); + } + $after = array( + $this->number_to_be($timeInForceEnum, 1), + $this->number_to_be($selfTradePreventionEnum, 1), + $this->number_to_be(0, 8), // unused + ); + $allBytes = $this->array_concat($byteArray, $after); + $binary = $this->binary_concat_array($allBytes); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'market' => $market['id'], + 'side' => $side, + 'type' => $type, + 'wallet' => $this->walletAddress, + 'selfTradePrevention' => $selfTradePrevention, + ), + 'signature' => $signature, + ); + if ($type !== 'market') { + $request['parameters']['timeInForce'] = $timeInForce; + } + if ($limitOrder) { + $request['parameters']['price'] = $priceString; + } + if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { + $request['parameters']['stopPrice'] = $stopPriceString || $priceString; + } + if ($amountEnum === 0) { + $request['parameters']['quantity'] = $amountString; + } else { + $request['parameters']['quoteOrderQuantity'] = $amountString; + } + if ($clientOrderId !== null) { + $request['parameters']['clientOrderId'] = $clientOrderId; + } + // { + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "price" => "1.00000000", + // "fills" => array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txStatus" => "pending" + // } + // ), + // "avgExecutionPrice" => "0.09905990" + // } + // we don't use extend here because it is a signed endpoint + $response = null; + if ($testOrder) { + $response = Async\await($this->privatePostOrdersTest ($request)); + } else { + $response = Async\await($this->privatePostOrders ($request)); + } + return $this->parse_order($response, $market); + }) (); + } + + public function withdraw(string $code, float $amount, string $address, $tag = null, $params = array ()) { + return Async\async(function () use ($code, $amount, $address, $tag, $params) { + /** + * make a withdrawal + * @see https://api-docs-v3.geniusyield.io/#withdraw-funds + * @param {string} $code unified $currency $code + * @param {float} $amount the $amount to withdraw + * @param {string} $address the $address to withdraw to + * @param {string} $tag + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~ + */ + list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params); + $this->check_required_credentials(); + Async\await($this->load_markets()); + $nonce = $this->uuidv1(); + $amountString = $this->currency_to_precision($code, $amount); + $currency = $this->currency($code); + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $byteArray = [ + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + $this->encode($currency['id']), + $this->encode($amountString), + $this->number_to_be(1, 1), // bool set to true + ]; + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $address, + 'asset' => $currency['id'], + 'quantity' => $amountString, + ), + 'signature' => $signature, + ); + $response = Async\await($this->privatePostWithdrawals ($request)); + // + // { + // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset" => "ETH", + // "assetContractAddress" => "0x0000000000000000000000000000000000000000", + // "quantity" => "0.20000000", + // "time" => 1598962883190, + // "fee" => "0.00024000", + // "txStatus" => "pending", + // "txId" => null + // } + // + return $this->parse_transaction($response, $currency); + }) (); + } + + public function cancel_all_orders(?string $symbol = null, $params = array ()) { + return Async\async(function () use ($symbol, $params) { + /** + * cancel all open orders + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are cancelled when $symbol is not null + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ + */ + $this->check_required_credentials(); + Async\await($this->load_markets()); + $market = null; + if ($symbol !== null) { + $market = $this->market($symbol); + } + $nonce = $this->uuidv1(); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + ), + ); + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $byteArray = array( + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + ); + if ($market !== null) { + $byteArray[] = $this->encode($market['id']); + $request['parameters']['market'] = $market['id']; + } + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request['signature'] = $signature; + // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) + $response = Async\await($this->privateDeleteOrders ($this->extend($request, $params))); + return $this->parse_orders($response, $market); + }) (); + } + + public function cancel_order(string $id, ?string $symbol = null, $params = array ()) { + return Async\async(function () use ($id, $symbol, $params) { + /** + * cancels an open order + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} $id order $id + * @param {string} $symbol unified $symbol of the $market the order was made in + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ + */ + $this->check_required_credentials(); + Async\await($this->load_markets()); + $market = null; + if ($symbol !== null) { + $market = $this->market($symbol); + } + $nonce = $this->uuidv1(); + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $byteArray = array( + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + $this->encode($id), + ); + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + 'orderId' => $id, + ), + 'signature' => $signature, + ); + // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) + $response = Async\await($this->privateDeleteOrders ($this->extend($request, $params))); + $canceledOrder = $this->safe_dict($response, 0); + return $this->parse_order($canceledOrder, $market); + }) (); + } + + public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) { + $errorCode = $this->safe_string($response, 'code'); + $message = $this->safe_string($response, 'message'); + if ($errorCode !== null) { + $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $message); + throw new ExchangeError($this->id . ' ' . $message); + } + return null; + } + + public function fetch_deposit(string $id, ?string $code = null, $params = array ()) { + return Async\async(function () use ($id, $code, $params) { + /** + * fetch information on a deposit + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} $id deposit $id + * @param {string} $code not used by geniusyield fetchDeposit () + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ + */ + Async\await($this->load_markets()); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + 'depositId' => $id, + ); + $response = Async\await($this->privateGetDeposits ($this->extend($request, $params))); + return $this->parse_transaction($response); + }) (); + } + + public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($code, $since, $limit, $params) { + /** + * fetch all deposits made to an account + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} $code unified currency $code + * @param {int} [$since] the earliest time in ms to fetch deposits for + * @param {int} [$limit] the maximum number of deposits structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ + */ + $params = $this->extend(array( + 'method' => 'privateGetDeposits', + ), $params); + return Async\await($this->fetch_transactions_helper($code, $since, $limit, $params)); + }) (); + } + + public function fetch_status($params = array ()) { + return Async\async(function () use ($params) { + /** + * the latest known information on the availability of the exchange API + * @see https://api-docs-v3.geniusyield.io/#get-ping + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=exchange-status-structure status structure~ + */ + $response = Async\await($this->publicGetPing ($params)); + return array( + 'status' => 'ok', // if there's no Errors, status = 'ok' + 'updated' => null, + 'eta' => null, + 'url' => null, + 'info' => $response, + ); + }) (); + } + + public function fetch_time($params = array ()) { + return Async\async(function () use ($params) { + /** + * fetches the current integer timestamp in milliseconds from the exchange server + * @see https://api-docs-v3.geniusyield.io/#get-time + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {int} the current integer timestamp in milliseconds from the exchange server + */ + $response = Async\await($this->publicGetTime ($params)); + // + // array( serverTime => "1655258263236" ) + // + return $this->safe_integer($response, 'serverTime'); + }) (); + } + + public function fetch_withdrawal(string $id, ?string $code = null, $params = array ()) { + return Async\async(function () use ($id, $code, $params) { + /** + * fetch data on a currency withdrawal via the withdrawal $id + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} $id withdrawal $id + * @param {string} $code not used by geniusyield.fetchWithdrawal + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ + */ + Async\await($this->load_markets()); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + 'withdrawalId' => $id, + ); + $response = Async\await($this->privateGetWithdrawals ($this->extend($request, $params))); + return $this->parse_transaction($response); + }) (); + } + + public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { + return Async\async(function () use ($code, $since, $limit, $params) { + /** + * fetch all withdrawals made from an account + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} $code unified currency $code + * @param {int} [$since] the earliest time in ms to fetch withdrawals for + * @param {int} [$limit] the maximum number of withdrawals structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ + */ + $params = $this->extend(array( + 'method' => 'privateGetWithdrawals', + ), $params); + return Async\await($this->fetch_transactions_helper($code, $since, $limit, $params)); + }) (); + } + + public function fetch_transactions_helper(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()) { + return Async\async(function () use ($code, $since, $limit, $params) { + Async\await($this->load_markets()); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + ); + $currency = null; + if ($code !== null) { + $currency = $this->currency($code); + $request['asset'] = $currency['id']; + } + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = $limit; + } + // array( + // { + // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + // "asset" => "ETH", + // "quantity" => "1.00000000", + // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime" => 1598865853000, + // "confirmationTime" => 1598865930231 + // } + // ) + $method = $params['method']; + $params = $this->omit($params, 'method'); + $response = null; + if ($method === 'privateGetDeposits') { + $response = Async\await($this->privateGetDeposits ($this->extend($request, $params))); + } elseif ($method === 'privateGetWithdrawals') { + $response = Async\await($this->privateGetWithdrawals ($this->extend($request, $params))); + } else { + throw new NotSupported($this->id . ' fetchTransactionsHelper() not support this method'); + } + return $this->parse_transactions($response, $currency, $since, $limit); + }) (); + } + + public function parse_transaction_status(?string $status) { + $statuses = array( + 'mined' => 'ok', + ); + return $this->safe_string($statuses, $status, $status); + } + + public function parse_transaction(array $transaction, ?array $currency = null): array { + // + // fetchDeposits + // + // { + // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + // "asset" => "ETH", + // "quantity" => "1.00000000", + // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime" => 1598865853000, + // "confirmationTime" => 1598865930231 + // } + // + // fetchWithdrwalas + // + // { + // "withdrawalId" => "a62d8760-ec4d-11ea-9fa6-47904c19499b", + // "asset" => "ETH", + // "assetContractAddress" => "0x0000000000000000000000000000000000000000", + // "quantity" => "0.20000000", + // "time" => 1598962883288, + // "fee" => "0.00024000", + // "txId" => "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + // "txStatus" => "mined" + // } + // + // withdraw + // + // { + // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset" => "ETH", + // "assetContractAddress" => "0x0000000000000000000000000000000000000000", + // "quantity" => "0.20000000", + // "time" => 1598962883190, + // "fee" => "0.00024000", + // "txStatus" => "pending", + // "txId" => null + // } + // + $type = null; + if (is_array($transaction) && array_key_exists('depositId', $transaction)) { + $type = 'deposit'; + } elseif ((is_array($transaction) && array_key_exists('withdrawId', $transaction)) || (is_array($transaction) && array_key_exists('withdrawalId', $transaction))) { + $type = 'withdrawal'; + } + $id = $this->safe_string_2($transaction, 'depositId', 'withdrawId'); + $id = $this->safe_string($transaction, 'withdrawalId', $id); + $code = $this->safe_currency_code($this->safe_string($transaction, 'asset'), $currency); + $amount = $this->safe_number($transaction, 'quantity'); + $txid = $this->safe_string($transaction, 'txId'); + $timestamp = $this->safe_integer_2($transaction, 'txTime', 'time'); + $fee = null; + if (is_array($transaction) && array_key_exists('fee', $transaction)) { + $fee = array( + 'cost' => $this->safe_number($transaction, 'fee'), + 'currency' => 'ETH', + ); + } + $rawStatus = $this->safe_string($transaction, 'txStatus'); + $status = $this->parse_transaction_status($rawStatus); + $updated = $this->safe_integer($transaction, 'confirmationTime'); + return array( + 'info' => $transaction, + 'id' => $id, + 'txid' => $txid, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'network' => null, + 'address' => null, + 'addressTo' => null, + 'addressFrom' => null, + 'tag' => null, + 'tagTo' => null, + 'tagFrom' => null, + 'type' => $type, + 'amount' => $amount, + 'currency' => $code, + 'status' => $status, + 'updated' => $updated, + 'comment' => null, + 'internal' => null, + 'fee' => $fee, + ); + } + + public function calculate_rate_limiter_cost($api, $method, $path, $params, $config = array ()) { + $hasApiKey = ($this->apiKey !== null); + $hasSecret = ($this->secret !== null); + $hasWalletAddress = ($this->walletAddress !== null); + $hasPrivateKey = ($this->privateKey !== null); + $defaultCost = $this->safe_value($config, 'cost', 1); + $authenticated = $hasApiKey && $hasSecret && $hasWalletAddress && $hasPrivateKey; + return $authenticated ? ($defaultCost / 2) : $defaultCost; + } + + public function fetch_deposit_address(?string $code = null, $params = array ()) { + return Async\async(function () use ($code, $params) { + /** + * fetch the Polygon address of the wallet + * @see https://api-docs-v3.geniusyield.io/#get-wallets + * @param {string} $code not used by geniusyield + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~ + */ + $request = array(); + $request['nonce'] = $this->uuidv1(); + $response = Async\await($this->privateGetWallets ($this->extend($request, $params))); + // + // array( + // array( + // address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd => "0.00", + // time => "1678342148086" + // ), + // { + // address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd => "15.90", + // time => "1691697811659" + // } + // ) + // + return $this->parse_deposit_address($response); + }) (); + } + + public function parse_deposit_address($depositAddress, ?array $currency = null) { + // + // array( + // array( + // $address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd => "0.00", + // time => "1678342148086" + // ), + // { + // $address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd => "15.90", + // time => "1691697811659" + // } + // ) + // + $length = count($depositAddress); + $entry = $this->safe_dict($depositAddress, $length - 1); + $address = $this->safe_string($entry, 'address'); + $this->check_address($address); + return array( + 'info' => $depositAddress, + 'currency' => null, + 'address' => $address, + 'tag' => null, + 'network' => 'MATIC', + ); + } + + public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) { + $network = $this->safe_string($this->options, 'network', 'ETH'); + $version = $this->safe_string($this->options, 'version', 'v1'); + $url = $this->urls['api'][$network] . '/' . $version . '/' . $path; + $keys = is_array($params) ? array_keys($params) : array(); + $length = count($keys); + $query = null; + if ($length > 0) { + if ($method === 'GET') { + $query = $this->urlencode($params); + $url = $url . '?' . $query; + } else { + $body = $this->json($params); + } + } + $headers = array( + 'Content-Type' => 'application/json', + ); + if ($this->apiKey !== null) { + $headers['Genius Yield-API-Key'] = $this->apiKey; + } + if ($api === 'private') { + $payload = null; + if ($method === 'GET') { + $payload = $query; + } else { + $payload = $body; + } + $headers['Genius Yield-HMAC-Signature'] = $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha256', 'hex'); + } + return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers ); + } + + public function remove0x_prefix($hexData) { + if (mb_substr($hexData, 0, 2 - 0) === '0x') { + return mb_substr($hexData, 2); + } else { + return $hexData; + } + } + + public function hash_message($message) { + // takes a hex encoded $message + $binaryMessage = $this->base16_to_binary($this->remove0x_prefix($message)); + $prefix = $this->encode('\x19Ethereum Signed Message:\n' . $binaryMessage->byteLength); + return '0x' . $this->hash($this->binary_concat($prefix, $binaryMessage), 'keccak', 'hex'); + } + + public function sign_hash($hash, $privateKey) { + $signature = $this->ecdsa(mb_substr($hash, -64), mb_substr($privateKey, -64), 'secp256k1', null); + return array( + 'r' => '0x' . $signature['r'], + 's' => '0x' . $signature['s'], + 'v' => 27 . $signature['v'], + ); + } + + public function sign_message($message, $privateKey) { + return $this->sign_hash($this->hash_message($message), mb_substr($privateKey, -64)); + } + + public function sign_message_string($message, $privateKey) { + // still takes the input hex string + // same but returns a string instead of an object + $signature = $this->sign_message($message, $privateKey); + return $signature['r'] . $this->remove0x_prefix($signature['s']) . bin2hex($this->number_to_be($signature['v'], 1)); + } +} diff --git a/php/geniusyield.php b/php/geniusyield.php new file mode 100644 index 000000000000..49ad0881fc14 --- /dev/null +++ b/php/geniusyield.php @@ -0,0 +1,1855 @@ +deep_extend(parent::describe(), array( + 'id' => 'geniusyield', + 'name' => 'Genius Yield', + 'countries' => array( 'CH' ), + 'rateLimit' => 1000, + 'version' => 'v0', + 'pro' => false, + 'dex' => true, + 'certified' => false, + 'requiresWeb3' => true, + 'has' => array( + 'CORS' => null, + 'spot' => true, + 'margin' => false, + 'swap' => false, + 'future' => false, + 'option' => false, + 'addMargin' => false, + 'cancelAllOrders' => true, + 'cancelOrder' => true, + 'cancelOrders' => false, + 'closeAllPositions' => false, + 'closePosition' => false, + 'createDepositAddress' => false, + 'createOrder' => true, + 'createReduceOnlyOrder' => false, + 'createStopLimitOrder' => true, + 'createStopMarketOrder' => true, + 'createStopOrder' => true, + 'fetchBalance' => true, + 'fetchBorrowRateHistories' => false, + 'fetchBorrowRateHistory' => false, + 'fetchClosedOrders' => true, + 'fetchCrossBorrowRate' => false, + 'fetchCrossBorrowRates' => false, + 'fetchCurrencies' => true, + 'fetchDeposit' => true, + 'fetchDepositAddress' => true, + 'fetchDepositAddresses' => false, + 'fetchDepositAddressesByNetwork' => false, + 'fetchDeposits' => true, + 'fetchFundingHistory' => false, + 'fetchFundingRate' => false, + 'fetchFundingRateHistory' => false, + 'fetchFundingRates' => false, + 'fetchIndexOHLCV' => false, + 'fetchIsolatedBorrowRate' => false, + 'fetchIsolatedBorrowRates' => false, + 'fetchLeverage' => false, + 'fetchLeverageTiers' => false, + 'fetchMarginMode' => false, + 'fetchMarkets' => true, + 'fetchMarkOHLCV' => false, + 'fetchMyTrades' => true, + 'fetchOHLCV' => true, + 'fetchOpenInterestHistory' => false, + 'fetchOpenOrders' => true, + 'fetchOrder' => true, + 'fetchOrderBook' => true, + 'fetchOrders' => false, + 'fetchPosition' => false, + 'fetchPositionHistory' => false, + 'fetchPositionMode' => false, + 'fetchPositions' => false, + 'fetchPositionsForSymbol' => false, + 'fetchPositionsHistory' => false, + 'fetchPositionsRisk' => false, + 'fetchPremiumIndexOHLCV' => false, + 'fetchStatus' => true, + 'fetchTicker' => true, + 'fetchTickers' => true, + 'fetchTime' => true, + 'fetchTrades' => true, + 'fetchTradingFee' => false, + 'fetchTradingFees' => true, + 'fetchTransactions' => false, + 'fetchWithdrawal' => true, + 'fetchWithdrawals' => true, + 'reduceMargin' => false, + 'sandbox' => true, + 'setLeverage' => false, + 'setMarginMode' => false, + 'setPositionMode' => false, + 'transfer' => false, + 'withdraw' => true, + ), + 'timeframes' => array( + '1m' => '1m', + '5m' => '5m', + '15m' => '15m', + '30m' => '30m', + '1h' => '1h', + '6h' => '6h', + '1d' => '1d', + ), + 'urls' => array( + 'test' => array( + 'MATIC' => 'https://api-sandbox-matic.geniusyield.io', + ), + 'logo' => 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api' => array( + 'MATIC' => 'https://api-matic.geniusyield.io', + ), + 'www' => 'https://geniusyield.io', + 'doc' => array( + 'https://api-docs-v3.geniusyield.io/', + ), + ), + 'api' => array( + 'public' => array( + 'get' => array( + 'ping' => 1, + 'time' => 1, + 'exchange' => 1, + 'assets' => 1, + 'markets' => 1, + 'tickers' => 1, + 'candles' => 1, + 'trades' => 1, + 'orderbook' => 1, + ), + ), + 'private' => array( + 'get' => array( + 'user' => 1, + 'wallets' => 1, + 'balances' => 1, + 'orders' => 0.1, + 'fills' => 0.1, + 'deposits' => 1, + 'withdrawals' => 1, + 'wsToken' => 1, + ), + 'post' => array( + 'wallets' => 1, + 'orders' => 0.1, + 'orders/test' => 0.1, + 'withdrawals' => 1, + ), + 'delete' => array( + 'orders' => 0.1, + ), + ), + ), + 'options' => array( + 'defaultTimeInForce' => 'gtc', + 'defaultSelfTradePrevention' => 'cn', + 'network' => 'MATIC', + ), + 'exceptions' => array( + 'exact' => array( + 'INVALID_ORDER_QUANTITY' => '\\ccxt\\InvalidOrder', + 'INSUFFICIENT_FUNDS' => '\\ccxt\\InsufficientFunds', + 'SERVICE_UNAVAILABLE' => '\\ccxt\\ExchangeNotAvailable', + 'EXCEEDED_RATE_LIMIT' => '\\ccxt\\DDoSProtection', + 'INVALID_PARAMETER' => '\\ccxt\\BadRequest', + 'WALLET_NOT_ASSOCIATED' => '\\ccxt\\InvalidAddress', + 'INVALID_WALLET_SIGNATURE' => '\\ccxt\\AuthenticationError', + ), + ), + 'requiredCredentials' => array( + 'walletAddress' => true, + 'privateKey' => true, + 'apiKey' => true, + 'secret' => true, + ), + 'precisionMode' => TICK_SIZE, + 'paddingMode' => PAD_WITH_ZERO, + 'commonCurrencies' => array(), + )); + } + + public function price_to_precision($symbol, $price) { + // + // we override priceToPrecision to fix the following issue + // https://github.com/ccxt/ccxt/issues/13367 + // array("code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\" => all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes") + // + $market = $this->market($symbol); + $info = $this->safe_value($market, 'info', array()); + $quoteAssetPrecision = $this->safe_integer($info, 'quoteAssetPrecision'); + $price = $this->decimal_to_precision($price, ROUND, $market['precision']['price'], $this->precisionMode); + return $this->decimal_to_precision($price, TRUNCATE, $quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); + } + + public function fetch_markets($params = array ()): array { + /** + * retrieves data on all markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-markets + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} an array of objects representing market data + */ + $response = $this->publicGetMarkets ($params); + // + // array( + // array( + // "market" => "ETH-USDC", + // "type" => "hybrid", + // "status" => "activeHybrid", + // "baseAsset" => "ETH", + // "baseAssetPrecision" => "8", + // "quoteAsset" => "USDC", + // "quoteAssetPrecision" => "8", + // "makerFeeRate" => "0.0000", + // "takerFeeRate" => "0.2500", + // "takerIdexFeeRate" => "0.0500", + // "takerLiquidityProviderFeeRate" => "0.2000", + // "tickSize" => "0.01000000" + // ), + // ) + // + $response2 = $this->publicGetExchange (); + // + // { + // "timeZone" => "UTC", + // "serverTime" => "1654460599952", + // "maticDepositContractAddress" => "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + // "maticCustodyContractAddress" => "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + // "maticUsdPrice" => "0.60", + // "gasPrice" => "180", + // "volume24hUsd" => "10015814.46", + // "totalVolumeUsd" => "1589273533.28", + // "totalTrades" => "1534904", + // "totalValueLockedUsd" => "12041929.44", + // "geniusyieldStakingValueLockedUsd" => "20133816.98", + // "geniusyieldTokenAddress" => "0x9Cb74C8032b007466865f060ad2c46145d45553D", + // "geniusyieldUsdPrice" => "0.07", + // "geniusyieldMarketCapUsd" => "48012346.00", + // "makerFeeRate" => "0.0000", + // "takerFeeRate" => "0.0025", + // "takerIdexFeeRate" => "0.0005", + // "takerLiquidityProviderFeeRate" => "0.0020", + // "makerTradeMinimum" => "10.00000000", + // "takerTradeMinimum" => "1.00000000", + // "withdrawMinimum" => "0.50000000", + // "liquidityAdditionMinimum" => "0.50000000", + // "liquidityRemovalMinimum" => "0.40000000", + // "blockConfirmationDelay" => "64" + // } + // + $maker = $this->safe_number($response2, 'makerFeeRate'); + $taker = $this->safe_number($response2, 'takerFeeRate'); + $makerMin = $this->safe_string($response2, 'makerTradeMinimum'); + $takerMin = $this->safe_string($response2, 'takerTradeMinimum'); + $minCostETH = $this->parse_number(Precise::string_min($makerMin, $takerMin)); + $result = array(); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $marketId = $this->safe_string($entry, 'market'); + $baseId = $this->safe_string($entry, 'baseAsset'); + $quoteId = $this->safe_string($entry, 'quoteAsset'); + $base = $this->safe_currency_code($baseId); + $quote = $this->safe_currency_code($quoteId); + $basePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'baseAssetPrecision'))); + $quotePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'quoteAssetPrecision'))); + $status = $this->safe_string($entry, 'status'); + $minCost = null; + if ($quote === 'ETH') { + $minCost = $minCostETH; + } + $result[] = array( + 'id' => $marketId, + 'symbol' => $base . '/' . $quote, + 'base' => $base, + 'quote' => $quote, + 'settle' => null, + 'baseId' => $baseId, + 'quoteId' => $quoteId, + 'settleId' => null, + 'type' => 'spot', + 'spot' => true, + 'margin' => false, + 'swap' => false, + 'future' => false, + 'option' => false, + 'active' => ($status !== 'inactive'), + 'contract' => false, + 'linear' => null, + 'inverse' => null, + 'taker' => $taker, + 'maker' => $maker, + 'contractSize' => null, + 'expiry' => null, + 'expiryDatetime' => null, + 'strike' => null, + 'optionType' => null, + 'precision' => array( + 'amount' => $basePrecision, + 'price' => $this->safe_number($entry, 'tickSize'), + ), + 'limits' => array( + 'leverage' => array( + 'min' => null, + 'max' => null, + ), + 'amount' => array( + 'min' => $basePrecision, + 'max' => null, + ), + 'price' => array( + 'min' => $quotePrecision, + 'max' => null, + ), + 'cost' => array( + 'min' => $minCost, + 'max' => null, + ), + ), + 'created' => null, + 'info' => $entry, + ); + } + return $result; + } + + public function fetch_ticker(string $symbol, $params = array ()): array { + /** + * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string} $symbol unified $symbol of the $market to fetch the $ticker for + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=$ticker-structure $ticker structure~ + */ + $this->load_markets(); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + ); + // array( + // { + // "market" => "DIL-ETH", + // "time" => 1598367493008, + // "open" => "0.09695361", + // "high" => "0.10245881", + // "low" => "0.09572507", + // "close" => "0.09917079", + // "closeQuantity" => "0.71320950", + // "baseVolume" => "309.17380612", + // "quoteVolume" => "30.57633981", + // "percentChange" => "2.28", + // "numTrades" => 205, + // "ask" => "0.09910476", + // "bid" => "0.09688340", + // "sequence" => 3902 + // } + // ) + $response = $this->publicGetTickers ($this->extend($request, $params)); + $ticker = $this->safe_dict($response, 0); + return $this->parse_ticker($ticker, $market); + } + + public function fetch_tickers(?array $symbols = null, $params = array ()): array { + /** + * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + * @see https://api-docs-v3.geniusyield.io/#get-tickers + * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~ + */ + $this->load_markets(); + // array( + // array( + // "market" => "DIL-ETH", + // "time" => 1598367493008, + // "open" => "0.09695361", + // "high" => "0.10245881", + // "low" => "0.09572507", + // "close" => "0.09917079", + // "closeQuantity" => "0.71320950", + // "baseVolume" => "309.17380612", + // "quoteVolume" => "30.57633981", + // "percentChange" => "2.28", + // "numTrades" => 205, + // "ask" => "0.09910476", + // "bid" => "0.09688340", + // "sequence" => 3902 + // ), ... + // ) + $response = $this->publicGetTickers ($params); + return $this->parse_tickers($response, $symbols); + } + + public function parse_ticker(array $ticker, ?array $market = null): array { + // { + // "market" => "DIL-ETH", + // "time" => 1598367493008, + // "open" => "0.09695361", + // "high" => "0.10245881", + // "low" => "0.09572507", + // "close" => "0.09917079", + // "closeQuantity" => "0.71320950", + // "baseVolume" => "309.17380612", + // "quoteVolume" => "30.57633981", + // "percentChange" => "2.28", + // "numTrades" => 205, + // "ask" => "0.09910476", + // "bid" => "0.09688340", + // "sequence" => 3902 + // } + $marketId = $this->safe_string($ticker, 'market'); + $market = $this->safe_market($marketId, $market, '-'); + $symbol = $market['symbol']; + $timestamp = $this->safe_integer($ticker, 'time'); + $close = $this->safe_string($ticker, 'close'); + return $this->safe_ticker(array( + 'symbol' => $symbol, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'high' => $this->safe_string($ticker, 'high'), + 'low' => $this->safe_string($ticker, 'low'), + 'bid' => $this->safe_string($ticker, 'bid'), + 'bidVolume' => null, + 'ask' => $this->safe_string($ticker, 'ask'), + 'askVolume' => null, + 'vwap' => null, + 'open' => $this->safe_string($ticker, 'open'), + 'close' => $close, + 'last' => $close, + 'previousClose' => null, + 'change' => null, + 'percentage' => $this->safe_string($ticker, 'percentChange'), + 'average' => null, + 'baseVolume' => $this->safe_string($ticker, 'baseVolume'), + 'quoteVolume' => $this->safe_string($ticker, 'quoteVolume'), + 'info' => $ticker, + ), $market); + } + + public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): array { + /** + * fetches historical candlestick data containing the open, high, low, and close price, and the volume of a $market + * @see https://api-docs-v3.geniusyield.io/#get-candles + * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for + * @param {string} $timeframe the length of time each candle represents + * @param {int} [$since] timestamp in ms of the earliest candle to fetch + * @param {int} [$limit] the maximum amount of candles to fetch + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {int[][]} A list of candles ordered, open, high, low, close, volume + */ + $this->load_markets(); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + 'interval' => $timeframe, + ); + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = min ($limit, 1000); + } + $response = $this->publicGetCandles ($this->extend($request, $params)); + if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { + // array( + // array( + // "start" => 1598345580000, + // "open" => "0.09771286", + // "high" => "0.09771286", + // "low" => "0.09771286", + // "close" => "0.09771286", + // "volume" => "1.45340410", + // "sequence" => 3853 + // ), ... + // ) + return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit); + } else { + // array("nextTime":1595536440000) + return array(); + } + } + + public function parse_ohlcv($ohlcv, ?array $market = null): array { + // { + // "start" => 1598345580000, + // "open" => "0.09771286", + // "high" => "0.09771286", + // "low" => "0.09771286", + // "close" => "0.09771286", + // "volume" => "1.45340410", + // "sequence" => 3853 + // } + $timestamp = $this->safe_integer($ohlcv, 'start'); + $open = $this->safe_number($ohlcv, 'open'); + $high = $this->safe_number($ohlcv, 'high'); + $low = $this->safe_number($ohlcv, 'low'); + $close = $this->safe_number($ohlcv, 'close'); + $volume = $this->safe_number($ohlcv, 'volume'); + return array( $timestamp, $open, $high, $low, $close, $volume ); + } + + public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): array { + /** + * get the list of most recent trades for a particular $symbol + * @see https://api-docs-v3.geniusyield.io/#get-trades + * @param {string} $symbol unified $symbol of the $market to fetch trades for + * @param {int} [$since] timestamp in ms of the earliest trade to fetch + * @param {int} [$limit] the maximum amount of trades to fetch + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~ + */ + $this->load_markets(); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + ); + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = min ($limit, 1000); + } + // array( + // array( + // "fillId" => "b5467d00-b13e-3fa9-8216-dd66735550fc", + // "price" => "0.09771286", + // "quantity" => "1.45340410", + // "quoteQuantity" => "0.14201627", + // "time" => 1598345638994, + // "makerSide" => "buy", + // "sequence" => 3853 + // ), ... + // ) + $response = $this->publicGetTrades ($this->extend($request, $params)); + return $this->parse_trades($response, $market, $since, $limit); + } + + public function parse_trade(array $trade, ?array $market = null): array { + // + // public trades + // { + // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + // "price":"0.20377008", + // "quantity":"47.58448728", + // "quoteQuantity":"9.69629509", + // "time":1642091300873, + // "makerSide":"buy", + // "type":"hybrid", // one of either => "orderBook", "hybrid", or "pool" + // "sequence":31876 + // } + // + // private trades + // { + // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + // "price":"0.20717368", + // "quantity":"15.00000000", + // "quoteQuantity":"3.10760523", + // "orderBookQuantity":"0.00000003", + // "orderBookQuoteQuantity":"0.00000001", + // "poolQuantity":"14.99999997", + // "poolQuoteQuantity":"3.10760522", + // "time":1642083351215, + // "makerSide":"sell", + // "sequence":31795, + // "market":"Genius Yield-USDC", + // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + // "side":"buy", + // "fee":"0.03749989", + // "feeAsset":"Genius Yield", + // "gas":"0.40507261", + // "liquidity":"taker", + // "type":"hybrid", + // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + // "txStatus":"mined" + // } + // + $id = $this->safe_string($trade, 'fillId'); + $priceString = $this->safe_string($trade, 'price'); + $amountString = $this->safe_string($trade, 'quantity'); + $costString = $this->safe_string($trade, 'quoteQuantity'); + $timestamp = $this->safe_integer($trade, 'time'); + $marketId = $this->safe_string($trade, 'market'); + $symbol = $this->safe_symbol($marketId, $market, '-'); + // this code handles the duality of public vs private trades + $makerSide = $this->safe_string($trade, 'makerSide'); + $oppositeSide = ($makerSide === 'buy') ? 'sell' : 'buy'; + $side = $this->safe_string($trade, 'side', $oppositeSide); + $takerOrMaker = $this->safe_string($trade, 'liquidity', 'taker'); + $feeCostString = $this->safe_string($trade, 'fee'); + $fee = null; + if ($feeCostString !== null) { + $feeCurrencyId = $this->safe_string($trade, 'feeAsset'); + $fee = array( + 'cost' => $feeCostString, + 'currency' => $this->safe_currency_code($feeCurrencyId), + ); + } + $orderId = $this->safe_string($trade, 'orderId'); + return $this->safe_trade(array( + 'info' => $trade, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'symbol' => $symbol, + 'id' => $id, + 'order' => $orderId, + 'type' => 'limit', + 'side' => $side, + 'takerOrMaker' => $takerOrMaker, + 'price' => $priceString, + 'amount' => $amountString, + 'cost' => $costString, + 'fee' => $fee, + ), $market); + } + + public function fetch_trading_fees($params = array ()): array { + /** + * fetch the trading fees for multiple markets + * @see https://api-docs-v3.geniusyield.io/#get-api-account + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~ indexed by market symbols + */ + $this->check_required_credentials(); + $this->load_markets(); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + ); + $response = null; + $response = $this->privateGetUser ($this->extend($request, $params)); + // + // { + // "depositEnabled" => true, + // "orderEnabled" => true, + // "cancelEnabled" => true, + // "withdrawEnabled" => true, + // "totalPortfolioValueUsd" => "0.00", + // "makerFeeRate" => "0.0000", + // "takerFeeRate" => "0.0025", + // "takerIdexFeeRate" => "0.0005", + // "takerLiquidityProviderFeeRate" => "0.0020" + // } + // + $maker = $this->safe_number($response, 'makerFeeRate'); + $taker = $this->safe_number($response, 'takerFeeRate'); + $result = array(); + for ($i = 0; $i < count($this->symbols); $i++) { + $symbol = $this->symbols[$i]; + $result[$symbol] = array( + 'info' => $response, + 'symbol' => $symbol, + 'maker' => $maker, + 'taker' => $taker, + 'percentage' => true, + 'tierBased' => false, + ); + } + return $result; + } + + public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): array { + /** + * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + * @see https://api-docs-v3.geniusyield.io/#get-order-books + * @param {string} $symbol unified $symbol of the $market to fetch the order book for + * @param {int} [$limit] the maximum amount of order book entries to return + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols + */ + $this->load_markets(); + $market = $this->market($symbol); + $request = array( + 'market' => $market['id'], + 'level' => 2, + ); + if ($limit !== null) { + $request['limit'] = $limit; + } + // { + // "sequence" => 36416753, + // "bids" => array( + // array( '0.09672815', "8.22284267", 1 ), + // array( '0.09672814', "1.83685554", 1 ), + // array( '0.09672143', "4.10962617", 1 ), + // array( '0.09658884', "4.03863759", 1 ), + // array( '0.09653781', "3.35730684", 1 ), + // array( '0.09624660', "2.54163586", 1 ), + // array( '0.09617490', "1.93065030", 1 ) + // ), + // "asks" => array( + // array( '0.09910476', "3.22840154", 1 ), + // array( '0.09940587', "3.39796593", 1 ), + // array( '0.09948189', "4.25088898", 1 ), + // array( '0.09958362', "2.42195784", 1 ), + // array( '0.09974393', "4.25234367", 1 ), + // array( '0.09995250', "3.40192141", 1 ) + // ) + // } + $response = $this->publicGetOrderbook ($this->extend($request, $params)); + $nonce = $this->safe_integer($response, 'sequence'); + return array( + 'symbol' => $symbol, + 'timestamp' => null, + 'datetime' => null, + 'nonce' => $nonce, + 'bids' => $this->parse_side($response, 'bids'), + 'asks' => $this->parse_side($response, 'asks'), + ); + } + + public function parse_side($book, $side) { + $bookSide = $this->safe_value($book, $side, array()); + $result = array(); + for ($i = 0; $i < count($bookSide); $i++) { + $order = $bookSide[$i]; + $price = $this->safe_number($order, 0); + $amount = $this->safe_number($order, 1); + $orderCount = $this->safe_integer($order, 2); + $result[] = array( $price, $amount, $orderCount ); + } + $descending = $side === 'bids'; + return $this->sort_by($result, 0, $descending); + } + + public function fetch_currencies($params = array ()): ?array { + /** + * fetches all available currencies on an exchange + * @see https://api-docs-v3.geniusyield.io/#get-assets + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} an associative dictionary of currencies + */ + $response = $this->publicGetAssets ($params); + // + // array( + // array( + // "name" => "Ethereum", + // "symbol" => "ETH", + // "contractAddress" => "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + // "assetDecimals" => "18", + // "exchangeDecimals" => "8", + // "maticPrice" => "3029.38503483" + // ), + // ) + // + $result = array(); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $name = $this->safe_string($entry, 'name'); + $currencyId = $this->safe_string($entry, 'symbol'); + $code = $this->safe_currency_code($currencyId); + $precision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'exchangeDecimals'))); + $result[$code] = array( + 'id' => $currencyId, + 'code' => $code, + 'info' => $entry, + 'type' => null, + 'name' => $name, + 'active' => null, + 'deposit' => null, + 'withdraw' => null, + 'fee' => null, + 'precision' => $precision, + 'limits' => array( + 'amount' => array( 'min' => $precision, 'max' => null ), + 'withdraw' => array( 'min' => $precision, 'max' => null ), + ), + ); + } + return $result; + } + + public function parse_balance($response): array { + $result = array( + 'info' => $response, + 'timestamp' => null, + 'datetime' => null, + ); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $currencyId = $this->safe_string($entry, 'asset'); + $code = $this->safe_currency_code($currencyId); + $account = $this->account(); + $account['total'] = $this->safe_string($entry, 'quantity'); + $account['free'] = $this->safe_string($entry, 'availableForTrade'); + $account['used'] = $this->safe_string($entry, 'locked'); + $result[$code] = $account; + } + return $this->safe_balance($result); + } + + public function fetch_balance($params = array ()): array { + /** + * query for balance and get the amount of funds available for trading or funds locked in orders + * @see https://api-docs-v3.geniusyield.io/#get-balances + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~ + */ + $this->check_required_credentials(); + $this->load_markets(); + $nonce1 = $this->uuidv1(); + $request = array( + 'nonce' => $nonce1, + 'wallet' => $this->walletAddress, + ); + // array( + // array( + // "asset" => "DIL", + // "quantity" => "0.00000000", + // "availableForTrade" => "0.00000000", + // "locked" => "0.00000000", + // "usdValue" => null + // ), ... + // ) + $extendedRequest = $this->extend($request, $params); + if ($extendedRequest['wallet'] === null) { + throw new BadRequest($this->id . ' fetchBalance() wallet is null, set $this->walletAddress or "address" in params'); + } + $response = null; + try { + $response = $this->privateGetBalances ($extendedRequest); + } catch (Exception $e) { + if ($e instanceof InvalidAddress) { + $walletAddress = $extendedRequest['wallet']; + $this->associate_wallet($walletAddress); + $response = $this->privateGetBalances ($extendedRequest); + } else { + throw $e; + } + } + return $this->parse_balance($response); + } + + public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { + /** + * fetch all trades made by the user + * @see https://api-docs-v3.geniusyield.io/#get-fills + * @param {string} $symbol unified $market $symbol + * @param {int} [$since] the earliest time in ms to fetch trades for + * @param {int} [$limit] the maximum number of trades structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~ + */ + $this->check_required_credentials(); + $this->load_markets(); + $market = null; + $request = array( + 'nonce' => $this->uuidv1(), + 'wallet' => $this->walletAddress, + ); + if ($symbol !== null) { + $market = $this->market($symbol); + $request['market'] = $market['id']; + } + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = $limit; + } + // array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478762, + // "makerSide" => "sell", + // "sequence" => 5053, + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "side" => "buy", + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" + // } + // ) + $extendedRequest = $this->extend($request, $params); + if ($extendedRequest['wallet'] === null) { + throw new BadRequest($this->id . ' fetchMyTrades() $walletAddress is null, set $this->walletAddress or "address" in params'); + } + $response = null; + try { + $response = $this->privateGetFills ($extendedRequest); + } catch (Exception $e) { + if ($e instanceof InvalidAddress) { + $walletAddress = $extendedRequest['wallet']; + $this->associate_wallet($walletAddress); + $response = $this->privateGetFills ($extendedRequest); + } else { + throw $e; + } + } + return $this->parse_trades($response, $market, $since, $limit); + } + + public function fetch_order(string $id, ?string $symbol = null, $params = array ()) { + /** + * fetches information on an order made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} $symbol unified $symbol of the market the order was made in + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ + */ + $request = array( + 'orderId' => $id, + ); + return $this->fetch_orders_helper($symbol, null, null, $this->extend($request, $params)); + } + + public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array { + /** + * fetch all unfilled currently open orders + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} $symbol unified market $symbol + * @param {int} [$since] the earliest time in ms to fetch open orders for + * @param {int} [$limit] the maximum number of open orders structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ + */ + $request = array( + 'closed' => false, + ); + return $this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params)); + } + + public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array { + /** + * fetches information on multiple closed orders made by the user + * @see https://api-docs-v3.geniusyield.io/#get-orders + * @param {string} $symbol unified market $symbol of the market orders were made in + * @param {int} [$since] the earliest time in ms to fetch orders for + * @param {int} [$limit] the maximum number of order structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ + */ + $request = array( + 'closed' => true, + ); + return $this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params)); + } + + public function fetch_orders_helper(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { + $this->load_markets(); + $request = array( + 'nonce' => $this->uuidv1(), + 'wallet' => $this->walletAddress, + ); + $market = null; + if ($symbol !== null) { + $market = $this->market($symbol); + $request['market'] = $market['id']; + } + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = $limit; + } + $response = $this->privateGetOrders ($this->extend($request, $params)); + // fetchClosedOrders / fetchOpenOrders + // array( + // { + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "avgExecutionPrice" => "0.09905990", + // "price" => "1.00000000", + // "fills" => array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" + // } + // ) + // } + // ) + // fetchOrder + // { $market => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "avgExecutionPrice" => "0.09905990", + // "price" => "1.00000000", + // "fills": + // array( { fillId => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" } ) } + if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { + return $this->parse_orders($response, $market, $since, $limit); + } else { + return $this->parse_order($response, $market); + } + } + + public function parse_order_status(?string $status) { + // https://docs.geniusyield.io/#order-states-amp-lifecycle + $statuses = array( + 'active' => 'open', + 'partiallyFilled' => 'open', + 'rejected' => 'canceled', + 'filled' => 'closed', + ); + return $this->safe_string($statuses, $status, $status); + } + + public function parse_order(array $order, ?array $market = null): array { + // + // { + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "avgExecutionPrice" => "0.09905990", + // "price" => "1.00000000", + // "fills" => array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + // "txStatus" => "mined" + // } + // ) + // } + // + $timestamp = $this->safe_integer($order, 'time'); + $fills = $this->safe_value($order, 'fills', array()); + $id = $this->safe_string($order, 'orderId'); + $clientOrderId = $this->safe_string($order, 'clientOrderId'); + $marketId = $this->safe_string($order, 'market'); + $side = $this->safe_string($order, 'side'); + $symbol = $this->safe_symbol($marketId, $market, '-'); + $type = $this->safe_string($order, 'type'); + $amount = $this->safe_string($order, 'originalQuantity'); + $filled = $this->safe_string($order, 'executedQuantity'); + $average = $this->safe_string($order, 'avgExecutionPrice'); + $price = $this->safe_string($order, 'price'); + $rawStatus = $this->safe_string($order, 'status'); + $timeInForce = $this->safe_string_upper($order, 'timeInForce'); + $status = $this->parse_order_status($rawStatus); + return $this->safe_order(array( + 'info' => $order, + 'id' => $id, + 'clientOrderId' => $clientOrderId, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'lastTradeTimestamp' => null, + 'symbol' => $symbol, + 'type' => $type, + 'timeInForce' => $timeInForce, + 'postOnly' => null, + 'side' => $side, + 'price' => $price, + 'stopPrice' => null, + 'triggerPrice' => null, + 'amount' => $amount, + 'cost' => null, + 'average' => $average, + 'filled' => $filled, + 'remaining' => null, + 'status' => $status, + 'fee' => null, + 'trades' => $fills, + ), $market); + } + + public function associate_wallet($walletAddress, $params = array ()) { + $nonce = $this->uuidv1(); + $noPrefix = $this->remove0x_prefix($walletAddress); + $byteArray = array( + $this->base16_to_binary($nonce), + $this->base16_to_binary($noPrefix), + ); + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + // { + // "address" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "totalPortfolioValueUsd" => "0.00", + // "time" => 1598468353626 + // } + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $walletAddress, + ), + 'signature' => $signature, + ); + $result = $this->privatePostWallets ($request); + return $result; + } + + public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) { + /** + * create a trade order, https://docs.geniusyield.io/#create-order + * @see https://api-docs-v3.geniusyield.io/#create-order + * @param {string} $symbol unified $symbol of the $market to create an order in + * @param {string} $type 'market' or 'limit' + * @param {string} $side 'buy' or 'sell' + * @param {float} $amount how much of currency you want to trade in units of base currency + * @param {float} [$price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in $market orders + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @param {bool} [$params->test] set to true to test an order, no order will be created but the $request will be validated + * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~ + */ + $this->check_required_credentials(); + $this->load_markets(); + $testOrder = $this->safe_bool($params, 'test', false); + $params = $this->omit($params, 'test'); + $market = $this->market($symbol); + $nonce = $this->uuidv1(); + $typeEnum = null; + $stopLossTypeEnums = array( + 'stopLoss' => 3, + 'stopLossLimit' => 4, + 'takeProfit' => 5, + 'takeProfitLimit' => 6, + ); + $stopPriceString = null; + if (($type === 'stopLossLimit') || ($type === 'takeProfitLimit') || (is_array($params) && array_key_exists('stopPrice', $params))) { + if (!(is_array($params) && array_key_exists('stopPrice', $params))) { + throw new BadRequest($this->id . ' createOrder() stopPrice is a required parameter for ' . $type . 'orders'); + } + $stopPriceString = $this->price_to_precision($symbol, $params['stopPrice']); + } + $limitTypeEnums = array( + 'limit' => 1, + 'limitMaker' => 2, + ); + $priceString = null; + $typeLower = strtolower($type); + $limitOrder = mb_strpos($typeLower, 'limit') !== false; + if (is_array($limitTypeEnums) && array_key_exists($type, $limitTypeEnums)) { + $typeEnum = $limitTypeEnums[$type]; + $priceString = $this->price_to_precision($symbol, $price); + } elseif (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { + $typeEnum = $stopLossTypeEnums[$type]; + $priceString = $this->price_to_precision($symbol, $price); + } elseif ($type === 'market') { + $typeEnum = 0; + } else { + throw new BadRequest($this->id . ' ' . $type . ' is not a valid order type'); + } + $amountEnum = 0; // base quantity + if (is_array($params) && array_key_exists('quoteOrderQuantity', $params)) { + if ($type !== 'market') { + throw new NotSupported($this->id . ' createOrder() quoteOrderQuantity is not supported for ' . $type . ' orders, only supported for $market orders'); + } + $amountEnum = 1; + $amount = $this->safe_number($params, 'quoteOrderQuantity'); + } + $sideEnum = ($side === 'buy') ? 0 : 1; + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $network = $this->safe_string($this->options, 'network', 'ETH'); + $orderVersion = $this->get_supported_mapping($network, array( + 'ETH' => 1, + 'BSC' => 2, + 'MATIC' => 4, + )); + $amountString = $this->amount_to_precision($symbol, $amount); + // https://docs.geniusyield.io/#time-in-force + $timeInForceEnums = array( + 'gtc' => 0, + 'ioc' => 2, + 'fok' => 3, + ); + $defaultTimeInForce = $this->safe_string($this->options, 'defaultTimeInForce', 'gtc'); + $timeInForce = $this->safe_string($params, 'timeInForce', $defaultTimeInForce); + $timeInForceEnum = null; + if (is_array($timeInForceEnums) && array_key_exists($timeInForce, $timeInForceEnums)) { + $timeInForceEnum = $timeInForceEnums[$timeInForce]; + } else { + $allOptions = is_array($timeInForceEnums) ? array_keys($timeInForceEnums) : array(); + $asString = implode(', ', $allOptions); + throw new BadRequest($this->id . ' ' . $timeInForce . ' is not a valid $timeInForce, please choose one of ' . $asString); + } + // https://docs.geniusyield.io/#self-trade-prevention + $selfTradePreventionEnums = array( + 'dc' => 0, + 'co' => 1, + 'cn' => 2, + 'cb' => 3, + ); + $defaultSelfTradePrevention = $this->safe_string($this->options, 'defaultSelfTradePrevention', 'cn'); + $selfTradePrevention = $this->safe_string($params, 'selfTradePrevention', $defaultSelfTradePrevention); + $selfTradePreventionEnum = null; + if (is_array($selfTradePreventionEnums) && array_key_exists($selfTradePrevention, $selfTradePreventionEnums)) { + $selfTradePreventionEnum = $selfTradePreventionEnums[$selfTradePrevention]; + } else { + $allOptions = is_array($selfTradePreventionEnums) ? array_keys($selfTradePreventionEnums) : array(); + $asString = implode(', ', $allOptions); + throw new BadRequest($this->id . ' ' . $selfTradePrevention . ' is not a valid $selfTradePrevention, please choose one of ' . $asString); + } + $byteArray = [ + $this->number_to_be($orderVersion, 1), + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + $this->encode($market['id']), + $this->number_to_be($typeEnum, 1), + $this->number_to_be($sideEnum, 1), + $this->encode($amountString), + $this->number_to_be($amountEnum, 1), + ]; + if ($limitOrder) { + $encodedPrice = $this->encode($priceString); + $byteArray[] = $encodedPrice; + } + if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { + $encodedPrice = $this->encode($stopPriceString || $priceString); + $byteArray[] = $encodedPrice; + } + $clientOrderId = $this->safe_string($params, 'clientOrderId'); + if ($clientOrderId !== null) { + $byteArray[] = $this->encode($clientOrderId); + } + $after = array( + $this->number_to_be($timeInForceEnum, 1), + $this->number_to_be($selfTradePreventionEnum, 1), + $this->number_to_be(0, 8), // unused + ); + $allBytes = $this->array_concat($byteArray, $after); + $binary = $this->binary_concat_array($allBytes); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'market' => $market['id'], + 'side' => $side, + 'type' => $type, + 'wallet' => $this->walletAddress, + 'selfTradePrevention' => $selfTradePrevention, + ), + 'signature' => $signature, + ); + if ($type !== 'market') { + $request['parameters']['timeInForce'] = $timeInForce; + } + if ($limitOrder) { + $request['parameters']['price'] = $priceString; + } + if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { + $request['parameters']['stopPrice'] = $stopPriceString || $priceString; + } + if ($amountEnum === 0) { + $request['parameters']['quantity'] = $amountString; + } else { + $request['parameters']['quoteOrderQuantity'] = $amountString; + } + if ($clientOrderId !== null) { + $request['parameters']['clientOrderId'] = $clientOrderId; + } + // { + // "market" => "DIL-ETH", + // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + // "time" => 1598873478650, + // "status" => "filled", + // "type" => "limit", + // "side" => "buy", + // "originalQuantity" => "0.40000000", + // "executedQuantity" => "0.40000000", + // "cumulativeQuoteQuantity" => "0.03962396", + // "price" => "1.00000000", + // "fills" => array( + // { + // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", + // "price" => "0.09905990", + // "quantity" => "0.40000000", + // "quoteQuantity" => "0.03962396", + // "time" => 1598873478650, + // "makerSide" => "sell", + // "sequence" => 5053, + // "fee" => "0.00080000", + // "feeAsset" => "DIL", + // "gas" => "0.00857497", + // "liquidity" => "taker", + // "txStatus" => "pending" + // } + // ), + // "avgExecutionPrice" => "0.09905990" + // } + // we don't use extend here because it is a signed endpoint + $response = null; + if ($testOrder) { + $response = $this->privatePostOrdersTest ($request); + } else { + $response = $this->privatePostOrders ($request); + } + return $this->parse_order($response, $market); + } + + public function withdraw(string $code, float $amount, string $address, $tag = null, $params = array ()) { + /** + * make a withdrawal + * @see https://api-docs-v3.geniusyield.io/#withdraw-funds + * @param {string} $code unified $currency $code + * @param {float} $amount the $amount to withdraw + * @param {string} $address the $address to withdraw to + * @param {string} $tag + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~ + */ + list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params); + $this->check_required_credentials(); + $this->load_markets(); + $nonce = $this->uuidv1(); + $amountString = $this->currency_to_precision($code, $amount); + $currency = $this->currency($code); + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $byteArray = [ + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + $this->encode($currency['id']), + $this->encode($amountString), + $this->number_to_be(1, 1), // bool set to true + ]; + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $address, + 'asset' => $currency['id'], + 'quantity' => $amountString, + ), + 'signature' => $signature, + ); + $response = $this->privatePostWithdrawals ($request); + // + // { + // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset" => "ETH", + // "assetContractAddress" => "0x0000000000000000000000000000000000000000", + // "quantity" => "0.20000000", + // "time" => 1598962883190, + // "fee" => "0.00024000", + // "txStatus" => "pending", + // "txId" => null + // } + // + return $this->parse_transaction($response, $currency); + } + + public function cancel_all_orders(?string $symbol = null, $params = array ()) { + /** + * cancel all open orders + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are cancelled when $symbol is not null + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ + */ + $this->check_required_credentials(); + $this->load_markets(); + $market = null; + if ($symbol !== null) { + $market = $this->market($symbol); + } + $nonce = $this->uuidv1(); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + ), + ); + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $byteArray = array( + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + ); + if ($market !== null) { + $byteArray[] = $this->encode($market['id']); + $request['parameters']['market'] = $market['id']; + } + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request['signature'] = $signature; + // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) + $response = $this->privateDeleteOrders ($this->extend($request, $params)); + return $this->parse_orders($response, $market); + } + + public function cancel_order(string $id, ?string $symbol = null, $params = array ()) { + /** + * cancels an open order + * @see https://api-docs-v3.geniusyield.io/#cancel-order + * @param {string} $id order $id + * @param {string} $symbol unified $symbol of the $market the order was made in + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ + */ + $this->check_required_credentials(); + $this->load_markets(); + $market = null; + if ($symbol !== null) { + $market = $this->market($symbol); + } + $nonce = $this->uuidv1(); + $walletBytes = $this->remove0x_prefix($this->walletAddress); + $byteArray = array( + $this->base16_to_binary($nonce), + $this->base16_to_binary($walletBytes), + $this->encode($id), + ); + $binary = $this->binary_concat_array($byteArray); + $hash = $this->hash($binary, 'keccak', 'hex'); + $signature = $this->sign_message_string($hash, $this->privateKey); + $request = array( + 'parameters' => array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + 'orderId' => $id, + ), + 'signature' => $signature, + ); + // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) + $response = $this->privateDeleteOrders ($this->extend($request, $params)); + $canceledOrder = $this->safe_dict($response, 0); + return $this->parse_order($canceledOrder, $market); + } + + public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) { + $errorCode = $this->safe_string($response, 'code'); + $message = $this->safe_string($response, 'message'); + if ($errorCode !== null) { + $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $message); + throw new ExchangeError($this->id . ' ' . $message); + } + return null; + } + + public function fetch_deposit(string $id, ?string $code = null, $params = array ()) { + /** + * fetch information on a deposit + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} $id deposit $id + * @param {string} $code not used by geniusyield fetchDeposit () + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ + */ + $this->load_markets(); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + 'depositId' => $id, + ); + $response = $this->privateGetDeposits ($this->extend($request, $params)); + return $this->parse_transaction($response); + } + + public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array { + /** + * fetch all deposits made to an account + * @see https://api-docs-v3.geniusyield.io/#get-deposits + * @param {string} $code unified currency $code + * @param {int} [$since] the earliest time in ms to fetch deposits for + * @param {int} [$limit] the maximum number of deposits structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ + */ + $params = $this->extend(array( + 'method' => 'privateGetDeposits', + ), $params); + return $this->fetch_transactions_helper($code, $since, $limit, $params); + } + + public function fetch_status($params = array ()) { + /** + * the latest known information on the availability of the exchange API + * @see https://api-docs-v3.geniusyield.io/#get-ping + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?id=exchange-status-structure status structure~ + */ + $response = $this->publicGetPing ($params); + return array( + 'status' => 'ok', // if there's no Errors, status = 'ok' + 'updated' => null, + 'eta' => null, + 'url' => null, + 'info' => $response, + ); + } + + public function fetch_time($params = array ()) { + /** + * fetches the current integer timestamp in milliseconds from the exchange server + * @see https://api-docs-v3.geniusyield.io/#get-time + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {int} the current integer timestamp in milliseconds from the exchange server + */ + $response = $this->publicGetTime ($params); + // + // array( serverTime => "1655258263236" ) + // + return $this->safe_integer($response, 'serverTime'); + } + + public function fetch_withdrawal(string $id, ?string $code = null, $params = array ()) { + /** + * fetch data on a currency withdrawal via the withdrawal $id + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} $id withdrawal $id + * @param {string} $code not used by geniusyield.fetchWithdrawal + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ + */ + $this->load_markets(); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + 'withdrawalId' => $id, + ); + $response = $this->privateGetWithdrawals ($this->extend($request, $params)); + return $this->parse_transaction($response); + } + + public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array { + /** + * fetch all withdrawals made from an account + * @see https://api-docs-v3.geniusyield.io/#get-withdrawals + * @param {string} $code unified currency $code + * @param {int} [$since] the earliest time in ms to fetch withdrawals for + * @param {int} [$limit] the maximum number of withdrawals structures to retrieve + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ + */ + $params = $this->extend(array( + 'method' => 'privateGetWithdrawals', + ), $params); + return $this->fetch_transactions_helper($code, $since, $limit, $params); + } + + public function fetch_transactions_helper(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()) { + $this->load_markets(); + $nonce = $this->uuidv1(); + $request = array( + 'nonce' => $nonce, + 'wallet' => $this->walletAddress, + ); + $currency = null; + if ($code !== null) { + $currency = $this->currency($code); + $request['asset'] = $currency['id']; + } + if ($since !== null) { + $request['start'] = $since; + } + if ($limit !== null) { + $request['limit'] = $limit; + } + // array( + // { + // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + // "asset" => "ETH", + // "quantity" => "1.00000000", + // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime" => 1598865853000, + // "confirmationTime" => 1598865930231 + // } + // ) + $method = $params['method']; + $params = $this->omit($params, 'method'); + $response = null; + if ($method === 'privateGetDeposits') { + $response = $this->privateGetDeposits ($this->extend($request, $params)); + } elseif ($method === 'privateGetWithdrawals') { + $response = $this->privateGetWithdrawals ($this->extend($request, $params)); + } else { + throw new NotSupported($this->id . ' fetchTransactionsHelper() not support this method'); + } + return $this->parse_transactions($response, $currency, $since, $limit); + } + + public function parse_transaction_status(?string $status) { + $statuses = array( + 'mined' => 'ok', + ); + return $this->safe_string($statuses, $status, $status); + } + + public function parse_transaction(array $transaction, ?array $currency = null): array { + // + // fetchDeposits + // + // { + // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + // "asset" => "ETH", + // "quantity" => "1.00000000", + // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + // "txTime" => 1598865853000, + // "confirmationTime" => 1598865930231 + // } + // + // fetchWithdrwalas + // + // { + // "withdrawalId" => "a62d8760-ec4d-11ea-9fa6-47904c19499b", + // "asset" => "ETH", + // "assetContractAddress" => "0x0000000000000000000000000000000000000000", + // "quantity" => "0.20000000", + // "time" => 1598962883288, + // "fee" => "0.00024000", + // "txId" => "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + // "txStatus" => "mined" + // } + // + // withdraw + // + // { + // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + // "asset" => "ETH", + // "assetContractAddress" => "0x0000000000000000000000000000000000000000", + // "quantity" => "0.20000000", + // "time" => 1598962883190, + // "fee" => "0.00024000", + // "txStatus" => "pending", + // "txId" => null + // } + // + $type = null; + if (is_array($transaction) && array_key_exists('depositId', $transaction)) { + $type = 'deposit'; + } elseif ((is_array($transaction) && array_key_exists('withdrawId', $transaction)) || (is_array($transaction) && array_key_exists('withdrawalId', $transaction))) { + $type = 'withdrawal'; + } + $id = $this->safe_string_2($transaction, 'depositId', 'withdrawId'); + $id = $this->safe_string($transaction, 'withdrawalId', $id); + $code = $this->safe_currency_code($this->safe_string($transaction, 'asset'), $currency); + $amount = $this->safe_number($transaction, 'quantity'); + $txid = $this->safe_string($transaction, 'txId'); + $timestamp = $this->safe_integer_2($transaction, 'txTime', 'time'); + $fee = null; + if (is_array($transaction) && array_key_exists('fee', $transaction)) { + $fee = array( + 'cost' => $this->safe_number($transaction, 'fee'), + 'currency' => 'ETH', + ); + } + $rawStatus = $this->safe_string($transaction, 'txStatus'); + $status = $this->parse_transaction_status($rawStatus); + $updated = $this->safe_integer($transaction, 'confirmationTime'); + return array( + 'info' => $transaction, + 'id' => $id, + 'txid' => $txid, + 'timestamp' => $timestamp, + 'datetime' => $this->iso8601($timestamp), + 'network' => null, + 'address' => null, + 'addressTo' => null, + 'addressFrom' => null, + 'tag' => null, + 'tagTo' => null, + 'tagFrom' => null, + 'type' => $type, + 'amount' => $amount, + 'currency' => $code, + 'status' => $status, + 'updated' => $updated, + 'comment' => null, + 'internal' => null, + 'fee' => $fee, + ); + } + + public function calculate_rate_limiter_cost($api, $method, $path, $params, $config = array ()) { + $hasApiKey = ($this->apiKey !== null); + $hasSecret = ($this->secret !== null); + $hasWalletAddress = ($this->walletAddress !== null); + $hasPrivateKey = ($this->privateKey !== null); + $defaultCost = $this->safe_value($config, 'cost', 1); + $authenticated = $hasApiKey && $hasSecret && $hasWalletAddress && $hasPrivateKey; + return $authenticated ? ($defaultCost / 2) : $defaultCost; + } + + public function fetch_deposit_address(?string $code = null, $params = array ()) { + /** + * fetch the Polygon address of the wallet + * @see https://api-docs-v3.geniusyield.io/#get-wallets + * @param {string} $code not used by geniusyield + * @param {array} [$params] extra parameters specific to the exchange API endpoint + * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~ + */ + $request = array(); + $request['nonce'] = $this->uuidv1(); + $response = $this->privateGetWallets ($this->extend($request, $params)); + // + // array( + // array( + // address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd => "0.00", + // time => "1678342148086" + // ), + // { + // address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd => "15.90", + // time => "1691697811659" + // } + // ) + // + return $this->parse_deposit_address($response); + } + + public function parse_deposit_address($depositAddress, ?array $currency = null) { + // + // array( + // array( + // $address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + // totalPortfolioValueUsd => "0.00", + // time => "1678342148086" + // ), + // { + // $address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + // totalPortfolioValueUsd => "15.90", + // time => "1691697811659" + // } + // ) + // + $length = count($depositAddress); + $entry = $this->safe_dict($depositAddress, $length - 1); + $address = $this->safe_string($entry, 'address'); + $this->check_address($address); + return array( + 'info' => $depositAddress, + 'currency' => null, + 'address' => $address, + 'tag' => null, + 'network' => 'MATIC', + ); + } + + public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) { + $network = $this->safe_string($this->options, 'network', 'ETH'); + $version = $this->safe_string($this->options, 'version', 'v1'); + $url = $this->urls['api'][$network] . '/' . $version . '/' . $path; + $keys = is_array($params) ? array_keys($params) : array(); + $length = count($keys); + $query = null; + if ($length > 0) { + if ($method === 'GET') { + $query = $this->urlencode($params); + $url = $url . '?' . $query; + } else { + $body = $this->json($params); + } + } + $headers = array( + 'Content-Type' => 'application/json', + ); + if ($this->apiKey !== null) { + $headers['Genius Yield-API-Key'] = $this->apiKey; + } + if ($api === 'private') { + $payload = null; + if ($method === 'GET') { + $payload = $query; + } else { + $payload = $body; + } + $headers['Genius Yield-HMAC-Signature'] = $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha256', 'hex'); + } + return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers ); + } + + public function remove0x_prefix($hexData) { + if (mb_substr($hexData, 0, 2 - 0) === '0x') { + return mb_substr($hexData, 2); + } else { + return $hexData; + } + } + + public function hash_message($message) { + // takes a hex encoded $message + $binaryMessage = $this->base16_to_binary($this->remove0x_prefix($message)); + $prefix = $this->encode('\x19Ethereum Signed Message:\n' . $binaryMessage->byteLength); + return '0x' . $this->hash($this->binary_concat($prefix, $binaryMessage), 'keccak', 'hex'); + } + + public function sign_hash($hash, $privateKey) { + $signature = $this->ecdsa(mb_substr($hash, -64), mb_substr($privateKey, -64), 'secp256k1', null); + return array( + 'r' => '0x' . $signature['r'], + 's' => '0x' . $signature['s'], + 'v' => 27 . $signature['v'], + ); + } + + public function sign_message($message, $privateKey) { + return $this->sign_hash($this->hash_message($message), mb_substr($privateKey, -64)); + } + + public function sign_message_string($message, $privateKey) { + // still takes the input hex string + // same but returns a string instead of an object + $signature = $this->sign_message($message, $privateKey); + return $signature['r'] . $this->remove0x_prefix($signature['s']) . bin2hex($this->number_to_be($signature['v'], 1)); + } +} diff --git a/python/ccxt/abstract/geniusyield.py b/python/ccxt/abstract/geniusyield.py index 4392f685b1b6..6f58a8664e99 100644 --- a/python/ccxt/abstract/geniusyield.py +++ b/python/ccxt/abstract/geniusyield.py @@ -2,25 +2,5 @@ class ImplicitAPI: - public_get_ping = publicGetPing = Entry('ping', 'public', 'GET', {'cost': 1}) - public_get_time = publicGetTime = Entry('time', 'public', 'GET', {'cost': 1}) - public_get_exchange = publicGetExchange = Entry('exchange', 'public', 'GET', {'cost': 1}) - public_get_assets = publicGetAssets = Entry('assets', 'public', 'GET', {'cost': 1}) - public_get_markets = publicGetMarkets = Entry('markets', 'public', 'GET', {'cost': 1}) - public_get_tickers = publicGetTickers = Entry('tickers', 'public', 'GET', {'cost': 1}) - public_get_candles = publicGetCandles = Entry('candles', 'public', 'GET', {'cost': 1}) - public_get_trades = publicGetTrades = Entry('trades', 'public', 'GET', {'cost': 1}) - public_get_orderbook = publicGetOrderbook = Entry('orderbook', 'public', 'GET', {'cost': 1}) - private_get_user = privateGetUser = Entry('user', 'private', 'GET', {'cost': 1}) - private_get_wallets = privateGetWallets = Entry('wallets', 'private', 'GET', {'cost': 1}) - private_get_balances = privateGetBalances = Entry('balances', 'private', 'GET', {'cost': 1}) - private_get_orders = privateGetOrders = Entry('orders', 'private', 'GET', {'cost': 0.1}) - private_get_fills = privateGetFills = Entry('fills', 'private', 'GET', {'cost': 0.1}) - private_get_deposits = privateGetDeposits = Entry('deposits', 'private', 'GET', {'cost': 1}) - private_get_withdrawals = privateGetWithdrawals = Entry('withdrawals', 'private', 'GET', {'cost': 1}) - private_get_wstoken = privateGetWsToken = Entry('wsToken', 'private', 'GET', {'cost': 1}) - private_post_wallets = privatePostWallets = Entry('wallets', 'private', 'POST', {'cost': 1}) - private_post_orders = privatePostOrders = Entry('orders', 'private', 'POST', {'cost': 0.1}) - private_post_orders_test = privatePostOrdersTest = Entry('orders/test', 'private', 'POST', {'cost': 0.1}) - private_post_withdrawals = privatePostWithdrawals = Entry('withdrawals', 'private', 'POST', {'cost': 1}) - private_delete_orders = privateDeleteOrders = Entry('orders', 'private', 'DELETE', {'cost': 0.1}) + private_get_markets = privateGetMarkets = Entry('markets', 'private', 'GET', {'cost': 0}) + private_get_trading_fees = privateGetTradingFees = Entry('trading-fees', 'private', 'GET', {'cost': 0}) diff --git a/python/ccxt/async_support/geniusyield.py b/python/ccxt/async_support/geniusyield.py new file mode 100644 index 000000000000..dec6eee0cbd3 --- /dev/null +++ b/python/ccxt/async_support/geniusyield.py @@ -0,0 +1,1766 @@ +# -*- coding: utf-8 -*- + +# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code + +from ccxt.async_support.base.exchange import Exchange +from ccxt.abstract.geniusyield import ImplicitAPI +import hashlib +from ccxt.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction +from typing import List +from ccxt.base.errors import ExchangeError +from ccxt.base.errors import AuthenticationError +from ccxt.base.errors import BadRequest +from ccxt.base.errors import InsufficientFunds +from ccxt.base.errors import InvalidAddress +from ccxt.base.errors import InvalidOrder +from ccxt.base.errors import NotSupported +from ccxt.base.errors import DDoSProtection +from ccxt.base.errors import ExchangeNotAvailable +from ccxt.base.decimal_to_precision import ROUND +from ccxt.base.decimal_to_precision import TRUNCATE +from ccxt.base.decimal_to_precision import DECIMAL_PLACES +from ccxt.base.decimal_to_precision import TICK_SIZE +from ccxt.base.decimal_to_precision import PAD_WITH_ZERO +from ccxt.base.precise import Precise + + +class geniusyield(Exchange, ImplicitAPI): + + def describe(self): + return self.deep_extend(super(geniusyield, self).describe(), { + 'id': 'geniusyield', + 'name': 'Genius Yield', + 'countries': ['CH'], + 'rateLimit': 1000, + 'version': 'v0', + 'pro': False, + 'dex': True, + 'certified': False, + 'requiresWeb3': True, + 'has': { + 'CORS': None, + 'spot': True, + 'margin': False, + 'swap': False, + 'future': False, + 'option': False, + 'addMargin': False, + 'cancelAllOrders': True, + 'cancelOrder': True, + 'cancelOrders': False, + 'closeAllPositions': False, + 'closePosition': False, + 'createDepositAddress': False, + 'createOrder': True, + 'createReduceOnlyOrder': False, + 'createStopLimitOrder': True, + 'createStopMarketOrder': True, + 'createStopOrder': True, + 'fetchBalance': True, + 'fetchBorrowRateHistories': False, + 'fetchBorrowRateHistory': False, + 'fetchClosedOrders': True, + 'fetchCrossBorrowRate': False, + 'fetchCrossBorrowRates': False, + 'fetchCurrencies': True, + 'fetchDeposit': True, + 'fetchDepositAddress': True, + 'fetchDepositAddresses': False, + 'fetchDepositAddressesByNetwork': False, + 'fetchDeposits': True, + 'fetchFundingHistory': False, + 'fetchFundingRate': False, + 'fetchFundingRateHistory': False, + 'fetchFundingRates': False, + 'fetchIndexOHLCV': False, + 'fetchIsolatedBorrowRate': False, + 'fetchIsolatedBorrowRates': False, + 'fetchLeverage': False, + 'fetchLeverageTiers': False, + 'fetchMarginMode': False, + 'fetchMarkets': True, + 'fetchMarkOHLCV': False, + 'fetchMyTrades': True, + 'fetchOHLCV': True, + 'fetchOpenInterestHistory': False, + 'fetchOpenOrders': True, + 'fetchOrder': True, + 'fetchOrderBook': True, + 'fetchOrders': False, + 'fetchPosition': False, + 'fetchPositionHistory': False, + 'fetchPositionMode': False, + 'fetchPositions': False, + 'fetchPositionsForSymbol': False, + 'fetchPositionsHistory': False, + 'fetchPositionsRisk': False, + 'fetchPremiumIndexOHLCV': False, + 'fetchStatus': True, + 'fetchTicker': True, + 'fetchTickers': True, + 'fetchTime': True, + 'fetchTrades': True, + 'fetchTradingFee': False, + 'fetchTradingFees': True, + 'fetchTransactions': False, + 'fetchWithdrawal': True, + 'fetchWithdrawals': True, + 'reduceMargin': False, + 'sandbox': True, + 'setLeverage': False, + 'setMarginMode': False, + 'setPositionMode': False, + 'transfer': False, + 'withdraw': True, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'MATIC': 'https://api-sandbox-matic.geniusyield.io', + }, + 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api': { + 'MATIC': 'https://api-matic.geniusyield.io', + }, + 'www': 'https://geniusyield.io', + 'doc': [ + 'https://api-docs-v3.geniusyield.io/', + ], + }, + 'api': { + 'public': { + 'get': { + 'ping': 1, + 'time': 1, + 'exchange': 1, + 'assets': 1, + 'markets': 1, + 'tickers': 1, + 'candles': 1, + 'trades': 1, + 'orderbook': 1, + }, + }, + 'private': { + 'get': { + 'user': 1, + 'wallets': 1, + 'balances': 1, + 'orders': 0.1, + 'fills': 0.1, + 'deposits': 1, + 'withdrawals': 1, + 'wsToken': 1, + }, + 'post': { + 'wallets': 1, + 'orders': 0.1, + 'orders/test': 0.1, + 'withdrawals': 1, + }, + 'delete': { + 'orders': 0.1, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'gtc', + 'defaultSelfTradePrevention': 'cn', + 'network': 'MATIC', + }, + 'exceptions': { + 'exact': { + 'INVALID_ORDER_QUANTITY': InvalidOrder, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + 'SERVICE_UNAVAILABLE': ExchangeNotAvailable, + 'EXCEEDED_RATE_LIMIT': DDoSProtection, + 'INVALID_PARAMETER': BadRequest, + 'WALLET_NOT_ASSOCIATED': InvalidAddress, + 'INVALID_WALLET_SIGNATURE': AuthenticationError, + }, + }, + 'requiredCredentials': { + 'walletAddress': True, + 'privateKey': True, + 'apiKey': True, + 'secret': True, + }, + 'precisionMode': TICK_SIZE, + 'paddingMode': PAD_WITH_ZERO, + 'commonCurrencies': {}, + }) + + def price_to_precision(self, symbol, price): + # + # we override priceToPrecision to fix the following issue + # https://github.com/ccxt/ccxt/issues/13367 + # {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes"} + # + market = self.market(symbol) + info = self.safe_value(market, 'info', {}) + quoteAssetPrecision = self.safe_integer(info, 'quoteAssetPrecision') + price = self.decimal_to_precision(price, ROUND, market['precision']['price'], self.precisionMode) + return self.decimal_to_precision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO) + + async def fetch_markets(self, params={}) -> List[Market]: + """ + retrieves data on all markets for geniusyield + :see: https://api-docs-v3.geniusyield.io/#get-markets + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: an array of objects representing market data + """ + response = await self.publicGetMarkets(params) + # + # [ + # { + # "market": "ETH-USDC", + # "type": "hybrid", + # "status": "activeHybrid", + # "baseAsset": "ETH", + # "baseAssetPrecision": "8", + # "quoteAsset": "USDC", + # "quoteAssetPrecision": "8", + # "makerFeeRate": "0.0000", + # "takerFeeRate": "0.2500", + # "takerIdexFeeRate": "0.0500", + # "takerLiquidityProviderFeeRate": "0.2000", + # "tickSize": "0.01000000" + # }, + # ] + # + response2 = await self.publicGetExchange() + # + # { + # "timeZone": "UTC", + # "serverTime": "1654460599952", + # "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + # "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + # "maticUsdPrice": "0.60", + # "gasPrice": "180", + # "volume24hUsd": "10015814.46", + # "totalVolumeUsd": "1589273533.28", + # "totalTrades": "1534904", + # "totalValueLockedUsd": "12041929.44", + # "geniusyieldStakingValueLockedUsd": "20133816.98", + # "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", + # "geniusyieldUsdPrice": "0.07", + # "geniusyieldMarketCapUsd": "48012346.00", + # "makerFeeRate": "0.0000", + # "takerFeeRate": "0.0025", + # "takerIdexFeeRate": "0.0005", + # "takerLiquidityProviderFeeRate": "0.0020", + # "makerTradeMinimum": "10.00000000", + # "takerTradeMinimum": "1.00000000", + # "withdrawMinimum": "0.50000000", + # "liquidityAdditionMinimum": "0.50000000", + # "liquidityRemovalMinimum": "0.40000000", + # "blockConfirmationDelay": "64" + # } + # + maker = self.safe_number(response2, 'makerFeeRate') + taker = self.safe_number(response2, 'takerFeeRate') + makerMin = self.safe_string(response2, 'makerTradeMinimum') + takerMin = self.safe_string(response2, 'takerTradeMinimum') + minCostETH = self.parse_number(Precise.string_min(makerMin, takerMin)) + result = [] + for i in range(0, len(response)): + entry = response[i] + marketId = self.safe_string(entry, 'market') + baseId = self.safe_string(entry, 'baseAsset') + quoteId = self.safe_string(entry, 'quoteAsset') + base = self.safe_currency_code(baseId) + quote = self.safe_currency_code(quoteId) + basePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'baseAssetPrecision'))) + quotePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'quoteAssetPrecision'))) + status = self.safe_string(entry, 'status') + minCost = None + if quote == 'ETH': + minCost = minCostETH + result.append({ + 'id': marketId, + 'symbol': base + '/' + quote, + 'base': base, + 'quote': quote, + 'settle': None, + 'baseId': baseId, + 'quoteId': quoteId, + 'settleId': None, + 'type': 'spot', + 'spot': True, + 'margin': False, + 'swap': False, + 'future': False, + 'option': False, + 'active': (status != 'inactive'), + 'contract': False, + 'linear': None, + 'inverse': None, + 'taker': taker, + 'maker': maker, + 'contractSize': None, + 'expiry': None, + 'expiryDatetime': None, + 'strike': None, + 'optionType': None, + 'precision': { + 'amount': basePrecision, + 'price': self.safe_number(entry, 'tickSize'), + }, + 'limits': { + 'leverage': { + 'min': None, + 'max': None, + }, + 'amount': { + 'min': basePrecision, + 'max': None, + }, + 'price': { + 'min': quotePrecision, + 'max': None, + }, + 'cost': { + 'min': minCost, + 'max': None, + }, + }, + 'created': None, + 'info': entry, + }) + return result + + async def fetch_ticker(self, symbol: str, params={}) -> Ticker: + """ + fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + :see: https://api-docs-v3.geniusyield.io/#get-tickers + :param str symbol: unified symbol of the market to fetch the ticker for + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `ticker structure ` + """ + await self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + } + # [ + # { + # "market": "DIL-ETH", + # "time": 1598367493008, + # "open": "0.09695361", + # "high": "0.10245881", + # "low": "0.09572507", + # "close": "0.09917079", + # "closeQuantity": "0.71320950", + # "baseVolume": "309.17380612", + # "quoteVolume": "30.57633981", + # "percentChange": "2.28", + # "numTrades": 205, + # "ask": "0.09910476", + # "bid": "0.09688340", + # "sequence": 3902 + # } + # ] + response = await self.publicGetTickers(self.extend(request, params)) + ticker = self.safe_dict(response, 0) + return self.parse_ticker(ticker, market) + + async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers: + """ + fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + :see: https://api-docs-v3.geniusyield.io/#get-tickers + :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a dictionary of `ticker structures ` + """ + await self.load_markets() + # [ + # { + # "market": "DIL-ETH", + # "time": 1598367493008, + # "open": "0.09695361", + # "high": "0.10245881", + # "low": "0.09572507", + # "close": "0.09917079", + # "closeQuantity": "0.71320950", + # "baseVolume": "309.17380612", + # "quoteVolume": "30.57633981", + # "percentChange": "2.28", + # "numTrades": 205, + # "ask": "0.09910476", + # "bid": "0.09688340", + # "sequence": 3902 + # }, ... + # ] + response = await self.publicGetTickers(params) + return self.parse_tickers(response, symbols) + + def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker: + # { + # "market": "DIL-ETH", + # "time": 1598367493008, + # "open": "0.09695361", + # "high": "0.10245881", + # "low": "0.09572507", + # "close": "0.09917079", + # "closeQuantity": "0.71320950", + # "baseVolume": "309.17380612", + # "quoteVolume": "30.57633981", + # "percentChange": "2.28", + # "numTrades": 205, + # "ask": "0.09910476", + # "bid": "0.09688340", + # "sequence": 3902 + # } + marketId = self.safe_string(ticker, 'market') + market = self.safe_market(marketId, market, '-') + symbol = market['symbol'] + timestamp = self.safe_integer(ticker, 'time') + close = self.safe_string(ticker, 'close') + return self.safe_ticker({ + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'high': self.safe_string(ticker, 'high'), + 'low': self.safe_string(ticker, 'low'), + 'bid': self.safe_string(ticker, 'bid'), + 'bidVolume': None, + 'ask': self.safe_string(ticker, 'ask'), + 'askVolume': None, + 'vwap': None, + 'open': self.safe_string(ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': None, + 'change': None, + 'percentage': self.safe_string(ticker, 'percentChange'), + 'average': None, + 'baseVolume': self.safe_string(ticker, 'baseVolume'), + 'quoteVolume': self.safe_string(ticker, 'quoteVolume'), + 'info': ticker, + }, market) + + async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]: + """ + fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + :see: https://api-docs-v3.geniusyield.io/#get-candles + :param str symbol: unified symbol of the market to fetch OHLCV data for + :param str timeframe: the length of time each candle represents + :param int [since]: timestamp in ms of the earliest candle to fetch + :param int [limit]: the maximum amount of candles to fetch + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns int[][]: A list of candles ordered, open, high, low, close, volume + """ + await self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + 'interval': timeframe, + } + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = min(limit, 1000) + response = await self.publicGetCandles(self.extend(request, params)) + if isinstance(response, list): + # [ + # { + # "start": 1598345580000, + # "open": "0.09771286", + # "high": "0.09771286", + # "low": "0.09771286", + # "close": "0.09771286", + # "volume": "1.45340410", + # "sequence": 3853 + # }, ... + # ] + return self.parse_ohlcvs(response, market, timeframe, since, limit) + else: + # {"nextTime":1595536440000} + return [] + + def parse_ohlcv(self, ohlcv, market: Market = None) -> list: + # { + # "start": 1598345580000, + # "open": "0.09771286", + # "high": "0.09771286", + # "low": "0.09771286", + # "close": "0.09771286", + # "volume": "1.45340410", + # "sequence": 3853 + # } + timestamp = self.safe_integer(ohlcv, 'start') + open = self.safe_number(ohlcv, 'open') + high = self.safe_number(ohlcv, 'high') + low = self.safe_number(ohlcv, 'low') + close = self.safe_number(ohlcv, 'close') + volume = self.safe_number(ohlcv, 'volume') + return [timestamp, open, high, low, close, volume] + + async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: + """ + get the list of most recent trades for a particular symbol + :see: https://api-docs-v3.geniusyield.io/#get-trades + :param str symbol: unified symbol of the market to fetch trades for + :param int [since]: timestamp in ms of the earliest trade to fetch + :param int [limit]: the maximum amount of trades to fetch + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Trade[]: a list of `trade structures ` + """ + await self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + } + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = min(limit, 1000) + # [ + # { + # "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", + # "price": "0.09771286", + # "quantity": "1.45340410", + # "quoteQuantity": "0.14201627", + # "time": 1598345638994, + # "makerSide": "buy", + # "sequence": 3853 + # }, ... + # ] + response = await self.publicGetTrades(self.extend(request, params)) + return self.parse_trades(response, market, since, limit) + + def parse_trade(self, trade: dict, market: Market = None) -> Trade: + # + # public trades + # { + # "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + # "price":"0.20377008", + # "quantity":"47.58448728", + # "quoteQuantity":"9.69629509", + # "time":1642091300873, + # "makerSide":"buy", + # "type":"hybrid", # one of either: "orderBook", "hybrid", or "pool" + # "sequence":31876 + # } + # + # private trades + # { + # "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + # "price":"0.20717368", + # "quantity":"15.00000000", + # "quoteQuantity":"3.10760523", + # "orderBookQuantity":"0.00000003", + # "orderBookQuoteQuantity":"0.00000001", + # "poolQuantity":"14.99999997", + # "poolQuoteQuantity":"3.10760522", + # "time":1642083351215, + # "makerSide":"sell", + # "sequence":31795, + # "market":"Genius Yield-USDC", + # "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + # "side":"buy", + # "fee":"0.03749989", + # "feeAsset":"Genius Yield", + # "gas":"0.40507261", + # "liquidity":"taker", + # "type":"hybrid", + # "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + # "txStatus":"mined" + # } + # + id = self.safe_string(trade, 'fillId') + priceString = self.safe_string(trade, 'price') + amountString = self.safe_string(trade, 'quantity') + costString = self.safe_string(trade, 'quoteQuantity') + timestamp = self.safe_integer(trade, 'time') + marketId = self.safe_string(trade, 'market') + symbol = self.safe_symbol(marketId, market, '-') + # self code handles the duality of public vs private trades + makerSide = self.safe_string(trade, 'makerSide') + oppositeSide = 'sell' if (makerSide == 'buy') else 'buy' + side = self.safe_string(trade, 'side', oppositeSide) + takerOrMaker = self.safe_string(trade, 'liquidity', 'taker') + feeCostString = self.safe_string(trade, 'fee') + fee = None + if feeCostString is not None: + feeCurrencyId = self.safe_string(trade, 'feeAsset') + fee = { + 'cost': feeCostString, + 'currency': self.safe_currency_code(feeCurrencyId), + } + orderId = self.safe_string(trade, 'orderId') + return self.safe_trade({ + 'info': trade, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': priceString, + 'amount': amountString, + 'cost': costString, + 'fee': fee, + }, market) + + async def fetch_trading_fees(self, params={}) -> TradingFees: + """ + fetch the trading fees for multiple markets + :see: https://api-docs-v3.geniusyield.io/#get-api-account + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a dictionary of `fee structures ` indexed by market symbols + """ + self.check_required_credentials() + await self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + } + response = None + response = await self.privateGetUser(self.extend(request, params)) + # + # { + # "depositEnabled": True, + # "orderEnabled": True, + # "cancelEnabled": True, + # "withdrawEnabled": True, + # "totalPortfolioValueUsd": "0.00", + # "makerFeeRate": "0.0000", + # "takerFeeRate": "0.0025", + # "takerIdexFeeRate": "0.0005", + # "takerLiquidityProviderFeeRate": "0.0020" + # } + # + maker = self.safe_number(response, 'makerFeeRate') + taker = self.safe_number(response, 'takerFeeRate') + result: dict = {} + for i in range(0, len(self.symbols)): + symbol = self.symbols[i] + result[symbol] = { + 'info': response, + 'symbol': symbol, + 'maker': maker, + 'taker': taker, + 'percentage': True, + 'tierBased': False, + } + return result + + async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: + """ + fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data + :see: https://api-docs-v3.geniusyield.io/#get-order-books + :param str symbol: unified symbol of the market to fetch the order book for + :param int [limit]: the maximum amount of order book entries to return + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: A dictionary of `order book structures ` indexed by market symbols + """ + await self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + 'level': 2, + } + if limit is not None: + request['limit'] = limit + # { + # "sequence": 36416753, + # "bids": [ + # ['0.09672815', "8.22284267", 1], + # ['0.09672814', "1.83685554", 1], + # ['0.09672143', "4.10962617", 1], + # ['0.09658884', "4.03863759", 1], + # ['0.09653781', "3.35730684", 1], + # ['0.09624660', "2.54163586", 1], + # ['0.09617490', "1.93065030", 1] + # ], + # "asks": [ + # ['0.09910476', "3.22840154", 1], + # ['0.09940587', "3.39796593", 1], + # ['0.09948189', "4.25088898", 1], + # ['0.09958362', "2.42195784", 1], + # ['0.09974393', "4.25234367", 1], + # ['0.09995250', "3.40192141", 1] + # ] + # } + response = await self.publicGetOrderbook(self.extend(request, params)) + nonce = self.safe_integer(response, 'sequence') + return { + 'symbol': symbol, + 'timestamp': None, + 'datetime': None, + 'nonce': nonce, + 'bids': self.parse_side(response, 'bids'), + 'asks': self.parse_side(response, 'asks'), + } + + def parse_side(self, book, side): + bookSide = self.safe_value(book, side, []) + result = [] + for i in range(0, len(bookSide)): + order = bookSide[i] + price = self.safe_number(order, 0) + amount = self.safe_number(order, 1) + orderCount = self.safe_integer(order, 2) + result.append([price, amount, orderCount]) + descending = side == 'bids' + return self.sort_by(result, 0, descending) + + async def fetch_currencies(self, params={}) -> Currencies: + """ + fetches all available currencies on an exchange + :see: https://api-docs-v3.geniusyield.io/#get-assets + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: an associative dictionary of currencies + """ + response = await self.publicGetAssets(params) + # + # [ + # { + # "name": "Ethereum", + # "symbol": "ETH", + # "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + # "assetDecimals": "18", + # "exchangeDecimals": "8", + # "maticPrice": "3029.38503483" + # }, + # ] + # + result: dict = {} + for i in range(0, len(response)): + entry = response[i] + name = self.safe_string(entry, 'name') + currencyId = self.safe_string(entry, 'symbol') + code = self.safe_currency_code(currencyId) + precision = self.parse_number(self.parse_precision(self.safe_string(entry, 'exchangeDecimals'))) + result[code] = { + 'id': currencyId, + 'code': code, + 'info': entry, + 'type': None, + 'name': name, + 'active': None, + 'deposit': None, + 'withdraw': None, + 'fee': None, + 'precision': precision, + 'limits': { + 'amount': {'min': precision, 'max': None}, + 'withdraw': {'min': precision, 'max': None}, + }, + } + return result + + def parse_balance(self, response) -> Balances: + result: dict = { + 'info': response, + 'timestamp': None, + 'datetime': None, + } + for i in range(0, len(response)): + entry = response[i] + currencyId = self.safe_string(entry, 'asset') + code = self.safe_currency_code(currencyId) + account = self.account() + account['total'] = self.safe_string(entry, 'quantity') + account['free'] = self.safe_string(entry, 'availableForTrade') + account['used'] = self.safe_string(entry, 'locked') + result[code] = account + return self.safe_balance(result) + + async def fetch_balance(self, params={}) -> Balances: + """ + query for balance and get the amount of funds available for trading or funds locked in orders + :see: https://api-docs-v3.geniusyield.io/#get-balances + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `balance structure ` + """ + self.check_required_credentials() + await self.load_markets() + nonce1 = self.uuidv1() + request: dict = { + 'nonce': nonce1, + 'wallet': self.walletAddress, + } + # [ + # { + # "asset": "DIL", + # "quantity": "0.00000000", + # "availableForTrade": "0.00000000", + # "locked": "0.00000000", + # "usdValue": null + # }, ... + # ] + extendedRequest = self.extend(request, params) + if extendedRequest['wallet'] is None: + raise BadRequest(self.id + ' fetchBalance() wallet is None, set self.walletAddress or "address" in params') + response = None + try: + response = await self.privateGetBalances(extendedRequest) + except Exception as e: + if isinstance(e, InvalidAddress): + walletAddress = extendedRequest['wallet'] + await self.associate_wallet(walletAddress) + response = await self.privateGetBalances(extendedRequest) + else: + raise e + return self.parse_balance(response) + + async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): + """ + fetch all trades made by the user + :see: https://api-docs-v3.geniusyield.io/#get-fills + :param str symbol: unified market symbol + :param int [since]: the earliest time in ms to fetch trades for + :param int [limit]: the maximum number of trades structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Trade[]: a list of `trade structures ` + """ + self.check_required_credentials() + await self.load_markets() + market = None + request: dict = { + 'nonce': self.uuidv1(), + 'wallet': self.walletAddress, + } + if symbol is not None: + market = self.market(symbol) + request['market'] = market['id'] + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = limit + # [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478762, + # "makerSide": "sell", + # "sequence": 5053, + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "side": "buy", + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined" + # } + # ] + extendedRequest = self.extend(request, params) + if extendedRequest['wallet'] is None: + raise BadRequest(self.id + ' fetchMyTrades() walletAddress is None, set self.walletAddress or "address" in params') + response = None + try: + response = await self.privateGetFills(extendedRequest) + except Exception as e: + if isinstance(e, InvalidAddress): + walletAddress = extendedRequest['wallet'] + await self.associate_wallet(walletAddress) + response = await self.privateGetFills(extendedRequest) + else: + raise e + return self.parse_trades(response, market, since, limit) + + async def fetch_order(self, id: str, symbol: Str = None, params={}): + """ + fetches information on an order made by the user + :see: https://api-docs-v3.geniusyield.io/#get-orders + :param str symbol: unified symbol of the market the order was made in + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: An `order structure ` + """ + request: dict = { + 'orderId': id, + } + return await self.fetch_orders_helper(symbol, None, None, self.extend(request, params)) + + async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: + """ + fetch all unfilled currently open orders + :see: https://api-docs-v3.geniusyield.io/#get-orders + :param str symbol: unified market symbol + :param int [since]: the earliest time in ms to fetch open orders for + :param int [limit]: the maximum number of open orders structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Order[]: a list of `order structures ` + """ + request: dict = { + 'closed': False, + } + return await self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) + + async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: + """ + fetches information on multiple closed orders made by the user + :see: https://api-docs-v3.geniusyield.io/#get-orders + :param str symbol: unified market symbol of the market orders were made in + :param int [since]: the earliest time in ms to fetch orders for + :param int [limit]: the maximum number of order structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Order[]: a list of `order structures ` + """ + request: dict = { + 'closed': True, + } + return await self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) + + async def fetch_orders_helper(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): + await self.load_markets() + request: dict = { + 'nonce': self.uuidv1(), + 'wallet': self.walletAddress, + } + market = None + if symbol is not None: + market = self.market(symbol) + request['market'] = market['id'] + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = limit + response = await self.privateGetOrders(self.extend(request, params)) + # fetchClosedOrders / fetchOpenOrders + # [ + # { + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "avgExecutionPrice": "0.09905990", + # "price": "1.00000000", + # "fills": [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined" + # } + # ] + # } + # ] + # fetchOrder + # {market: "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "avgExecutionPrice": "0.09905990", + # "price": "1.00000000", + # "fills": + # [{fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined"}]} + if isinstance(response, list): + return self.parse_orders(response, market, since, limit) + else: + return self.parse_order(response, market) + + def parse_order_status(self, status: Str): + # https://docs.geniusyield.io/#order-states-amp-lifecycle + statuses: dict = { + 'active': 'open', + 'partiallyFilled': 'open', + 'rejected': 'canceled', + 'filled': 'closed', + } + return self.safe_string(statuses, status, status) + + def parse_order(self, order: dict, market: Market = None) -> Order: + # + # { + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "avgExecutionPrice": "0.09905990", + # "price": "1.00000000", + # "fills": [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined" + # } + # ] + # } + # + timestamp = self.safe_integer(order, 'time') + fills = self.safe_value(order, 'fills', []) + id = self.safe_string(order, 'orderId') + clientOrderId = self.safe_string(order, 'clientOrderId') + marketId = self.safe_string(order, 'market') + side = self.safe_string(order, 'side') + symbol = self.safe_symbol(marketId, market, '-') + type = self.safe_string(order, 'type') + amount = self.safe_string(order, 'originalQuantity') + filled = self.safe_string(order, 'executedQuantity') + average = self.safe_string(order, 'avgExecutionPrice') + price = self.safe_string(order, 'price') + rawStatus = self.safe_string(order, 'status') + timeInForce = self.safe_string_upper(order, 'timeInForce') + status = self.parse_order_status(rawStatus) + return self.safe_order({ + 'info': order, + 'id': id, + 'clientOrderId': clientOrderId, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'lastTradeTimestamp': None, + 'symbol': symbol, + 'type': type, + 'timeInForce': timeInForce, + 'postOnly': None, + 'side': side, + 'price': price, + 'stopPrice': None, + 'triggerPrice': None, + 'amount': amount, + 'cost': None, + 'average': average, + 'filled': filled, + 'remaining': None, + 'status': status, + 'fee': None, + 'trades': fills, + }, market) + + async def associate_wallet(self, walletAddress, params={}): + nonce = self.uuidv1() + noPrefix = self.remove0x_prefix(walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(noPrefix), + ] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + # { + # "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "totalPortfolioValueUsd": "0.00", + # "time": 1598468353626 + # } + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': walletAddress, + }, + 'signature': signature, + } + result = await self.privatePostWallets(request) + return result + + async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): + """ + create a trade order, https://docs.geniusyield.io/#create-order + :see: https://api-docs-v3.geniusyield.io/#create-order + :param str symbol: unified symbol of the market to create an order in + :param str type: 'market' or 'limit' + :param str side: 'buy' or 'sell' + :param float amount: how much of currency you want to trade in units of base currency + :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + :param dict [params]: extra parameters specific to the exchange API endpoint + :param bool [params.test]: set to True to test an order, no order will be created but the request will be validated + :returns dict: an `order structure ` + """ + self.check_required_credentials() + await self.load_markets() + testOrder = self.safe_bool(params, 'test', False) + params = self.omit(params, 'test') + market = self.market(symbol) + nonce = self.uuidv1() + typeEnum = None + stopLossTypeEnums: dict = { + 'stopLoss': 3, + 'stopLossLimit': 4, + 'takeProfit': 5, + 'takeProfitLimit': 6, + } + stopPriceString = None + if (type == 'stopLossLimit') or (type == 'takeProfitLimit') or ('stopPrice' in params): + if not ('stopPrice' in params): + raise BadRequest(self.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders') + stopPriceString = self.price_to_precision(symbol, params['stopPrice']) + limitTypeEnums: dict = { + 'limit': 1, + 'limitMaker': 2, + } + priceString = None + typeLower = type.lower() + limitOrder = typeLower.find('limit') >= 0 + if type in limitTypeEnums: + typeEnum = limitTypeEnums[type] + priceString = self.price_to_precision(symbol, price) + elif type in stopLossTypeEnums: + typeEnum = stopLossTypeEnums[type] + priceString = self.price_to_precision(symbol, price) + elif type == 'market': + typeEnum = 0 + else: + raise BadRequest(self.id + ' ' + type + ' is not a valid order type') + amountEnum = 0 # base quantity + if 'quoteOrderQuantity' in params: + if type != 'market': + raise NotSupported(self.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders') + amountEnum = 1 + amount = self.safe_number(params, 'quoteOrderQuantity') + sideEnum = 0 if (side == 'buy') else 1 + walletBytes = self.remove0x_prefix(self.walletAddress) + network = self.safe_string(self.options, 'network', 'ETH') + orderVersion = self.get_supported_mapping(network, { + 'ETH': 1, + 'BSC': 2, + 'MATIC': 4, + }) + amountString = self.amount_to_precision(symbol, amount) + # https://docs.geniusyield.io/#time-in-force + timeInForceEnums: dict = { + 'gtc': 0, + 'ioc': 2, + 'fok': 3, + } + defaultTimeInForce = self.safe_string(self.options, 'defaultTimeInForce', 'gtc') + timeInForce = self.safe_string(params, 'timeInForce', defaultTimeInForce) + timeInForceEnum = None + if timeInForce in timeInForceEnums: + timeInForceEnum = timeInForceEnums[timeInForce] + else: + allOptions = list(timeInForceEnums.keys()) + asString = ', '.join(allOptions) + raise BadRequest(self.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString) + # https://docs.geniusyield.io/#self-trade-prevention + selfTradePreventionEnums: dict = { + 'dc': 0, + 'co': 1, + 'cn': 2, + 'cb': 3, + } + defaultSelfTradePrevention = self.safe_string(self.options, 'defaultSelfTradePrevention', 'cn') + selfTradePrevention = self.safe_string(params, 'selfTradePrevention', defaultSelfTradePrevention) + selfTradePreventionEnum = None + if selfTradePrevention in selfTradePreventionEnums: + selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention] + else: + allOptions = list(selfTradePreventionEnums.keys()) + asString = ', '.join(allOptions) + raise BadRequest(self.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString) + byteArray = [ + self.number_to_be(orderVersion, 1), + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + self.encode(market['id']), + self.number_to_be(typeEnum, 1), + self.number_to_be(sideEnum, 1), + self.encode(amountString), + self.number_to_be(amountEnum, 1), + ] + if limitOrder: + encodedPrice = self.encode(priceString) + byteArray.append(encodedPrice) + if type in stopLossTypeEnums: + encodedPrice = self.encode(stopPriceString or priceString) + byteArray.append(encodedPrice) + clientOrderId = self.safe_string(params, 'clientOrderId') + if clientOrderId is not None: + byteArray.append(self.encode(clientOrderId)) + after = [ + self.number_to_be(timeInForceEnum, 1), + self.number_to_be(selfTradePreventionEnum, 1), + self.number_to_be(0, 8), # unused + ] + allBytes = self.array_concat(byteArray, after) + binary = self.binary_concat_array(allBytes) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request: dict = { + 'parameters': { + 'nonce': nonce, + 'market': market['id'], + 'side': side, + 'type': type, + 'wallet': self.walletAddress, + 'selfTradePrevention': selfTradePrevention, + }, + 'signature': signature, + } + if type != 'market': + request['parameters']['timeInForce'] = timeInForce + if limitOrder: + request['parameters']['price'] = priceString + if type in stopLossTypeEnums: + request['parameters']['stopPrice'] = stopPriceString or priceString + if amountEnum == 0: + request['parameters']['quantity'] = amountString + else: + request['parameters']['quoteOrderQuantity'] = amountString + if clientOrderId is not None: + request['parameters']['clientOrderId'] = clientOrderId + # { + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "price": "1.00000000", + # "fills": [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txStatus": "pending" + # } + # ], + # "avgExecutionPrice": "0.09905990" + # } + # we don't use self.extend here because it is a signed endpoint + response = None + if testOrder: + response = await self.privatePostOrdersTest(request) + else: + response = await self.privatePostOrders(request) + return self.parse_order(response, market) + + async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}): + """ + make a withdrawal + :see: https://api-docs-v3.geniusyield.io/#withdraw-funds + :param str code: unified currency code + :param float amount: the amount to withdraw + :param str address: the address to withdraw to + :param str tag: + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `transaction structure ` + """ + tag, params = self.handle_withdraw_tag_and_params(tag, params) + self.check_required_credentials() + await self.load_markets() + nonce = self.uuidv1() + amountString = self.currency_to_precision(code, amount) + currency = self.currency(code) + walletBytes = self.remove0x_prefix(self.walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + self.encode(currency['id']), + self.encode(amountString), + self.number_to_be(1, 1), # bool set to True + ] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': address, + 'asset': currency['id'], + 'quantity': amountString, + }, + 'signature': signature, + } + response = await self.privatePostWithdrawals(request) + # + # { + # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + # "asset": "ETH", + # "assetContractAddress": "0x0000000000000000000000000000000000000000", + # "quantity": "0.20000000", + # "time": 1598962883190, + # "fee": "0.00024000", + # "txStatus": "pending", + # "txId": null + # } + # + return self.parse_transaction(response, currency) + + async def cancel_all_orders(self, symbol: Str = None, params={}): + """ + cancel all open orders + :see: https://api-docs-v3.geniusyield.io/#cancel-order + :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: a list of `order structures ` + """ + self.check_required_credentials() + await self.load_markets() + market = None + if symbol is not None: + market = self.market(symbol) + nonce = self.uuidv1() + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': self.walletAddress, + }, + } + walletBytes = self.remove0x_prefix(self.walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + ] + if market is not None: + byteArray.append(self.encode(market['id'])) + request['parameters']['market'] = market['id'] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request['signature'] = signature + # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] + response = await self.privateDeleteOrders(self.extend(request, params)) + return self.parse_orders(response, market) + + async def cancel_order(self, id: str, symbol: Str = None, params={}): + """ + cancels an open order + :see: https://api-docs-v3.geniusyield.io/#cancel-order + :param str id: order id + :param str symbol: unified symbol of the market the order was made in + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: An `order structure ` + """ + self.check_required_credentials() + await self.load_markets() + market = None + if symbol is not None: + market = self.market(symbol) + nonce = self.uuidv1() + walletBytes = self.remove0x_prefix(self.walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + self.encode(id), + ] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': self.walletAddress, + 'orderId': id, + }, + 'signature': signature, + } + # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] + response = await self.privateDeleteOrders(self.extend(request, params)) + canceledOrder = self.safe_dict(response, 0) + return self.parse_order(canceledOrder, market) + + def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): + errorCode = self.safe_string(response, 'code') + message = self.safe_string(response, 'message') + if errorCode is not None: + self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, message) + raise ExchangeError(self.id + ' ' + message) + return None + + async def fetch_deposit(self, id: str, code: Str = None, params={}): + """ + fetch information on a deposit + :see: https://api-docs-v3.geniusyield.io/#get-deposits + :param str id: deposit id + :param str code: not used by geniusyield fetchDeposit() + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `transaction structure ` + """ + await self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + 'wallet': self.walletAddress, + 'depositId': id, + } + response = await self.privateGetDeposits(self.extend(request, params)) + return self.parse_transaction(response) + + async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: + """ + fetch all deposits made to an account + :see: https://api-docs-v3.geniusyield.io/#get-deposits + :param str code: unified currency code + :param int [since]: the earliest time in ms to fetch deposits for + :param int [limit]: the maximum number of deposits structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: a list of `transaction structures ` + """ + params = self.extend({ + 'method': 'privateGetDeposits', + }, params) + return await self.fetch_transactions_helper(code, since, limit, params) + + async def fetch_status(self, params={}): + """ + the latest known information on the availability of the exchange API + :see: https://api-docs-v3.geniusyield.io/#get-ping + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `status structure ` + """ + response = await self.publicGetPing(params) + return { + 'status': 'ok', # if there's no Errors, status = 'ok' + 'updated': None, + 'eta': None, + 'url': None, + 'info': response, + } + + async def fetch_time(self, params={}): + """ + fetches the current integer timestamp in milliseconds from the exchange server + :see: https://api-docs-v3.geniusyield.io/#get-time + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns int: the current integer timestamp in milliseconds from the exchange server + """ + response = await self.publicGetTime(params) + # + # {serverTime: "1655258263236"} + # + return self.safe_integer(response, 'serverTime') + + async def fetch_withdrawal(self, id: str, code: Str = None, params={}): + """ + fetch data on a currency withdrawal via the withdrawal id + :see: https://api-docs-v3.geniusyield.io/#get-withdrawals + :param str id: withdrawal id + :param str code: not used by geniusyield.fetchWithdrawal + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `transaction structure ` + """ + await self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + 'wallet': self.walletAddress, + 'withdrawalId': id, + } + response = await self.privateGetWithdrawals(self.extend(request, params)) + return self.parse_transaction(response) + + async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: + """ + fetch all withdrawals made from an account + :see: https://api-docs-v3.geniusyield.io/#get-withdrawals + :param str code: unified currency code + :param int [since]: the earliest time in ms to fetch withdrawals for + :param int [limit]: the maximum number of withdrawals structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: a list of `transaction structures ` + """ + params = self.extend({ + 'method': 'privateGetWithdrawals', + }, params) + return await self.fetch_transactions_helper(code, since, limit, params) + + async def fetch_transactions_helper(self, code: Str = None, since: Int = None, limit: Int = None, params={}): + await self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + 'wallet': self.walletAddress, + } + currency = None + if code is not None: + currency = self.currency(code) + request['asset'] = currency['id'] + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = limit + # [ + # { + # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + # "asset": "ETH", + # "quantity": "1.00000000", + # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + # "txTime": 1598865853000, + # "confirmationTime": 1598865930231 + # } + # ] + method = params['method'] + params = self.omit(params, 'method') + response = None + if method == 'privateGetDeposits': + response = await self.privateGetDeposits(self.extend(request, params)) + elif method == 'privateGetWithdrawals': + response = await self.privateGetWithdrawals(self.extend(request, params)) + else: + raise NotSupported(self.id + ' fetchTransactionsHelper() not support self method') + return self.parse_transactions(response, currency, since, limit) + + def parse_transaction_status(self, status: Str): + statuses: dict = { + 'mined': 'ok', + } + return self.safe_string(statuses, status, status) + + def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction: + # + # fetchDeposits + # + # { + # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + # "asset": "ETH", + # "quantity": "1.00000000", + # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + # "txTime": 1598865853000, + # "confirmationTime": 1598865930231 + # } + # + # fetchWithdrwalas + # + # { + # "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", + # "asset": "ETH", + # "assetContractAddress": "0x0000000000000000000000000000000000000000", + # "quantity": "0.20000000", + # "time": 1598962883288, + # "fee": "0.00024000", + # "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + # "txStatus": "mined" + # } + # + # withdraw + # + # { + # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + # "asset": "ETH", + # "assetContractAddress": "0x0000000000000000000000000000000000000000", + # "quantity": "0.20000000", + # "time": 1598962883190, + # "fee": "0.00024000", + # "txStatus": "pending", + # "txId": null + # } + # + type = None + if 'depositId' in transaction: + type = 'deposit' + elif ('withdrawId' in transaction) or ('withdrawalId' in transaction): + type = 'withdrawal' + id = self.safe_string_2(transaction, 'depositId', 'withdrawId') + id = self.safe_string(transaction, 'withdrawalId', id) + code = self.safe_currency_code(self.safe_string(transaction, 'asset'), currency) + amount = self.safe_number(transaction, 'quantity') + txid = self.safe_string(transaction, 'txId') + timestamp = self.safe_integer_2(transaction, 'txTime', 'time') + fee = None + if 'fee' in transaction: + fee = { + 'cost': self.safe_number(transaction, 'fee'), + 'currency': 'ETH', + } + rawStatus = self.safe_string(transaction, 'txStatus') + status = self.parse_transaction_status(rawStatus) + updated = self.safe_integer(transaction, 'confirmationTime') + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'network': None, + 'address': None, + 'addressTo': None, + 'addressFrom': None, + 'tag': None, + 'tagTo': None, + 'tagFrom': None, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'comment': None, + 'internal': None, + 'fee': fee, + } + + def calculate_rate_limiter_cost(self, api, method, path, params, config={}): + hasApiKey = (self.apiKey is not None) + hasSecret = (self.secret is not None) + hasWalletAddress = (self.walletAddress is not None) + hasPrivateKey = (self.privateKey is not None) + defaultCost = self.safe_value(config, 'cost', 1) + authenticated = hasApiKey and hasSecret and hasWalletAddress and hasPrivateKey + return(defaultCost / 2) if authenticated else defaultCost + + async def fetch_deposit_address(self, code: Str = None, params={}): + """ + fetch the Polygon address of the wallet + :see: https://api-docs-v3.geniusyield.io/#get-wallets + :param str code: not used by geniusyield + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: an `address structure ` + """ + request: dict = {} + request['nonce'] = self.uuidv1() + response = await self.privateGetWallets(self.extend(request, params)) + # + # [ + # { + # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + # totalPortfolioValueUsd: "0.00", + # time: "1678342148086" + # }, + # { + # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + # totalPortfolioValueUsd: "15.90", + # time: "1691697811659" + # } + # ] + # + return self.parse_deposit_address(response) + + def parse_deposit_address(self, depositAddress, currency: Currency = None): + # + # [ + # { + # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + # totalPortfolioValueUsd: "0.00", + # time: "1678342148086" + # }, + # { + # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + # totalPortfolioValueUsd: "15.90", + # time: "1691697811659" + # } + # ] + # + length = len(depositAddress) + entry = self.safe_dict(depositAddress, length - 1) + address = self.safe_string(entry, 'address') + self.check_address(address) + return { + 'info': depositAddress, + 'currency': None, + 'address': address, + 'tag': None, + 'network': 'MATIC', + } + + def sign(self, path, api='public', method='GET', params={}, headers=None, body=None): + network = self.safe_string(self.options, 'network', 'ETH') + version = self.safe_string(self.options, 'version', 'v1') + url = self.urls['api'][network] + '/' + version + '/' + path + keys = list(params.keys()) + length = len(keys) + query = None + if length > 0: + if method == 'GET': + query = self.urlencode(params) + url = url + '?' + query + else: + body = self.json(params) + headers = { + 'Content-Type': 'application/json', + } + if self.apiKey is not None: + headers['Genius Yield-API-Key'] = self.apiKey + if api == 'private': + payload = None + if method == 'GET': + payload = query + else: + payload = body + headers['Genius Yield-HMAC-Signature'] = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex') + return {'url': url, 'method': method, 'body': body, 'headers': headers} + + def remove0x_prefix(self, hexData): + if hexData[0:2] == '0x': + return hexData[2:] + else: + return hexData + + def hash_message(self, message): + # takes a hex encoded message + binaryMessage = self.base16_to_binary(self.remove0x_prefix(message)) + prefix = self.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength) + return '0x' + self.hash(self.binary_concat(prefix, binaryMessage), 'keccak', 'hex') + + def sign_hash(self, hash, privateKey): + signature = self.ecdsa(hash[-64:], privateKey[-64:], 'secp256k1', None) + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + } + + def sign_message(self, message, privateKey): + return self.sign_hash(self.hash_message(message), privateKey[-64:]) + + def sign_message_string(self, message, privateKey): + # still takes the input hex string + # same but returns a string instead of an object + signature = self.sign_message(message, privateKey) + return signature['r'] + self.remove0x_prefix(signature['s']) + self.binary_to_base16(self.number_to_be(signature['v'], 1)) diff --git a/python/ccxt/geniusyield.py b/python/ccxt/geniusyield.py new file mode 100644 index 000000000000..5e3d66f84b65 --- /dev/null +++ b/python/ccxt/geniusyield.py @@ -0,0 +1,1766 @@ +# -*- coding: utf-8 -*- + +# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code + +from ccxt.base.exchange import Exchange +from ccxt.abstract.geniusyield import ImplicitAPI +import hashlib +from ccxt.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction +from typing import List +from ccxt.base.errors import ExchangeError +from ccxt.base.errors import AuthenticationError +from ccxt.base.errors import BadRequest +from ccxt.base.errors import InsufficientFunds +from ccxt.base.errors import InvalidAddress +from ccxt.base.errors import InvalidOrder +from ccxt.base.errors import NotSupported +from ccxt.base.errors import DDoSProtection +from ccxt.base.errors import ExchangeNotAvailable +from ccxt.base.decimal_to_precision import ROUND +from ccxt.base.decimal_to_precision import TRUNCATE +from ccxt.base.decimal_to_precision import DECIMAL_PLACES +from ccxt.base.decimal_to_precision import TICK_SIZE +from ccxt.base.decimal_to_precision import PAD_WITH_ZERO +from ccxt.base.precise import Precise + + +class geniusyield(Exchange, ImplicitAPI): + + def describe(self): + return self.deep_extend(super(geniusyield, self).describe(), { + 'id': 'geniusyield', + 'name': 'Genius Yield', + 'countries': ['CH'], + 'rateLimit': 1000, + 'version': 'v0', + 'pro': False, + 'dex': True, + 'certified': False, + 'requiresWeb3': True, + 'has': { + 'CORS': None, + 'spot': True, + 'margin': False, + 'swap': False, + 'future': False, + 'option': False, + 'addMargin': False, + 'cancelAllOrders': True, + 'cancelOrder': True, + 'cancelOrders': False, + 'closeAllPositions': False, + 'closePosition': False, + 'createDepositAddress': False, + 'createOrder': True, + 'createReduceOnlyOrder': False, + 'createStopLimitOrder': True, + 'createStopMarketOrder': True, + 'createStopOrder': True, + 'fetchBalance': True, + 'fetchBorrowRateHistories': False, + 'fetchBorrowRateHistory': False, + 'fetchClosedOrders': True, + 'fetchCrossBorrowRate': False, + 'fetchCrossBorrowRates': False, + 'fetchCurrencies': True, + 'fetchDeposit': True, + 'fetchDepositAddress': True, + 'fetchDepositAddresses': False, + 'fetchDepositAddressesByNetwork': False, + 'fetchDeposits': True, + 'fetchFundingHistory': False, + 'fetchFundingRate': False, + 'fetchFundingRateHistory': False, + 'fetchFundingRates': False, + 'fetchIndexOHLCV': False, + 'fetchIsolatedBorrowRate': False, + 'fetchIsolatedBorrowRates': False, + 'fetchLeverage': False, + 'fetchLeverageTiers': False, + 'fetchMarginMode': False, + 'fetchMarkets': True, + 'fetchMarkOHLCV': False, + 'fetchMyTrades': True, + 'fetchOHLCV': True, + 'fetchOpenInterestHistory': False, + 'fetchOpenOrders': True, + 'fetchOrder': True, + 'fetchOrderBook': True, + 'fetchOrders': False, + 'fetchPosition': False, + 'fetchPositionHistory': False, + 'fetchPositionMode': False, + 'fetchPositions': False, + 'fetchPositionsForSymbol': False, + 'fetchPositionsHistory': False, + 'fetchPositionsRisk': False, + 'fetchPremiumIndexOHLCV': False, + 'fetchStatus': True, + 'fetchTicker': True, + 'fetchTickers': True, + 'fetchTime': True, + 'fetchTrades': True, + 'fetchTradingFee': False, + 'fetchTradingFees': True, + 'fetchTransactions': False, + 'fetchWithdrawal': True, + 'fetchWithdrawals': True, + 'reduceMargin': False, + 'sandbox': True, + 'setLeverage': False, + 'setMarginMode': False, + 'setPositionMode': False, + 'transfer': False, + 'withdraw': True, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'MATIC': 'https://api-sandbox-matic.geniusyield.io', + }, + 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', + 'api': { + 'MATIC': 'https://api-matic.geniusyield.io', + }, + 'www': 'https://geniusyield.io', + 'doc': [ + 'https://api-docs-v3.geniusyield.io/', + ], + }, + 'api': { + 'public': { + 'get': { + 'ping': 1, + 'time': 1, + 'exchange': 1, + 'assets': 1, + 'markets': 1, + 'tickers': 1, + 'candles': 1, + 'trades': 1, + 'orderbook': 1, + }, + }, + 'private': { + 'get': { + 'user': 1, + 'wallets': 1, + 'balances': 1, + 'orders': 0.1, + 'fills': 0.1, + 'deposits': 1, + 'withdrawals': 1, + 'wsToken': 1, + }, + 'post': { + 'wallets': 1, + 'orders': 0.1, + 'orders/test': 0.1, + 'withdrawals': 1, + }, + 'delete': { + 'orders': 0.1, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'gtc', + 'defaultSelfTradePrevention': 'cn', + 'network': 'MATIC', + }, + 'exceptions': { + 'exact': { + 'INVALID_ORDER_QUANTITY': InvalidOrder, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + 'SERVICE_UNAVAILABLE': ExchangeNotAvailable, + 'EXCEEDED_RATE_LIMIT': DDoSProtection, + 'INVALID_PARAMETER': BadRequest, + 'WALLET_NOT_ASSOCIATED': InvalidAddress, + 'INVALID_WALLET_SIGNATURE': AuthenticationError, + }, + }, + 'requiredCredentials': { + 'walletAddress': True, + 'privateKey': True, + 'apiKey': True, + 'secret': True, + }, + 'precisionMode': TICK_SIZE, + 'paddingMode': PAD_WITH_ZERO, + 'commonCurrencies': {}, + }) + + def price_to_precision(self, symbol, price): + # + # we override priceToPrecision to fix the following issue + # https://github.com/ccxt/ccxt/issues/13367 + # {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes"} + # + market = self.market(symbol) + info = self.safe_value(market, 'info', {}) + quoteAssetPrecision = self.safe_integer(info, 'quoteAssetPrecision') + price = self.decimal_to_precision(price, ROUND, market['precision']['price'], self.precisionMode) + return self.decimal_to_precision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO) + + def fetch_markets(self, params={}) -> List[Market]: + """ + retrieves data on all markets for geniusyield + :see: https://api-docs-v3.geniusyield.io/#get-markets + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: an array of objects representing market data + """ + response = self.publicGetMarkets(params) + # + # [ + # { + # "market": "ETH-USDC", + # "type": "hybrid", + # "status": "activeHybrid", + # "baseAsset": "ETH", + # "baseAssetPrecision": "8", + # "quoteAsset": "USDC", + # "quoteAssetPrecision": "8", + # "makerFeeRate": "0.0000", + # "takerFeeRate": "0.2500", + # "takerIdexFeeRate": "0.0500", + # "takerLiquidityProviderFeeRate": "0.2000", + # "tickSize": "0.01000000" + # }, + # ] + # + response2 = self.publicGetExchange() + # + # { + # "timeZone": "UTC", + # "serverTime": "1654460599952", + # "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", + # "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", + # "maticUsdPrice": "0.60", + # "gasPrice": "180", + # "volume24hUsd": "10015814.46", + # "totalVolumeUsd": "1589273533.28", + # "totalTrades": "1534904", + # "totalValueLockedUsd": "12041929.44", + # "geniusyieldStakingValueLockedUsd": "20133816.98", + # "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", + # "geniusyieldUsdPrice": "0.07", + # "geniusyieldMarketCapUsd": "48012346.00", + # "makerFeeRate": "0.0000", + # "takerFeeRate": "0.0025", + # "takerIdexFeeRate": "0.0005", + # "takerLiquidityProviderFeeRate": "0.0020", + # "makerTradeMinimum": "10.00000000", + # "takerTradeMinimum": "1.00000000", + # "withdrawMinimum": "0.50000000", + # "liquidityAdditionMinimum": "0.50000000", + # "liquidityRemovalMinimum": "0.40000000", + # "blockConfirmationDelay": "64" + # } + # + maker = self.safe_number(response2, 'makerFeeRate') + taker = self.safe_number(response2, 'takerFeeRate') + makerMin = self.safe_string(response2, 'makerTradeMinimum') + takerMin = self.safe_string(response2, 'takerTradeMinimum') + minCostETH = self.parse_number(Precise.string_min(makerMin, takerMin)) + result = [] + for i in range(0, len(response)): + entry = response[i] + marketId = self.safe_string(entry, 'market') + baseId = self.safe_string(entry, 'baseAsset') + quoteId = self.safe_string(entry, 'quoteAsset') + base = self.safe_currency_code(baseId) + quote = self.safe_currency_code(quoteId) + basePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'baseAssetPrecision'))) + quotePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'quoteAssetPrecision'))) + status = self.safe_string(entry, 'status') + minCost = None + if quote == 'ETH': + minCost = minCostETH + result.append({ + 'id': marketId, + 'symbol': base + '/' + quote, + 'base': base, + 'quote': quote, + 'settle': None, + 'baseId': baseId, + 'quoteId': quoteId, + 'settleId': None, + 'type': 'spot', + 'spot': True, + 'margin': False, + 'swap': False, + 'future': False, + 'option': False, + 'active': (status != 'inactive'), + 'contract': False, + 'linear': None, + 'inverse': None, + 'taker': taker, + 'maker': maker, + 'contractSize': None, + 'expiry': None, + 'expiryDatetime': None, + 'strike': None, + 'optionType': None, + 'precision': { + 'amount': basePrecision, + 'price': self.safe_number(entry, 'tickSize'), + }, + 'limits': { + 'leverage': { + 'min': None, + 'max': None, + }, + 'amount': { + 'min': basePrecision, + 'max': None, + }, + 'price': { + 'min': quotePrecision, + 'max': None, + }, + 'cost': { + 'min': minCost, + 'max': None, + }, + }, + 'created': None, + 'info': entry, + }) + return result + + def fetch_ticker(self, symbol: str, params={}) -> Ticker: + """ + fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + :see: https://api-docs-v3.geniusyield.io/#get-tickers + :param str symbol: unified symbol of the market to fetch the ticker for + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `ticker structure ` + """ + self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + } + # [ + # { + # "market": "DIL-ETH", + # "time": 1598367493008, + # "open": "0.09695361", + # "high": "0.10245881", + # "low": "0.09572507", + # "close": "0.09917079", + # "closeQuantity": "0.71320950", + # "baseVolume": "309.17380612", + # "quoteVolume": "30.57633981", + # "percentChange": "2.28", + # "numTrades": 205, + # "ask": "0.09910476", + # "bid": "0.09688340", + # "sequence": 3902 + # } + # ] + response = self.publicGetTickers(self.extend(request, params)) + ticker = self.safe_dict(response, 0) + return self.parse_ticker(ticker, market) + + def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers: + """ + fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + :see: https://api-docs-v3.geniusyield.io/#get-tickers + :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a dictionary of `ticker structures ` + """ + self.load_markets() + # [ + # { + # "market": "DIL-ETH", + # "time": 1598367493008, + # "open": "0.09695361", + # "high": "0.10245881", + # "low": "0.09572507", + # "close": "0.09917079", + # "closeQuantity": "0.71320950", + # "baseVolume": "309.17380612", + # "quoteVolume": "30.57633981", + # "percentChange": "2.28", + # "numTrades": 205, + # "ask": "0.09910476", + # "bid": "0.09688340", + # "sequence": 3902 + # }, ... + # ] + response = self.publicGetTickers(params) + return self.parse_tickers(response, symbols) + + def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker: + # { + # "market": "DIL-ETH", + # "time": 1598367493008, + # "open": "0.09695361", + # "high": "0.10245881", + # "low": "0.09572507", + # "close": "0.09917079", + # "closeQuantity": "0.71320950", + # "baseVolume": "309.17380612", + # "quoteVolume": "30.57633981", + # "percentChange": "2.28", + # "numTrades": 205, + # "ask": "0.09910476", + # "bid": "0.09688340", + # "sequence": 3902 + # } + marketId = self.safe_string(ticker, 'market') + market = self.safe_market(marketId, market, '-') + symbol = market['symbol'] + timestamp = self.safe_integer(ticker, 'time') + close = self.safe_string(ticker, 'close') + return self.safe_ticker({ + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'high': self.safe_string(ticker, 'high'), + 'low': self.safe_string(ticker, 'low'), + 'bid': self.safe_string(ticker, 'bid'), + 'bidVolume': None, + 'ask': self.safe_string(ticker, 'ask'), + 'askVolume': None, + 'vwap': None, + 'open': self.safe_string(ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': None, + 'change': None, + 'percentage': self.safe_string(ticker, 'percentChange'), + 'average': None, + 'baseVolume': self.safe_string(ticker, 'baseVolume'), + 'quoteVolume': self.safe_string(ticker, 'quoteVolume'), + 'info': ticker, + }, market) + + def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]: + """ + fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + :see: https://api-docs-v3.geniusyield.io/#get-candles + :param str symbol: unified symbol of the market to fetch OHLCV data for + :param str timeframe: the length of time each candle represents + :param int [since]: timestamp in ms of the earliest candle to fetch + :param int [limit]: the maximum amount of candles to fetch + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns int[][]: A list of candles ordered, open, high, low, close, volume + """ + self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + 'interval': timeframe, + } + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = min(limit, 1000) + response = self.publicGetCandles(self.extend(request, params)) + if isinstance(response, list): + # [ + # { + # "start": 1598345580000, + # "open": "0.09771286", + # "high": "0.09771286", + # "low": "0.09771286", + # "close": "0.09771286", + # "volume": "1.45340410", + # "sequence": 3853 + # }, ... + # ] + return self.parse_ohlcvs(response, market, timeframe, since, limit) + else: + # {"nextTime":1595536440000} + return [] + + def parse_ohlcv(self, ohlcv, market: Market = None) -> list: + # { + # "start": 1598345580000, + # "open": "0.09771286", + # "high": "0.09771286", + # "low": "0.09771286", + # "close": "0.09771286", + # "volume": "1.45340410", + # "sequence": 3853 + # } + timestamp = self.safe_integer(ohlcv, 'start') + open = self.safe_number(ohlcv, 'open') + high = self.safe_number(ohlcv, 'high') + low = self.safe_number(ohlcv, 'low') + close = self.safe_number(ohlcv, 'close') + volume = self.safe_number(ohlcv, 'volume') + return [timestamp, open, high, low, close, volume] + + def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: + """ + get the list of most recent trades for a particular symbol + :see: https://api-docs-v3.geniusyield.io/#get-trades + :param str symbol: unified symbol of the market to fetch trades for + :param int [since]: timestamp in ms of the earliest trade to fetch + :param int [limit]: the maximum amount of trades to fetch + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Trade[]: a list of `trade structures ` + """ + self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + } + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = min(limit, 1000) + # [ + # { + # "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", + # "price": "0.09771286", + # "quantity": "1.45340410", + # "quoteQuantity": "0.14201627", + # "time": 1598345638994, + # "makerSide": "buy", + # "sequence": 3853 + # }, ... + # ] + response = self.publicGetTrades(self.extend(request, params)) + return self.parse_trades(response, market, since, limit) + + def parse_trade(self, trade: dict, market: Market = None) -> Trade: + # + # public trades + # { + # "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", + # "price":"0.20377008", + # "quantity":"47.58448728", + # "quoteQuantity":"9.69629509", + # "time":1642091300873, + # "makerSide":"buy", + # "type":"hybrid", # one of either: "orderBook", "hybrid", or "pool" + # "sequence":31876 + # } + # + # private trades + # { + # "fillId":"83429066-9334-3582-b710-78858b2f0d6b", + # "price":"0.20717368", + # "quantity":"15.00000000", + # "quoteQuantity":"3.10760523", + # "orderBookQuantity":"0.00000003", + # "orderBookQuoteQuantity":"0.00000001", + # "poolQuantity":"14.99999997", + # "poolQuoteQuantity":"3.10760522", + # "time":1642083351215, + # "makerSide":"sell", + # "sequence":31795, + # "market":"Genius Yield-USDC", + # "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", + # "side":"buy", + # "fee":"0.03749989", + # "feeAsset":"Genius Yield", + # "gas":"0.40507261", + # "liquidity":"taker", + # "type":"hybrid", + # "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", + # "txStatus":"mined" + # } + # + id = self.safe_string(trade, 'fillId') + priceString = self.safe_string(trade, 'price') + amountString = self.safe_string(trade, 'quantity') + costString = self.safe_string(trade, 'quoteQuantity') + timestamp = self.safe_integer(trade, 'time') + marketId = self.safe_string(trade, 'market') + symbol = self.safe_symbol(marketId, market, '-') + # self code handles the duality of public vs private trades + makerSide = self.safe_string(trade, 'makerSide') + oppositeSide = 'sell' if (makerSide == 'buy') else 'buy' + side = self.safe_string(trade, 'side', oppositeSide) + takerOrMaker = self.safe_string(trade, 'liquidity', 'taker') + feeCostString = self.safe_string(trade, 'fee') + fee = None + if feeCostString is not None: + feeCurrencyId = self.safe_string(trade, 'feeAsset') + fee = { + 'cost': feeCostString, + 'currency': self.safe_currency_code(feeCurrencyId), + } + orderId = self.safe_string(trade, 'orderId') + return self.safe_trade({ + 'info': trade, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': priceString, + 'amount': amountString, + 'cost': costString, + 'fee': fee, + }, market) + + def fetch_trading_fees(self, params={}) -> TradingFees: + """ + fetch the trading fees for multiple markets + :see: https://api-docs-v3.geniusyield.io/#get-api-account + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a dictionary of `fee structures ` indexed by market symbols + """ + self.check_required_credentials() + self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + } + response = None + response = self.privateGetUser(self.extend(request, params)) + # + # { + # "depositEnabled": True, + # "orderEnabled": True, + # "cancelEnabled": True, + # "withdrawEnabled": True, + # "totalPortfolioValueUsd": "0.00", + # "makerFeeRate": "0.0000", + # "takerFeeRate": "0.0025", + # "takerIdexFeeRate": "0.0005", + # "takerLiquidityProviderFeeRate": "0.0020" + # } + # + maker = self.safe_number(response, 'makerFeeRate') + taker = self.safe_number(response, 'takerFeeRate') + result: dict = {} + for i in range(0, len(self.symbols)): + symbol = self.symbols[i] + result[symbol] = { + 'info': response, + 'symbol': symbol, + 'maker': maker, + 'taker': taker, + 'percentage': True, + 'tierBased': False, + } + return result + + def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: + """ + fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data + :see: https://api-docs-v3.geniusyield.io/#get-order-books + :param str symbol: unified symbol of the market to fetch the order book for + :param int [limit]: the maximum amount of order book entries to return + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: A dictionary of `order book structures ` indexed by market symbols + """ + self.load_markets() + market = self.market(symbol) + request: dict = { + 'market': market['id'], + 'level': 2, + } + if limit is not None: + request['limit'] = limit + # { + # "sequence": 36416753, + # "bids": [ + # ['0.09672815', "8.22284267", 1], + # ['0.09672814', "1.83685554", 1], + # ['0.09672143', "4.10962617", 1], + # ['0.09658884', "4.03863759", 1], + # ['0.09653781', "3.35730684", 1], + # ['0.09624660', "2.54163586", 1], + # ['0.09617490', "1.93065030", 1] + # ], + # "asks": [ + # ['0.09910476', "3.22840154", 1], + # ['0.09940587', "3.39796593", 1], + # ['0.09948189', "4.25088898", 1], + # ['0.09958362', "2.42195784", 1], + # ['0.09974393', "4.25234367", 1], + # ['0.09995250', "3.40192141", 1] + # ] + # } + response = self.publicGetOrderbook(self.extend(request, params)) + nonce = self.safe_integer(response, 'sequence') + return { + 'symbol': symbol, + 'timestamp': None, + 'datetime': None, + 'nonce': nonce, + 'bids': self.parse_side(response, 'bids'), + 'asks': self.parse_side(response, 'asks'), + } + + def parse_side(self, book, side): + bookSide = self.safe_value(book, side, []) + result = [] + for i in range(0, len(bookSide)): + order = bookSide[i] + price = self.safe_number(order, 0) + amount = self.safe_number(order, 1) + orderCount = self.safe_integer(order, 2) + result.append([price, amount, orderCount]) + descending = side == 'bids' + return self.sort_by(result, 0, descending) + + def fetch_currencies(self, params={}) -> Currencies: + """ + fetches all available currencies on an exchange + :see: https://api-docs-v3.geniusyield.io/#get-assets + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: an associative dictionary of currencies + """ + response = self.publicGetAssets(params) + # + # [ + # { + # "name": "Ethereum", + # "symbol": "ETH", + # "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + # "assetDecimals": "18", + # "exchangeDecimals": "8", + # "maticPrice": "3029.38503483" + # }, + # ] + # + result: dict = {} + for i in range(0, len(response)): + entry = response[i] + name = self.safe_string(entry, 'name') + currencyId = self.safe_string(entry, 'symbol') + code = self.safe_currency_code(currencyId) + precision = self.parse_number(self.parse_precision(self.safe_string(entry, 'exchangeDecimals'))) + result[code] = { + 'id': currencyId, + 'code': code, + 'info': entry, + 'type': None, + 'name': name, + 'active': None, + 'deposit': None, + 'withdraw': None, + 'fee': None, + 'precision': precision, + 'limits': { + 'amount': {'min': precision, 'max': None}, + 'withdraw': {'min': precision, 'max': None}, + }, + } + return result + + def parse_balance(self, response) -> Balances: + result: dict = { + 'info': response, + 'timestamp': None, + 'datetime': None, + } + for i in range(0, len(response)): + entry = response[i] + currencyId = self.safe_string(entry, 'asset') + code = self.safe_currency_code(currencyId) + account = self.account() + account['total'] = self.safe_string(entry, 'quantity') + account['free'] = self.safe_string(entry, 'availableForTrade') + account['used'] = self.safe_string(entry, 'locked') + result[code] = account + return self.safe_balance(result) + + def fetch_balance(self, params={}) -> Balances: + """ + query for balance and get the amount of funds available for trading or funds locked in orders + :see: https://api-docs-v3.geniusyield.io/#get-balances + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `balance structure ` + """ + self.check_required_credentials() + self.load_markets() + nonce1 = self.uuidv1() + request: dict = { + 'nonce': nonce1, + 'wallet': self.walletAddress, + } + # [ + # { + # "asset": "DIL", + # "quantity": "0.00000000", + # "availableForTrade": "0.00000000", + # "locked": "0.00000000", + # "usdValue": null + # }, ... + # ] + extendedRequest = self.extend(request, params) + if extendedRequest['wallet'] is None: + raise BadRequest(self.id + ' fetchBalance() wallet is None, set self.walletAddress or "address" in params') + response = None + try: + response = self.privateGetBalances(extendedRequest) + except Exception as e: + if isinstance(e, InvalidAddress): + walletAddress = extendedRequest['wallet'] + self.associate_wallet(walletAddress) + response = self.privateGetBalances(extendedRequest) + else: + raise e + return self.parse_balance(response) + + def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): + """ + fetch all trades made by the user + :see: https://api-docs-v3.geniusyield.io/#get-fills + :param str symbol: unified market symbol + :param int [since]: the earliest time in ms to fetch trades for + :param int [limit]: the maximum number of trades structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Trade[]: a list of `trade structures ` + """ + self.check_required_credentials() + self.load_markets() + market = None + request: dict = { + 'nonce': self.uuidv1(), + 'wallet': self.walletAddress, + } + if symbol is not None: + market = self.market(symbol) + request['market'] = market['id'] + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = limit + # [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478762, + # "makerSide": "sell", + # "sequence": 5053, + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "side": "buy", + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined" + # } + # ] + extendedRequest = self.extend(request, params) + if extendedRequest['wallet'] is None: + raise BadRequest(self.id + ' fetchMyTrades() walletAddress is None, set self.walletAddress or "address" in params') + response = None + try: + response = self.privateGetFills(extendedRequest) + except Exception as e: + if isinstance(e, InvalidAddress): + walletAddress = extendedRequest['wallet'] + self.associate_wallet(walletAddress) + response = self.privateGetFills(extendedRequest) + else: + raise e + return self.parse_trades(response, market, since, limit) + + def fetch_order(self, id: str, symbol: Str = None, params={}): + """ + fetches information on an order made by the user + :see: https://api-docs-v3.geniusyield.io/#get-orders + :param str symbol: unified symbol of the market the order was made in + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: An `order structure ` + """ + request: dict = { + 'orderId': id, + } + return self.fetch_orders_helper(symbol, None, None, self.extend(request, params)) + + def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: + """ + fetch all unfilled currently open orders + :see: https://api-docs-v3.geniusyield.io/#get-orders + :param str symbol: unified market symbol + :param int [since]: the earliest time in ms to fetch open orders for + :param int [limit]: the maximum number of open orders structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Order[]: a list of `order structures ` + """ + request: dict = { + 'closed': False, + } + return self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) + + def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: + """ + fetches information on multiple closed orders made by the user + :see: https://api-docs-v3.geniusyield.io/#get-orders + :param str symbol: unified market symbol of the market orders were made in + :param int [since]: the earliest time in ms to fetch orders for + :param int [limit]: the maximum number of order structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns Order[]: a list of `order structures ` + """ + request: dict = { + 'closed': True, + } + return self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) + + def fetch_orders_helper(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): + self.load_markets() + request: dict = { + 'nonce': self.uuidv1(), + 'wallet': self.walletAddress, + } + market = None + if symbol is not None: + market = self.market(symbol) + request['market'] = market['id'] + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = limit + response = self.privateGetOrders(self.extend(request, params)) + # fetchClosedOrders / fetchOpenOrders + # [ + # { + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "avgExecutionPrice": "0.09905990", + # "price": "1.00000000", + # "fills": [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined" + # } + # ] + # } + # ] + # fetchOrder + # {market: "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "avgExecutionPrice": "0.09905990", + # "price": "1.00000000", + # "fills": + # [{fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined"}]} + if isinstance(response, list): + return self.parse_orders(response, market, since, limit) + else: + return self.parse_order(response, market) + + def parse_order_status(self, status: Str): + # https://docs.geniusyield.io/#order-states-amp-lifecycle + statuses: dict = { + 'active': 'open', + 'partiallyFilled': 'open', + 'rejected': 'canceled', + 'filled': 'closed', + } + return self.safe_string(statuses, status, status) + + def parse_order(self, order: dict, market: Market = None) -> Order: + # + # { + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "avgExecutionPrice": "0.09905990", + # "price": "1.00000000", + # "fills": [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", + # "txStatus": "mined" + # } + # ] + # } + # + timestamp = self.safe_integer(order, 'time') + fills = self.safe_value(order, 'fills', []) + id = self.safe_string(order, 'orderId') + clientOrderId = self.safe_string(order, 'clientOrderId') + marketId = self.safe_string(order, 'market') + side = self.safe_string(order, 'side') + symbol = self.safe_symbol(marketId, market, '-') + type = self.safe_string(order, 'type') + amount = self.safe_string(order, 'originalQuantity') + filled = self.safe_string(order, 'executedQuantity') + average = self.safe_string(order, 'avgExecutionPrice') + price = self.safe_string(order, 'price') + rawStatus = self.safe_string(order, 'status') + timeInForce = self.safe_string_upper(order, 'timeInForce') + status = self.parse_order_status(rawStatus) + return self.safe_order({ + 'info': order, + 'id': id, + 'clientOrderId': clientOrderId, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'lastTradeTimestamp': None, + 'symbol': symbol, + 'type': type, + 'timeInForce': timeInForce, + 'postOnly': None, + 'side': side, + 'price': price, + 'stopPrice': None, + 'triggerPrice': None, + 'amount': amount, + 'cost': None, + 'average': average, + 'filled': filled, + 'remaining': None, + 'status': status, + 'fee': None, + 'trades': fills, + }, market) + + def associate_wallet(self, walletAddress, params={}): + nonce = self.uuidv1() + noPrefix = self.remove0x_prefix(walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(noPrefix), + ] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + # { + # "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "totalPortfolioValueUsd": "0.00", + # "time": 1598468353626 + # } + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': walletAddress, + }, + 'signature': signature, + } + result = self.privatePostWallets(request) + return result + + def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): + """ + create a trade order, https://docs.geniusyield.io/#create-order + :see: https://api-docs-v3.geniusyield.io/#create-order + :param str symbol: unified symbol of the market to create an order in + :param str type: 'market' or 'limit' + :param str side: 'buy' or 'sell' + :param float amount: how much of currency you want to trade in units of base currency + :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders + :param dict [params]: extra parameters specific to the exchange API endpoint + :param bool [params.test]: set to True to test an order, no order will be created but the request will be validated + :returns dict: an `order structure ` + """ + self.check_required_credentials() + self.load_markets() + testOrder = self.safe_bool(params, 'test', False) + params = self.omit(params, 'test') + market = self.market(symbol) + nonce = self.uuidv1() + typeEnum = None + stopLossTypeEnums: dict = { + 'stopLoss': 3, + 'stopLossLimit': 4, + 'takeProfit': 5, + 'takeProfitLimit': 6, + } + stopPriceString = None + if (type == 'stopLossLimit') or (type == 'takeProfitLimit') or ('stopPrice' in params): + if not ('stopPrice' in params): + raise BadRequest(self.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders') + stopPriceString = self.price_to_precision(symbol, params['stopPrice']) + limitTypeEnums: dict = { + 'limit': 1, + 'limitMaker': 2, + } + priceString = None + typeLower = type.lower() + limitOrder = typeLower.find('limit') >= 0 + if type in limitTypeEnums: + typeEnum = limitTypeEnums[type] + priceString = self.price_to_precision(symbol, price) + elif type in stopLossTypeEnums: + typeEnum = stopLossTypeEnums[type] + priceString = self.price_to_precision(symbol, price) + elif type == 'market': + typeEnum = 0 + else: + raise BadRequest(self.id + ' ' + type + ' is not a valid order type') + amountEnum = 0 # base quantity + if 'quoteOrderQuantity' in params: + if type != 'market': + raise NotSupported(self.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders') + amountEnum = 1 + amount = self.safe_number(params, 'quoteOrderQuantity') + sideEnum = 0 if (side == 'buy') else 1 + walletBytes = self.remove0x_prefix(self.walletAddress) + network = self.safe_string(self.options, 'network', 'ETH') + orderVersion = self.get_supported_mapping(network, { + 'ETH': 1, + 'BSC': 2, + 'MATIC': 4, + }) + amountString = self.amount_to_precision(symbol, amount) + # https://docs.geniusyield.io/#time-in-force + timeInForceEnums: dict = { + 'gtc': 0, + 'ioc': 2, + 'fok': 3, + } + defaultTimeInForce = self.safe_string(self.options, 'defaultTimeInForce', 'gtc') + timeInForce = self.safe_string(params, 'timeInForce', defaultTimeInForce) + timeInForceEnum = None + if timeInForce in timeInForceEnums: + timeInForceEnum = timeInForceEnums[timeInForce] + else: + allOptions = list(timeInForceEnums.keys()) + asString = ', '.join(allOptions) + raise BadRequest(self.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString) + # https://docs.geniusyield.io/#self-trade-prevention + selfTradePreventionEnums: dict = { + 'dc': 0, + 'co': 1, + 'cn': 2, + 'cb': 3, + } + defaultSelfTradePrevention = self.safe_string(self.options, 'defaultSelfTradePrevention', 'cn') + selfTradePrevention = self.safe_string(params, 'selfTradePrevention', defaultSelfTradePrevention) + selfTradePreventionEnum = None + if selfTradePrevention in selfTradePreventionEnums: + selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention] + else: + allOptions = list(selfTradePreventionEnums.keys()) + asString = ', '.join(allOptions) + raise BadRequest(self.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString) + byteArray = [ + self.number_to_be(orderVersion, 1), + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + self.encode(market['id']), + self.number_to_be(typeEnum, 1), + self.number_to_be(sideEnum, 1), + self.encode(amountString), + self.number_to_be(amountEnum, 1), + ] + if limitOrder: + encodedPrice = self.encode(priceString) + byteArray.append(encodedPrice) + if type in stopLossTypeEnums: + encodedPrice = self.encode(stopPriceString or priceString) + byteArray.append(encodedPrice) + clientOrderId = self.safe_string(params, 'clientOrderId') + if clientOrderId is not None: + byteArray.append(self.encode(clientOrderId)) + after = [ + self.number_to_be(timeInForceEnum, 1), + self.number_to_be(selfTradePreventionEnum, 1), + self.number_to_be(0, 8), # unused + ] + allBytes = self.array_concat(byteArray, after) + binary = self.binary_concat_array(allBytes) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request: dict = { + 'parameters': { + 'nonce': nonce, + 'market': market['id'], + 'side': side, + 'type': type, + 'wallet': self.walletAddress, + 'selfTradePrevention': selfTradePrevention, + }, + 'signature': signature, + } + if type != 'market': + request['parameters']['timeInForce'] = timeInForce + if limitOrder: + request['parameters']['price'] = priceString + if type in stopLossTypeEnums: + request['parameters']['stopPrice'] = stopPriceString or priceString + if amountEnum == 0: + request['parameters']['quantity'] = amountString + else: + request['parameters']['quoteOrderQuantity'] = amountString + if clientOrderId is not None: + request['parameters']['clientOrderId'] = clientOrderId + # { + # "market": "DIL-ETH", + # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", + # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", + # "time": 1598873478650, + # "status": "filled", + # "type": "limit", + # "side": "buy", + # "originalQuantity": "0.40000000", + # "executedQuantity": "0.40000000", + # "cumulativeQuoteQuantity": "0.03962396", + # "price": "1.00000000", + # "fills": [ + # { + # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", + # "price": "0.09905990", + # "quantity": "0.40000000", + # "quoteQuantity": "0.03962396", + # "time": 1598873478650, + # "makerSide": "sell", + # "sequence": 5053, + # "fee": "0.00080000", + # "feeAsset": "DIL", + # "gas": "0.00857497", + # "liquidity": "taker", + # "txStatus": "pending" + # } + # ], + # "avgExecutionPrice": "0.09905990" + # } + # we don't use self.extend here because it is a signed endpoint + response = None + if testOrder: + response = self.privatePostOrdersTest(request) + else: + response = self.privatePostOrders(request) + return self.parse_order(response, market) + + def withdraw(self, code: str, amount: float, address: str, tag=None, params={}): + """ + make a withdrawal + :see: https://api-docs-v3.geniusyield.io/#withdraw-funds + :param str code: unified currency code + :param float amount: the amount to withdraw + :param str address: the address to withdraw to + :param str tag: + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `transaction structure ` + """ + tag, params = self.handle_withdraw_tag_and_params(tag, params) + self.check_required_credentials() + self.load_markets() + nonce = self.uuidv1() + amountString = self.currency_to_precision(code, amount) + currency = self.currency(code) + walletBytes = self.remove0x_prefix(self.walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + self.encode(currency['id']), + self.encode(amountString), + self.number_to_be(1, 1), # bool set to True + ] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': address, + 'asset': currency['id'], + 'quantity': amountString, + }, + 'signature': signature, + } + response = self.privatePostWithdrawals(request) + # + # { + # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + # "asset": "ETH", + # "assetContractAddress": "0x0000000000000000000000000000000000000000", + # "quantity": "0.20000000", + # "time": 1598962883190, + # "fee": "0.00024000", + # "txStatus": "pending", + # "txId": null + # } + # + return self.parse_transaction(response, currency) + + def cancel_all_orders(self, symbol: Str = None, params={}): + """ + cancel all open orders + :see: https://api-docs-v3.geniusyield.io/#cancel-order + :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: a list of `order structures ` + """ + self.check_required_credentials() + self.load_markets() + market = None + if symbol is not None: + market = self.market(symbol) + nonce = self.uuidv1() + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': self.walletAddress, + }, + } + walletBytes = self.remove0x_prefix(self.walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + ] + if market is not None: + byteArray.append(self.encode(market['id'])) + request['parameters']['market'] = market['id'] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request['signature'] = signature + # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] + response = self.privateDeleteOrders(self.extend(request, params)) + return self.parse_orders(response, market) + + def cancel_order(self, id: str, symbol: Str = None, params={}): + """ + cancels an open order + :see: https://api-docs-v3.geniusyield.io/#cancel-order + :param str id: order id + :param str symbol: unified symbol of the market the order was made in + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: An `order structure ` + """ + self.check_required_credentials() + self.load_markets() + market = None + if symbol is not None: + market = self.market(symbol) + nonce = self.uuidv1() + walletBytes = self.remove0x_prefix(self.walletAddress) + byteArray = [ + self.base16_to_binary(nonce), + self.base16_to_binary(walletBytes), + self.encode(id), + ] + binary = self.binary_concat_array(byteArray) + hash = self.hash(binary, 'keccak', 'hex') + signature = self.sign_message_string(hash, self.privateKey) + request: dict = { + 'parameters': { + 'nonce': nonce, + 'wallet': self.walletAddress, + 'orderId': id, + }, + 'signature': signature, + } + # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] + response = self.privateDeleteOrders(self.extend(request, params)) + canceledOrder = self.safe_dict(response, 0) + return self.parse_order(canceledOrder, market) + + def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): + errorCode = self.safe_string(response, 'code') + message = self.safe_string(response, 'message') + if errorCode is not None: + self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, message) + raise ExchangeError(self.id + ' ' + message) + return None + + def fetch_deposit(self, id: str, code: Str = None, params={}): + """ + fetch information on a deposit + :see: https://api-docs-v3.geniusyield.io/#get-deposits + :param str id: deposit id + :param str code: not used by geniusyield fetchDeposit() + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `transaction structure ` + """ + self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + 'wallet': self.walletAddress, + 'depositId': id, + } + response = self.privateGetDeposits(self.extend(request, params)) + return self.parse_transaction(response) + + def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: + """ + fetch all deposits made to an account + :see: https://api-docs-v3.geniusyield.io/#get-deposits + :param str code: unified currency code + :param int [since]: the earliest time in ms to fetch deposits for + :param int [limit]: the maximum number of deposits structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: a list of `transaction structures ` + """ + params = self.extend({ + 'method': 'privateGetDeposits', + }, params) + return self.fetch_transactions_helper(code, since, limit, params) + + def fetch_status(self, params={}): + """ + the latest known information on the availability of the exchange API + :see: https://api-docs-v3.geniusyield.io/#get-ping + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `status structure ` + """ + response = self.publicGetPing(params) + return { + 'status': 'ok', # if there's no Errors, status = 'ok' + 'updated': None, + 'eta': None, + 'url': None, + 'info': response, + } + + def fetch_time(self, params={}): + """ + fetches the current integer timestamp in milliseconds from the exchange server + :see: https://api-docs-v3.geniusyield.io/#get-time + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns int: the current integer timestamp in milliseconds from the exchange server + """ + response = self.publicGetTime(params) + # + # {serverTime: "1655258263236"} + # + return self.safe_integer(response, 'serverTime') + + def fetch_withdrawal(self, id: str, code: Str = None, params={}): + """ + fetch data on a currency withdrawal via the withdrawal id + :see: https://api-docs-v3.geniusyield.io/#get-withdrawals + :param str id: withdrawal id + :param str code: not used by geniusyield.fetchWithdrawal + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: a `transaction structure ` + """ + self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + 'wallet': self.walletAddress, + 'withdrawalId': id, + } + response = self.privateGetWithdrawals(self.extend(request, params)) + return self.parse_transaction(response) + + def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: + """ + fetch all withdrawals made from an account + :see: https://api-docs-v3.geniusyield.io/#get-withdrawals + :param str code: unified currency code + :param int [since]: the earliest time in ms to fetch withdrawals for + :param int [limit]: the maximum number of withdrawals structures to retrieve + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict[]: a list of `transaction structures ` + """ + params = self.extend({ + 'method': 'privateGetWithdrawals', + }, params) + return self.fetch_transactions_helper(code, since, limit, params) + + def fetch_transactions_helper(self, code: Str = None, since: Int = None, limit: Int = None, params={}): + self.load_markets() + nonce = self.uuidv1() + request: dict = { + 'nonce': nonce, + 'wallet': self.walletAddress, + } + currency = None + if code is not None: + currency = self.currency(code) + request['asset'] = currency['id'] + if since is not None: + request['start'] = since + if limit is not None: + request['limit'] = limit + # [ + # { + # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", + # "asset": "ETH", + # "quantity": "1.00000000", + # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + # "txTime": 1598865853000, + # "confirmationTime": 1598865930231 + # } + # ] + method = params['method'] + params = self.omit(params, 'method') + response = None + if method == 'privateGetDeposits': + response = self.privateGetDeposits(self.extend(request, params)) + elif method == 'privateGetWithdrawals': + response = self.privateGetWithdrawals(self.extend(request, params)) + else: + raise NotSupported(self.id + ' fetchTransactionsHelper() not support self method') + return self.parse_transactions(response, currency, since, limit) + + def parse_transaction_status(self, status: Str): + statuses: dict = { + 'mined': 'ok', + } + return self.safe_string(statuses, status, status) + + def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction: + # + # fetchDeposits + # + # { + # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", + # "asset": "ETH", + # "quantity": "1.00000000", + # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", + # "txTime": 1598865853000, + # "confirmationTime": 1598865930231 + # } + # + # fetchWithdrwalas + # + # { + # "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", + # "asset": "ETH", + # "assetContractAddress": "0x0000000000000000000000000000000000000000", + # "quantity": "0.20000000", + # "time": 1598962883288, + # "fee": "0.00024000", + # "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", + # "txStatus": "mined" + # } + # + # withdraw + # + # { + # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", + # "asset": "ETH", + # "assetContractAddress": "0x0000000000000000000000000000000000000000", + # "quantity": "0.20000000", + # "time": 1598962883190, + # "fee": "0.00024000", + # "txStatus": "pending", + # "txId": null + # } + # + type = None + if 'depositId' in transaction: + type = 'deposit' + elif ('withdrawId' in transaction) or ('withdrawalId' in transaction): + type = 'withdrawal' + id = self.safe_string_2(transaction, 'depositId', 'withdrawId') + id = self.safe_string(transaction, 'withdrawalId', id) + code = self.safe_currency_code(self.safe_string(transaction, 'asset'), currency) + amount = self.safe_number(transaction, 'quantity') + txid = self.safe_string(transaction, 'txId') + timestamp = self.safe_integer_2(transaction, 'txTime', 'time') + fee = None + if 'fee' in transaction: + fee = { + 'cost': self.safe_number(transaction, 'fee'), + 'currency': 'ETH', + } + rawStatus = self.safe_string(transaction, 'txStatus') + status = self.parse_transaction_status(rawStatus) + updated = self.safe_integer(transaction, 'confirmationTime') + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': self.iso8601(timestamp), + 'network': None, + 'address': None, + 'addressTo': None, + 'addressFrom': None, + 'tag': None, + 'tagTo': None, + 'tagFrom': None, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'comment': None, + 'internal': None, + 'fee': fee, + } + + def calculate_rate_limiter_cost(self, api, method, path, params, config={}): + hasApiKey = (self.apiKey is not None) + hasSecret = (self.secret is not None) + hasWalletAddress = (self.walletAddress is not None) + hasPrivateKey = (self.privateKey is not None) + defaultCost = self.safe_value(config, 'cost', 1) + authenticated = hasApiKey and hasSecret and hasWalletAddress and hasPrivateKey + return(defaultCost / 2) if authenticated else defaultCost + + def fetch_deposit_address(self, code: Str = None, params={}): + """ + fetch the Polygon address of the wallet + :see: https://api-docs-v3.geniusyield.io/#get-wallets + :param str code: not used by geniusyield + :param dict [params]: extra parameters specific to the exchange API endpoint + :returns dict: an `address structure ` + """ + request: dict = {} + request['nonce'] = self.uuidv1() + response = self.privateGetWallets(self.extend(request, params)) + # + # [ + # { + # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + # totalPortfolioValueUsd: "0.00", + # time: "1678342148086" + # }, + # { + # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + # totalPortfolioValueUsd: "15.90", + # time: "1691697811659" + # } + # ] + # + return self.parse_deposit_address(response) + + def parse_deposit_address(self, depositAddress, currency: Currency = None): + # + # [ + # { + # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", + # totalPortfolioValueUsd: "0.00", + # time: "1678342148086" + # }, + # { + # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", + # totalPortfolioValueUsd: "15.90", + # time: "1691697811659" + # } + # ] + # + length = len(depositAddress) + entry = self.safe_dict(depositAddress, length - 1) + address = self.safe_string(entry, 'address') + self.check_address(address) + return { + 'info': depositAddress, + 'currency': None, + 'address': address, + 'tag': None, + 'network': 'MATIC', + } + + def sign(self, path, api='public', method='GET', params={}, headers=None, body=None): + network = self.safe_string(self.options, 'network', 'ETH') + version = self.safe_string(self.options, 'version', 'v1') + url = self.urls['api'][network] + '/' + version + '/' + path + keys = list(params.keys()) + length = len(keys) + query = None + if length > 0: + if method == 'GET': + query = self.urlencode(params) + url = url + '?' + query + else: + body = self.json(params) + headers = { + 'Content-Type': 'application/json', + } + if self.apiKey is not None: + headers['Genius Yield-API-Key'] = self.apiKey + if api == 'private': + payload = None + if method == 'GET': + payload = query + else: + payload = body + headers['Genius Yield-HMAC-Signature'] = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex') + return {'url': url, 'method': method, 'body': body, 'headers': headers} + + def remove0x_prefix(self, hexData): + if hexData[0:2] == '0x': + return hexData[2:] + else: + return hexData + + def hash_message(self, message): + # takes a hex encoded message + binaryMessage = self.base16_to_binary(self.remove0x_prefix(message)) + prefix = self.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength) + return '0x' + self.hash(self.binary_concat(prefix, binaryMessage), 'keccak', 'hex') + + def sign_hash(self, hash, privateKey): + signature = self.ecdsa(hash[-64:], privateKey[-64:], 'secp256k1', None) + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + } + + def sign_message(self, message, privateKey): + return self.sign_hash(self.hash_message(message), privateKey[-64:]) + + def sign_message_string(self, message, privateKey): + # still takes the input hex string + # same but returns a string instead of an object + signature = self.sign_message(message, privateKey) + return signature['r'] + self.remove0x_prefix(signature['s']) + self.binary_to_base16(self.number_to_be(signature['v'], 1)) diff --git a/ts/src/abstract/geniusyield.ts b/ts/src/abstract/geniusyield.ts index 34a705e21503..1d0e32cb9e77 100644 --- a/ts/src/abstract/geniusyield.ts +++ b/ts/src/abstract/geniusyield.ts @@ -9,28 +9,8 @@ import { implicitReturnType } from '../base/types.js'; import { Exchange as _Exchange } from '../base/Exchange.js'; interface Exchange { - publicGetPing (params?: {}): Promise; - publicGetTime (params?: {}): Promise; - publicGetExchange (params?: {}): Promise; - publicGetAssets (params?: {}): Promise; - publicGetMarkets (params?: {}): Promise; - publicGetTickers (params?: {}): Promise; - publicGetCandles (params?: {}): Promise; - publicGetTrades (params?: {}): Promise; - publicGetOrderbook (params?: {}): Promise; - privateGetUser (params?: {}): Promise; - privateGetWallets (params?: {}): Promise; - privateGetBalances (params?: {}): Promise; - privateGetOrders (params?: {}): Promise; - privateGetFills (params?: {}): Promise; - privateGetDeposits (params?: {}): Promise; - privateGetWithdrawals (params?: {}): Promise; - privateGetWsToken (params?: {}): Promise; - privatePostWallets (params?: {}): Promise; - privatePostOrders (params?: {}): Promise; - privatePostOrdersTest (params?: {}): Promise; - privatePostWithdrawals (params?: {}): Promise; - privateDeleteOrders (params?: {}): Promise; + privateGetMarkets (params?: {}): Promise; + privateGetTradingFees (params?: {}): Promise; } abstract class Exchange extends _Exchange {} diff --git a/ts/src/geniusyield.ts b/ts/src/geniusyield.ts index d72d0a064096..412364e573c2 100644 --- a/ts/src/geniusyield.ts +++ b/ts/src/geniusyield.ts @@ -2,14 +2,9 @@ // --------------------------------------------------------------------------- import Exchange from './abstract/geniusyield.js'; -import { TICK_SIZE, PAD_WITH_ZERO, ROUND, TRUNCATE, DECIMAL_PLACES } from './base/functions/number.js'; -import { InvalidOrder, InsufficientFunds, ExchangeError, ExchangeNotAvailable, DDoSProtection, BadRequest, NotSupported, InvalidAddress, AuthenticationError } from './base/errors.js'; -import { Precise } from './base/Precise.js'; -import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; -import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js'; -import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js'; -import { ecdsa } from './base/functions/crypto.js'; -import type { Balances, Currencies, Currency, Dict, Int, Market, Num, OHLCV, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, int } from './base/types.js'; +import { TICK_SIZE, PAD_WITH_ZERO } from './base/functions/number.js'; +import { InvalidOrder, InsufficientFunds, ExchangeNotAvailable, DDoSProtection, BadRequest, InvalidAddress, AuthenticationError } from './base/errors.js'; +import type { Market } from './base/types.js'; // --------------------------------------------------------------------------- @@ -37,29 +32,29 @@ export default class geniusyield extends Exchange { 'future': false, 'option': false, 'addMargin': false, - 'cancelAllOrders': true, - 'cancelOrder': true, + 'cancelAllOrders': false, + 'cancelOrder': false, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': true, + 'createOrder': false, 'createReduceOnlyOrder': false, - 'createStopLimitOrder': true, - 'createStopMarketOrder': true, - 'createStopOrder': true, - 'fetchBalance': true, + 'createStopLimitOrder': false, + 'createStopMarketOrder': false, + 'createStopOrder': false, + 'fetchBalance': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, - 'fetchClosedOrders': true, + 'fetchClosedOrders': false, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, - 'fetchCurrencies': true, - 'fetchDeposit': true, - 'fetchDepositAddress': true, + 'fetchCurrencies': false, + 'fetchDeposit': false, + 'fetchDepositAddress': false, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, - 'fetchDeposits': true, + 'fetchDeposits': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, @@ -72,12 +67,12 @@ export default class geniusyield extends Exchange { 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, - 'fetchMyTrades': true, - 'fetchOHLCV': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, 'fetchOpenInterestHistory': false, - 'fetchOpenOrders': true, - 'fetchOrder': true, - 'fetchOrderBook': true, + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': false, 'fetchOrders': false, 'fetchPosition': false, 'fetchPositionHistory': false, @@ -87,23 +82,23 @@ export default class geniusyield extends Exchange { 'fetchPositionsHistory': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, - 'fetchStatus': true, - 'fetchTicker': true, - 'fetchTickers': true, - 'fetchTime': true, - 'fetchTrades': true, + 'fetchStatus': false, + 'fetchTicker': false, + 'fetchTickers': false, + 'fetchTime': false, + 'fetchTrades': false, 'fetchTradingFee': false, - 'fetchTradingFees': true, + 'fetchTradingFees': false, 'fetchTransactions': false, - 'fetchWithdrawal': true, - 'fetchWithdrawals': true, + 'fetchWithdrawal': false, + 'fetchWithdrawals': false, 'reduceMargin': false, - 'sandbox': true, + 'sandbox': false, 'setLeverage': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, - 'withdraw': true, + 'withdraw': false, }, 'timeframes': { '1m': '1m', @@ -115,58 +110,31 @@ export default class geniusyield extends Exchange { '1d': '1d', }, 'urls': { - 'test': { - 'MATIC': 'https://api-sandbox-matic.geniusyield.io', - }, - 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api': { - 'MATIC': 'https://api-matic.geniusyield.io', + 'preprod': 'https://localhost:8082/v0/', + 'mainnet': 'https://localhost:8082/v0/', }, - 'www': 'https://geniusyield.io', + 'www': 'https://www.geniusyield.co/', 'doc': [ - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ], }, 'api': { 'public': { 'get': { - 'ping': 1, - 'time': 1, - 'exchange': 1, - 'assets': 1, - 'markets': 1, - 'tickers': 1, - 'candles': 1, - 'trades': 1, - 'orderbook': 1, }, }, 'private': { 'get': { - 'user': 1, - 'wallets': 1, - 'balances': 1, - 'orders': 0.1, - 'fills': 0.1, - 'deposits': 1, - 'withdrawals': 1, - 'wsToken': 1, - }, - 'post': { - 'wallets': 1, - 'orders': 0.1, - 'orders/test': 0.1, - 'withdrawals': 1, - }, - 'delete': { - 'orders': 0.1, + 'markets': 0, + 'trading-fees': 0, }, }, }, 'options': { - 'defaultTimeInForce': 'gtc', + 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', - 'network': 'MATIC', + 'network': 'mainnet', }, 'exceptions': { 'exact': { @@ -180,10 +148,10 @@ export default class geniusyield extends Exchange { }, }, 'requiredCredentials': { - 'walletAddress': true, - 'privateKey': true, + 'walletAddress': false, + 'privateKey': false, 'apiKey': true, - 'secret': true, + 'secret': false, }, 'precisionMode': TICK_SIZE, 'paddingMode': PAD_WITH_ZERO, @@ -191,19 +159,6 @@ export default class geniusyield extends Exchange { }); } - priceToPrecision (symbol, price) { - // - // we override priceToPrecision to fix the following issue - // https://github.com/ccxt/ccxt/issues/13367 - // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} - // - const market = this.market (symbol); - const info = this.safeValue (market, 'info', {}); - const quoteAssetPrecision = this.safeInteger (info, 'quoteAssetPrecision'); - price = this.decimalToPrecision (price, ROUND, market['precision']['price'], this.precisionMode); - return this.decimalToPrecision (price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); - } - async fetchMarkets (params = {}): Promise { /** * @method @@ -213,77 +168,34 @@ export default class geniusyield extends Exchange { * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ - const response = await this.publicGetMarkets (params); - // - // [ - // { - // "market": "ETH-USDC", - // "type": "hybrid", - // "status": "activeHybrid", - // "baseAsset": "ETH", - // "baseAssetPrecision": "8", - // "quoteAsset": "USDC", - // "quoteAssetPrecision": "8", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.2500", - // "takerIdexFeeRate": "0.0500", - // "takerLiquidityProviderFeeRate": "0.2000", - // "tickSize": "0.01000000" - // }, - // ] - // - const response2 = await this.publicGetExchange (); - // + const markets = await this.privateGetMarkets (params); + // [ // { - // "timeZone": "UTC", - // "serverTime": "1654460599952", - // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - // "maticUsdPrice": "0.60", - // "gasPrice": "180", - // "volume24hUsd": "10015814.46", - // "totalVolumeUsd": "1589273533.28", - // "totalTrades": "1534904", - // "totalValueLockedUsd": "12041929.44", - // "geniusyieldStakingValueLockedUsd": "20133816.98", - // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", - // "geniusyieldUsdPrice": "0.07", - // "geniusyieldMarketCapUsd": "48012346.00", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.0025", - // "takerIdexFeeRate": "0.0005", - // "takerLiquidityProviderFeeRate": "0.0020", - // "makerTradeMinimum": "10.00000000", - // "takerTradeMinimum": "1.00000000", - // "withdrawMinimum": "0.50000000", - // "liquidityAdditionMinimum": "0.50000000", - // "liquidityRemovalMinimum": "0.40000000", - // "blockConfirmationDelay": "64" + // "market_id": "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + // "base_asset": "lovelace", + // "target_asset": "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" // } - // - const maker = this.safeNumber (response2, 'makerFeeRate'); - const taker = this.safeNumber (response2, 'takerFeeRate'); - const makerMin = this.safeString (response2, 'makerTradeMinimum'); - const takerMin = this.safeString (response2, 'takerTradeMinimum'); - const minCostETH = this.parseNumber (Precise.stringMin (makerMin, takerMin)); + // ] + const fees = await this.privateGetTradingFees (); + // { + // "flat_maker_fee": "1000000", + // "flat_taker_fee": "1000000", + // "percentage_maker_fee": "0.3", + // "percentage_taker_fee": "0.3" + // } + const maker = this.safeNumber (fees, 'percentage_maker_fee'); + const taker = this.safeNumber (fees, 'percentage_taker_fee'); const result = []; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const marketId = this.safeString (entry, 'market'); - const baseId = this.safeString (entry, 'baseAsset'); - const quoteId = this.safeString (entry, 'quoteAsset'); + for (let i = 0; i < markets.length; i++) { + const entry = markets[i]; + const marketId = this.safeString (entry, 'market_id'); + const baseId = this.safeString (entry, 'base_asset'); + const quoteId = this.safeString (entry, 'target_asset'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); - const basePrecision = this.parseNumber (this.parsePrecision (this.safeString (entry, 'baseAssetPrecision'))); - const quotePrecision = this.parseNumber (this.parsePrecision (this.safeString (entry, 'quoteAssetPrecision'))); - const status = this.safeString (entry, 'status'); - let minCost = undefined; - if (quote === 'ETH') { - minCost = minCostETH; - } result.push ({ 'id': marketId, - 'symbol': base + '/' + quote, + 'symbol': marketId, 'base': base, 'quote': quote, 'settle': undefined, @@ -296,7 +208,7 @@ export default class geniusyield extends Exchange { 'swap': false, 'future': false, 'option': false, - 'active': (status !== 'inactive'), + 'active': true, 'contract': false, 'linear': undefined, 'inverse': undefined, @@ -308,8 +220,8 @@ export default class geniusyield extends Exchange { 'strike': undefined, 'optionType': undefined, 'precision': { - 'amount': basePrecision, - 'price': this.safeNumber (entry, 'tickSize'), + 'amount': undefined, + 'price': undefined, }, 'limits': { 'leverage': { @@ -317,15 +229,15 @@ export default class geniusyield extends Exchange { 'max': undefined, }, 'amount': { - 'min': basePrecision, + 'min': undefined, 'max': undefined, }, 'price': { - 'min': quotePrecision, + 'min': undefined, 'max': undefined, }, 'cost': { - 'min': minCost, + 'min': undefined, 'max': undefined, }, }, @@ -335,1577 +247,4 @@ export default class geniusyield extends Exchange { } return result; } - - async fetchTicker (symbol: string, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchTicker - * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string} symbol unified symbol of the market to fetch the ticker for - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} - */ - await this.loadMarkets (); - const market = this.market (symbol); - const request: Dict = { - 'market': market['id'], - }; - // [ - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // } - // ] - const response = await this.publicGetTickers (this.extend (request, params)); - const ticker = this.safeDict (response, 0); - return this.parseTicker (ticker, market); - } - - async fetchTickers (symbols: Strings = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchTickers - * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} - */ - await this.loadMarkets (); - // [ - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // }, ... - // ] - const response = await this.publicGetTickers (params); - return this.parseTickers (response, symbols); - } - - parseTicker (ticker: Dict, market: Market = undefined): Ticker { - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // } - const marketId = this.safeString (ticker, 'market'); - market = this.safeMarket (marketId, market, '-'); - const symbol = market['symbol']; - const timestamp = this.safeInteger (ticker, 'time'); - const close = this.safeString (ticker, 'close'); - return this.safeTicker ({ - 'symbol': symbol, - 'timestamp': timestamp, - 'datetime': this.iso8601 (timestamp), - 'high': this.safeString (ticker, 'high'), - 'low': this.safeString (ticker, 'low'), - 'bid': this.safeString (ticker, 'bid'), - 'bidVolume': undefined, - 'ask': this.safeString (ticker, 'ask'), - 'askVolume': undefined, - 'vwap': undefined, - 'open': this.safeString (ticker, 'open'), - 'close': close, - 'last': close, - 'previousClose': undefined, - 'change': undefined, - 'percentage': this.safeString (ticker, 'percentChange'), - 'average': undefined, - 'baseVolume': this.safeString (ticker, 'baseVolume'), - 'quoteVolume': this.safeString (ticker, 'quoteVolume'), - 'info': ticker, - }, market); - } - - async fetchOHLCV (symbol: string, timeframe = '1m', since: Int = undefined, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchOHLCV - * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market - * @see https://api-docs-v3.geniusyield.io/#get-candles - * @param {string} symbol unified symbol of the market to fetch OHLCV data for - * @param {string} timeframe the length of time each candle represents - * @param {int} [since] timestamp in ms of the earliest candle to fetch - * @param {int} [limit] the maximum amount of candles to fetch - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume - */ - await this.loadMarkets (); - const market = this.market (symbol); - const request: Dict = { - 'market': market['id'], - 'interval': timeframe, - }; - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = Math.min (limit, 1000); - } - const response = await this.publicGetCandles (this.extend (request, params)); - if (Array.isArray (response)) { - // [ - // { - // "start": 1598345580000, - // "open": "0.09771286", - // "high": "0.09771286", - // "low": "0.09771286", - // "close": "0.09771286", - // "volume": "1.45340410", - // "sequence": 3853 - // }, ... - // ] - return this.parseOHLCVs (response, market, timeframe, since, limit); - } else { - // {"nextTime":1595536440000} - return []; - } - } - - parseOHLCV (ohlcv, market: Market = undefined): OHLCV { - // { - // "start": 1598345580000, - // "open": "0.09771286", - // "high": "0.09771286", - // "low": "0.09771286", - // "close": "0.09771286", - // "volume": "1.45340410", - // "sequence": 3853 - // } - const timestamp = this.safeInteger (ohlcv, 'start'); - const open = this.safeNumber (ohlcv, 'open'); - const high = this.safeNumber (ohlcv, 'high'); - const low = this.safeNumber (ohlcv, 'low'); - const close = this.safeNumber (ohlcv, 'close'); - const volume = this.safeNumber (ohlcv, 'volume'); - return [ timestamp, open, high, low, close, volume ]; - } - - async fetchTrades (symbol: string, since: Int = undefined, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchTrades - * @description get the list of most recent trades for a particular symbol - * @see https://api-docs-v3.geniusyield.io/#get-trades - * @param {string} symbol unified symbol of the market to fetch trades for - * @param {int} [since] timestamp in ms of the earliest trade to fetch - * @param {int} [limit] the maximum amount of trades to fetch - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} - */ - await this.loadMarkets (); - const market = this.market (symbol); - const request: Dict = { - 'market': market['id'], - }; - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = Math.min (limit, 1000); - } - // [ - // { - // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", - // "price": "0.09771286", - // "quantity": "1.45340410", - // "quoteQuantity": "0.14201627", - // "time": 1598345638994, - // "makerSide": "buy", - // "sequence": 3853 - // }, ... - // ] - const response = await this.publicGetTrades (this.extend (request, params)); - return this.parseTrades (response, market, since, limit); - } - - parseTrade (trade: Dict, market: Market = undefined): Trade { - // - // public trades - // { - // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - // "price":"0.20377008", - // "quantity":"47.58448728", - // "quoteQuantity":"9.69629509", - // "time":1642091300873, - // "makerSide":"buy", - // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" - // "sequence":31876 - // } - // - // private trades - // { - // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - // "price":"0.20717368", - // "quantity":"15.00000000", - // "quoteQuantity":"3.10760523", - // "orderBookQuantity":"0.00000003", - // "orderBookQuoteQuantity":"0.00000001", - // "poolQuantity":"14.99999997", - // "poolQuoteQuantity":"3.10760522", - // "time":1642083351215, - // "makerSide":"sell", - // "sequence":31795, - // "market":"Genius Yield-USDC", - // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - // "side":"buy", - // "fee":"0.03749989", - // "feeAsset":"Genius Yield", - // "gas":"0.40507261", - // "liquidity":"taker", - // "type":"hybrid", - // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - // "txStatus":"mined" - // } - // - const id = this.safeString (trade, 'fillId'); - const priceString = this.safeString (trade, 'price'); - const amountString = this.safeString (trade, 'quantity'); - const costString = this.safeString (trade, 'quoteQuantity'); - const timestamp = this.safeInteger (trade, 'time'); - const marketId = this.safeString (trade, 'market'); - const symbol = this.safeSymbol (marketId, market, '-'); - // this code handles the duality of public vs private trades - const makerSide = this.safeString (trade, 'makerSide'); - const oppositeSide = (makerSide === 'buy') ? 'sell' : 'buy'; - const side = this.safeString (trade, 'side', oppositeSide); - const takerOrMaker = this.safeString (trade, 'liquidity', 'taker'); - const feeCostString = this.safeString (trade, 'fee'); - let fee = undefined; - if (feeCostString !== undefined) { - const feeCurrencyId = this.safeString (trade, 'feeAsset'); - fee = { - 'cost': feeCostString, - 'currency': this.safeCurrencyCode (feeCurrencyId), - }; - } - const orderId = this.safeString (trade, 'orderId'); - return this.safeTrade ({ - 'info': trade, - 'timestamp': timestamp, - 'datetime': this.iso8601 (timestamp), - 'symbol': symbol, - 'id': id, - 'order': orderId, - 'type': 'limit', - 'side': side, - 'takerOrMaker': takerOrMaker, - 'price': priceString, - 'amount': amountString, - 'cost': costString, - 'fee': fee, - }, market); - } - - async fetchTradingFees (params = {}): Promise { - /** - * @method - * @name geniusyield#fetchTradingFees - * @description fetch the trading fees for multiple markets - * @see https://api-docs-v3.geniusyield.io/#get-api-account - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols - */ - this.checkRequiredCredentials (); - await this.loadMarkets (); - const nonce = this.uuidv1 (); - const request: Dict = { - 'nonce': nonce, - }; - let response = undefined; - response = await this.privateGetUser (this.extend (request, params)); - // - // { - // "depositEnabled": true, - // "orderEnabled": true, - // "cancelEnabled": true, - // "withdrawEnabled": true, - // "totalPortfolioValueUsd": "0.00", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.0025", - // "takerIdexFeeRate": "0.0005", - // "takerLiquidityProviderFeeRate": "0.0020" - // } - // - const maker = this.safeNumber (response, 'makerFeeRate'); - const taker = this.safeNumber (response, 'takerFeeRate'); - const result: Dict = {}; - for (let i = 0; i < this.symbols.length; i++) { - const symbol = this.symbols[i]; - result[symbol] = { - 'info': response, - 'symbol': symbol, - 'maker': maker, - 'taker': taker, - 'percentage': true, - 'tierBased': false, - }; - } - return result; - } - - async fetchOrderBook (symbol: string, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchOrderBook - * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data - * @see https://api-docs-v3.geniusyield.io/#get-order-books - * @param {string} symbol unified symbol of the market to fetch the order book for - * @param {int} [limit] the maximum amount of order book entries to return - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols - */ - await this.loadMarkets (); - const market = this.market (symbol); - const request: Dict = { - 'market': market['id'], - 'level': 2, - }; - if (limit !== undefined) { - request['limit'] = limit; - } - // { - // "sequence": 36416753, - // "bids": [ - // [ '0.09672815', "8.22284267", 1 ], - // [ '0.09672814', "1.83685554", 1 ], - // [ '0.09672143', "4.10962617", 1 ], - // [ '0.09658884', "4.03863759", 1 ], - // [ '0.09653781', "3.35730684", 1 ], - // [ '0.09624660', "2.54163586", 1 ], - // [ '0.09617490', "1.93065030", 1 ] - // ], - // "asks": [ - // [ '0.09910476', "3.22840154", 1 ], - // [ '0.09940587', "3.39796593", 1 ], - // [ '0.09948189', "4.25088898", 1 ], - // [ '0.09958362', "2.42195784", 1 ], - // [ '0.09974393', "4.25234367", 1 ], - // [ '0.09995250', "3.40192141", 1 ] - // ] - // } - const response = await this.publicGetOrderbook (this.extend (request, params)); - const nonce = this.safeInteger (response, 'sequence'); - return { - 'symbol': symbol, - 'timestamp': undefined, - 'datetime': undefined, - 'nonce': nonce, - 'bids': this.parseSide (response, 'bids'), - 'asks': this.parseSide (response, 'asks'), - } as OrderBook; - } - - parseSide (book, side) { - const bookSide = this.safeValue (book, side, []); - const result = []; - for (let i = 0; i < bookSide.length; i++) { - const order = bookSide[i]; - const price = this.safeNumber (order, 0); - const amount = this.safeNumber (order, 1); - const orderCount = this.safeInteger (order, 2); - result.push ([ price, amount, orderCount ]); - } - const descending = side === 'bids'; - return this.sortBy (result, 0, descending); - } - - async fetchCurrencies (params = {}): Promise { - /** - * @method - * @name geniusyield#fetchCurrencies - * @description fetches all available currencies on an exchange - * @see https://api-docs-v3.geniusyield.io/#get-assets - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} an associative dictionary of currencies - */ - const response = await this.publicGetAssets (params); - // - // [ - // { - // "name": "Ethereum", - // "symbol": "ETH", - // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - // "assetDecimals": "18", - // "exchangeDecimals": "8", - // "maticPrice": "3029.38503483" - // }, - // ] - // - const result: Dict = {}; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const name = this.safeString (entry, 'name'); - const currencyId = this.safeString (entry, 'symbol'); - const code = this.safeCurrencyCode (currencyId); - const precision = this.parseNumber (this.parsePrecision (this.safeString (entry, 'exchangeDecimals'))); - result[code] = { - 'id': currencyId, - 'code': code, - 'info': entry, - 'type': undefined, - 'name': name, - 'active': undefined, - 'deposit': undefined, - 'withdraw': undefined, - 'fee': undefined, - 'precision': precision, - 'limits': { - 'amount': { 'min': precision, 'max': undefined }, - 'withdraw': { 'min': precision, 'max': undefined }, - }, - }; - } - return result; - } - - parseBalance (response): Balances { - const result: Dict = { - 'info': response, - 'timestamp': undefined, - 'datetime': undefined, - }; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const currencyId = this.safeString (entry, 'asset'); - const code = this.safeCurrencyCode (currencyId); - const account = this.account (); - account['total'] = this.safeString (entry, 'quantity'); - account['free'] = this.safeString (entry, 'availableForTrade'); - account['used'] = this.safeString (entry, 'locked'); - result[code] = account; - } - return this.safeBalance (result); - } - - async fetchBalance (params = {}): Promise { - /** - * @method - * @name geniusyield#fetchBalance - * @description query for balance and get the amount of funds available for trading or funds locked in orders - * @see https://api-docs-v3.geniusyield.io/#get-balances - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} - */ - this.checkRequiredCredentials (); - await this.loadMarkets (); - const nonce1 = this.uuidv1 (); - const request: Dict = { - 'nonce': nonce1, - 'wallet': this.walletAddress, - }; - // [ - // { - // "asset": "DIL", - // "quantity": "0.00000000", - // "availableForTrade": "0.00000000", - // "locked": "0.00000000", - // "usdValue": null - // }, ... - // ] - const extendedRequest = this.extend (request, params); - if (extendedRequest['wallet'] === undefined) { - throw new BadRequest (this.id + ' fetchBalance() wallet is undefined, set this.walletAddress or "address" in params'); - } - let response = undefined; - try { - response = await this.privateGetBalances (extendedRequest); - } catch (e) { - if (e instanceof InvalidAddress) { - const walletAddress = extendedRequest['wallet']; - await this.associateWallet (walletAddress); - response = await this.privateGetBalances (extendedRequest); - } else { - throw e; - } - } - return this.parseBalance (response); - } - - async fetchMyTrades (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchMyTrades - * @description fetch all trades made by the user - * @see https://api-docs-v3.geniusyield.io/#get-fills - * @param {string} symbol unified market symbol - * @param {int} [since] the earliest time in ms to fetch trades for - * @param {int} [limit] the maximum number of trades structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} - */ - this.checkRequiredCredentials (); - await this.loadMarkets (); - let market = undefined; - const request: Dict = { - 'nonce': this.uuidv1 (), - 'wallet': this.walletAddress, - }; - if (symbol !== undefined) { - market = this.market (symbol); - request['market'] = market['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - // [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478762, - // "makerSide": "sell", - // "sequence": 5053, - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "side": "buy", - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - const extendedRequest = this.extend (request, params); - if (extendedRequest['wallet'] === undefined) { - throw new BadRequest (this.id + ' fetchMyTrades() walletAddress is undefined, set this.walletAddress or "address" in params'); - } - let response = undefined; - try { - response = await this.privateGetFills (extendedRequest); - } catch (e) { - if (e instanceof InvalidAddress) { - const walletAddress = extendedRequest['wallet']; - await this.associateWallet (walletAddress); - response = await this.privateGetFills (extendedRequest); - } else { - throw e; - } - } - return this.parseTrades (response, market, since, limit); - } - - async fetchOrder (id: string, symbol: Str = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOrder - * @description fetches information on an order made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified symbol of the market the order was made in - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request: Dict = { - 'orderId': id, - }; - return await this.fetchOrdersHelper (symbol, undefined, undefined, this.extend (request, params)) as Order; - } - - async fetchOpenOrders (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchOpenOrders - * @description fetch all unfilled currently open orders - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified market symbol - * @param {int} [since] the earliest time in ms to fetch open orders for - * @param {int} [limit] the maximum number of open orders structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request: Dict = { - 'closed': false, - }; - return await this.fetchOrdersHelper (symbol, since, limit, this.extend (request, params)) as Order[]; - } - - async fetchClosedOrders (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchClosedOrders - * @description fetches information on multiple closed orders made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified market symbol of the market orders were made in - * @param {int} [since] the earliest time in ms to fetch orders for - * @param {int} [limit] the maximum number of order structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request: Dict = { - 'closed': true, - }; - return await this.fetchOrdersHelper (symbol, since, limit, this.extend (request, params)) as Order[]; - } - - async fetchOrdersHelper (symbol: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}) { - await this.loadMarkets (); - const request: Dict = { - 'nonce': this.uuidv1 (), - 'wallet': this.walletAddress, - }; - let market = undefined; - if (symbol !== undefined) { - market = this.market (symbol); - request['market'] = market['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - const response = await this.privateGetOrders (this.extend (request, params)); - // fetchClosedOrders / fetchOpenOrders - // [ - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - // } - // ] - // fetchOrder - // { market: "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": - // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" } ] } - if (Array.isArray (response)) { - return this.parseOrders (response, market, since, limit) as any; - } else { - return this.parseOrder (response, market); - } - } - - parseOrderStatus (status: Str) { - // https://docs.geniusyield.io/#order-states-amp-lifecycle - const statuses: Dict = { - 'active': 'open', - 'partiallyFilled': 'open', - 'rejected': 'canceled', - 'filled': 'closed', - }; - return this.safeString (statuses, status, status); - } - - parseOrder (order: Dict, market: Market = undefined): Order { - // - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - // } - // - const timestamp = this.safeInteger (order, 'time'); - const fills = this.safeValue (order, 'fills', []); - const id = this.safeString (order, 'orderId'); - const clientOrderId = this.safeString (order, 'clientOrderId'); - const marketId = this.safeString (order, 'market'); - const side = this.safeString (order, 'side'); - const symbol = this.safeSymbol (marketId, market, '-'); - const type = this.safeString (order, 'type'); - const amount = this.safeString (order, 'originalQuantity'); - const filled = this.safeString (order, 'executedQuantity'); - const average = this.safeString (order, 'avgExecutionPrice'); - const price = this.safeString (order, 'price'); - const rawStatus = this.safeString (order, 'status'); - const timeInForce = this.safeStringUpper (order, 'timeInForce'); - const status = this.parseOrderStatus (rawStatus); - return this.safeOrder ({ - 'info': order, - 'id': id, - 'clientOrderId': clientOrderId, - 'timestamp': timestamp, - 'datetime': this.iso8601 (timestamp), - 'lastTradeTimestamp': undefined, - 'symbol': symbol, - 'type': type, - 'timeInForce': timeInForce, - 'postOnly': undefined, - 'side': side, - 'price': price, - 'stopPrice': undefined, - 'triggerPrice': undefined, - 'amount': amount, - 'cost': undefined, - 'average': average, - 'filled': filled, - 'remaining': undefined, - 'status': status, - 'fee': undefined, - 'trades': fills, - }, market); - } - - async associateWallet (walletAddress, params = {}) { - const nonce = this.uuidv1 (); - const noPrefix = this.remove0xPrefix (walletAddress); - const byteArray = [ - this.base16ToBinary (nonce), - this.base16ToBinary (noPrefix), - ]; - const binary = this.binaryConcatArray (byteArray); - const hash = this.hash (binary, keccak, 'hex'); - const signature = this.signMessageString (hash, this.privateKey); - // { - // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "totalPortfolioValueUsd": "0.00", - // "time": 1598468353626 - // } - const request: Dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': walletAddress, - }, - 'signature': signature, - }; - const result = await this.privatePostWallets (request); - return result; - } - - async createOrder (symbol: string, type: OrderType, side: OrderSide, amount: number, price: Num = undefined, params = {}) { - /** - * @method - * @name geniusyield#createOrder - * @description create a trade order, https://docs.geniusyield.io/#create-order - * @see https://api-docs-v3.geniusyield.io/#create-order - * @param {string} symbol unified symbol of the market to create an order in - * @param {string} type 'market' or 'limit' - * @param {string} side 'buy' or 'sell' - * @param {float} amount how much of currency you want to trade in units of base currency - * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated - * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials (); - await this.loadMarkets (); - const testOrder = this.safeBool (params, 'test', false); - params = this.omit (params, 'test'); - const market = this.market (symbol); - const nonce = this.uuidv1 (); - let typeEnum = undefined; - const stopLossTypeEnums: Dict = { - 'stopLoss': 3, - 'stopLossLimit': 4, - 'takeProfit': 5, - 'takeProfitLimit': 6, - }; - let stopPriceString = undefined; - if ((type === 'stopLossLimit') || (type === 'takeProfitLimit') || ('stopPrice' in params)) { - if (!('stopPrice' in params)) { - throw new BadRequest (this.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders'); - } - stopPriceString = this.priceToPrecision (symbol, params['stopPrice']); - } - const limitTypeEnums: Dict = { - 'limit': 1, - 'limitMaker': 2, - }; - let priceString = undefined; - const typeLower = type.toLowerCase (); - const limitOrder = typeLower.indexOf ('limit') >= 0; - if (type in limitTypeEnums) { - typeEnum = limitTypeEnums[type]; - priceString = this.priceToPrecision (symbol, price); - } else if (type in stopLossTypeEnums) { - typeEnum = stopLossTypeEnums[type]; - priceString = this.priceToPrecision (symbol, price); - } else if (type === 'market') { - typeEnum = 0; - } else { - throw new BadRequest (this.id + ' ' + type + ' is not a valid order type'); - } - let amountEnum = 0; // base quantity - if ('quoteOrderQuantity' in params) { - if (type !== 'market') { - throw new NotSupported (this.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders'); - } - amountEnum = 1; - amount = this.safeNumber (params, 'quoteOrderQuantity'); - } - const sideEnum = (side === 'buy') ? 0 : 1; - const walletBytes = this.remove0xPrefix (this.walletAddress); - const network = this.safeString (this.options, 'network', 'ETH'); - const orderVersion = this.getSupportedMapping (network, { - 'ETH': 1, - 'BSC': 2, - 'MATIC': 4, - }); - const amountString = this.amountToPrecision (symbol, amount); - // https://docs.geniusyield.io/#time-in-force - const timeInForceEnums: Dict = { - 'gtc': 0, - 'ioc': 2, - 'fok': 3, - }; - const defaultTimeInForce = this.safeString (this.options, 'defaultTimeInForce', 'gtc'); - const timeInForce = this.safeString (params, 'timeInForce', defaultTimeInForce); - let timeInForceEnum = undefined; - if (timeInForce in timeInForceEnums) { - timeInForceEnum = timeInForceEnums[timeInForce]; - } else { - const allOptions = Object.keys (timeInForceEnums); - const asString = allOptions.join (', '); - throw new BadRequest (this.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString); - } - // https://docs.geniusyield.io/#self-trade-prevention - const selfTradePreventionEnums: Dict = { - 'dc': 0, - 'co': 1, - 'cn': 2, - 'cb': 3, - }; - const defaultSelfTradePrevention = this.safeString (this.options, 'defaultSelfTradePrevention', 'cn'); - const selfTradePrevention = this.safeString (params, 'selfTradePrevention', defaultSelfTradePrevention); - let selfTradePreventionEnum = undefined; - if (selfTradePrevention in selfTradePreventionEnums) { - selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]; - } else { - const allOptions = Object.keys (selfTradePreventionEnums); - const asString = allOptions.join (', '); - throw new BadRequest (this.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString); - } - const byteArray = [ - this.numberToBE (orderVersion, 1), - this.base16ToBinary (nonce), - this.base16ToBinary (walletBytes), - this.encode (market['id']), - this.numberToBE (typeEnum, 1), - this.numberToBE (sideEnum, 1), - this.encode (amountString), - this.numberToBE (amountEnum, 1), - ]; - if (limitOrder) { - const encodedPrice = this.encode (priceString); - byteArray.push (encodedPrice); - } - if (type in stopLossTypeEnums) { - const encodedPrice = this.encode (stopPriceString || priceString); - byteArray.push (encodedPrice); - } - const clientOrderId = this.safeString (params, 'clientOrderId'); - if (clientOrderId !== undefined) { - byteArray.push (this.encode (clientOrderId)); - } - const after = [ - this.numberToBE (timeInForceEnum, 1), - this.numberToBE (selfTradePreventionEnum, 1), - this.numberToBE (0, 8), // unused - ]; - const allBytes = this.arrayConcat (byteArray, after); - const binary = this.binaryConcatArray (allBytes); - const hash = this.hash (binary, keccak, 'hex'); - const signature = this.signMessageString (hash, this.privateKey); - const request: Dict = { - 'parameters': { - 'nonce': nonce, - 'market': market['id'], - 'side': side, - 'type': type, - 'wallet': this.walletAddress, - 'selfTradePrevention': selfTradePrevention, - }, - 'signature': signature, - }; - if (type !== 'market') { - request['parameters']['timeInForce'] = timeInForce; - } - if (limitOrder) { - request['parameters']['price'] = priceString; - } - if (type in stopLossTypeEnums) { - request['parameters']['stopPrice'] = stopPriceString || priceString; - } - if (amountEnum === 0) { - request['parameters']['quantity'] = amountString; - } else { - request['parameters']['quoteOrderQuantity'] = amountString; - } - if (clientOrderId !== undefined) { - request['parameters']['clientOrderId'] = clientOrderId; - } - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txStatus": "pending" - // } - // ], - // "avgExecutionPrice": "0.09905990" - // } - // we don't use extend here because it is a signed endpoint - let response = undefined; - if (testOrder) { - response = await this.privatePostOrdersTest (request); - } else { - response = await this.privatePostOrders (request); - } - return this.parseOrder (response, market); - } - - async withdraw (code: string, amount: number, address: string, tag = undefined, params = {}) { - /** - * @method - * @name geniusyield#withdraw - * @description make a withdrawal - * @see https://api-docs-v3.geniusyield.io/#withdraw-funds - * @param {string} code unified currency code - * @param {float} amount the amount to withdraw - * @param {string} address the address to withdraw to - * @param {string} tag - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - [ tag, params ] = this.handleWithdrawTagAndParams (tag, params); - this.checkRequiredCredentials (); - await this.loadMarkets (); - const nonce = this.uuidv1 (); - const amountString = this.currencyToPrecision (code, amount); - const currency = this.currency (code); - const walletBytes = this.remove0xPrefix (this.walletAddress); - const byteArray = [ - this.base16ToBinary (nonce), - this.base16ToBinary (walletBytes), - this.encode (currency['id']), - this.encode (amountString), - this.numberToBE (1, 1), // bool set to true - ]; - const binary = this.binaryConcatArray (byteArray); - const hash = this.hash (binary, keccak, 'hex'); - const signature = this.signMessageString (hash, this.privateKey); - const request: Dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': address, - 'asset': currency['id'], - 'quantity': amountString, - }, - 'signature': signature, - }; - const response = await this.privatePostWithdrawals (request); - // - // { - // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883190, - // "fee": "0.00024000", - // "txStatus": "pending", - // "txId": null - // } - // - return this.parseTransaction (response, currency); - } - - async cancelAllOrders (symbol: Str = undefined, params = {}) { - /** - * @method - * @name geniusyield#cancelAllOrders - * @description cancel all open orders - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials (); - await this.loadMarkets (); - let market = undefined; - if (symbol !== undefined) { - market = this.market (symbol); - } - const nonce = this.uuidv1 (); - const request: Dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': this.walletAddress, - }, - }; - const walletBytes = this.remove0xPrefix (this.walletAddress); - const byteArray = [ - this.base16ToBinary (nonce), - this.base16ToBinary (walletBytes), - ]; - if (market !== undefined) { - byteArray.push (this.encode (market['id'])); - request['parameters']['market'] = market['id']; - } - const binary = this.binaryConcatArray (byteArray); - const hash = this.hash (binary, keccak, 'hex'); - const signature = this.signMessageString (hash, this.privateKey); - request['signature'] = signature; - // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] - const response = await this.privateDeleteOrders (this.extend (request, params)); - return this.parseOrders (response, market); - } - - async cancelOrder (id: string, symbol: Str = undefined, params = {}) { - /** - * @method - * @name geniusyield#cancelOrder - * @description cancels an open order - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} id order id - * @param {string} symbol unified symbol of the market the order was made in - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials (); - await this.loadMarkets (); - let market = undefined; - if (symbol !== undefined) { - market = this.market (symbol); - } - const nonce = this.uuidv1 (); - const walletBytes = this.remove0xPrefix (this.walletAddress); - const byteArray = [ - this.base16ToBinary (nonce), - this.base16ToBinary (walletBytes), - this.encode (id), - ]; - const binary = this.binaryConcatArray (byteArray); - const hash = this.hash (binary, keccak, 'hex'); - const signature = this.signMessageString (hash, this.privateKey); - const request: Dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'orderId': id, - }, - 'signature': signature, - }; - // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] - const response = await this.privateDeleteOrders (this.extend (request, params)); - const canceledOrder = this.safeDict (response, 0); - return this.parseOrder (canceledOrder, market); - } - - handleErrors (code: int, reason: string, url: string, method: string, headers: Dict, body: string, response, requestHeaders, requestBody) { - const errorCode = this.safeString (response, 'code'); - const message = this.safeString (response, 'message'); - if (errorCode !== undefined) { - this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, message); - throw new ExchangeError (this.id + ' ' + message); - } - return undefined; - } - - async fetchDeposit (id: string, code: Str = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDeposit - * @description fetch information on a deposit - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} id deposit id - * @param {string} code not used by geniusyield fetchDeposit () - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - await this.loadMarkets (); - const nonce = this.uuidv1 (); - const request: Dict = { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'depositId': id, - }; - const response = await this.privateGetDeposits (this.extend (request, params)); - return this.parseTransaction (response); - } - - async fetchDeposits (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchDeposits - * @description fetch all deposits made to an account - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} code unified currency code - * @param {int} [since] the earliest time in ms to fetch deposits for - * @param {int} [limit] the maximum number of deposits structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - params = this.extend ({ - 'method': 'privateGetDeposits', - }, params); - return await this.fetchTransactionsHelper (code, since, limit, params); - } - - async fetchStatus (params = {}) { - /** - * @method - * @name geniusyield#fetchStatus - * @description the latest known information on the availability of the exchange API - * @see https://api-docs-v3.geniusyield.io/#get-ping - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} - */ - const response = await this.publicGetPing (params); - return { - 'status': 'ok', // if there's no Errors, status = 'ok' - 'updated': undefined, - 'eta': undefined, - 'url': undefined, - 'info': response, - }; - } - - async fetchTime (params = {}) { - /** - * @method - * @name geniusyield#fetchTime - * @description fetches the current integer timestamp in milliseconds from the exchange server - * @see https://api-docs-v3.geniusyield.io/#get-time - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {int} the current integer timestamp in milliseconds from the exchange server - */ - const response = await this.publicGetTime (params); - // - // { serverTime: "1655258263236" } - // - return this.safeInteger (response, 'serverTime'); - } - - async fetchWithdrawal (id: string, code: Str = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchWithdrawal - * @description fetch data on a currency withdrawal via the withdrawal id - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} id withdrawal id - * @param {string} code not used by geniusyield.fetchWithdrawal - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - await this.loadMarkets (); - const nonce = this.uuidv1 (); - const request: Dict = { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'withdrawalId': id, - }; - const response = await this.privateGetWithdrawals (this.extend (request, params)); - return this.parseTransaction (response); - } - - async fetchWithdrawals (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise { - /** - * @method - * @name geniusyield#fetchWithdrawals - * @description fetch all withdrawals made from an account - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} code unified currency code - * @param {int} [since] the earliest time in ms to fetch withdrawals for - * @param {int} [limit] the maximum number of withdrawals structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - params = this.extend ({ - 'method': 'privateGetWithdrawals', - }, params); - return await this.fetchTransactionsHelper (code, since, limit, params); - } - - async fetchTransactionsHelper (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}) { - await this.loadMarkets (); - const nonce = this.uuidv1 (); - const request: Dict = { - 'nonce': nonce, - 'wallet': this.walletAddress, - }; - let currency = undefined; - if (code !== undefined) { - currency = this.currency (code); - request['asset'] = currency['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - // [ - // { - // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - // "asset": "ETH", - // "quantity": "1.00000000", - // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime": 1598865853000, - // "confirmationTime": 1598865930231 - // } - // ] - const method = params['method']; - params = this.omit (params, 'method'); - let response = undefined; - if (method === 'privateGetDeposits') { - response = await this.privateGetDeposits (this.extend (request, params)); - } else if (method === 'privateGetWithdrawals') { - response = await this.privateGetWithdrawals (this.extend (request, params)); - } else { - throw new NotSupported (this.id + ' fetchTransactionsHelper() not support this method'); - } - return this.parseTransactions (response, currency, since, limit); - } - - parseTransactionStatus (status: Str) { - const statuses: Dict = { - 'mined': 'ok', - }; - return this.safeString (statuses, status, status); - } - - parseTransaction (transaction: Dict, currency: Currency = undefined): Transaction { - // - // fetchDeposits - // - // { - // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - // "asset": "ETH", - // "quantity": "1.00000000", - // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime": 1598865853000, - // "confirmationTime": 1598865930231 - // } - // - // fetchWithdrwalas - // - // { - // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883288, - // "fee": "0.00024000", - // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - // "txStatus": "mined" - // } - // - // withdraw - // - // { - // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883190, - // "fee": "0.00024000", - // "txStatus": "pending", - // "txId": null - // } - // - let type = undefined; - if ('depositId' in transaction) { - type = 'deposit'; - } else if (('withdrawId' in transaction) || ('withdrawalId' in transaction)) { - type = 'withdrawal'; - } - let id = this.safeString2 (transaction, 'depositId', 'withdrawId'); - id = this.safeString (transaction, 'withdrawalId', id); - const code = this.safeCurrencyCode (this.safeString (transaction, 'asset'), currency); - const amount = this.safeNumber (transaction, 'quantity'); - const txid = this.safeString (transaction, 'txId'); - const timestamp = this.safeInteger2 (transaction, 'txTime', 'time'); - let fee = undefined; - if ('fee' in transaction) { - fee = { - 'cost': this.safeNumber (transaction, 'fee'), - 'currency': 'ETH', - }; - } - const rawStatus = this.safeString (transaction, 'txStatus'); - const status = this.parseTransactionStatus (rawStatus); - const updated = this.safeInteger (transaction, 'confirmationTime'); - return { - 'info': transaction, - 'id': id, - 'txid': txid, - 'timestamp': timestamp, - 'datetime': this.iso8601 (timestamp), - 'network': undefined, - 'address': undefined, - 'addressTo': undefined, - 'addressFrom': undefined, - 'tag': undefined, - 'tagTo': undefined, - 'tagFrom': undefined, - 'type': type, - 'amount': amount, - 'currency': code, - 'status': status, - 'updated': updated, - 'comment': undefined, - 'internal': undefined, - 'fee': fee, - }; - } - - calculateRateLimiterCost (api, method, path, params, config = {}) { - const hasApiKey = (this.apiKey !== undefined); - const hasSecret = (this.secret !== undefined); - const hasWalletAddress = (this.walletAddress !== undefined); - const hasPrivateKey = (this.privateKey !== undefined); - const defaultCost = this.safeValue (config, 'cost', 1); - const authenticated = hasApiKey && hasSecret && hasWalletAddress && hasPrivateKey; - return authenticated ? (defaultCost / 2) : defaultCost; - } - - async fetchDepositAddress (code: Str = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDepositAddress - * @description fetch the Polygon address of the wallet - * @see https://api-docs-v3.geniusyield.io/#get-wallets - * @param {string} code not used by geniusyield - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} - */ - const request: Dict = {}; - request['nonce'] = this.uuidv1 (); - const response = await this.privateGetWallets (this.extend (request, params)); - // - // [ - // { - // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd: "0.00", - // time: "1678342148086" - // }, - // { - // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd: "15.90", - // time: "1691697811659" - // } - // ] - // - return this.parseDepositAddress (response); - } - - parseDepositAddress (depositAddress, currency: Currency = undefined) { - // - // [ - // { - // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd: "0.00", - // time: "1678342148086" - // }, - // { - // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd: "15.90", - // time: "1691697811659" - // } - // ] - // - const length = depositAddress.length; - const entry = this.safeDict (depositAddress, length - 1); - const address = this.safeString (entry, 'address'); - this.checkAddress (address); - return { - 'info': depositAddress, - 'currency': undefined, - 'address': address, - 'tag': undefined, - 'network': 'MATIC', - }; - } - - sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { - const network = this.safeString (this.options, 'network', 'ETH'); - const version = this.safeString (this.options, 'version', 'v1'); - let url = this.urls['api'][network] + '/' + version + '/' + path; - const keys = Object.keys (params); - const length = keys.length; - let query = undefined; - if (length > 0) { - if (method === 'GET') { - query = this.urlencode (params); - url = url + '?' + query; - } else { - body = this.json (params); - } - } - headers = { - 'Content-Type': 'application/json', - }; - if (this.apiKey !== undefined) { - headers['Genius Yield-API-Key'] = this.apiKey; - } - if (api === 'private') { - let payload = undefined; - if (method === 'GET') { - payload = query; - } else { - payload = body; - } - headers['Genius Yield-HMAC-Signature'] = this.hmac (this.encode (payload), this.encode (this.secret), sha256, 'hex'); - } - return { 'url': url, 'method': method, 'body': body, 'headers': headers }; - } - - remove0xPrefix (hexData) { - if (hexData.slice (0, 2) === '0x') { - return hexData.slice (2); - } else { - return hexData; - } - } - - hashMessage (message) { - // takes a hex encoded message - const binaryMessage = this.base16ToBinary (this.remove0xPrefix (message)); - const prefix = this.encode ('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength); - return '0x' + this.hash (this.binaryConcat (prefix, binaryMessage), keccak, 'hex'); - } - - signHash (hash, privateKey) { - const signature = ecdsa (hash.slice (-64), privateKey.slice (-64), secp256k1, undefined); - return { - 'r': '0x' + signature['r'], - 's': '0x' + signature['s'], - 'v': 27 + signature['v'], - }; - } - - signMessage (message, privateKey) { - return this.signHash (this.hashMessage (message), privateKey.slice (-64)); - } - - signMessageString (message, privateKey) { - // still takes the input as a hex string - // same as above but returns a string instead of an object - const signature = this.signMessage (message, privateKey); - return signature['r'] + this.remove0xPrefix (signature['s']) + this.binaryToBase16 (this.numberToBE (signature['v'], 1)); - } } diff --git a/wiki/exchanges/geniusyield.md b/wiki/exchanges/geniusyield.md new file mode 100644 index 000000000000..477d4ffb389d --- /dev/null +++ b/wiki/exchanges/geniusyield.md @@ -0,0 +1,556 @@ + + + +## geniusyield{docsify-ignore} +**Kind**: global class +**Extends**: Exchange + +* [fetchMarkets](#fetchmarkets) +* [fetchTicker](#fetchticker) +* [fetchTickers](#fetchtickers) +* [fetchOHLCV](#fetchohlcv) +* [fetchTrades](#fetchtrades) +* [fetchTradingFees](#fetchtradingfees) +* [fetchOrderBook](#fetchorderbook) +* [fetchCurrencies](#fetchcurrencies) +* [fetchBalance](#fetchbalance) +* [fetchMyTrades](#fetchmytrades) +* [fetchOrder](#fetchorder) +* [fetchOpenOrders](#fetchopenorders) +* [fetchClosedOrders](#fetchclosedorders) +* [createOrder](#createorder) +* [withdraw](#withdraw) +* [cancelAllOrders](#cancelallorders) +* [cancelOrder](#cancelorder) +* [fetchDeposit](#fetchdeposit) +* [fetchDeposits](#fetchdeposits) +* [fetchStatus](#fetchstatus) +* [fetchTime](#fetchtime) +* [fetchWithdrawal](#fetchwithdrawal) +* [fetchWithdrawals](#fetchwithdrawals) +* [fetchDepositAddress](#fetchdepositaddress) + + + +### fetchMarkets{docsify-ignore} +retrieves data on all markets for geniusyield + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<object> - an array of objects representing market data + +**See**: https://api-docs-v3.geniusyield.io/#get-markets + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchMarkets ([params]) +``` + + + + +### fetchTicker{docsify-ignore} +fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a [ticker structure](https://docs.ccxt.com/#/?id=ticker-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-tickers + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified symbol of the market to fetch the ticker for | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchTicker (symbol[, params]) +``` + + + + +### fetchTickers{docsify-ignore} +fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a dictionary of [ticker structures](https://docs.ccxt.com/#/?id=ticker-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-tickers + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbols | Array<string>, undefined | Yes | unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchTickers (symbols[, params]) +``` + + + + +### fetchOHLCV{docsify-ignore} +fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<Array<int>> - A list of candles ordered as timestamp, open, high, low, close, volume + +**See**: https://api-docs-v3.geniusyield.io/#get-candles + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified symbol of the market to fetch OHLCV data for | +| timeframe | string | Yes | the length of time each candle represents | +| since | int | No | timestamp in ms of the earliest candle to fetch | +| limit | int | No | the maximum amount of candles to fetch | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchOHLCV (symbol, timeframe[, since, limit, params]) +``` + + + + +### fetchTrades{docsify-ignore} +get the list of most recent trades for a particular symbol + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<Trade> - a list of [trade structures](https://docs.ccxt.com/#/?id=public-trades) + +**See**: https://api-docs-v3.geniusyield.io/#get-trades + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified symbol of the market to fetch trades for | +| since | int | No | timestamp in ms of the earliest trade to fetch | +| limit | int | No | the maximum amount of trades to fetch | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchTrades (symbol[, since, limit, params]) +``` + + + + +### fetchTradingFees{docsify-ignore} +fetch the trading fees for multiple markets + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a dictionary of [fee structures](https://docs.ccxt.com/#/?id=fee-structure) indexed by market symbols + +**See**: https://api-docs-v3.geniusyield.io/#get-api-account + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchTradingFees ([params]) +``` + + + + +### fetchOrderBook{docsify-ignore} +fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - A dictionary of [order book structures](https://docs.ccxt.com/#/?id=order-book-structure) indexed by market symbols + +**See**: https://api-docs-v3.geniusyield.io/#get-order-books + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified symbol of the market to fetch the order book for | +| limit | int | No | the maximum amount of order book entries to return | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchOrderBook (symbol[, limit, params]) +``` + + + + +### fetchCurrencies{docsify-ignore} +fetches all available currencies on an exchange + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - an associative dictionary of currencies + +**See**: https://api-docs-v3.geniusyield.io/#get-assets + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchCurrencies ([params]) +``` + + + + +### fetchBalance{docsify-ignore} +query for balance and get the amount of funds available for trading or funds locked in orders + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a [balance structure](https://docs.ccxt.com/#/?id=balance-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-balances + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchBalance ([params]) +``` + + + + +### fetchMyTrades{docsify-ignore} +fetch all trades made by the user + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<Trade> - a list of [trade structures](https://docs.ccxt.com/#/?id=trade-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-fills + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified market symbol | +| since | int | No | the earliest time in ms to fetch trades for | +| limit | int | No | the maximum number of trades structures to retrieve | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchMyTrades (symbol[, since, limit, params]) +``` + + + + +### fetchOrder{docsify-ignore} +fetches information on an order made by the user + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - An [order structure](https://docs.ccxt.com/#/?id=order-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-orders + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified symbol of the market the order was made in | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchOrder (symbol[, params]) +``` + + + + +### fetchOpenOrders{docsify-ignore} +fetch all unfilled currently open orders + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<Order> - a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-orders + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified market symbol | +| since | int | No | the earliest time in ms to fetch open orders for | +| limit | int | No | the maximum number of open orders structures to retrieve | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchOpenOrders (symbol[, since, limit, params]) +``` + + + + +### fetchClosedOrders{docsify-ignore} +fetches information on multiple closed orders made by the user + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<Order> - a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-orders + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified market symbol of the market orders were made in | +| since | int | No | the earliest time in ms to fetch orders for | +| limit | int | No | the maximum number of order structures to retrieve | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchClosedOrders (symbol[, since, limit, params]) +``` + + + + +### createOrder{docsify-ignore} +create a trade order, https://docs.geniusyield.io/#create-order + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - an [order structure](https://docs.ccxt.com/#/?id=order-structure) + +**See**: https://api-docs-v3.geniusyield.io/#create-order + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified symbol of the market to create an order in | +| type | string | Yes | 'market' or 'limit' | +| side | string | Yes | 'buy' or 'sell' | +| amount | float | Yes | how much of currency you want to trade in units of base currency | +| price | float | No | the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders | +| params | object | No | extra parameters specific to the exchange API endpoint | +| params.test | bool | No | set to true to test an order, no order will be created but the request will be validated | + + +```javascript +geniusyield.createOrder (symbol, type, side, amount[, price, params]) +``` + + + + +### withdraw{docsify-ignore} +make a withdrawal + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a [transaction structure](https://docs.ccxt.com/#/?id=transaction-structure) + +**See**: https://api-docs-v3.geniusyield.io/#withdraw-funds + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| code | string | Yes | unified currency code | +| amount | float | Yes | the amount to withdraw | +| address | string | Yes | the address to withdraw to | +| tag | string | Yes | | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.withdraw (code, amount, address, tag[, params]) +``` + + + + +### cancelAllOrders{docsify-ignore} +cancel all open orders + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<object> - a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) + +**See**: https://api-docs-v3.geniusyield.io/#cancel-order + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| symbol | string | Yes | unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.cancelAllOrders (symbol[, params]) +``` + + + + +### cancelOrder{docsify-ignore} +cancels an open order + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - An [order structure](https://docs.ccxt.com/#/?id=order-structure) + +**See**: https://api-docs-v3.geniusyield.io/#cancel-order + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| id | string | Yes | order id | +| symbol | string | Yes | unified symbol of the market the order was made in | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.cancelOrder (id, symbol[, params]) +``` + + + + +### fetchDeposit{docsify-ignore} +fetch information on a deposit + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a [transaction structure](https://docs.ccxt.com/#/?id=transaction-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-deposits + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| id | string | Yes | deposit id | +| code | string | Yes | not used by geniusyield fetchDeposit () | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchDeposit (id, code[, params]) +``` + + + + +### fetchDeposits{docsify-ignore} +fetch all deposits made to an account + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<object> - a list of [transaction structures](https://docs.ccxt.com/#/?id=transaction-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-deposits + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| code | string | Yes | unified currency code | +| since | int | No | the earliest time in ms to fetch deposits for | +| limit | int | No | the maximum number of deposits structures to retrieve | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchDeposits (code[, since, limit, params]) +``` + + + + +### fetchStatus{docsify-ignore} +the latest known information on the availability of the exchange API + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a [status structure](https://docs.ccxt.com/#/?id=exchange-status-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-ping + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchStatus ([params]) +``` + + + + +### fetchTime{docsify-ignore} +fetches the current integer timestamp in milliseconds from the exchange server + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: int - the current integer timestamp in milliseconds from the exchange server + +**See**: https://api-docs-v3.geniusyield.io/#get-time + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchTime ([params]) +``` + + + + +### fetchWithdrawal{docsify-ignore} +fetch data on a currency withdrawal via the withdrawal id + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - a [transaction structure](https://docs.ccxt.com/#/?id=transaction-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-withdrawals + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| id | string | Yes | withdrawal id | +| code | string | Yes | not used by geniusyield.fetchWithdrawal | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchWithdrawal (id, code[, params]) +``` + + + + +### fetchWithdrawals{docsify-ignore} +fetch all withdrawals made from an account + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: Array<object> - a list of [transaction structures](https://docs.ccxt.com/#/?id=transaction-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-withdrawals + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| code | string | Yes | unified currency code | +| since | int | No | the earliest time in ms to fetch withdrawals for | +| limit | int | No | the maximum number of withdrawals structures to retrieve | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchWithdrawals (code[, since, limit, params]) +``` + + + + +### fetchDepositAddress{docsify-ignore} +fetch the Polygon address of the wallet + +**Kind**: instance method of [geniusyield](#geniusyield) +**Returns**: object - an [address structure](https://docs.ccxt.com/#/?id=address-structure) + +**See**: https://api-docs-v3.geniusyield.io/#get-wallets + +| Param | Type | Required | Description | +| --- | --- | --- | --- | +| code | string | Yes | not used by geniusyield | +| params | object | No | extra parameters specific to the exchange API endpoint | + + +```javascript +geniusyield.fetchDepositAddress (code[, params]) +``` + From 47a14339f11eba07bac952474f527938c8e0a529 Mon Sep 17 00:00:00 2001 From: 4TT1L4 <2914096+4TT1L4@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:28:35 +0200 Subject: [PATCH 4/5] Implemented fetch markets --- dist/cjs/src/geniusyield.js | 1757 +-------------------- js/src/abstract/geniusyield.js | 6 + js/src/geniusyield.d.ts | 9 +- js/src/geniusyield.js | 84 +- php/abstract/geniusyield.php | 8 +- php/async/abstract/geniusyield.php | 8 +- php/async/geniusyield.php | 1791 +--------------------- php/geniusyield.php | 1734 +-------------------- python/ccxt/abstract/geniusyield.py | 4 +- python/ccxt/async_support/geniusyield.py | 1639 +------------------- python/ccxt/geniusyield.py | 1639 +------------------- ts/src/geniusyield.ts | 54 +- 12 files changed, 520 insertions(+), 8213 deletions(-) diff --git a/dist/cjs/src/geniusyield.js b/dist/cjs/src/geniusyield.js index 93cdcfd1cf1b..ce8b11b314ee 100644 --- a/dist/cjs/src/geniusyield.js +++ b/dist/cjs/src/geniusyield.js @@ -3,11 +3,6 @@ var geniusyield$1 = require('./abstract/geniusyield.js'); var number = require('./base/functions/number.js'); var errors = require('./base/errors.js'); -var Precise = require('./base/Precise.js'); -var sha256 = require('./static_dependencies/noble-hashes/sha256.js'); -var sha3 = require('./static_dependencies/noble-hashes/sha3.js'); -var secp256k1 = require('./static_dependencies/noble-curves/secp256k1.js'); -var crypto = require('./base/functions/crypto.js'); // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- @@ -16,6 +11,14 @@ var crypto = require('./base/functions/crypto.js'); * @augments Exchange */ class geniusyield extends geniusyield$1 { + safeMarket(marketId = undefined, market = undefined, delimiter = undefined, marketType = undefined) { + const isOption = (marketId !== undefined) && ((marketId.indexOf('-C') > -1) || (marketId.indexOf('-P') > -1)); + if (isOption && !(marketId in this.markets_by_id)) { + // handle expired option contracts + return this.createExpiredOptionMarket(marketId); + } + return super.safeMarket(marketId, market, delimiter, marketType); + } describe() { return this.deepExtend(super.describe(), { 'id': 'geniusyield', @@ -35,7 +38,7 @@ class geniusyield extends geniusyield$1 { 'future': false, 'option': false, 'addMargin': false, - 'cancelAllOrders': true, + 'cancelAllOrders': false, 'cancelOrder': true, 'cancelOrders': false, 'closeAllPositions': false, @@ -43,21 +46,21 @@ class geniusyield extends geniusyield$1 { 'createDepositAddress': false, 'createOrder': true, 'createReduceOnlyOrder': false, - 'createStopLimitOrder': true, - 'createStopMarketOrder': true, - 'createStopOrder': true, + 'createStopLimitOrder': false, + 'createStopMarketOrder': false, + 'createStopOrder': false, 'fetchBalance': true, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, - 'fetchClosedOrders': true, + 'fetchClosedOrders': false, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, - 'fetchCurrencies': true, - 'fetchDeposit': true, - 'fetchDepositAddress': true, + 'fetchCurrencies': false, + 'fetchDeposit': false, + 'fetchDepositAddress': false, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, - 'fetchDeposits': true, + 'fetchDeposits': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, @@ -70,13 +73,13 @@ class geniusyield extends geniusyield$1 { 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, - 'fetchMyTrades': true, - 'fetchOHLCV': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, 'fetchOpenInterestHistory': false, - 'fetchOpenOrders': true, - 'fetchOrder': true, - 'fetchOrderBook': true, - 'fetchOrders': false, + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': false, + 'fetchOrders': true, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, @@ -85,23 +88,23 @@ class geniusyield extends geniusyield$1 { 'fetchPositionsHistory': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, - 'fetchStatus': true, - 'fetchTicker': true, - 'fetchTickers': true, - 'fetchTime': true, - 'fetchTrades': true, + 'fetchStatus': false, + 'fetchTicker': false, + 'fetchTickers': false, + 'fetchTime': false, + 'fetchTrades': false, 'fetchTradingFee': false, - 'fetchTradingFees': true, + 'fetchTradingFees': false, 'fetchTransactions': false, - 'fetchWithdrawal': true, - 'fetchWithdrawals': true, + 'fetchWithdrawal': false, + 'fetchWithdrawals': false, 'reduceMargin': false, - 'sandbox': true, + 'sandbox': false, 'setLeverage': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, - 'withdraw': true, + 'withdraw': false, }, 'timeframes': { '1m': '1m', @@ -113,58 +116,33 @@ class geniusyield extends geniusyield$1 { '1d': '1d', }, 'urls': { - 'test': { - 'MATIC': 'https://api-sandbox-matic.geniusyield.io', - }, - 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api': { - 'MATIC': 'https://api-matic.geniusyield.io', + 'preprod': 'http://localhost:8082', + 'mainnet': 'http://localhost:8082', }, - 'www': 'https://geniusyield.io', + 'www': 'https://www.geniusyield.co/', 'doc': [ - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ], }, 'api': { 'public': { - 'get': { - 'ping': 1, - 'time': 1, - 'exchange': 1, - 'assets': 1, - 'markets': 1, - 'tickers': 1, - 'candles': 1, - 'trades': 1, - 'orderbook': 1, - }, + 'get': {}, }, 'private': { 'get': { - 'user': 1, - 'wallets': 1, - 'balances': 1, - 'orders': 0.1, - 'fills': 0.1, - 'deposits': 1, - 'withdrawals': 1, - 'wsToken': 1, - }, - 'post': { - 'wallets': 1, - 'orders': 0.1, - 'orders/test': 0.1, - 'withdrawals': 1, - }, - 'delete': { - 'orders': 0.1, + 'markets': 10, + 'trading-fees': 10, }, }, }, + 'headers': { + 'X-Gate-Channel-Id': 'ccxt', + }, 'options': { - 'defaultTimeInForce': 'gtc', + 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', - 'network': 'MATIC', + 'network': 'mainnet', }, 'exceptions': { 'exact': { @@ -178,28 +156,16 @@ class geniusyield extends geniusyield$1 { }, }, 'requiredCredentials': { - 'walletAddress': true, - 'privateKey': true, + 'walletAddress': false, + 'privateKey': false, 'apiKey': true, - 'secret': true, + 'secret': false, }, 'precisionMode': number.TICK_SIZE, 'paddingMode': number.PAD_WITH_ZERO, 'commonCurrencies': {}, }); } - priceToPrecision(symbol, price) { - // - // we override priceToPrecision to fix the following issue - // https://github.com/ccxt/ccxt/issues/13367 - // {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"} - // - const market = this.market(symbol); - const info = this.safeValue(market, 'info', {}); - const quoteAssetPrecision = this.safeInteger(info, 'quoteAssetPrecision'); - price = this.decimalToPrecision(price, number.ROUND, market['precision']['price'], this.precisionMode); - return this.decimalToPrecision(price, number.TRUNCATE, quoteAssetPrecision, number.DECIMAL_PLACES, number.PAD_WITH_ZERO); - } async fetchMarkets(params = {}) { /** * @method @@ -209,77 +175,34 @@ class geniusyield extends geniusyield$1 { * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ - const response = await this.publicGetMarkets(params); - // - // [ - // { - // "market": "ETH-USDC", - // "type": "hybrid", - // "status": "activeHybrid", - // "baseAsset": "ETH", - // "baseAssetPrecision": "8", - // "quoteAsset": "USDC", - // "quoteAssetPrecision": "8", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.2500", - // "takerIdexFeeRate": "0.0500", - // "takerLiquidityProviderFeeRate": "0.2000", - // "tickSize": "0.01000000" - // }, - // ] - // - const response2 = await this.publicGetExchange(); - // + const markets = await this.privateGetMarkets(params); + // [ // { - // "timeZone": "UTC", - // "serverTime": "1654460599952", - // "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - // "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - // "maticUsdPrice": "0.60", - // "gasPrice": "180", - // "volume24hUsd": "10015814.46", - // "totalVolumeUsd": "1589273533.28", - // "totalTrades": "1534904", - // "totalValueLockedUsd": "12041929.44", - // "geniusyieldStakingValueLockedUsd": "20133816.98", - // "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", - // "geniusyieldUsdPrice": "0.07", - // "geniusyieldMarketCapUsd": "48012346.00", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.0025", - // "takerIdexFeeRate": "0.0005", - // "takerLiquidityProviderFeeRate": "0.0020", - // "makerTradeMinimum": "10.00000000", - // "takerTradeMinimum": "1.00000000", - // "withdrawMinimum": "0.50000000", - // "liquidityAdditionMinimum": "0.50000000", - // "liquidityRemovalMinimum": "0.40000000", - // "blockConfirmationDelay": "64" + // "market_id": "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + // "base_asset": "lovelace", + // "target_asset": "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" // } - // - const maker = this.safeNumber(response2, 'makerFeeRate'); - const taker = this.safeNumber(response2, 'takerFeeRate'); - const makerMin = this.safeString(response2, 'makerTradeMinimum'); - const takerMin = this.safeString(response2, 'takerTradeMinimum'); - const minCostETH = this.parseNumber(Precise["default"].stringMin(makerMin, takerMin)); + // ] + const fees = await this.privateGetTradingFees(); + // { + // "flat_maker_fee": "1000000", + // "flat_taker_fee": "1000000", + // "percentage_maker_fee": "0.3", + // "percentage_taker_fee": "0.3" + // } + const maker = this.safeNumber(fees, 'percentage_maker_fee'); + const taker = this.safeNumber(fees, 'percentage_taker_fee'); const result = []; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const marketId = this.safeString(entry, 'market'); - const baseId = this.safeString(entry, 'baseAsset'); - const quoteId = this.safeString(entry, 'quoteAsset'); + for (let i = 0; i < markets.length; i++) { + const entry = markets[i]; + const marketId = this.safeString(entry, 'market_id'); + const baseId = this.safeString(entry, 'base_asset'); + const quoteId = this.safeString(entry, 'target_asset'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); - const basePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'baseAssetPrecision'))); - const quotePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'quoteAssetPrecision'))); - const status = this.safeString(entry, 'status'); - let minCost = undefined; - if (quote === 'ETH') { - minCost = minCostETH; - } result.push({ 'id': marketId, - 'symbol': base + '/' + quote, + 'symbol': marketId, 'base': base, 'quote': quote, 'settle': undefined, @@ -292,7 +215,7 @@ class geniusyield extends geniusyield$1 { 'swap': false, 'future': false, 'option': false, - 'active': (status !== 'inactive'), + 'active': true, 'contract': false, 'linear': undefined, 'inverse': undefined, @@ -304,8 +227,8 @@ class geniusyield extends geniusyield$1 { 'strike': undefined, 'optionType': undefined, 'precision': { - 'amount': basePrecision, - 'price': this.safeNumber(entry, 'tickSize'), + 'amount': undefined, + 'price': undefined, }, 'limits': { 'leverage': { @@ -313,15 +236,15 @@ class geniusyield extends geniusyield$1 { 'max': undefined, }, 'amount': { - 'min': basePrecision, + 'min': undefined, 'max': undefined, }, 'price': { - 'min': quotePrecision, + 'min': undefined, 'max': undefined, }, 'cost': { - 'min': minCost, + 'min': undefined, 'max': undefined, }, }, @@ -331,1492 +254,9 @@ class geniusyield extends geniusyield$1 { } return result; } - async fetchTicker(symbol, params = {}) { - /** - * @method - * @name geniusyield#fetchTicker - * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string} symbol unified symbol of the market to fetch the ticker for - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - }; - // [ - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // } - // ] - const response = await this.publicGetTickers(this.extend(request, params)); - const ticker = this.safeDict(response, 0); - return this.parseTicker(ticker, market); - } - async fetchTickers(symbols = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchTickers - * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} - */ - await this.loadMarkets(); - // [ - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // }, ... - // ] - const response = await this.publicGetTickers(params); - return this.parseTickers(response, symbols); - } - parseTicker(ticker, market = undefined) { - // { - // "market": "DIL-ETH", - // "time": 1598367493008, - // "open": "0.09695361", - // "high": "0.10245881", - // "low": "0.09572507", - // "close": "0.09917079", - // "closeQuantity": "0.71320950", - // "baseVolume": "309.17380612", - // "quoteVolume": "30.57633981", - // "percentChange": "2.28", - // "numTrades": 205, - // "ask": "0.09910476", - // "bid": "0.09688340", - // "sequence": 3902 - // } - const marketId = this.safeString(ticker, 'market'); - market = this.safeMarket(marketId, market, '-'); - const symbol = market['symbol']; - const timestamp = this.safeInteger(ticker, 'time'); - const close = this.safeString(ticker, 'close'); - return this.safeTicker({ - 'symbol': symbol, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'high': this.safeString(ticker, 'high'), - 'low': this.safeString(ticker, 'low'), - 'bid': this.safeString(ticker, 'bid'), - 'bidVolume': undefined, - 'ask': this.safeString(ticker, 'ask'), - 'askVolume': undefined, - 'vwap': undefined, - 'open': this.safeString(ticker, 'open'), - 'close': close, - 'last': close, - 'previousClose': undefined, - 'change': undefined, - 'percentage': this.safeString(ticker, 'percentChange'), - 'average': undefined, - 'baseVolume': this.safeString(ticker, 'baseVolume'), - 'quoteVolume': this.safeString(ticker, 'quoteVolume'), - 'info': ticker, - }, market); - } - async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOHLCV - * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market - * @see https://api-docs-v3.geniusyield.io/#get-candles - * @param {string} symbol unified symbol of the market to fetch OHLCV data for - * @param {string} timeframe the length of time each candle represents - * @param {int} [since] timestamp in ms of the earliest candle to fetch - * @param {int} [limit] the maximum amount of candles to fetch - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - 'interval': timeframe, - }; - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = Math.min(limit, 1000); - } - const response = await this.publicGetCandles(this.extend(request, params)); - if (Array.isArray(response)) { - // [ - // { - // "start": 1598345580000, - // "open": "0.09771286", - // "high": "0.09771286", - // "low": "0.09771286", - // "close": "0.09771286", - // "volume": "1.45340410", - // "sequence": 3853 - // }, ... - // ] - return this.parseOHLCVs(response, market, timeframe, since, limit); - } - else { - // {"nextTime":1595536440000} - return []; - } - } - parseOHLCV(ohlcv, market = undefined) { - // { - // "start": 1598345580000, - // "open": "0.09771286", - // "high": "0.09771286", - // "low": "0.09771286", - // "close": "0.09771286", - // "volume": "1.45340410", - // "sequence": 3853 - // } - const timestamp = this.safeInteger(ohlcv, 'start'); - const open = this.safeNumber(ohlcv, 'open'); - const high = this.safeNumber(ohlcv, 'high'); - const low = this.safeNumber(ohlcv, 'low'); - const close = this.safeNumber(ohlcv, 'close'); - const volume = this.safeNumber(ohlcv, 'volume'); - return [timestamp, open, high, low, close, volume]; - } - async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchTrades - * @description get the list of most recent trades for a particular symbol - * @see https://api-docs-v3.geniusyield.io/#get-trades - * @param {string} symbol unified symbol of the market to fetch trades for - * @param {int} [since] timestamp in ms of the earliest trade to fetch - * @param {int} [limit] the maximum amount of trades to fetch - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - }; - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = Math.min(limit, 1000); - } - // [ - // { - // "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", - // "price": "0.09771286", - // "quantity": "1.45340410", - // "quoteQuantity": "0.14201627", - // "time": 1598345638994, - // "makerSide": "buy", - // "sequence": 3853 - // }, ... - // ] - const response = await this.publicGetTrades(this.extend(request, params)); - return this.parseTrades(response, market, since, limit); - } - parseTrade(trade, market = undefined) { - // - // public trades - // { - // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - // "price":"0.20377008", - // "quantity":"47.58448728", - // "quoteQuantity":"9.69629509", - // "time":1642091300873, - // "makerSide":"buy", - // "type":"hybrid", // one of either: "orderBook", "hybrid", or "pool" - // "sequence":31876 - // } - // - // private trades - // { - // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - // "price":"0.20717368", - // "quantity":"15.00000000", - // "quoteQuantity":"3.10760523", - // "orderBookQuantity":"0.00000003", - // "orderBookQuoteQuantity":"0.00000001", - // "poolQuantity":"14.99999997", - // "poolQuoteQuantity":"3.10760522", - // "time":1642083351215, - // "makerSide":"sell", - // "sequence":31795, - // "market":"Genius Yield-USDC", - // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - // "side":"buy", - // "fee":"0.03749989", - // "feeAsset":"Genius Yield", - // "gas":"0.40507261", - // "liquidity":"taker", - // "type":"hybrid", - // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - // "txStatus":"mined" - // } - // - const id = this.safeString(trade, 'fillId'); - const priceString = this.safeString(trade, 'price'); - const amountString = this.safeString(trade, 'quantity'); - const costString = this.safeString(trade, 'quoteQuantity'); - const timestamp = this.safeInteger(trade, 'time'); - const marketId = this.safeString(trade, 'market'); - const symbol = this.safeSymbol(marketId, market, '-'); - // this code handles the duality of public vs private trades - const makerSide = this.safeString(trade, 'makerSide'); - const oppositeSide = (makerSide === 'buy') ? 'sell' : 'buy'; - const side = this.safeString(trade, 'side', oppositeSide); - const takerOrMaker = this.safeString(trade, 'liquidity', 'taker'); - const feeCostString = this.safeString(trade, 'fee'); - let fee = undefined; - if (feeCostString !== undefined) { - const feeCurrencyId = this.safeString(trade, 'feeAsset'); - fee = { - 'cost': feeCostString, - 'currency': this.safeCurrencyCode(feeCurrencyId), - }; - } - const orderId = this.safeString(trade, 'orderId'); - return this.safeTrade({ - 'info': trade, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'symbol': symbol, - 'id': id, - 'order': orderId, - 'type': 'limit', - 'side': side, - 'takerOrMaker': takerOrMaker, - 'price': priceString, - 'amount': amountString, - 'cost': costString, - 'fee': fee, - }, market); - } - async fetchTradingFees(params = {}) { - /** - * @method - * @name geniusyield#fetchTradingFees - * @description fetch the trading fees for multiple markets - * @see https://api-docs-v3.geniusyield.io/#get-api-account - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - }; - let response = undefined; - response = await this.privateGetUser(this.extend(request, params)); - // - // { - // "depositEnabled": true, - // "orderEnabled": true, - // "cancelEnabled": true, - // "withdrawEnabled": true, - // "totalPortfolioValueUsd": "0.00", - // "makerFeeRate": "0.0000", - // "takerFeeRate": "0.0025", - // "takerIdexFeeRate": "0.0005", - // "takerLiquidityProviderFeeRate": "0.0020" - // } - // - const maker = this.safeNumber(response, 'makerFeeRate'); - const taker = this.safeNumber(response, 'takerFeeRate'); - const result = {}; - for (let i = 0; i < this.symbols.length; i++) { - const symbol = this.symbols[i]; - result[symbol] = { - 'info': response, - 'symbol': symbol, - 'maker': maker, - 'taker': taker, - 'percentage': true, - 'tierBased': false, - }; - } - return result; - } - async fetchOrderBook(symbol, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOrderBook - * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data - * @see https://api-docs-v3.geniusyield.io/#get-order-books - * @param {string} symbol unified symbol of the market to fetch the order book for - * @param {int} [limit] the maximum amount of order book entries to return - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols - */ - await this.loadMarkets(); - const market = this.market(symbol); - const request = { - 'market': market['id'], - 'level': 2, - }; - if (limit !== undefined) { - request['limit'] = limit; - } - // { - // "sequence": 36416753, - // "bids": [ - // [ '0.09672815', "8.22284267", 1 ], - // [ '0.09672814', "1.83685554", 1 ], - // [ '0.09672143', "4.10962617", 1 ], - // [ '0.09658884', "4.03863759", 1 ], - // [ '0.09653781', "3.35730684", 1 ], - // [ '0.09624660', "2.54163586", 1 ], - // [ '0.09617490', "1.93065030", 1 ] - // ], - // "asks": [ - // [ '0.09910476', "3.22840154", 1 ], - // [ '0.09940587', "3.39796593", 1 ], - // [ '0.09948189', "4.25088898", 1 ], - // [ '0.09958362', "2.42195784", 1 ], - // [ '0.09974393', "4.25234367", 1 ], - // [ '0.09995250', "3.40192141", 1 ] - // ] - // } - const response = await this.publicGetOrderbook(this.extend(request, params)); - const nonce = this.safeInteger(response, 'sequence'); - return { - 'symbol': symbol, - 'timestamp': undefined, - 'datetime': undefined, - 'nonce': nonce, - 'bids': this.parseSide(response, 'bids'), - 'asks': this.parseSide(response, 'asks'), - }; - } - parseSide(book, side) { - const bookSide = this.safeValue(book, side, []); - const result = []; - for (let i = 0; i < bookSide.length; i++) { - const order = bookSide[i]; - const price = this.safeNumber(order, 0); - const amount = this.safeNumber(order, 1); - const orderCount = this.safeInteger(order, 2); - result.push([price, amount, orderCount]); - } - const descending = side === 'bids'; - return this.sortBy(result, 0, descending); - } - async fetchCurrencies(params = {}) { - /** - * @method - * @name geniusyield#fetchCurrencies - * @description fetches all available currencies on an exchange - * @see https://api-docs-v3.geniusyield.io/#get-assets - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} an associative dictionary of currencies - */ - const response = await this.publicGetAssets(params); - // - // [ - // { - // "name": "Ethereum", - // "symbol": "ETH", - // "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - // "assetDecimals": "18", - // "exchangeDecimals": "8", - // "maticPrice": "3029.38503483" - // }, - // ] - // - const result = {}; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const name = this.safeString(entry, 'name'); - const currencyId = this.safeString(entry, 'symbol'); - const code = this.safeCurrencyCode(currencyId); - const precision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'exchangeDecimals'))); - result[code] = { - 'id': currencyId, - 'code': code, - 'info': entry, - 'type': undefined, - 'name': name, - 'active': undefined, - 'deposit': undefined, - 'withdraw': undefined, - 'fee': undefined, - 'precision': precision, - 'limits': { - 'amount': { 'min': precision, 'max': undefined }, - 'withdraw': { 'min': precision, 'max': undefined }, - }, - }; - } - return result; - } - parseBalance(response) { - const result = { - 'info': response, - 'timestamp': undefined, - 'datetime': undefined, - }; - for (let i = 0; i < response.length; i++) { - const entry = response[i]; - const currencyId = this.safeString(entry, 'asset'); - const code = this.safeCurrencyCode(currencyId); - const account = this.account(); - account['total'] = this.safeString(entry, 'quantity'); - account['free'] = this.safeString(entry, 'availableForTrade'); - account['used'] = this.safeString(entry, 'locked'); - result[code] = account; - } - return this.safeBalance(result); - } - async fetchBalance(params = {}) { - /** - * @method - * @name geniusyield#fetchBalance - * @description query for balance and get the amount of funds available for trading or funds locked in orders - * @see https://api-docs-v3.geniusyield.io/#get-balances - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - const nonce1 = this.uuidv1(); - const request = { - 'nonce': nonce1, - 'wallet': this.walletAddress, - }; - // [ - // { - // "asset": "DIL", - // "quantity": "0.00000000", - // "availableForTrade": "0.00000000", - // "locked": "0.00000000", - // "usdValue": null - // }, ... - // ] - const extendedRequest = this.extend(request, params); - if (extendedRequest['wallet'] === undefined) { - throw new errors.BadRequest(this.id + ' fetchBalance() wallet is undefined, set this.walletAddress or "address" in params'); - } - let response = undefined; - try { - response = await this.privateGetBalances(extendedRequest); - } - catch (e) { - if (e instanceof errors.InvalidAddress) { - const walletAddress = extendedRequest['wallet']; - await this.associateWallet(walletAddress); - response = await this.privateGetBalances(extendedRequest); - } - else { - throw e; - } - } - return this.parseBalance(response); - } - async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchMyTrades - * @description fetch all trades made by the user - * @see https://api-docs-v3.geniusyield.io/#get-fills - * @param {string} symbol unified market symbol - * @param {int} [since] the earliest time in ms to fetch trades for - * @param {int} [limit] the maximum number of trades structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - let market = undefined; - const request = { - 'nonce': this.uuidv1(), - 'wallet': this.walletAddress, - }; - if (symbol !== undefined) { - market = this.market(symbol); - request['market'] = market['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - // [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478762, - // "makerSide": "sell", - // "sequence": 5053, - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "side": "buy", - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - const extendedRequest = this.extend(request, params); - if (extendedRequest['wallet'] === undefined) { - throw new errors.BadRequest(this.id + ' fetchMyTrades() walletAddress is undefined, set this.walletAddress or "address" in params'); - } - let response = undefined; - try { - response = await this.privateGetFills(extendedRequest); - } - catch (e) { - if (e instanceof errors.InvalidAddress) { - const walletAddress = extendedRequest['wallet']; - await this.associateWallet(walletAddress); - response = await this.privateGetFills(extendedRequest); - } - else { - throw e; - } - } - return this.parseTrades(response, market, since, limit); - } - async fetchOrder(id, symbol = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOrder - * @description fetches information on an order made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified symbol of the market the order was made in - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request = { - 'orderId': id, - }; - return await this.fetchOrdersHelper(symbol, undefined, undefined, this.extend(request, params)); - } - async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchOpenOrders - * @description fetch all unfilled currently open orders - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified market symbol - * @param {int} [since] the earliest time in ms to fetch open orders for - * @param {int} [limit] the maximum number of open orders structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request = { - 'closed': false, - }; - return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); - } - async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchClosedOrders - * @description fetches information on multiple closed orders made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} symbol unified market symbol of the market orders were made in - * @param {int} [since] the earliest time in ms to fetch orders for - * @param {int} [limit] the maximum number of order structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - const request = { - 'closed': true, - }; - return await this.fetchOrdersHelper(symbol, since, limit, this.extend(request, params)); - } - async fetchOrdersHelper(symbol = undefined, since = undefined, limit = undefined, params = {}) { - await this.loadMarkets(); - const request = { - 'nonce': this.uuidv1(), - 'wallet': this.walletAddress, - }; - let market = undefined; - if (symbol !== undefined) { - market = this.market(symbol); - request['market'] = market['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - const response = await this.privateGetOrders(this.extend(request, params)); - // fetchClosedOrders / fetchOpenOrders - // [ - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - // } - // ] - // fetchOrder - // { market: "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": - // [ { fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" } ] } - if (Array.isArray(response)) { - return this.parseOrders(response, market, since, limit); - } - else { - return this.parseOrder(response, market); - } - } - parseOrderStatus(status) { - // https://docs.geniusyield.io/#order-states-amp-lifecycle - const statuses = { - 'active': 'open', - 'partiallyFilled': 'open', - 'rejected': 'canceled', - 'filled': 'closed', - }; - return this.safeString(statuses, status, status); - } - parseOrder(order, market = undefined) { - // - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "avgExecutionPrice": "0.09905990", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus": "mined" - // } - // ] - // } - // - const timestamp = this.safeInteger(order, 'time'); - const fills = this.safeValue(order, 'fills', []); - const id = this.safeString(order, 'orderId'); - const clientOrderId = this.safeString(order, 'clientOrderId'); - const marketId = this.safeString(order, 'market'); - const side = this.safeString(order, 'side'); - const symbol = this.safeSymbol(marketId, market, '-'); - const type = this.safeString(order, 'type'); - const amount = this.safeString(order, 'originalQuantity'); - const filled = this.safeString(order, 'executedQuantity'); - const average = this.safeString(order, 'avgExecutionPrice'); - const price = this.safeString(order, 'price'); - const rawStatus = this.safeString(order, 'status'); - const timeInForce = this.safeStringUpper(order, 'timeInForce'); - const status = this.parseOrderStatus(rawStatus); - return this.safeOrder({ - 'info': order, - 'id': id, - 'clientOrderId': clientOrderId, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'lastTradeTimestamp': undefined, - 'symbol': symbol, - 'type': type, - 'timeInForce': timeInForce, - 'postOnly': undefined, - 'side': side, - 'price': price, - 'stopPrice': undefined, - 'triggerPrice': undefined, - 'amount': amount, - 'cost': undefined, - 'average': average, - 'filled': filled, - 'remaining': undefined, - 'status': status, - 'fee': undefined, - 'trades': fills, - }, market); - } - async associateWallet(walletAddress, params = {}) { - const nonce = this.uuidv1(); - const noPrefix = this.remove0xPrefix(walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(noPrefix), - ]; - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, sha3.keccak_256, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - // { - // "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "totalPortfolioValueUsd": "0.00", - // "time": 1598468353626 - // } - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': walletAddress, - }, - 'signature': signature, - }; - const result = await this.privatePostWallets(request); - return result; - } - async createOrder(symbol, type, side, amount, price = undefined, params = {}) { - /** - * @method - * @name geniusyield#createOrder - * @description create a trade order, https://docs.geniusyield.io/#create-order - * @see https://api-docs-v3.geniusyield.io/#create-order - * @param {string} symbol unified symbol of the market to create an order in - * @param {string} type 'market' or 'limit' - * @param {string} side 'buy' or 'sell' - * @param {float} amount how much of currency you want to trade in units of base currency - * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @param {bool} [params.test] set to true to test an order, no order will be created but the request will be validated - * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - const testOrder = this.safeBool(params, 'test', false); - params = this.omit(params, 'test'); - const market = this.market(symbol); - const nonce = this.uuidv1(); - let typeEnum = undefined; - const stopLossTypeEnums = { - 'stopLoss': 3, - 'stopLossLimit': 4, - 'takeProfit': 5, - 'takeProfitLimit': 6, - }; - let stopPriceString = undefined; - if ((type === 'stopLossLimit') || (type === 'takeProfitLimit') || ('stopPrice' in params)) { - if (!('stopPrice' in params)) { - throw new errors.BadRequest(this.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders'); - } - stopPriceString = this.priceToPrecision(symbol, params['stopPrice']); - } - const limitTypeEnums = { - 'limit': 1, - 'limitMaker': 2, - }; - let priceString = undefined; - const typeLower = type.toLowerCase(); - const limitOrder = typeLower.indexOf('limit') >= 0; - if (type in limitTypeEnums) { - typeEnum = limitTypeEnums[type]; - priceString = this.priceToPrecision(symbol, price); - } - else if (type in stopLossTypeEnums) { - typeEnum = stopLossTypeEnums[type]; - priceString = this.priceToPrecision(symbol, price); - } - else if (type === 'market') { - typeEnum = 0; - } - else { - throw new errors.BadRequest(this.id + ' ' + type + ' is not a valid order type'); - } - let amountEnum = 0; // base quantity - if ('quoteOrderQuantity' in params) { - if (type !== 'market') { - throw new errors.NotSupported(this.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders'); - } - amountEnum = 1; - amount = this.safeNumber(params, 'quoteOrderQuantity'); - } - const sideEnum = (side === 'buy') ? 0 : 1; - const walletBytes = this.remove0xPrefix(this.walletAddress); - const network = this.safeString(this.options, 'network', 'ETH'); - const orderVersion = this.getSupportedMapping(network, { - 'ETH': 1, - 'BSC': 2, - 'MATIC': 4, - }); - const amountString = this.amountToPrecision(symbol, amount); - // https://docs.geniusyield.io/#time-in-force - const timeInForceEnums = { - 'gtc': 0, - 'ioc': 2, - 'fok': 3, - }; - const defaultTimeInForce = this.safeString(this.options, 'defaultTimeInForce', 'gtc'); - const timeInForce = this.safeString(params, 'timeInForce', defaultTimeInForce); - let timeInForceEnum = undefined; - if (timeInForce in timeInForceEnums) { - timeInForceEnum = timeInForceEnums[timeInForce]; - } - else { - const allOptions = Object.keys(timeInForceEnums); - const asString = allOptions.join(', '); - throw new errors.BadRequest(this.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString); - } - // https://docs.geniusyield.io/#self-trade-prevention - const selfTradePreventionEnums = { - 'dc': 0, - 'co': 1, - 'cn': 2, - 'cb': 3, - }; - const defaultSelfTradePrevention = this.safeString(this.options, 'defaultSelfTradePrevention', 'cn'); - const selfTradePrevention = this.safeString(params, 'selfTradePrevention', defaultSelfTradePrevention); - let selfTradePreventionEnum = undefined; - if (selfTradePrevention in selfTradePreventionEnums) { - selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]; - } - else { - const allOptions = Object.keys(selfTradePreventionEnums); - const asString = allOptions.join(', '); - throw new errors.BadRequest(this.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString); - } - const byteArray = [ - this.numberToBE(orderVersion, 1), - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - this.encode(market['id']), - this.numberToBE(typeEnum, 1), - this.numberToBE(sideEnum, 1), - this.encode(amountString), - this.numberToBE(amountEnum, 1), - ]; - if (limitOrder) { - const encodedPrice = this.encode(priceString); - byteArray.push(encodedPrice); - } - if (type in stopLossTypeEnums) { - const encodedPrice = this.encode(stopPriceString || priceString); - byteArray.push(encodedPrice); - } - const clientOrderId = this.safeString(params, 'clientOrderId'); - if (clientOrderId !== undefined) { - byteArray.push(this.encode(clientOrderId)); - } - const after = [ - this.numberToBE(timeInForceEnum, 1), - this.numberToBE(selfTradePreventionEnum, 1), - this.numberToBE(0, 8), // unused - ]; - const allBytes = this.arrayConcat(byteArray, after); - const binary = this.binaryConcatArray(allBytes); - const hash = this.hash(binary, sha3.keccak_256, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - const request = { - 'parameters': { - 'nonce': nonce, - 'market': market['id'], - 'side': side, - 'type': type, - 'wallet': this.walletAddress, - 'selfTradePrevention': selfTradePrevention, - }, - 'signature': signature, - }; - if (type !== 'market') { - request['parameters']['timeInForce'] = timeInForce; - } - if (limitOrder) { - request['parameters']['price'] = priceString; - } - if (type in stopLossTypeEnums) { - request['parameters']['stopPrice'] = stopPriceString || priceString; - } - if (amountEnum === 0) { - request['parameters']['quantity'] = amountString; - } - else { - request['parameters']['quoteOrderQuantity'] = amountString; - } - if (clientOrderId !== undefined) { - request['parameters']['clientOrderId'] = clientOrderId; - } - // { - // "market": "DIL-ETH", - // "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time": 1598873478650, - // "status": "filled", - // "type": "limit", - // "side": "buy", - // "originalQuantity": "0.40000000", - // "executedQuantity": "0.40000000", - // "cumulativeQuoteQuantity": "0.03962396", - // "price": "1.00000000", - // "fills": [ - // { - // "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price": "0.09905990", - // "quantity": "0.40000000", - // "quoteQuantity": "0.03962396", - // "time": 1598873478650, - // "makerSide": "sell", - // "sequence": 5053, - // "fee": "0.00080000", - // "feeAsset": "DIL", - // "gas": "0.00857497", - // "liquidity": "taker", - // "txStatus": "pending" - // } - // ], - // "avgExecutionPrice": "0.09905990" - // } - // we don't use extend here because it is a signed endpoint - let response = undefined; - if (testOrder) { - response = await this.privatePostOrdersTest(request); - } - else { - response = await this.privatePostOrders(request); - } - return this.parseOrder(response, market); - } - async withdraw(code, amount, address, tag = undefined, params = {}) { - /** - * @method - * @name geniusyield#withdraw - * @description make a withdrawal - * @see https://api-docs-v3.geniusyield.io/#withdraw-funds - * @param {string} code unified currency code - * @param {float} amount the amount to withdraw - * @param {string} address the address to withdraw to - * @param {string} tag - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - [tag, params] = this.handleWithdrawTagAndParams(tag, params); - this.checkRequiredCredentials(); - await this.loadMarkets(); - const nonce = this.uuidv1(); - const amountString = this.currencyToPrecision(code, amount); - const currency = this.currency(code); - const walletBytes = this.remove0xPrefix(this.walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - this.encode(currency['id']), - this.encode(amountString), - this.numberToBE(1, 1), // bool set to true - ]; - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, sha3.keccak_256, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': address, - 'asset': currency['id'], - 'quantity': amountString, - }, - 'signature': signature, - }; - const response = await this.privatePostWithdrawals(request); - // - // { - // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883190, - // "fee": "0.00024000", - // "txStatus": "pending", - // "txId": null - // } - // - return this.parseTransaction(response, currency); - } - async cancelAllOrders(symbol = undefined, params = {}) { - /** - * @method - * @name geniusyield#cancelAllOrders - * @description cancel all open orders - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} symbol unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - let market = undefined; - if (symbol !== undefined) { - market = this.market(symbol); - } - const nonce = this.uuidv1(); - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': this.walletAddress, - }, - }; - const walletBytes = this.remove0xPrefix(this.walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - ]; - if (market !== undefined) { - byteArray.push(this.encode(market['id'])); - request['parameters']['market'] = market['id']; - } - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, sha3.keccak_256, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - request['signature'] = signature; - // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] - const response = await this.privateDeleteOrders(this.extend(request, params)); - return this.parseOrders(response, market); - } - async cancelOrder(id, symbol = undefined, params = {}) { - /** - * @method - * @name geniusyield#cancelOrder - * @description cancels an open order - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} id order id - * @param {string} symbol unified symbol of the market the order was made in - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} - */ - this.checkRequiredCredentials(); - await this.loadMarkets(); - let market = undefined; - if (symbol !== undefined) { - market = this.market(symbol); - } - const nonce = this.uuidv1(); - const walletBytes = this.remove0xPrefix(this.walletAddress); - const byteArray = [ - this.base16ToBinary(nonce), - this.base16ToBinary(walletBytes), - this.encode(id), - ]; - const binary = this.binaryConcatArray(byteArray); - const hash = this.hash(binary, sha3.keccak_256, 'hex'); - const signature = this.signMessageString(hash, this.privateKey); - const request = { - 'parameters': { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'orderId': id, - }, - 'signature': signature, - }; - // [ { orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e" } ] - const response = await this.privateDeleteOrders(this.extend(request, params)); - const canceledOrder = this.safeDict(response, 0); - return this.parseOrder(canceledOrder, market); - } - handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) { - const errorCode = this.safeString(response, 'code'); - const message = this.safeString(response, 'message'); - if (errorCode !== undefined) { - this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, message); - throw new errors.ExchangeError(this.id + ' ' + message); - } - return undefined; - } - async fetchDeposit(id, code = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDeposit - * @description fetch information on a deposit - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} id deposit id - * @param {string} code not used by geniusyield fetchDeposit () - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'depositId': id, - }; - const response = await this.privateGetDeposits(this.extend(request, params)); - return this.parseTransaction(response); - } - async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDeposits - * @description fetch all deposits made to an account - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} code unified currency code - * @param {int} [since] the earliest time in ms to fetch deposits for - * @param {int} [limit] the maximum number of deposits structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - params = this.extend({ - 'method': 'privateGetDeposits', - }, params); - return await this.fetchTransactionsHelper(code, since, limit, params); - } - async fetchStatus(params = {}) { - /** - * @method - * @name geniusyield#fetchStatus - * @description the latest known information on the availability of the exchange API - * @see https://api-docs-v3.geniusyield.io/#get-ping - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} - */ - const response = await this.publicGetPing(params); - return { - 'status': 'ok', - 'updated': undefined, - 'eta': undefined, - 'url': undefined, - 'info': response, - }; - } - async fetchTime(params = {}) { - /** - * @method - * @name geniusyield#fetchTime - * @description fetches the current integer timestamp in milliseconds from the exchange server - * @see https://api-docs-v3.geniusyield.io/#get-time - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {int} the current integer timestamp in milliseconds from the exchange server - */ - const response = await this.publicGetTime(params); - // - // { serverTime: "1655258263236" } - // - return this.safeInteger(response, 'serverTime'); - } - async fetchWithdrawal(id, code = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchWithdrawal - * @description fetch data on a currency withdrawal via the withdrawal id - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} id withdrawal id - * @param {string} code not used by geniusyield.fetchWithdrawal - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - 'wallet': this.walletAddress, - 'withdrawalId': id, - }; - const response = await this.privateGetWithdrawals(this.extend(request, params)); - return this.parseTransaction(response); - } - async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchWithdrawals - * @description fetch all withdrawals made from an account - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} code unified currency code - * @param {int} [since] the earliest time in ms to fetch withdrawals for - * @param {int} [limit] the maximum number of withdrawals structures to retrieve - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure} - */ - params = this.extend({ - 'method': 'privateGetWithdrawals', - }, params); - return await this.fetchTransactionsHelper(code, since, limit, params); - } - async fetchTransactionsHelper(code = undefined, since = undefined, limit = undefined, params = {}) { - await this.loadMarkets(); - const nonce = this.uuidv1(); - const request = { - 'nonce': nonce, - 'wallet': this.walletAddress, - }; - let currency = undefined; - if (code !== undefined) { - currency = this.currency(code); - request['asset'] = currency['id']; - } - if (since !== undefined) { - request['start'] = since; - } - if (limit !== undefined) { - request['limit'] = limit; - } - // [ - // { - // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - // "asset": "ETH", - // "quantity": "1.00000000", - // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime": 1598865853000, - // "confirmationTime": 1598865930231 - // } - // ] - const method = params['method']; - params = this.omit(params, 'method'); - let response = undefined; - if (method === 'privateGetDeposits') { - response = await this.privateGetDeposits(this.extend(request, params)); - } - else if (method === 'privateGetWithdrawals') { - response = await this.privateGetWithdrawals(this.extend(request, params)); - } - else { - throw new errors.NotSupported(this.id + ' fetchTransactionsHelper() not support this method'); - } - return this.parseTransactions(response, currency, since, limit); - } - parseTransactionStatus(status) { - const statuses = { - 'mined': 'ok', - }; - return this.safeString(statuses, status, status); - } - parseTransaction(transaction, currency = undefined) { - // - // fetchDeposits - // - // { - // "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - // "asset": "ETH", - // "quantity": "1.00000000", - // "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime": 1598865853000, - // "confirmationTime": 1598865930231 - // } - // - // fetchWithdrwalas - // - // { - // "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883288, - // "fee": "0.00024000", - // "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - // "txStatus": "mined" - // } - // - // withdraw - // - // { - // "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset": "ETH", - // "assetContractAddress": "0x0000000000000000000000000000000000000000", - // "quantity": "0.20000000", - // "time": 1598962883190, - // "fee": "0.00024000", - // "txStatus": "pending", - // "txId": null - // } - // - let type = undefined; - if ('depositId' in transaction) { - type = 'deposit'; - } - else if (('withdrawId' in transaction) || ('withdrawalId' in transaction)) { - type = 'withdrawal'; - } - let id = this.safeString2(transaction, 'depositId', 'withdrawId'); - id = this.safeString(transaction, 'withdrawalId', id); - const code = this.safeCurrencyCode(this.safeString(transaction, 'asset'), currency); - const amount = this.safeNumber(transaction, 'quantity'); - const txid = this.safeString(transaction, 'txId'); - const timestamp = this.safeInteger2(transaction, 'txTime', 'time'); - let fee = undefined; - if ('fee' in transaction) { - fee = { - 'cost': this.safeNumber(transaction, 'fee'), - 'currency': 'ETH', - }; - } - const rawStatus = this.safeString(transaction, 'txStatus'); - const status = this.parseTransactionStatus(rawStatus); - const updated = this.safeInteger(transaction, 'confirmationTime'); - return { - 'info': transaction, - 'id': id, - 'txid': txid, - 'timestamp': timestamp, - 'datetime': this.iso8601(timestamp), - 'network': undefined, - 'address': undefined, - 'addressTo': undefined, - 'addressFrom': undefined, - 'tag': undefined, - 'tagTo': undefined, - 'tagFrom': undefined, - 'type': type, - 'amount': amount, - 'currency': code, - 'status': status, - 'updated': updated, - 'comment': undefined, - 'internal': undefined, - 'fee': fee, - }; - } - calculateRateLimiterCost(api, method, path, params, config = {}) { - const hasApiKey = (this.apiKey !== undefined); - const hasSecret = (this.secret !== undefined); - const hasWalletAddress = (this.walletAddress !== undefined); - const hasPrivateKey = (this.privateKey !== undefined); - const defaultCost = this.safeValue(config, 'cost', 1); - const authenticated = hasApiKey && hasSecret && hasWalletAddress && hasPrivateKey; - return authenticated ? (defaultCost / 2) : defaultCost; - } - async fetchDepositAddress(code = undefined, params = {}) { - /** - * @method - * @name geniusyield#fetchDepositAddress - * @description fetch the Polygon address of the wallet - * @see https://api-docs-v3.geniusyield.io/#get-wallets - * @param {string} code not used by geniusyield - * @param {object} [params] extra parameters specific to the exchange API endpoint - * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} - */ - const request = {}; - request['nonce'] = this.uuidv1(); - const response = await this.privateGetWallets(this.extend(request, params)); - // - // [ - // { - // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd: "0.00", - // time: "1678342148086" - // }, - // { - // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd: "15.90", - // time: "1691697811659" - // } - // ] - // - return this.parseDepositAddress(response); - } - parseDepositAddress(depositAddress, currency = undefined) { - // - // [ - // { - // address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd: "0.00", - // time: "1678342148086" - // }, - // { - // address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd: "15.90", - // time: "1691697811659" - // } - // ] - // - const length = depositAddress.length; - const entry = this.safeDict(depositAddress, length - 1); - const address = this.safeString(entry, 'address'); - this.checkAddress(address); - return { - 'info': depositAddress, - 'currency': undefined, - 'address': address, - 'tag': undefined, - 'network': 'MATIC', - }; - } sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { - const network = this.safeString(this.options, 'network', 'ETH'); - const version = this.safeString(this.options, 'version', 'v1'); + const network = this.safeString(this.options, 'network', 'mainnet'); + const version = this.safeString(this.options, 'version', 'v0'); let url = this.urls['api'][network] + '/' + version + '/' + path; const keys = Object.keys(params); const length = keys.length; @@ -1834,51 +274,10 @@ class geniusyield extends geniusyield$1 { 'Content-Type': 'application/json', }; if (this.apiKey !== undefined) { - headers['Genius Yield-API-Key'] = this.apiKey; - } - if (api === 'private') { - let payload = undefined; - if (method === 'GET') { - payload = query; - } - else { - payload = body; - } - headers['Genius Yield-HMAC-Signature'] = this.hmac(this.encode(payload), this.encode(this.secret), sha256.sha256, 'hex'); + headers['api-key'] = this.apiKey; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } - remove0xPrefix(hexData) { - if (hexData.slice(0, 2) === '0x') { - return hexData.slice(2); - } - else { - return hexData; - } - } - hashMessage(message) { - // takes a hex encoded message - const binaryMessage = this.base16ToBinary(this.remove0xPrefix(message)); - const prefix = this.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength); - return '0x' + this.hash(this.binaryConcat(prefix, binaryMessage), sha3.keccak_256, 'hex'); - } - signHash(hash, privateKey) { - const signature = crypto.ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1.secp256k1, undefined); - return { - 'r': '0x' + signature['r'], - 's': '0x' + signature['s'], - 'v': 27 + signature['v'], - }; - } - signMessage(message, privateKey) { - return this.signHash(this.hashMessage(message), privateKey.slice(-64)); - } - signMessageString(message, privateKey) { - // still takes the input as a hex string - // same as above but returns a string instead of an object - const signature = this.signMessage(message, privateKey); - return signature['r'] + this.remove0xPrefix(signature['s']) + this.binaryToBase16(this.numberToBE(signature['v'], 1)); - } } module.exports = geniusyield; diff --git a/js/src/abstract/geniusyield.js b/js/src/abstract/geniusyield.js index d3776174f72c..864db8b8baec 100644 --- a/js/src/abstract/geniusyield.js +++ b/js/src/abstract/geniusyield.js @@ -1,3 +1,9 @@ +// ---------------------------------------------------------------------------- + +// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code +// EDIT THE CORRESPONDENT .ts FILE INSTEAD + // ------------------------------------------------------------------------------- import { Exchange as _Exchange } from '../base/Exchange.js'; class Exchange extends _Exchange { diff --git a/js/src/geniusyield.d.ts b/js/src/geniusyield.d.ts index 401cc8ef37f9..c6160cb8faa0 100644 --- a/js/src/geniusyield.d.ts +++ b/js/src/geniusyield.d.ts @@ -1,10 +1,17 @@ import Exchange from './abstract/geniusyield.js'; -import type { Market } from './base/types.js'; +import type { Market, Str, MarketInterface } from './base/types.js'; /** * @class geniusyield * @augments Exchange */ export default class geniusyield extends Exchange { + safeMarket(marketId?: Str, market?: Market, delimiter?: Str, marketType?: Str): MarketInterface; describe(): any; fetchMarkets(params?: {}): Promise; + sign(path: any, api?: string, method?: string, params?: {}, headers?: any, body?: any): { + url: string; + method: string; + body: any; + headers: any; + }; } diff --git a/js/src/geniusyield.js b/js/src/geniusyield.js index 9e06f1293015..e01f2e08371f 100644 --- a/js/src/geniusyield.js +++ b/js/src/geniusyield.js @@ -1,14 +1,27 @@ +// ---------------------------------------------------------------------------- + +// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: +// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code +// EDIT THE CORRESPONDENT .ts FILE INSTEAD + // --------------------------------------------------------------------------- import Exchange from './abstract/geniusyield.js'; import { TICK_SIZE, PAD_WITH_ZERO } from './base/functions/number.js'; import { InvalidOrder, InsufficientFunds, ExchangeNotAvailable, DDoSProtection, BadRequest, InvalidAddress, AuthenticationError } from './base/errors.js'; -import { Precise } from './base/Precise.js'; // --------------------------------------------------------------------------- /** * @class geniusyield * @augments Exchange */ export default class geniusyield extends Exchange { + safeMarket(marketId = undefined, market = undefined, delimiter = undefined, marketType = undefined) { + const isOption = (marketId !== undefined) && ((marketId.indexOf('-C') > -1) || (marketId.indexOf('-P') > -1)); + if (isOption && !(marketId in this.markets_by_id)) { + // handle expired option contracts + return this.createExpiredOptionMarket(marketId); + } + return super.safeMarket(marketId, market, delimiter, marketType); + } describe() { return this.deepExtend(super.describe(), { 'id': 'geniusyield', @@ -29,17 +42,17 @@ export default class geniusyield extends Exchange { 'option': false, 'addMargin': false, 'cancelAllOrders': false, - 'cancelOrder': false, + 'cancelOrder': true, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': false, + 'createOrder': true, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, 'createStopOrder': false, - 'fetchBalance': false, + 'fetchBalance': true, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchClosedOrders': false, @@ -69,7 +82,7 @@ export default class geniusyield extends Exchange { 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': false, - 'fetchOrders': false, + 'fetchOrders': true, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, @@ -107,8 +120,8 @@ export default class geniusyield extends Exchange { }, 'urls': { 'api': { - 'preprod': 'https://localhost:8082/v0/', - 'mainnet': 'https://localhost:8082/v0/', + 'preprod': 'http://localhost:8082', + 'mainnet': 'http://localhost:8082', }, 'www': 'https://www.geniusyield.co/', 'doc': [ @@ -121,11 +134,14 @@ export default class geniusyield extends Exchange { }, 'private': { 'get': { - 'markets': 0, - 'trading-fees': 0, - } + 'markets': 10, + 'trading-fees': 10, + }, }, }, + 'headers': { + 'X-Gate-Channel-Id': 'ccxt', + }, 'options': { 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', @@ -177,11 +193,8 @@ export default class geniusyield extends Exchange { // "percentage_maker_fee": "0.3", // "percentage_taker_fee": "0.3" // } - const maker = this.safeNumber(fees, 'makerFeeRate'); - const taker = this.safeNumber(fees, 'takerFeeRate'); - const makerMin = this.safeString(fees, 'makerTradeMinimum'); - const takerMin = this.safeString(fees, 'takerTradeMinimum'); - const minCost = this.parseNumber(Precise.stringMin(makerMin, takerMin)); + const maker = this.safeNumber(fees, 'percentage_maker_fee'); + const taker = this.safeNumber(fees, 'percentage_taker_fee'); const result = []; for (let i = 0; i < markets.length; i++) { const entry = markets[i]; @@ -190,12 +203,9 @@ export default class geniusyield extends Exchange { const quoteId = this.safeString(entry, 'target_asset'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); - const basePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'baseAssetPrecision'))); - const quotePrecision = this.parseNumber(this.parsePrecision(this.safeString(entry, 'quoteAssetPrecision'))); - const status = this.safeString(entry, 'status'); result.push({ 'id': marketId, - 'symbol': base + '/' + quote, + 'symbol': marketId, 'base': base, 'quote': quote, 'settle': undefined, @@ -208,7 +218,7 @@ export default class geniusyield extends Exchange { 'swap': false, 'future': false, 'option': false, - 'active': (status !== 'inactive'), + 'active': true, 'contract': false, 'linear': undefined, 'inverse': undefined, @@ -220,8 +230,8 @@ export default class geniusyield extends Exchange { 'strike': undefined, 'optionType': undefined, 'precision': { - 'amount': basePrecision, - 'price': this.safeNumber(entry, 'tickSize'), + 'amount': undefined, + 'price': undefined, }, 'limits': { 'leverage': { @@ -229,15 +239,15 @@ export default class geniusyield extends Exchange { 'max': undefined, }, 'amount': { - 'min': basePrecision, + 'min': undefined, 'max': undefined, }, 'price': { - 'min': quotePrecision, + 'min': undefined, 'max': undefined, }, 'cost': { - 'min': minCost, + 'min': undefined, 'max': undefined, }, }, @@ -247,4 +257,28 @@ export default class geniusyield extends Exchange { } return result; } + sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const network = this.safeString(this.options, 'network', 'mainnet'); + const version = this.safeString(this.options, 'version', 'v0'); + let url = this.urls['api'][network] + '/' + version + '/' + path; + const keys = Object.keys(params); + const length = keys.length; + let query = undefined; + if (length > 0) { + if (method === 'GET') { + query = this.urlencode(params); + url = url + '?' + query; + } + else { + body = this.json(params); + } + } + headers = { + 'Content-Type': 'application/json', + }; + if (this.apiKey !== undefined) { + headers['api-key'] = this.apiKey; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } } diff --git a/php/abstract/geniusyield.php b/php/abstract/geniusyield.php index ce2ab30e3683..bbe02f2545fb 100644 --- a/php/abstract/geniusyield.php +++ b/php/abstract/geniusyield.php @@ -8,15 +8,15 @@ abstract class geniusyield extends \ccxt\Exchange { public function private_get_markets($params = array()) { - return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function private_get_trading_fees($params = array()) { - return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function privateGetMarkets($params = array()) { - return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function privateGetTradingFees($params = array()) { - return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } } diff --git a/php/async/abstract/geniusyield.php b/php/async/abstract/geniusyield.php index f8c05fa0f57b..7eb7857705ff 100644 --- a/php/async/abstract/geniusyield.php +++ b/php/async/abstract/geniusyield.php @@ -8,15 +8,15 @@ abstract class geniusyield extends \ccxt\async\Exchange { public function private_get_markets($params = array()) { - return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function private_get_trading_fees($params = array()) { - return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function privateGetMarkets($params = array()) { - return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function privateGetTradingFees($params = array()) { - return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 0)); + return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } } diff --git a/php/async/geniusyield.php b/php/async/geniusyield.php index 737ad25ebe69..bb300a55c442 100644 --- a/php/async/geniusyield.php +++ b/php/async/geniusyield.php @@ -7,16 +7,20 @@ use Exception; // a common import use ccxt\async\abstract\geniusyield as Exchange; -use ccxt\ExchangeError; -use ccxt\BadRequest; -use ccxt\InvalidAddress; -use ccxt\NotSupported; -use ccxt\Precise; use React\Async; use React\Promise\PromiseInterface; class geniusyield extends Exchange { + public function safe_market(?string $marketId = null, ?array $market = null, ?string $delimiter = null, ?string $marketType = null): array { + $isOption = ($marketId !== null) && ((mb_strpos($marketId, '-C') > -1) || (mb_strpos($marketId, '-P') > -1)); + if ($isOption && !(is_array($this->markets_by_id) && array_key_exists($marketId, $this->markets_by_id))) { + // handle expired option contracts + return $this->create_expired_option_market($marketId); + } + return parent::safe_market($marketId, $market, $delimiter, $marketType); + } + public function describe() { return $this->deep_extend(parent::describe(), array( 'id' => 'geniusyield', @@ -36,7 +40,7 @@ public function describe() { 'future' => false, 'option' => false, 'addMargin' => false, - 'cancelAllOrders' => true, + 'cancelAllOrders' => false, 'cancelOrder' => true, 'cancelOrders' => false, 'closeAllPositions' => false, @@ -44,21 +48,21 @@ public function describe() { 'createDepositAddress' => false, 'createOrder' => true, 'createReduceOnlyOrder' => false, - 'createStopLimitOrder' => true, - 'createStopMarketOrder' => true, - 'createStopOrder' => true, + 'createStopLimitOrder' => false, + 'createStopMarketOrder' => false, + 'createStopOrder' => false, 'fetchBalance' => true, 'fetchBorrowRateHistories' => false, 'fetchBorrowRateHistory' => false, - 'fetchClosedOrders' => true, + 'fetchClosedOrders' => false, 'fetchCrossBorrowRate' => false, 'fetchCrossBorrowRates' => false, - 'fetchCurrencies' => true, - 'fetchDeposit' => true, - 'fetchDepositAddress' => true, + 'fetchCurrencies' => false, + 'fetchDeposit' => false, + 'fetchDepositAddress' => false, 'fetchDepositAddresses' => false, 'fetchDepositAddressesByNetwork' => false, - 'fetchDeposits' => true, + 'fetchDeposits' => false, 'fetchFundingHistory' => false, 'fetchFundingRate' => false, 'fetchFundingRateHistory' => false, @@ -71,13 +75,13 @@ public function describe() { 'fetchMarginMode' => false, 'fetchMarkets' => true, 'fetchMarkOHLCV' => false, - 'fetchMyTrades' => true, - 'fetchOHLCV' => true, + 'fetchMyTrades' => false, + 'fetchOHLCV' => false, 'fetchOpenInterestHistory' => false, - 'fetchOpenOrders' => true, - 'fetchOrder' => true, - 'fetchOrderBook' => true, - 'fetchOrders' => false, + 'fetchOpenOrders' => false, + 'fetchOrder' => false, + 'fetchOrderBook' => false, + 'fetchOrders' => true, 'fetchPosition' => false, 'fetchPositionHistory' => false, 'fetchPositionMode' => false, @@ -86,23 +90,23 @@ public function describe() { 'fetchPositionsHistory' => false, 'fetchPositionsRisk' => false, 'fetchPremiumIndexOHLCV' => false, - 'fetchStatus' => true, - 'fetchTicker' => true, - 'fetchTickers' => true, - 'fetchTime' => true, - 'fetchTrades' => true, + 'fetchStatus' => false, + 'fetchTicker' => false, + 'fetchTickers' => false, + 'fetchTime' => false, + 'fetchTrades' => false, 'fetchTradingFee' => false, - 'fetchTradingFees' => true, + 'fetchTradingFees' => false, 'fetchTransactions' => false, - 'fetchWithdrawal' => true, - 'fetchWithdrawals' => true, + 'fetchWithdrawal' => false, + 'fetchWithdrawals' => false, 'reduceMargin' => false, - 'sandbox' => true, + 'sandbox' => false, 'setLeverage' => false, 'setMarginMode' => false, 'setPositionMode' => false, 'transfer' => false, - 'withdraw' => true, + 'withdraw' => false, ), 'timeframes' => array( '1m' => '1m', @@ -114,58 +118,34 @@ public function describe() { '1d' => '1d', ), 'urls' => array( - 'test' => array( - 'MATIC' => 'https://api-sandbox-matic.geniusyield.io', - ), - 'logo' => 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api' => array( - 'MATIC' => 'https://api-matic.geniusyield.io', + 'preprod' => 'http://localhost:8082', + 'mainnet' => 'http://localhost:8082', ), - 'www' => 'https://geniusyield.io', + 'www' => 'https://www.geniusyield.co/', 'doc' => array( - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ), ), 'api' => array( 'public' => array( 'get' => array( - 'ping' => 1, - 'time' => 1, - 'exchange' => 1, - 'assets' => 1, - 'markets' => 1, - 'tickers' => 1, - 'candles' => 1, - 'trades' => 1, - 'orderbook' => 1, ), ), 'private' => array( 'get' => array( - 'user' => 1, - 'wallets' => 1, - 'balances' => 1, - 'orders' => 0.1, - 'fills' => 0.1, - 'deposits' => 1, - 'withdrawals' => 1, - 'wsToken' => 1, - ), - 'post' => array( - 'wallets' => 1, - 'orders' => 0.1, - 'orders/test' => 0.1, - 'withdrawals' => 1, - ), - 'delete' => array( - 'orders' => 0.1, + 'markets' => 10, + 'trading-fees' => 10, ), ), ), + 'headers' => array( + 'X-Gate-Channel-Id' => 'ccxt', + ), 'options' => array( - 'defaultTimeInForce' => 'gtc', + 'defaultTimeInForce' => 'utc', 'defaultSelfTradePrevention' => 'cn', - 'network' => 'MATIC', + 'network' => 'mainnet', ), 'exceptions' => array( 'exact' => array( @@ -179,10 +159,10 @@ public function describe() { ), ), 'requiredCredentials' => array( - 'walletAddress' => true, - 'privateKey' => true, + 'walletAddress' => false, + 'privateKey' => false, 'apiKey' => true, - 'secret' => true, + 'secret' => false, ), 'precisionMode' => TICK_SIZE, 'paddingMode' => PAD_WITH_ZERO, @@ -190,98 +170,42 @@ public function describe() { )); } - public function price_to_precision($symbol, $price) { - // - // we override priceToPrecision to fix the following issue - // https://github.com/ccxt/ccxt/issues/13367 - // array("code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\" => all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes") - // - $market = $this->market($symbol); - $info = $this->safe_value($market, 'info', array()); - $quoteAssetPrecision = $this->safe_integer($info, 'quoteAssetPrecision'); - $price = $this->decimal_to_precision($price, ROUND, $market['precision']['price'], $this->precisionMode); - return $this->decimal_to_precision($price, TRUNCATE, $quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); - } - public function fetch_markets($params = array ()): PromiseInterface { return Async\async(function () use ($params) { /** - * retrieves data on all markets for geniusyield - * @see https://api-docs-v3.geniusyield.io/#get-markets + * retrieves data on all $markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-$markets * @param {array} [$params] extra parameters specific to the exchange API endpoint * @return {array[]} an array of objects representing market data */ - $response = Async\await($this->publicGetMarkets ($params)); - // - // array( - // array( - // "market" => "ETH-USDC", - // "type" => "hybrid", - // "status" => "activeHybrid", - // "baseAsset" => "ETH", - // "baseAssetPrecision" => "8", - // "quoteAsset" => "USDC", - // "quoteAssetPrecision" => "8", - // "makerFeeRate" => "0.0000", - // "takerFeeRate" => "0.2500", - // "takerIdexFeeRate" => "0.0500", - // "takerLiquidityProviderFeeRate" => "0.2000", - // "tickSize" => "0.01000000" - // ), - // ) - // - $response2 = Async\await($this->publicGetExchange ()); - // + $markets = Async\await($this->privateGetMarkets ($params)); + // array( // { - // "timeZone" => "UTC", - // "serverTime" => "1654460599952", - // "maticDepositContractAddress" => "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - // "maticCustodyContractAddress" => "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - // "maticUsdPrice" => "0.60", - // "gasPrice" => "180", - // "volume24hUsd" => "10015814.46", - // "totalVolumeUsd" => "1589273533.28", - // "totalTrades" => "1534904", - // "totalValueLockedUsd" => "12041929.44", - // "geniusyieldStakingValueLockedUsd" => "20133816.98", - // "geniusyieldTokenAddress" => "0x9Cb74C8032b007466865f060ad2c46145d45553D", - // "geniusyieldUsdPrice" => "0.07", - // "geniusyieldMarketCapUsd" => "48012346.00", - // "makerFeeRate" => "0.0000", - // "takerFeeRate" => "0.0025", - // "takerIdexFeeRate" => "0.0005", - // "takerLiquidityProviderFeeRate" => "0.0020", - // "makerTradeMinimum" => "10.00000000", - // "takerTradeMinimum" => "1.00000000", - // "withdrawMinimum" => "0.50000000", - // "liquidityAdditionMinimum" => "0.50000000", - // "liquidityRemovalMinimum" => "0.40000000", - // "blockConfirmationDelay" => "64" + // "market_id" => "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + // "base_asset" => "lovelace", + // "target_asset" => "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" // } - // - $maker = $this->safe_number($response2, 'makerFeeRate'); - $taker = $this->safe_number($response2, 'takerFeeRate'); - $makerMin = $this->safe_string($response2, 'makerTradeMinimum'); - $takerMin = $this->safe_string($response2, 'takerTradeMinimum'); - $minCostETH = $this->parse_number(Precise::string_min($makerMin, $takerMin)); + // ) + $fees = Async\await($this->privateGetTradingFees ()); + // { + // "flat_maker_fee" => "1000000", + // "flat_taker_fee" => "1000000", + // "percentage_maker_fee" => "0.3", + // "percentage_taker_fee" => "0.3" + // } + $maker = $this->safe_number($fees, 'percentage_maker_fee'); + $taker = $this->safe_number($fees, 'percentage_taker_fee'); $result = array(); - for ($i = 0; $i < count($response); $i++) { - $entry = $response[$i]; - $marketId = $this->safe_string($entry, 'market'); - $baseId = $this->safe_string($entry, 'baseAsset'); - $quoteId = $this->safe_string($entry, 'quoteAsset'); + for ($i = 0; $i < count($markets); $i++) { + $entry = $markets[$i]; + $marketId = $this->safe_string($entry, 'market_id'); + $baseId = $this->safe_string($entry, 'base_asset'); + $quoteId = $this->safe_string($entry, 'target_asset'); $base = $this->safe_currency_code($baseId); $quote = $this->safe_currency_code($quoteId); - $basePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'baseAssetPrecision'))); - $quotePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'quoteAssetPrecision'))); - $status = $this->safe_string($entry, 'status'); - $minCost = null; - if ($quote === 'ETH') { - $minCost = $minCostETH; - } $result[] = array( 'id' => $marketId, - 'symbol' => $base . '/' . $quote, + 'symbol' => $marketId, 'base' => $base, 'quote' => $quote, 'settle' => null, @@ -294,7 +218,7 @@ public function fetch_markets($params = array ()): PromiseInterface { 'swap' => false, 'future' => false, 'option' => false, - 'active' => ($status !== 'inactive'), + 'active' => true, 'contract' => false, 'linear' => null, 'inverse' => null, @@ -306,8 +230,8 @@ public function fetch_markets($params = array ()): PromiseInterface { 'strike' => null, 'optionType' => null, 'precision' => array( - 'amount' => $basePrecision, - 'price' => $this->safe_number($entry, 'tickSize'), + 'amount' => null, + 'price' => null, ), 'limits' => array( 'leverage' => array( @@ -315,15 +239,15 @@ public function fetch_markets($params = array ()): PromiseInterface { 'max' => null, ), 'amount' => array( - 'min' => $basePrecision, + 'min' => null, 'max' => null, ), 'price' => array( - 'min' => $quotePrecision, + 'min' => null, 'max' => null, ), 'cost' => array( - 'min' => $minCost, + 'min' => null, 'max' => null, ), ), @@ -335,1520 +259,9 @@ public function fetch_markets($params = array ()): PromiseInterface { }) (); } - public function fetch_ticker(string $symbol, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbol, $params) { - /** - * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string} $symbol unified $symbol of the $market to fetch the $ticker for - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=$ticker-structure $ticker structure~ - */ - Async\await($this->load_markets()); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - ); - // array( - // { - // "market" => "DIL-ETH", - // "time" => 1598367493008, - // "open" => "0.09695361", - // "high" => "0.10245881", - // "low" => "0.09572507", - // "close" => "0.09917079", - // "closeQuantity" => "0.71320950", - // "baseVolume" => "309.17380612", - // "quoteVolume" => "30.57633981", - // "percentChange" => "2.28", - // "numTrades" => 205, - // "ask" => "0.09910476", - // "bid" => "0.09688340", - // "sequence" => 3902 - // } - // ) - $response = Async\await($this->publicGetTickers ($this->extend($request, $params))); - $ticker = $this->safe_dict($response, 0); - return $this->parse_ticker($ticker, $market); - }) (); - } - - public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbols, $params) { - /** - * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~ - */ - Async\await($this->load_markets()); - // array( - // array( - // "market" => "DIL-ETH", - // "time" => 1598367493008, - // "open" => "0.09695361", - // "high" => "0.10245881", - // "low" => "0.09572507", - // "close" => "0.09917079", - // "closeQuantity" => "0.71320950", - // "baseVolume" => "309.17380612", - // "quoteVolume" => "30.57633981", - // "percentChange" => "2.28", - // "numTrades" => 205, - // "ask" => "0.09910476", - // "bid" => "0.09688340", - // "sequence" => 3902 - // ), ... - // ) - $response = Async\await($this->publicGetTickers ($params)); - return $this->parse_tickers($response, $symbols); - }) (); - } - - public function parse_ticker(array $ticker, ?array $market = null): array { - // { - // "market" => "DIL-ETH", - // "time" => 1598367493008, - // "open" => "0.09695361", - // "high" => "0.10245881", - // "low" => "0.09572507", - // "close" => "0.09917079", - // "closeQuantity" => "0.71320950", - // "baseVolume" => "309.17380612", - // "quoteVolume" => "30.57633981", - // "percentChange" => "2.28", - // "numTrades" => 205, - // "ask" => "0.09910476", - // "bid" => "0.09688340", - // "sequence" => 3902 - // } - $marketId = $this->safe_string($ticker, 'market'); - $market = $this->safe_market($marketId, $market, '-'); - $symbol = $market['symbol']; - $timestamp = $this->safe_integer($ticker, 'time'); - $close = $this->safe_string($ticker, 'close'); - return $this->safe_ticker(array( - 'symbol' => $symbol, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'high' => $this->safe_string($ticker, 'high'), - 'low' => $this->safe_string($ticker, 'low'), - 'bid' => $this->safe_string($ticker, 'bid'), - 'bidVolume' => null, - 'ask' => $this->safe_string($ticker, 'ask'), - 'askVolume' => null, - 'vwap' => null, - 'open' => $this->safe_string($ticker, 'open'), - 'close' => $close, - 'last' => $close, - 'previousClose' => null, - 'change' => null, - 'percentage' => $this->safe_string($ticker, 'percentChange'), - 'average' => null, - 'baseVolume' => $this->safe_string($ticker, 'baseVolume'), - 'quoteVolume' => $this->safe_string($ticker, 'quoteVolume'), - 'info' => $ticker, - ), $market); - } - - public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) { - /** - * fetches historical candlestick data containing the open, high, low, and close price, and the volume of a $market - * @see https://api-docs-v3.geniusyield.io/#get-candles - * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for - * @param {string} $timeframe the length of time each candle represents - * @param {int} [$since] timestamp in ms of the earliest candle to fetch - * @param {int} [$limit] the maximum amount of candles to fetch - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {int[][]} A list of candles ordered, open, high, low, close, volume - */ - Async\await($this->load_markets()); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - 'interval' => $timeframe, - ); - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = min ($limit, 1000); - } - $response = Async\await($this->publicGetCandles ($this->extend($request, $params))); - if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { - // array( - // array( - // "start" => 1598345580000, - // "open" => "0.09771286", - // "high" => "0.09771286", - // "low" => "0.09771286", - // "close" => "0.09771286", - // "volume" => "1.45340410", - // "sequence" => 3853 - // ), ... - // ) - return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit); - } else { - // array("nextTime":1595536440000) - return array(); - } - }) (); - } - - public function parse_ohlcv($ohlcv, ?array $market = null): array { - // { - // "start" => 1598345580000, - // "open" => "0.09771286", - // "high" => "0.09771286", - // "low" => "0.09771286", - // "close" => "0.09771286", - // "volume" => "1.45340410", - // "sequence" => 3853 - // } - $timestamp = $this->safe_integer($ohlcv, 'start'); - $open = $this->safe_number($ohlcv, 'open'); - $high = $this->safe_number($ohlcv, 'high'); - $low = $this->safe_number($ohlcv, 'low'); - $close = $this->safe_number($ohlcv, 'close'); - $volume = $this->safe_number($ohlcv, 'volume'); - return array( $timestamp, $open, $high, $low, $close, $volume ); - } - - public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbol, $since, $limit, $params) { - /** - * get the list of most recent trades for a particular $symbol - * @see https://api-docs-v3.geniusyield.io/#get-trades - * @param {string} $symbol unified $symbol of the $market to fetch trades for - * @param {int} [$since] timestamp in ms of the earliest trade to fetch - * @param {int} [$limit] the maximum amount of trades to fetch - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~ - */ - Async\await($this->load_markets()); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - ); - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = min ($limit, 1000); - } - // array( - // array( - // "fillId" => "b5467d00-b13e-3fa9-8216-dd66735550fc", - // "price" => "0.09771286", - // "quantity" => "1.45340410", - // "quoteQuantity" => "0.14201627", - // "time" => 1598345638994, - // "makerSide" => "buy", - // "sequence" => 3853 - // ), ... - // ) - $response = Async\await($this->publicGetTrades ($this->extend($request, $params))); - return $this->parse_trades($response, $market, $since, $limit); - }) (); - } - - public function parse_trade(array $trade, ?array $market = null): array { - // - // public trades - // { - // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - // "price":"0.20377008", - // "quantity":"47.58448728", - // "quoteQuantity":"9.69629509", - // "time":1642091300873, - // "makerSide":"buy", - // "type":"hybrid", // one of either => "orderBook", "hybrid", or "pool" - // "sequence":31876 - // } - // - // private trades - // { - // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - // "price":"0.20717368", - // "quantity":"15.00000000", - // "quoteQuantity":"3.10760523", - // "orderBookQuantity":"0.00000003", - // "orderBookQuoteQuantity":"0.00000001", - // "poolQuantity":"14.99999997", - // "poolQuoteQuantity":"3.10760522", - // "time":1642083351215, - // "makerSide":"sell", - // "sequence":31795, - // "market":"Genius Yield-USDC", - // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - // "side":"buy", - // "fee":"0.03749989", - // "feeAsset":"Genius Yield", - // "gas":"0.40507261", - // "liquidity":"taker", - // "type":"hybrid", - // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - // "txStatus":"mined" - // } - // - $id = $this->safe_string($trade, 'fillId'); - $priceString = $this->safe_string($trade, 'price'); - $amountString = $this->safe_string($trade, 'quantity'); - $costString = $this->safe_string($trade, 'quoteQuantity'); - $timestamp = $this->safe_integer($trade, 'time'); - $marketId = $this->safe_string($trade, 'market'); - $symbol = $this->safe_symbol($marketId, $market, '-'); - // this code handles the duality of public vs private trades - $makerSide = $this->safe_string($trade, 'makerSide'); - $oppositeSide = ($makerSide === 'buy') ? 'sell' : 'buy'; - $side = $this->safe_string($trade, 'side', $oppositeSide); - $takerOrMaker = $this->safe_string($trade, 'liquidity', 'taker'); - $feeCostString = $this->safe_string($trade, 'fee'); - $fee = null; - if ($feeCostString !== null) { - $feeCurrencyId = $this->safe_string($trade, 'feeAsset'); - $fee = array( - 'cost' => $feeCostString, - 'currency' => $this->safe_currency_code($feeCurrencyId), - ); - } - $orderId = $this->safe_string($trade, 'orderId'); - return $this->safe_trade(array( - 'info' => $trade, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'symbol' => $symbol, - 'id' => $id, - 'order' => $orderId, - 'type' => 'limit', - 'side' => $side, - 'takerOrMaker' => $takerOrMaker, - 'price' => $priceString, - 'amount' => $amountString, - 'cost' => $costString, - 'fee' => $fee, - ), $market); - } - - public function fetch_trading_fees($params = array ()): PromiseInterface { - return Async\async(function () use ($params) { - /** - * fetch the trading fees for multiple markets - * @see https://api-docs-v3.geniusyield.io/#get-api-account - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~ indexed by market symbols - */ - $this->check_required_credentials(); - Async\await($this->load_markets()); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - ); - $response = null; - $response = Async\await($this->privateGetUser ($this->extend($request, $params))); - // - // { - // "depositEnabled" => true, - // "orderEnabled" => true, - // "cancelEnabled" => true, - // "withdrawEnabled" => true, - // "totalPortfolioValueUsd" => "0.00", - // "makerFeeRate" => "0.0000", - // "takerFeeRate" => "0.0025", - // "takerIdexFeeRate" => "0.0005", - // "takerLiquidityProviderFeeRate" => "0.0020" - // } - // - $maker = $this->safe_number($response, 'makerFeeRate'); - $taker = $this->safe_number($response, 'takerFeeRate'); - $result = array(); - for ($i = 0; $i < count($this->symbols); $i++) { - $symbol = $this->symbols[$i]; - $result[$symbol] = array( - 'info' => $response, - 'symbol' => $symbol, - 'maker' => $maker, - 'taker' => $taker, - 'percentage' => true, - 'tierBased' => false, - ); - } - return $result; - }) (); - } - - public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbol, $limit, $params) { - /** - * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data - * @see https://api-docs-v3.geniusyield.io/#get-order-books - * @param {string} $symbol unified $symbol of the $market to fetch the order book for - * @param {int} [$limit] the maximum amount of order book entries to return - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols - */ - Async\await($this->load_markets()); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - 'level' => 2, - ); - if ($limit !== null) { - $request['limit'] = $limit; - } - // { - // "sequence" => 36416753, - // "bids" => array( - // array( '0.09672815', "8.22284267", 1 ), - // array( '0.09672814', "1.83685554", 1 ), - // array( '0.09672143', "4.10962617", 1 ), - // array( '0.09658884', "4.03863759", 1 ), - // array( '0.09653781', "3.35730684", 1 ), - // array( '0.09624660', "2.54163586", 1 ), - // array( '0.09617490', "1.93065030", 1 ) - // ), - // "asks" => array( - // array( '0.09910476', "3.22840154", 1 ), - // array( '0.09940587', "3.39796593", 1 ), - // array( '0.09948189', "4.25088898", 1 ), - // array( '0.09958362', "2.42195784", 1 ), - // array( '0.09974393', "4.25234367", 1 ), - // array( '0.09995250', "3.40192141", 1 ) - // ) - // } - $response = Async\await($this->publicGetOrderbook ($this->extend($request, $params))); - $nonce = $this->safe_integer($response, 'sequence'); - return array( - 'symbol' => $symbol, - 'timestamp' => null, - 'datetime' => null, - 'nonce' => $nonce, - 'bids' => $this->parse_side($response, 'bids'), - 'asks' => $this->parse_side($response, 'asks'), - ); - }) (); - } - - public function parse_side($book, $side) { - $bookSide = $this->safe_value($book, $side, array()); - $result = array(); - for ($i = 0; $i < count($bookSide); $i++) { - $order = $bookSide[$i]; - $price = $this->safe_number($order, 0); - $amount = $this->safe_number($order, 1); - $orderCount = $this->safe_integer($order, 2); - $result[] = array( $price, $amount, $orderCount ); - } - $descending = $side === 'bids'; - return $this->sort_by($result, 0, $descending); - } - - public function fetch_currencies($params = array ()): PromiseInterface { - return Async\async(function () use ($params) { - /** - * fetches all available currencies on an exchange - * @see https://api-docs-v3.geniusyield.io/#get-assets - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} an associative dictionary of currencies - */ - $response = Async\await($this->publicGetAssets ($params)); - // - // array( - // array( - // "name" => "Ethereum", - // "symbol" => "ETH", - // "contractAddress" => "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - // "assetDecimals" => "18", - // "exchangeDecimals" => "8", - // "maticPrice" => "3029.38503483" - // ), - // ) - // - $result = array(); - for ($i = 0; $i < count($response); $i++) { - $entry = $response[$i]; - $name = $this->safe_string($entry, 'name'); - $currencyId = $this->safe_string($entry, 'symbol'); - $code = $this->safe_currency_code($currencyId); - $precision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'exchangeDecimals'))); - $result[$code] = array( - 'id' => $currencyId, - 'code' => $code, - 'info' => $entry, - 'type' => null, - 'name' => $name, - 'active' => null, - 'deposit' => null, - 'withdraw' => null, - 'fee' => null, - 'precision' => $precision, - 'limits' => array( - 'amount' => array( 'min' => $precision, 'max' => null ), - 'withdraw' => array( 'min' => $precision, 'max' => null ), - ), - ); - } - return $result; - }) (); - } - - public function parse_balance($response): array { - $result = array( - 'info' => $response, - 'timestamp' => null, - 'datetime' => null, - ); - for ($i = 0; $i < count($response); $i++) { - $entry = $response[$i]; - $currencyId = $this->safe_string($entry, 'asset'); - $code = $this->safe_currency_code($currencyId); - $account = $this->account(); - $account['total'] = $this->safe_string($entry, 'quantity'); - $account['free'] = $this->safe_string($entry, 'availableForTrade'); - $account['used'] = $this->safe_string($entry, 'locked'); - $result[$code] = $account; - } - return $this->safe_balance($result); - } - - public function fetch_balance($params = array ()): PromiseInterface { - return Async\async(function () use ($params) { - /** - * query for balance and get the amount of funds available for trading or funds locked in orders - * @see https://api-docs-v3.geniusyield.io/#get-balances - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~ - */ - $this->check_required_credentials(); - Async\await($this->load_markets()); - $nonce1 = $this->uuidv1(); - $request = array( - 'nonce' => $nonce1, - 'wallet' => $this->walletAddress, - ); - // array( - // array( - // "asset" => "DIL", - // "quantity" => "0.00000000", - // "availableForTrade" => "0.00000000", - // "locked" => "0.00000000", - // "usdValue" => null - // ), ... - // ) - $extendedRequest = $this->extend($request, $params); - if ($extendedRequest['wallet'] === null) { - throw new BadRequest($this->id . ' fetchBalance() wallet is null, set $this->walletAddress or "address" in params'); - } - $response = null; - try { - $response = Async\await($this->privateGetBalances ($extendedRequest)); - } catch (Exception $e) { - if ($e instanceof InvalidAddress) { - $walletAddress = $extendedRequest['wallet']; - Async\await($this->associate_wallet($walletAddress)); - $response = Async\await($this->privateGetBalances ($extendedRequest)); - } else { - throw $e; - } - } - return $this->parse_balance($response); - }) (); - } - - public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { - return Async\async(function () use ($symbol, $since, $limit, $params) { - /** - * fetch all trades made by the user - * @see https://api-docs-v3.geniusyield.io/#get-fills - * @param {string} $symbol unified $market $symbol - * @param {int} [$since] the earliest time in ms to fetch trades for - * @param {int} [$limit] the maximum number of trades structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~ - */ - $this->check_required_credentials(); - Async\await($this->load_markets()); - $market = null; - $request = array( - 'nonce' => $this->uuidv1(), - 'wallet' => $this->walletAddress, - ); - if ($symbol !== null) { - $market = $this->market($symbol); - $request['market'] = $market['id']; - } - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = $limit; - } - // array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478762, - // "makerSide" => "sell", - // "sequence" => 5053, - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "side" => "buy", - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" - // } - // ) - $extendedRequest = $this->extend($request, $params); - if ($extendedRequest['wallet'] === null) { - throw new BadRequest($this->id . ' fetchMyTrades() $walletAddress is null, set $this->walletAddress or "address" in params'); - } - $response = null; - try { - $response = Async\await($this->privateGetFills ($extendedRequest)); - } catch (Exception $e) { - if ($e instanceof InvalidAddress) { - $walletAddress = $extendedRequest['wallet']; - Async\await($this->associate_wallet($walletAddress)); - $response = Async\await($this->privateGetFills ($extendedRequest)); - } else { - throw $e; - } - } - return $this->parse_trades($response, $market, $since, $limit); - }) (); - } - - public function fetch_order(string $id, ?string $symbol = null, $params = array ()) { - return Async\async(function () use ($id, $symbol, $params) { - /** - * fetches information on an order made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} $symbol unified $symbol of the market the order was made in - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ - */ - $request = array( - 'orderId' => $id, - ); - return Async\await($this->fetch_orders_helper($symbol, null, null, $this->extend($request, $params))); - }) (); - } - - public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbol, $since, $limit, $params) { - /** - * fetch all unfilled currently open orders - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} $symbol unified market $symbol - * @param {int} [$since] the earliest time in ms to fetch open orders for - * @param {int} [$limit] the maximum number of open orders structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ - */ - $request = array( - 'closed' => false, - ); - return Async\await($this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params))); - }) (); - } - - public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($symbol, $since, $limit, $params) { - /** - * fetches information on multiple closed orders made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} $symbol unified market $symbol of the market orders were made in - * @param {int} [$since] the earliest time in ms to fetch orders for - * @param {int} [$limit] the maximum number of order structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ - */ - $request = array( - 'closed' => true, - ); - return Async\await($this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params))); - }) (); - } - - public function fetch_orders_helper(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { - return Async\async(function () use ($symbol, $since, $limit, $params) { - Async\await($this->load_markets()); - $request = array( - 'nonce' => $this->uuidv1(), - 'wallet' => $this->walletAddress, - ); - $market = null; - if ($symbol !== null) { - $market = $this->market($symbol); - $request['market'] = $market['id']; - } - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = $limit; - } - $response = Async\await($this->privateGetOrders ($this->extend($request, $params))); - // fetchClosedOrders / fetchOpenOrders - // array( - // { - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "avgExecutionPrice" => "0.09905990", - // "price" => "1.00000000", - // "fills" => array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" - // } - // ) - // } - // ) - // fetchOrder - // { $market => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "avgExecutionPrice" => "0.09905990", - // "price" => "1.00000000", - // "fills": - // array( { fillId => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" } ) } - if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { - return $this->parse_orders($response, $market, $since, $limit); - } else { - return $this->parse_order($response, $market); - } - }) (); - } - - public function parse_order_status(?string $status) { - // https://docs.geniusyield.io/#order-states-amp-lifecycle - $statuses = array( - 'active' => 'open', - 'partiallyFilled' => 'open', - 'rejected' => 'canceled', - 'filled' => 'closed', - ); - return $this->safe_string($statuses, $status, $status); - } - - public function parse_order(array $order, ?array $market = null): array { - // - // { - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "avgExecutionPrice" => "0.09905990", - // "price" => "1.00000000", - // "fills" => array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" - // } - // ) - // } - // - $timestamp = $this->safe_integer($order, 'time'); - $fills = $this->safe_value($order, 'fills', array()); - $id = $this->safe_string($order, 'orderId'); - $clientOrderId = $this->safe_string($order, 'clientOrderId'); - $marketId = $this->safe_string($order, 'market'); - $side = $this->safe_string($order, 'side'); - $symbol = $this->safe_symbol($marketId, $market, '-'); - $type = $this->safe_string($order, 'type'); - $amount = $this->safe_string($order, 'originalQuantity'); - $filled = $this->safe_string($order, 'executedQuantity'); - $average = $this->safe_string($order, 'avgExecutionPrice'); - $price = $this->safe_string($order, 'price'); - $rawStatus = $this->safe_string($order, 'status'); - $timeInForce = $this->safe_string_upper($order, 'timeInForce'); - $status = $this->parse_order_status($rawStatus); - return $this->safe_order(array( - 'info' => $order, - 'id' => $id, - 'clientOrderId' => $clientOrderId, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'lastTradeTimestamp' => null, - 'symbol' => $symbol, - 'type' => $type, - 'timeInForce' => $timeInForce, - 'postOnly' => null, - 'side' => $side, - 'price' => $price, - 'stopPrice' => null, - 'triggerPrice' => null, - 'amount' => $amount, - 'cost' => null, - 'average' => $average, - 'filled' => $filled, - 'remaining' => null, - 'status' => $status, - 'fee' => null, - 'trades' => $fills, - ), $market); - } - - public function associate_wallet($walletAddress, $params = array ()) { - return Async\async(function () use ($walletAddress, $params) { - $nonce = $this->uuidv1(); - $noPrefix = $this->remove0x_prefix($walletAddress); - $byteArray = array( - $this->base16_to_binary($nonce), - $this->base16_to_binary($noPrefix), - ); - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - // { - // "address" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "totalPortfolioValueUsd" => "0.00", - // "time" => 1598468353626 - // } - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $walletAddress, - ), - 'signature' => $signature, - ); - $result = Async\await($this->privatePostWallets ($request)); - return $result; - }) (); - } - - public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) { - return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) { - /** - * create a trade order, https://docs.geniusyield.io/#create-order - * @see https://api-docs-v3.geniusyield.io/#create-order - * @param {string} $symbol unified $symbol of the $market to create an order in - * @param {string} $type 'market' or 'limit' - * @param {string} $side 'buy' or 'sell' - * @param {float} $amount how much of currency you want to trade in units of base currency - * @param {float} [$price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in $market orders - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @param {bool} [$params->test] set to true to test an order, no order will be created but the $request will be validated - * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~ - */ - $this->check_required_credentials(); - Async\await($this->load_markets()); - $testOrder = $this->safe_bool($params, 'test', false); - $params = $this->omit($params, 'test'); - $market = $this->market($symbol); - $nonce = $this->uuidv1(); - $typeEnum = null; - $stopLossTypeEnums = array( - 'stopLoss' => 3, - 'stopLossLimit' => 4, - 'takeProfit' => 5, - 'takeProfitLimit' => 6, - ); - $stopPriceString = null; - if (($type === 'stopLossLimit') || ($type === 'takeProfitLimit') || (is_array($params) && array_key_exists('stopPrice', $params))) { - if (!(is_array($params) && array_key_exists('stopPrice', $params))) { - throw new BadRequest($this->id . ' createOrder() stopPrice is a required parameter for ' . $type . 'orders'); - } - $stopPriceString = $this->price_to_precision($symbol, $params['stopPrice']); - } - $limitTypeEnums = array( - 'limit' => 1, - 'limitMaker' => 2, - ); - $priceString = null; - $typeLower = strtolower($type); - $limitOrder = mb_strpos($typeLower, 'limit') !== false; - if (is_array($limitTypeEnums) && array_key_exists($type, $limitTypeEnums)) { - $typeEnum = $limitTypeEnums[$type]; - $priceString = $this->price_to_precision($symbol, $price); - } elseif (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { - $typeEnum = $stopLossTypeEnums[$type]; - $priceString = $this->price_to_precision($symbol, $price); - } elseif ($type === 'market') { - $typeEnum = 0; - } else { - throw new BadRequest($this->id . ' ' . $type . ' is not a valid order type'); - } - $amountEnum = 0; // base quantity - if (is_array($params) && array_key_exists('quoteOrderQuantity', $params)) { - if ($type !== 'market') { - throw new NotSupported($this->id . ' createOrder() quoteOrderQuantity is not supported for ' . $type . ' orders, only supported for $market orders'); - } - $amountEnum = 1; - $amount = $this->safe_number($params, 'quoteOrderQuantity'); - } - $sideEnum = ($side === 'buy') ? 0 : 1; - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $network = $this->safe_string($this->options, 'network', 'ETH'); - $orderVersion = $this->get_supported_mapping($network, array( - 'ETH' => 1, - 'BSC' => 2, - 'MATIC' => 4, - )); - $amountString = $this->amount_to_precision($symbol, $amount); - // https://docs.geniusyield.io/#time-in-force - $timeInForceEnums = array( - 'gtc' => 0, - 'ioc' => 2, - 'fok' => 3, - ); - $defaultTimeInForce = $this->safe_string($this->options, 'defaultTimeInForce', 'gtc'); - $timeInForce = $this->safe_string($params, 'timeInForce', $defaultTimeInForce); - $timeInForceEnum = null; - if (is_array($timeInForceEnums) && array_key_exists($timeInForce, $timeInForceEnums)) { - $timeInForceEnum = $timeInForceEnums[$timeInForce]; - } else { - $allOptions = is_array($timeInForceEnums) ? array_keys($timeInForceEnums) : array(); - $asString = implode(', ', $allOptions); - throw new BadRequest($this->id . ' ' . $timeInForce . ' is not a valid $timeInForce, please choose one of ' . $asString); - } - // https://docs.geniusyield.io/#self-trade-prevention - $selfTradePreventionEnums = array( - 'dc' => 0, - 'co' => 1, - 'cn' => 2, - 'cb' => 3, - ); - $defaultSelfTradePrevention = $this->safe_string($this->options, 'defaultSelfTradePrevention', 'cn'); - $selfTradePrevention = $this->safe_string($params, 'selfTradePrevention', $defaultSelfTradePrevention); - $selfTradePreventionEnum = null; - if (is_array($selfTradePreventionEnums) && array_key_exists($selfTradePrevention, $selfTradePreventionEnums)) { - $selfTradePreventionEnum = $selfTradePreventionEnums[$selfTradePrevention]; - } else { - $allOptions = is_array($selfTradePreventionEnums) ? array_keys($selfTradePreventionEnums) : array(); - $asString = implode(', ', $allOptions); - throw new BadRequest($this->id . ' ' . $selfTradePrevention . ' is not a valid $selfTradePrevention, please choose one of ' . $asString); - } - $byteArray = [ - $this->number_to_be($orderVersion, 1), - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - $this->encode($market['id']), - $this->number_to_be($typeEnum, 1), - $this->number_to_be($sideEnum, 1), - $this->encode($amountString), - $this->number_to_be($amountEnum, 1), - ]; - if ($limitOrder) { - $encodedPrice = $this->encode($priceString); - $byteArray[] = $encodedPrice; - } - if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { - $encodedPrice = $this->encode($stopPriceString || $priceString); - $byteArray[] = $encodedPrice; - } - $clientOrderId = $this->safe_string($params, 'clientOrderId'); - if ($clientOrderId !== null) { - $byteArray[] = $this->encode($clientOrderId); - } - $after = array( - $this->number_to_be($timeInForceEnum, 1), - $this->number_to_be($selfTradePreventionEnum, 1), - $this->number_to_be(0, 8), // unused - ); - $allBytes = $this->array_concat($byteArray, $after); - $binary = $this->binary_concat_array($allBytes); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'market' => $market['id'], - 'side' => $side, - 'type' => $type, - 'wallet' => $this->walletAddress, - 'selfTradePrevention' => $selfTradePrevention, - ), - 'signature' => $signature, - ); - if ($type !== 'market') { - $request['parameters']['timeInForce'] = $timeInForce; - } - if ($limitOrder) { - $request['parameters']['price'] = $priceString; - } - if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { - $request['parameters']['stopPrice'] = $stopPriceString || $priceString; - } - if ($amountEnum === 0) { - $request['parameters']['quantity'] = $amountString; - } else { - $request['parameters']['quoteOrderQuantity'] = $amountString; - } - if ($clientOrderId !== null) { - $request['parameters']['clientOrderId'] = $clientOrderId; - } - // { - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "price" => "1.00000000", - // "fills" => array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txStatus" => "pending" - // } - // ), - // "avgExecutionPrice" => "0.09905990" - // } - // we don't use extend here because it is a signed endpoint - $response = null; - if ($testOrder) { - $response = Async\await($this->privatePostOrdersTest ($request)); - } else { - $response = Async\await($this->privatePostOrders ($request)); - } - return $this->parse_order($response, $market); - }) (); - } - - public function withdraw(string $code, float $amount, string $address, $tag = null, $params = array ()) { - return Async\async(function () use ($code, $amount, $address, $tag, $params) { - /** - * make a withdrawal - * @see https://api-docs-v3.geniusyield.io/#withdraw-funds - * @param {string} $code unified $currency $code - * @param {float} $amount the $amount to withdraw - * @param {string} $address the $address to withdraw to - * @param {string} $tag - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~ - */ - list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params); - $this->check_required_credentials(); - Async\await($this->load_markets()); - $nonce = $this->uuidv1(); - $amountString = $this->currency_to_precision($code, $amount); - $currency = $this->currency($code); - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $byteArray = [ - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - $this->encode($currency['id']), - $this->encode($amountString), - $this->number_to_be(1, 1), // bool set to true - ]; - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $address, - 'asset' => $currency['id'], - 'quantity' => $amountString, - ), - 'signature' => $signature, - ); - $response = Async\await($this->privatePostWithdrawals ($request)); - // - // { - // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset" => "ETH", - // "assetContractAddress" => "0x0000000000000000000000000000000000000000", - // "quantity" => "0.20000000", - // "time" => 1598962883190, - // "fee" => "0.00024000", - // "txStatus" => "pending", - // "txId" => null - // } - // - return $this->parse_transaction($response, $currency); - }) (); - } - - public function cancel_all_orders(?string $symbol = null, $params = array ()) { - return Async\async(function () use ($symbol, $params) { - /** - * cancel all open orders - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are cancelled when $symbol is not null - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ - */ - $this->check_required_credentials(); - Async\await($this->load_markets()); - $market = null; - if ($symbol !== null) { - $market = $this->market($symbol); - } - $nonce = $this->uuidv1(); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - ), - ); - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $byteArray = array( - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - ); - if ($market !== null) { - $byteArray[] = $this->encode($market['id']); - $request['parameters']['market'] = $market['id']; - } - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request['signature'] = $signature; - // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) - $response = Async\await($this->privateDeleteOrders ($this->extend($request, $params))); - return $this->parse_orders($response, $market); - }) (); - } - - public function cancel_order(string $id, ?string $symbol = null, $params = array ()) { - return Async\async(function () use ($id, $symbol, $params) { - /** - * cancels an open order - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} $id order $id - * @param {string} $symbol unified $symbol of the $market the order was made in - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ - */ - $this->check_required_credentials(); - Async\await($this->load_markets()); - $market = null; - if ($symbol !== null) { - $market = $this->market($symbol); - } - $nonce = $this->uuidv1(); - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $byteArray = array( - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - $this->encode($id), - ); - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - 'orderId' => $id, - ), - 'signature' => $signature, - ); - // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) - $response = Async\await($this->privateDeleteOrders ($this->extend($request, $params))); - $canceledOrder = $this->safe_dict($response, 0); - return $this->parse_order($canceledOrder, $market); - }) (); - } - - public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) { - $errorCode = $this->safe_string($response, 'code'); - $message = $this->safe_string($response, 'message'); - if ($errorCode !== null) { - $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $message); - throw new ExchangeError($this->id . ' ' . $message); - } - return null; - } - - public function fetch_deposit(string $id, ?string $code = null, $params = array ()) { - return Async\async(function () use ($id, $code, $params) { - /** - * fetch information on a deposit - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} $id deposit $id - * @param {string} $code not used by geniusyield fetchDeposit () - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ - */ - Async\await($this->load_markets()); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - 'depositId' => $id, - ); - $response = Async\await($this->privateGetDeposits ($this->extend($request, $params))); - return $this->parse_transaction($response); - }) (); - } - - public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($code, $since, $limit, $params) { - /** - * fetch all deposits made to an account - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} $code unified currency $code - * @param {int} [$since] the earliest time in ms to fetch deposits for - * @param {int} [$limit] the maximum number of deposits structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ - */ - $params = $this->extend(array( - 'method' => 'privateGetDeposits', - ), $params); - return Async\await($this->fetch_transactions_helper($code, $since, $limit, $params)); - }) (); - } - - public function fetch_status($params = array ()) { - return Async\async(function () use ($params) { - /** - * the latest known information on the availability of the exchange API - * @see https://api-docs-v3.geniusyield.io/#get-ping - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=exchange-status-structure status structure~ - */ - $response = Async\await($this->publicGetPing ($params)); - return array( - 'status' => 'ok', // if there's no Errors, status = 'ok' - 'updated' => null, - 'eta' => null, - 'url' => null, - 'info' => $response, - ); - }) (); - } - - public function fetch_time($params = array ()) { - return Async\async(function () use ($params) { - /** - * fetches the current integer timestamp in milliseconds from the exchange server - * @see https://api-docs-v3.geniusyield.io/#get-time - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {int} the current integer timestamp in milliseconds from the exchange server - */ - $response = Async\await($this->publicGetTime ($params)); - // - // array( serverTime => "1655258263236" ) - // - return $this->safe_integer($response, 'serverTime'); - }) (); - } - - public function fetch_withdrawal(string $id, ?string $code = null, $params = array ()) { - return Async\async(function () use ($id, $code, $params) { - /** - * fetch data on a currency withdrawal via the withdrawal $id - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} $id withdrawal $id - * @param {string} $code not used by geniusyield.fetchWithdrawal - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ - */ - Async\await($this->load_markets()); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - 'withdrawalId' => $id, - ); - $response = Async\await($this->privateGetWithdrawals ($this->extend($request, $params))); - return $this->parse_transaction($response); - }) (); - } - - public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface { - return Async\async(function () use ($code, $since, $limit, $params) { - /** - * fetch all withdrawals made from an account - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} $code unified currency $code - * @param {int} [$since] the earliest time in ms to fetch withdrawals for - * @param {int} [$limit] the maximum number of withdrawals structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ - */ - $params = $this->extend(array( - 'method' => 'privateGetWithdrawals', - ), $params); - return Async\await($this->fetch_transactions_helper($code, $since, $limit, $params)); - }) (); - } - - public function fetch_transactions_helper(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()) { - return Async\async(function () use ($code, $since, $limit, $params) { - Async\await($this->load_markets()); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - ); - $currency = null; - if ($code !== null) { - $currency = $this->currency($code); - $request['asset'] = $currency['id']; - } - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = $limit; - } - // array( - // { - // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - // "asset" => "ETH", - // "quantity" => "1.00000000", - // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime" => 1598865853000, - // "confirmationTime" => 1598865930231 - // } - // ) - $method = $params['method']; - $params = $this->omit($params, 'method'); - $response = null; - if ($method === 'privateGetDeposits') { - $response = Async\await($this->privateGetDeposits ($this->extend($request, $params))); - } elseif ($method === 'privateGetWithdrawals') { - $response = Async\await($this->privateGetWithdrawals ($this->extend($request, $params))); - } else { - throw new NotSupported($this->id . ' fetchTransactionsHelper() not support this method'); - } - return $this->parse_transactions($response, $currency, $since, $limit); - }) (); - } - - public function parse_transaction_status(?string $status) { - $statuses = array( - 'mined' => 'ok', - ); - return $this->safe_string($statuses, $status, $status); - } - - public function parse_transaction(array $transaction, ?array $currency = null): array { - // - // fetchDeposits - // - // { - // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - // "asset" => "ETH", - // "quantity" => "1.00000000", - // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime" => 1598865853000, - // "confirmationTime" => 1598865930231 - // } - // - // fetchWithdrwalas - // - // { - // "withdrawalId" => "a62d8760-ec4d-11ea-9fa6-47904c19499b", - // "asset" => "ETH", - // "assetContractAddress" => "0x0000000000000000000000000000000000000000", - // "quantity" => "0.20000000", - // "time" => 1598962883288, - // "fee" => "0.00024000", - // "txId" => "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - // "txStatus" => "mined" - // } - // - // withdraw - // - // { - // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset" => "ETH", - // "assetContractAddress" => "0x0000000000000000000000000000000000000000", - // "quantity" => "0.20000000", - // "time" => 1598962883190, - // "fee" => "0.00024000", - // "txStatus" => "pending", - // "txId" => null - // } - // - $type = null; - if (is_array($transaction) && array_key_exists('depositId', $transaction)) { - $type = 'deposit'; - } elseif ((is_array($transaction) && array_key_exists('withdrawId', $transaction)) || (is_array($transaction) && array_key_exists('withdrawalId', $transaction))) { - $type = 'withdrawal'; - } - $id = $this->safe_string_2($transaction, 'depositId', 'withdrawId'); - $id = $this->safe_string($transaction, 'withdrawalId', $id); - $code = $this->safe_currency_code($this->safe_string($transaction, 'asset'), $currency); - $amount = $this->safe_number($transaction, 'quantity'); - $txid = $this->safe_string($transaction, 'txId'); - $timestamp = $this->safe_integer_2($transaction, 'txTime', 'time'); - $fee = null; - if (is_array($transaction) && array_key_exists('fee', $transaction)) { - $fee = array( - 'cost' => $this->safe_number($transaction, 'fee'), - 'currency' => 'ETH', - ); - } - $rawStatus = $this->safe_string($transaction, 'txStatus'); - $status = $this->parse_transaction_status($rawStatus); - $updated = $this->safe_integer($transaction, 'confirmationTime'); - return array( - 'info' => $transaction, - 'id' => $id, - 'txid' => $txid, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'network' => null, - 'address' => null, - 'addressTo' => null, - 'addressFrom' => null, - 'tag' => null, - 'tagTo' => null, - 'tagFrom' => null, - 'type' => $type, - 'amount' => $amount, - 'currency' => $code, - 'status' => $status, - 'updated' => $updated, - 'comment' => null, - 'internal' => null, - 'fee' => $fee, - ); - } - - public function calculate_rate_limiter_cost($api, $method, $path, $params, $config = array ()) { - $hasApiKey = ($this->apiKey !== null); - $hasSecret = ($this->secret !== null); - $hasWalletAddress = ($this->walletAddress !== null); - $hasPrivateKey = ($this->privateKey !== null); - $defaultCost = $this->safe_value($config, 'cost', 1); - $authenticated = $hasApiKey && $hasSecret && $hasWalletAddress && $hasPrivateKey; - return $authenticated ? ($defaultCost / 2) : $defaultCost; - } - - public function fetch_deposit_address(?string $code = null, $params = array ()) { - return Async\async(function () use ($code, $params) { - /** - * fetch the Polygon address of the wallet - * @see https://api-docs-v3.geniusyield.io/#get-wallets - * @param {string} $code not used by geniusyield - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~ - */ - $request = array(); - $request['nonce'] = $this->uuidv1(); - $response = Async\await($this->privateGetWallets ($this->extend($request, $params))); - // - // array( - // array( - // address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd => "0.00", - // time => "1678342148086" - // ), - // { - // address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd => "15.90", - // time => "1691697811659" - // } - // ) - // - return $this->parse_deposit_address($response); - }) (); - } - - public function parse_deposit_address($depositAddress, ?array $currency = null) { - // - // array( - // array( - // $address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd => "0.00", - // time => "1678342148086" - // ), - // { - // $address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd => "15.90", - // time => "1691697811659" - // } - // ) - // - $length = count($depositAddress); - $entry = $this->safe_dict($depositAddress, $length - 1); - $address = $this->safe_string($entry, 'address'); - $this->check_address($address); - return array( - 'info' => $depositAddress, - 'currency' => null, - 'address' => $address, - 'tag' => null, - 'network' => 'MATIC', - ); - } - public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) { - $network = $this->safe_string($this->options, 'network', 'ETH'); - $version = $this->safe_string($this->options, 'version', 'v1'); + $network = $this->safe_string($this->options, 'network', 'mainnet'); + $version = $this->safe_string($this->options, 'version', 'v0'); $url = $this->urls['api'][$network] . '/' . $version . '/' . $path; $keys = is_array($params) ? array_keys($params) : array(); $length = count($keys); @@ -1865,52 +278,8 @@ public function sign($path, $api = 'public', $method = 'GET', $params = array () 'Content-Type' => 'application/json', ); if ($this->apiKey !== null) { - $headers['Genius Yield-API-Key'] = $this->apiKey; - } - if ($api === 'private') { - $payload = null; - if ($method === 'GET') { - $payload = $query; - } else { - $payload = $body; - } - $headers['Genius Yield-HMAC-Signature'] = $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha256', 'hex'); + $headers['api-key'] = $this->apiKey; } return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers ); } - - public function remove0x_prefix($hexData) { - if (mb_substr($hexData, 0, 2 - 0) === '0x') { - return mb_substr($hexData, 2); - } else { - return $hexData; - } - } - - public function hash_message($message) { - // takes a hex encoded $message - $binaryMessage = $this->base16_to_binary($this->remove0x_prefix($message)); - $prefix = $this->encode('\x19Ethereum Signed Message:\n' . $binaryMessage->byteLength); - return '0x' . $this->hash($this->binary_concat($prefix, $binaryMessage), 'keccak', 'hex'); - } - - public function sign_hash($hash, $privateKey) { - $signature = $this->ecdsa(mb_substr($hash, -64), mb_substr($privateKey, -64), 'secp256k1', null); - return array( - 'r' => '0x' . $signature['r'], - 's' => '0x' . $signature['s'], - 'v' => 27 . $signature['v'], - ); - } - - public function sign_message($message, $privateKey) { - return $this->sign_hash($this->hash_message($message), mb_substr($privateKey, -64)); - } - - public function sign_message_string($message, $privateKey) { - // still takes the input hex string - // same but returns a string instead of an object - $signature = $this->sign_message($message, $privateKey); - return $signature['r'] . $this->remove0x_prefix($signature['s']) . bin2hex($this->number_to_be($signature['v'], 1)); - } } diff --git a/php/geniusyield.php b/php/geniusyield.php index 49ad0881fc14..fe54ecf34ac0 100644 --- a/php/geniusyield.php +++ b/php/geniusyield.php @@ -10,6 +10,15 @@ class geniusyield extends Exchange { + public function safe_market(?string $marketId = null, ?array $market = null, ?string $delimiter = null, ?string $marketType = null): array { + $isOption = ($marketId !== null) && ((mb_strpos($marketId, '-C') > -1) || (mb_strpos($marketId, '-P') > -1)); + if ($isOption && !(is_array($this->markets_by_id) && array_key_exists($marketId, $this->markets_by_id))) { + // handle expired option contracts + return $this->create_expired_option_market($marketId); + } + return parent::safe_market($marketId, $market, $delimiter, $marketType); + } + public function describe() { return $this->deep_extend(parent::describe(), array( 'id' => 'geniusyield', @@ -29,7 +38,7 @@ public function describe() { 'future' => false, 'option' => false, 'addMargin' => false, - 'cancelAllOrders' => true, + 'cancelAllOrders' => false, 'cancelOrder' => true, 'cancelOrders' => false, 'closeAllPositions' => false, @@ -37,21 +46,21 @@ public function describe() { 'createDepositAddress' => false, 'createOrder' => true, 'createReduceOnlyOrder' => false, - 'createStopLimitOrder' => true, - 'createStopMarketOrder' => true, - 'createStopOrder' => true, + 'createStopLimitOrder' => false, + 'createStopMarketOrder' => false, + 'createStopOrder' => false, 'fetchBalance' => true, 'fetchBorrowRateHistories' => false, 'fetchBorrowRateHistory' => false, - 'fetchClosedOrders' => true, + 'fetchClosedOrders' => false, 'fetchCrossBorrowRate' => false, 'fetchCrossBorrowRates' => false, - 'fetchCurrencies' => true, - 'fetchDeposit' => true, - 'fetchDepositAddress' => true, + 'fetchCurrencies' => false, + 'fetchDeposit' => false, + 'fetchDepositAddress' => false, 'fetchDepositAddresses' => false, 'fetchDepositAddressesByNetwork' => false, - 'fetchDeposits' => true, + 'fetchDeposits' => false, 'fetchFundingHistory' => false, 'fetchFundingRate' => false, 'fetchFundingRateHistory' => false, @@ -64,13 +73,13 @@ public function describe() { 'fetchMarginMode' => false, 'fetchMarkets' => true, 'fetchMarkOHLCV' => false, - 'fetchMyTrades' => true, - 'fetchOHLCV' => true, + 'fetchMyTrades' => false, + 'fetchOHLCV' => false, 'fetchOpenInterestHistory' => false, - 'fetchOpenOrders' => true, - 'fetchOrder' => true, - 'fetchOrderBook' => true, - 'fetchOrders' => false, + 'fetchOpenOrders' => false, + 'fetchOrder' => false, + 'fetchOrderBook' => false, + 'fetchOrders' => true, 'fetchPosition' => false, 'fetchPositionHistory' => false, 'fetchPositionMode' => false, @@ -79,23 +88,23 @@ public function describe() { 'fetchPositionsHistory' => false, 'fetchPositionsRisk' => false, 'fetchPremiumIndexOHLCV' => false, - 'fetchStatus' => true, - 'fetchTicker' => true, - 'fetchTickers' => true, - 'fetchTime' => true, - 'fetchTrades' => true, + 'fetchStatus' => false, + 'fetchTicker' => false, + 'fetchTickers' => false, + 'fetchTime' => false, + 'fetchTrades' => false, 'fetchTradingFee' => false, - 'fetchTradingFees' => true, + 'fetchTradingFees' => false, 'fetchTransactions' => false, - 'fetchWithdrawal' => true, - 'fetchWithdrawals' => true, + 'fetchWithdrawal' => false, + 'fetchWithdrawals' => false, 'reduceMargin' => false, - 'sandbox' => true, + 'sandbox' => false, 'setLeverage' => false, 'setMarginMode' => false, 'setPositionMode' => false, 'transfer' => false, - 'withdraw' => true, + 'withdraw' => false, ), 'timeframes' => array( '1m' => '1m', @@ -107,58 +116,34 @@ public function describe() { '1d' => '1d', ), 'urls' => array( - 'test' => array( - 'MATIC' => 'https://api-sandbox-matic.geniusyield.io', - ), - 'logo' => 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api' => array( - 'MATIC' => 'https://api-matic.geniusyield.io', + 'preprod' => 'http://localhost:8082', + 'mainnet' => 'http://localhost:8082', ), - 'www' => 'https://geniusyield.io', + 'www' => 'https://www.geniusyield.co/', 'doc' => array( - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ), ), 'api' => array( 'public' => array( 'get' => array( - 'ping' => 1, - 'time' => 1, - 'exchange' => 1, - 'assets' => 1, - 'markets' => 1, - 'tickers' => 1, - 'candles' => 1, - 'trades' => 1, - 'orderbook' => 1, ), ), 'private' => array( 'get' => array( - 'user' => 1, - 'wallets' => 1, - 'balances' => 1, - 'orders' => 0.1, - 'fills' => 0.1, - 'deposits' => 1, - 'withdrawals' => 1, - 'wsToken' => 1, - ), - 'post' => array( - 'wallets' => 1, - 'orders' => 0.1, - 'orders/test' => 0.1, - 'withdrawals' => 1, - ), - 'delete' => array( - 'orders' => 0.1, + 'markets' => 10, + 'trading-fees' => 10, ), ), ), + 'headers' => array( + 'X-Gate-Channel-Id' => 'ccxt', + ), 'options' => array( - 'defaultTimeInForce' => 'gtc', + 'defaultTimeInForce' => 'utc', 'defaultSelfTradePrevention' => 'cn', - 'network' => 'MATIC', + 'network' => 'mainnet', ), 'exceptions' => array( 'exact' => array( @@ -172,10 +157,10 @@ public function describe() { ), ), 'requiredCredentials' => array( - 'walletAddress' => true, - 'privateKey' => true, + 'walletAddress' => false, + 'privateKey' => false, 'apiKey' => true, - 'secret' => true, + 'secret' => false, ), 'precisionMode' => TICK_SIZE, 'paddingMode' => PAD_WITH_ZERO, @@ -183,97 +168,41 @@ public function describe() { )); } - public function price_to_precision($symbol, $price) { - // - // we override priceToPrecision to fix the following issue - // https://github.com/ccxt/ccxt/issues/13367 - // array("code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\" => all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes") - // - $market = $this->market($symbol); - $info = $this->safe_value($market, 'info', array()); - $quoteAssetPrecision = $this->safe_integer($info, 'quoteAssetPrecision'); - $price = $this->decimal_to_precision($price, ROUND, $market['precision']['price'], $this->precisionMode); - return $this->decimal_to_precision($price, TRUNCATE, $quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO); - } - public function fetch_markets($params = array ()): array { /** - * retrieves data on all markets for geniusyield - * @see https://api-docs-v3.geniusyield.io/#get-markets + * retrieves data on all $markets for geniusyield + * @see https://api-docs-v3.geniusyield.io/#get-$markets * @param {array} [$params] extra parameters specific to the exchange API endpoint * @return {array[]} an array of objects representing market data */ - $response = $this->publicGetMarkets ($params); - // - // array( - // array( - // "market" => "ETH-USDC", - // "type" => "hybrid", - // "status" => "activeHybrid", - // "baseAsset" => "ETH", - // "baseAssetPrecision" => "8", - // "quoteAsset" => "USDC", - // "quoteAssetPrecision" => "8", - // "makerFeeRate" => "0.0000", - // "takerFeeRate" => "0.2500", - // "takerIdexFeeRate" => "0.0500", - // "takerLiquidityProviderFeeRate" => "0.2000", - // "tickSize" => "0.01000000" - // ), - // ) - // - $response2 = $this->publicGetExchange (); - // + $markets = $this->privateGetMarkets ($params); + // array( // { - // "timeZone" => "UTC", - // "serverTime" => "1654460599952", - // "maticDepositContractAddress" => "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - // "maticCustodyContractAddress" => "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - // "maticUsdPrice" => "0.60", - // "gasPrice" => "180", - // "volume24hUsd" => "10015814.46", - // "totalVolumeUsd" => "1589273533.28", - // "totalTrades" => "1534904", - // "totalValueLockedUsd" => "12041929.44", - // "geniusyieldStakingValueLockedUsd" => "20133816.98", - // "geniusyieldTokenAddress" => "0x9Cb74C8032b007466865f060ad2c46145d45553D", - // "geniusyieldUsdPrice" => "0.07", - // "geniusyieldMarketCapUsd" => "48012346.00", - // "makerFeeRate" => "0.0000", - // "takerFeeRate" => "0.0025", - // "takerIdexFeeRate" => "0.0005", - // "takerLiquidityProviderFeeRate" => "0.0020", - // "makerTradeMinimum" => "10.00000000", - // "takerTradeMinimum" => "1.00000000", - // "withdrawMinimum" => "0.50000000", - // "liquidityAdditionMinimum" => "0.50000000", - // "liquidityRemovalMinimum" => "0.40000000", - // "blockConfirmationDelay" => "64" + // "market_id" => "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + // "base_asset" => "lovelace", + // "target_asset" => "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" // } - // - $maker = $this->safe_number($response2, 'makerFeeRate'); - $taker = $this->safe_number($response2, 'takerFeeRate'); - $makerMin = $this->safe_string($response2, 'makerTradeMinimum'); - $takerMin = $this->safe_string($response2, 'takerTradeMinimum'); - $minCostETH = $this->parse_number(Precise::string_min($makerMin, $takerMin)); + // ) + $fees = $this->privateGetTradingFees (); + // { + // "flat_maker_fee" => "1000000", + // "flat_taker_fee" => "1000000", + // "percentage_maker_fee" => "0.3", + // "percentage_taker_fee" => "0.3" + // } + $maker = $this->safe_number($fees, 'percentage_maker_fee'); + $taker = $this->safe_number($fees, 'percentage_taker_fee'); $result = array(); - for ($i = 0; $i < count($response); $i++) { - $entry = $response[$i]; - $marketId = $this->safe_string($entry, 'market'); - $baseId = $this->safe_string($entry, 'baseAsset'); - $quoteId = $this->safe_string($entry, 'quoteAsset'); + for ($i = 0; $i < count($markets); $i++) { + $entry = $markets[$i]; + $marketId = $this->safe_string($entry, 'market_id'); + $baseId = $this->safe_string($entry, 'base_asset'); + $quoteId = $this->safe_string($entry, 'target_asset'); $base = $this->safe_currency_code($baseId); $quote = $this->safe_currency_code($quoteId); - $basePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'baseAssetPrecision'))); - $quotePrecision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'quoteAssetPrecision'))); - $status = $this->safe_string($entry, 'status'); - $minCost = null; - if ($quote === 'ETH') { - $minCost = $minCostETH; - } $result[] = array( 'id' => $marketId, - 'symbol' => $base . '/' . $quote, + 'symbol' => $marketId, 'base' => $base, 'quote' => $quote, 'settle' => null, @@ -286,7 +215,7 @@ public function fetch_markets($params = array ()): array { 'swap' => false, 'future' => false, 'option' => false, - 'active' => ($status !== 'inactive'), + 'active' => true, 'contract' => false, 'linear' => null, 'inverse' => null, @@ -298,8 +227,8 @@ public function fetch_markets($params = array ()): array { 'strike' => null, 'optionType' => null, 'precision' => array( - 'amount' => $basePrecision, - 'price' => $this->safe_number($entry, 'tickSize'), + 'amount' => null, + 'price' => null, ), 'limits' => array( 'leverage' => array( @@ -307,15 +236,15 @@ public function fetch_markets($params = array ()): array { 'max' => null, ), 'amount' => array( - 'min' => $basePrecision, + 'min' => null, 'max' => null, ), 'price' => array( - 'min' => $quotePrecision, + 'min' => null, 'max' => null, ), 'cost' => array( - 'min' => $minCost, + 'min' => null, 'max' => null, ), ), @@ -326,1468 +255,9 @@ public function fetch_markets($params = array ()): array { return $result; } - public function fetch_ticker(string $symbol, $params = array ()): array { - /** - * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string} $symbol unified $symbol of the $market to fetch the $ticker for - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=$ticker-structure $ticker structure~ - */ - $this->load_markets(); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - ); - // array( - // { - // "market" => "DIL-ETH", - // "time" => 1598367493008, - // "open" => "0.09695361", - // "high" => "0.10245881", - // "low" => "0.09572507", - // "close" => "0.09917079", - // "closeQuantity" => "0.71320950", - // "baseVolume" => "309.17380612", - // "quoteVolume" => "30.57633981", - // "percentChange" => "2.28", - // "numTrades" => 205, - // "ask" => "0.09910476", - // "bid" => "0.09688340", - // "sequence" => 3902 - // } - // ) - $response = $this->publicGetTickers ($this->extend($request, $params)); - $ticker = $this->safe_dict($response, 0); - return $this->parse_ticker($ticker, $market); - } - - public function fetch_tickers(?array $symbols = null, $params = array ()): array { - /** - * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - * @see https://api-docs-v3.geniusyield.io/#get-tickers - * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~ - */ - $this->load_markets(); - // array( - // array( - // "market" => "DIL-ETH", - // "time" => 1598367493008, - // "open" => "0.09695361", - // "high" => "0.10245881", - // "low" => "0.09572507", - // "close" => "0.09917079", - // "closeQuantity" => "0.71320950", - // "baseVolume" => "309.17380612", - // "quoteVolume" => "30.57633981", - // "percentChange" => "2.28", - // "numTrades" => 205, - // "ask" => "0.09910476", - // "bid" => "0.09688340", - // "sequence" => 3902 - // ), ... - // ) - $response = $this->publicGetTickers ($params); - return $this->parse_tickers($response, $symbols); - } - - public function parse_ticker(array $ticker, ?array $market = null): array { - // { - // "market" => "DIL-ETH", - // "time" => 1598367493008, - // "open" => "0.09695361", - // "high" => "0.10245881", - // "low" => "0.09572507", - // "close" => "0.09917079", - // "closeQuantity" => "0.71320950", - // "baseVolume" => "309.17380612", - // "quoteVolume" => "30.57633981", - // "percentChange" => "2.28", - // "numTrades" => 205, - // "ask" => "0.09910476", - // "bid" => "0.09688340", - // "sequence" => 3902 - // } - $marketId = $this->safe_string($ticker, 'market'); - $market = $this->safe_market($marketId, $market, '-'); - $symbol = $market['symbol']; - $timestamp = $this->safe_integer($ticker, 'time'); - $close = $this->safe_string($ticker, 'close'); - return $this->safe_ticker(array( - 'symbol' => $symbol, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'high' => $this->safe_string($ticker, 'high'), - 'low' => $this->safe_string($ticker, 'low'), - 'bid' => $this->safe_string($ticker, 'bid'), - 'bidVolume' => null, - 'ask' => $this->safe_string($ticker, 'ask'), - 'askVolume' => null, - 'vwap' => null, - 'open' => $this->safe_string($ticker, 'open'), - 'close' => $close, - 'last' => $close, - 'previousClose' => null, - 'change' => null, - 'percentage' => $this->safe_string($ticker, 'percentChange'), - 'average' => null, - 'baseVolume' => $this->safe_string($ticker, 'baseVolume'), - 'quoteVolume' => $this->safe_string($ticker, 'quoteVolume'), - 'info' => $ticker, - ), $market); - } - - public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): array { - /** - * fetches historical candlestick data containing the open, high, low, and close price, and the volume of a $market - * @see https://api-docs-v3.geniusyield.io/#get-candles - * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for - * @param {string} $timeframe the length of time each candle represents - * @param {int} [$since] timestamp in ms of the earliest candle to fetch - * @param {int} [$limit] the maximum amount of candles to fetch - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {int[][]} A list of candles ordered, open, high, low, close, volume - */ - $this->load_markets(); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - 'interval' => $timeframe, - ); - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = min ($limit, 1000); - } - $response = $this->publicGetCandles ($this->extend($request, $params)); - if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { - // array( - // array( - // "start" => 1598345580000, - // "open" => "0.09771286", - // "high" => "0.09771286", - // "low" => "0.09771286", - // "close" => "0.09771286", - // "volume" => "1.45340410", - // "sequence" => 3853 - // ), ... - // ) - return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit); - } else { - // array("nextTime":1595536440000) - return array(); - } - } - - public function parse_ohlcv($ohlcv, ?array $market = null): array { - // { - // "start" => 1598345580000, - // "open" => "0.09771286", - // "high" => "0.09771286", - // "low" => "0.09771286", - // "close" => "0.09771286", - // "volume" => "1.45340410", - // "sequence" => 3853 - // } - $timestamp = $this->safe_integer($ohlcv, 'start'); - $open = $this->safe_number($ohlcv, 'open'); - $high = $this->safe_number($ohlcv, 'high'); - $low = $this->safe_number($ohlcv, 'low'); - $close = $this->safe_number($ohlcv, 'close'); - $volume = $this->safe_number($ohlcv, 'volume'); - return array( $timestamp, $open, $high, $low, $close, $volume ); - } - - public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): array { - /** - * get the list of most recent trades for a particular $symbol - * @see https://api-docs-v3.geniusyield.io/#get-trades - * @param {string} $symbol unified $symbol of the $market to fetch trades for - * @param {int} [$since] timestamp in ms of the earliest trade to fetch - * @param {int} [$limit] the maximum amount of trades to fetch - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~ - */ - $this->load_markets(); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - ); - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = min ($limit, 1000); - } - // array( - // array( - // "fillId" => "b5467d00-b13e-3fa9-8216-dd66735550fc", - // "price" => "0.09771286", - // "quantity" => "1.45340410", - // "quoteQuantity" => "0.14201627", - // "time" => 1598345638994, - // "makerSide" => "buy", - // "sequence" => 3853 - // ), ... - // ) - $response = $this->publicGetTrades ($this->extend($request, $params)); - return $this->parse_trades($response, $market, $since, $limit); - } - - public function parse_trade(array $trade, ?array $market = null): array { - // - // public trades - // { - // "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - // "price":"0.20377008", - // "quantity":"47.58448728", - // "quoteQuantity":"9.69629509", - // "time":1642091300873, - // "makerSide":"buy", - // "type":"hybrid", // one of either => "orderBook", "hybrid", or "pool" - // "sequence":31876 - // } - // - // private trades - // { - // "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - // "price":"0.20717368", - // "quantity":"15.00000000", - // "quoteQuantity":"3.10760523", - // "orderBookQuantity":"0.00000003", - // "orderBookQuoteQuantity":"0.00000001", - // "poolQuantity":"14.99999997", - // "poolQuoteQuantity":"3.10760522", - // "time":1642083351215, - // "makerSide":"sell", - // "sequence":31795, - // "market":"Genius Yield-USDC", - // "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - // "side":"buy", - // "fee":"0.03749989", - // "feeAsset":"Genius Yield", - // "gas":"0.40507261", - // "liquidity":"taker", - // "type":"hybrid", - // "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - // "txStatus":"mined" - // } - // - $id = $this->safe_string($trade, 'fillId'); - $priceString = $this->safe_string($trade, 'price'); - $amountString = $this->safe_string($trade, 'quantity'); - $costString = $this->safe_string($trade, 'quoteQuantity'); - $timestamp = $this->safe_integer($trade, 'time'); - $marketId = $this->safe_string($trade, 'market'); - $symbol = $this->safe_symbol($marketId, $market, '-'); - // this code handles the duality of public vs private trades - $makerSide = $this->safe_string($trade, 'makerSide'); - $oppositeSide = ($makerSide === 'buy') ? 'sell' : 'buy'; - $side = $this->safe_string($trade, 'side', $oppositeSide); - $takerOrMaker = $this->safe_string($trade, 'liquidity', 'taker'); - $feeCostString = $this->safe_string($trade, 'fee'); - $fee = null; - if ($feeCostString !== null) { - $feeCurrencyId = $this->safe_string($trade, 'feeAsset'); - $fee = array( - 'cost' => $feeCostString, - 'currency' => $this->safe_currency_code($feeCurrencyId), - ); - } - $orderId = $this->safe_string($trade, 'orderId'); - return $this->safe_trade(array( - 'info' => $trade, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'symbol' => $symbol, - 'id' => $id, - 'order' => $orderId, - 'type' => 'limit', - 'side' => $side, - 'takerOrMaker' => $takerOrMaker, - 'price' => $priceString, - 'amount' => $amountString, - 'cost' => $costString, - 'fee' => $fee, - ), $market); - } - - public function fetch_trading_fees($params = array ()): array { - /** - * fetch the trading fees for multiple markets - * @see https://api-docs-v3.geniusyield.io/#get-api-account - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~ indexed by market symbols - */ - $this->check_required_credentials(); - $this->load_markets(); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - ); - $response = null; - $response = $this->privateGetUser ($this->extend($request, $params)); - // - // { - // "depositEnabled" => true, - // "orderEnabled" => true, - // "cancelEnabled" => true, - // "withdrawEnabled" => true, - // "totalPortfolioValueUsd" => "0.00", - // "makerFeeRate" => "0.0000", - // "takerFeeRate" => "0.0025", - // "takerIdexFeeRate" => "0.0005", - // "takerLiquidityProviderFeeRate" => "0.0020" - // } - // - $maker = $this->safe_number($response, 'makerFeeRate'); - $taker = $this->safe_number($response, 'takerFeeRate'); - $result = array(); - for ($i = 0; $i < count($this->symbols); $i++) { - $symbol = $this->symbols[$i]; - $result[$symbol] = array( - 'info' => $response, - 'symbol' => $symbol, - 'maker' => $maker, - 'taker' => $taker, - 'percentage' => true, - 'tierBased' => false, - ); - } - return $result; - } - - public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): array { - /** - * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data - * @see https://api-docs-v3.geniusyield.io/#get-order-books - * @param {string} $symbol unified $symbol of the $market to fetch the order book for - * @param {int} [$limit] the maximum amount of order book entries to return - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols - */ - $this->load_markets(); - $market = $this->market($symbol); - $request = array( - 'market' => $market['id'], - 'level' => 2, - ); - if ($limit !== null) { - $request['limit'] = $limit; - } - // { - // "sequence" => 36416753, - // "bids" => array( - // array( '0.09672815', "8.22284267", 1 ), - // array( '0.09672814', "1.83685554", 1 ), - // array( '0.09672143', "4.10962617", 1 ), - // array( '0.09658884', "4.03863759", 1 ), - // array( '0.09653781', "3.35730684", 1 ), - // array( '0.09624660', "2.54163586", 1 ), - // array( '0.09617490', "1.93065030", 1 ) - // ), - // "asks" => array( - // array( '0.09910476', "3.22840154", 1 ), - // array( '0.09940587', "3.39796593", 1 ), - // array( '0.09948189', "4.25088898", 1 ), - // array( '0.09958362', "2.42195784", 1 ), - // array( '0.09974393', "4.25234367", 1 ), - // array( '0.09995250', "3.40192141", 1 ) - // ) - // } - $response = $this->publicGetOrderbook ($this->extend($request, $params)); - $nonce = $this->safe_integer($response, 'sequence'); - return array( - 'symbol' => $symbol, - 'timestamp' => null, - 'datetime' => null, - 'nonce' => $nonce, - 'bids' => $this->parse_side($response, 'bids'), - 'asks' => $this->parse_side($response, 'asks'), - ); - } - - public function parse_side($book, $side) { - $bookSide = $this->safe_value($book, $side, array()); - $result = array(); - for ($i = 0; $i < count($bookSide); $i++) { - $order = $bookSide[$i]; - $price = $this->safe_number($order, 0); - $amount = $this->safe_number($order, 1); - $orderCount = $this->safe_integer($order, 2); - $result[] = array( $price, $amount, $orderCount ); - } - $descending = $side === 'bids'; - return $this->sort_by($result, 0, $descending); - } - - public function fetch_currencies($params = array ()): ?array { - /** - * fetches all available currencies on an exchange - * @see https://api-docs-v3.geniusyield.io/#get-assets - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} an associative dictionary of currencies - */ - $response = $this->publicGetAssets ($params); - // - // array( - // array( - // "name" => "Ethereum", - // "symbol" => "ETH", - // "contractAddress" => "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - // "assetDecimals" => "18", - // "exchangeDecimals" => "8", - // "maticPrice" => "3029.38503483" - // ), - // ) - // - $result = array(); - for ($i = 0; $i < count($response); $i++) { - $entry = $response[$i]; - $name = $this->safe_string($entry, 'name'); - $currencyId = $this->safe_string($entry, 'symbol'); - $code = $this->safe_currency_code($currencyId); - $precision = $this->parse_number($this->parse_precision($this->safe_string($entry, 'exchangeDecimals'))); - $result[$code] = array( - 'id' => $currencyId, - 'code' => $code, - 'info' => $entry, - 'type' => null, - 'name' => $name, - 'active' => null, - 'deposit' => null, - 'withdraw' => null, - 'fee' => null, - 'precision' => $precision, - 'limits' => array( - 'amount' => array( 'min' => $precision, 'max' => null ), - 'withdraw' => array( 'min' => $precision, 'max' => null ), - ), - ); - } - return $result; - } - - public function parse_balance($response): array { - $result = array( - 'info' => $response, - 'timestamp' => null, - 'datetime' => null, - ); - for ($i = 0; $i < count($response); $i++) { - $entry = $response[$i]; - $currencyId = $this->safe_string($entry, 'asset'); - $code = $this->safe_currency_code($currencyId); - $account = $this->account(); - $account['total'] = $this->safe_string($entry, 'quantity'); - $account['free'] = $this->safe_string($entry, 'availableForTrade'); - $account['used'] = $this->safe_string($entry, 'locked'); - $result[$code] = $account; - } - return $this->safe_balance($result); - } - - public function fetch_balance($params = array ()): array { - /** - * query for balance and get the amount of funds available for trading or funds locked in orders - * @see https://api-docs-v3.geniusyield.io/#get-balances - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~ - */ - $this->check_required_credentials(); - $this->load_markets(); - $nonce1 = $this->uuidv1(); - $request = array( - 'nonce' => $nonce1, - 'wallet' => $this->walletAddress, - ); - // array( - // array( - // "asset" => "DIL", - // "quantity" => "0.00000000", - // "availableForTrade" => "0.00000000", - // "locked" => "0.00000000", - // "usdValue" => null - // ), ... - // ) - $extendedRequest = $this->extend($request, $params); - if ($extendedRequest['wallet'] === null) { - throw new BadRequest($this->id . ' fetchBalance() wallet is null, set $this->walletAddress or "address" in params'); - } - $response = null; - try { - $response = $this->privateGetBalances ($extendedRequest); - } catch (Exception $e) { - if ($e instanceof InvalidAddress) { - $walletAddress = $extendedRequest['wallet']; - $this->associate_wallet($walletAddress); - $response = $this->privateGetBalances ($extendedRequest); - } else { - throw $e; - } - } - return $this->parse_balance($response); - } - - public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { - /** - * fetch all trades made by the user - * @see https://api-docs-v3.geniusyield.io/#get-fills - * @param {string} $symbol unified $market $symbol - * @param {int} [$since] the earliest time in ms to fetch trades for - * @param {int} [$limit] the maximum number of trades structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~ - */ - $this->check_required_credentials(); - $this->load_markets(); - $market = null; - $request = array( - 'nonce' => $this->uuidv1(), - 'wallet' => $this->walletAddress, - ); - if ($symbol !== null) { - $market = $this->market($symbol); - $request['market'] = $market['id']; - } - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = $limit; - } - // array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478762, - // "makerSide" => "sell", - // "sequence" => 5053, - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "side" => "buy", - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" - // } - // ) - $extendedRequest = $this->extend($request, $params); - if ($extendedRequest['wallet'] === null) { - throw new BadRequest($this->id . ' fetchMyTrades() $walletAddress is null, set $this->walletAddress or "address" in params'); - } - $response = null; - try { - $response = $this->privateGetFills ($extendedRequest); - } catch (Exception $e) { - if ($e instanceof InvalidAddress) { - $walletAddress = $extendedRequest['wallet']; - $this->associate_wallet($walletAddress); - $response = $this->privateGetFills ($extendedRequest); - } else { - throw $e; - } - } - return $this->parse_trades($response, $market, $since, $limit); - } - - public function fetch_order(string $id, ?string $symbol = null, $params = array ()) { - /** - * fetches information on an order made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} $symbol unified $symbol of the market the order was made in - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ - */ - $request = array( - 'orderId' => $id, - ); - return $this->fetch_orders_helper($symbol, null, null, $this->extend($request, $params)); - } - - public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array { - /** - * fetch all unfilled currently open orders - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} $symbol unified market $symbol - * @param {int} [$since] the earliest time in ms to fetch open orders for - * @param {int} [$limit] the maximum number of open orders structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ - */ - $request = array( - 'closed' => false, - ); - return $this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params)); - } - - public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array { - /** - * fetches information on multiple closed orders made by the user - * @see https://api-docs-v3.geniusyield.io/#get-orders - * @param {string} $symbol unified market $symbol of the market orders were made in - * @param {int} [$since] the earliest time in ms to fetch orders for - * @param {int} [$limit] the maximum number of order structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ - */ - $request = array( - 'closed' => true, - ); - return $this->fetch_orders_helper($symbol, $since, $limit, $this->extend($request, $params)); - } - - public function fetch_orders_helper(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) { - $this->load_markets(); - $request = array( - 'nonce' => $this->uuidv1(), - 'wallet' => $this->walletAddress, - ); - $market = null; - if ($symbol !== null) { - $market = $this->market($symbol); - $request['market'] = $market['id']; - } - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = $limit; - } - $response = $this->privateGetOrders ($this->extend($request, $params)); - // fetchClosedOrders / fetchOpenOrders - // array( - // { - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "avgExecutionPrice" => "0.09905990", - // "price" => "1.00000000", - // "fills" => array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" - // } - // ) - // } - // ) - // fetchOrder - // { $market => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "avgExecutionPrice" => "0.09905990", - // "price" => "1.00000000", - // "fills": - // array( { fillId => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" } ) } - if (gettype($response) === 'array' && array_keys($response) === array_keys(array_keys($response))) { - return $this->parse_orders($response, $market, $since, $limit); - } else { - return $this->parse_order($response, $market); - } - } - - public function parse_order_status(?string $status) { - // https://docs.geniusyield.io/#order-states-amp-lifecycle - $statuses = array( - 'active' => 'open', - 'partiallyFilled' => 'open', - 'rejected' => 'canceled', - 'filled' => 'closed', - ); - return $this->safe_string($statuses, $status, $status); - } - - public function parse_order(array $order, ?array $market = null): array { - // - // { - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "avgExecutionPrice" => "0.09905990", - // "price" => "1.00000000", - // "fills" => array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txId" => "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - // "txStatus" => "mined" - // } - // ) - // } - // - $timestamp = $this->safe_integer($order, 'time'); - $fills = $this->safe_value($order, 'fills', array()); - $id = $this->safe_string($order, 'orderId'); - $clientOrderId = $this->safe_string($order, 'clientOrderId'); - $marketId = $this->safe_string($order, 'market'); - $side = $this->safe_string($order, 'side'); - $symbol = $this->safe_symbol($marketId, $market, '-'); - $type = $this->safe_string($order, 'type'); - $amount = $this->safe_string($order, 'originalQuantity'); - $filled = $this->safe_string($order, 'executedQuantity'); - $average = $this->safe_string($order, 'avgExecutionPrice'); - $price = $this->safe_string($order, 'price'); - $rawStatus = $this->safe_string($order, 'status'); - $timeInForce = $this->safe_string_upper($order, 'timeInForce'); - $status = $this->parse_order_status($rawStatus); - return $this->safe_order(array( - 'info' => $order, - 'id' => $id, - 'clientOrderId' => $clientOrderId, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'lastTradeTimestamp' => null, - 'symbol' => $symbol, - 'type' => $type, - 'timeInForce' => $timeInForce, - 'postOnly' => null, - 'side' => $side, - 'price' => $price, - 'stopPrice' => null, - 'triggerPrice' => null, - 'amount' => $amount, - 'cost' => null, - 'average' => $average, - 'filled' => $filled, - 'remaining' => null, - 'status' => $status, - 'fee' => null, - 'trades' => $fills, - ), $market); - } - - public function associate_wallet($walletAddress, $params = array ()) { - $nonce = $this->uuidv1(); - $noPrefix = $this->remove0x_prefix($walletAddress); - $byteArray = array( - $this->base16_to_binary($nonce), - $this->base16_to_binary($noPrefix), - ); - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - // { - // "address" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "totalPortfolioValueUsd" => "0.00", - // "time" => 1598468353626 - // } - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $walletAddress, - ), - 'signature' => $signature, - ); - $result = $this->privatePostWallets ($request); - return $result; - } - - public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) { - /** - * create a trade order, https://docs.geniusyield.io/#create-order - * @see https://api-docs-v3.geniusyield.io/#create-order - * @param {string} $symbol unified $symbol of the $market to create an order in - * @param {string} $type 'market' or 'limit' - * @param {string} $side 'buy' or 'sell' - * @param {float} $amount how much of currency you want to trade in units of base currency - * @param {float} [$price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in $market orders - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @param {bool} [$params->test] set to true to test an order, no order will be created but the $request will be validated - * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~ - */ - $this->check_required_credentials(); - $this->load_markets(); - $testOrder = $this->safe_bool($params, 'test', false); - $params = $this->omit($params, 'test'); - $market = $this->market($symbol); - $nonce = $this->uuidv1(); - $typeEnum = null; - $stopLossTypeEnums = array( - 'stopLoss' => 3, - 'stopLossLimit' => 4, - 'takeProfit' => 5, - 'takeProfitLimit' => 6, - ); - $stopPriceString = null; - if (($type === 'stopLossLimit') || ($type === 'takeProfitLimit') || (is_array($params) && array_key_exists('stopPrice', $params))) { - if (!(is_array($params) && array_key_exists('stopPrice', $params))) { - throw new BadRequest($this->id . ' createOrder() stopPrice is a required parameter for ' . $type . 'orders'); - } - $stopPriceString = $this->price_to_precision($symbol, $params['stopPrice']); - } - $limitTypeEnums = array( - 'limit' => 1, - 'limitMaker' => 2, - ); - $priceString = null; - $typeLower = strtolower($type); - $limitOrder = mb_strpos($typeLower, 'limit') !== false; - if (is_array($limitTypeEnums) && array_key_exists($type, $limitTypeEnums)) { - $typeEnum = $limitTypeEnums[$type]; - $priceString = $this->price_to_precision($symbol, $price); - } elseif (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { - $typeEnum = $stopLossTypeEnums[$type]; - $priceString = $this->price_to_precision($symbol, $price); - } elseif ($type === 'market') { - $typeEnum = 0; - } else { - throw new BadRequest($this->id . ' ' . $type . ' is not a valid order type'); - } - $amountEnum = 0; // base quantity - if (is_array($params) && array_key_exists('quoteOrderQuantity', $params)) { - if ($type !== 'market') { - throw new NotSupported($this->id . ' createOrder() quoteOrderQuantity is not supported for ' . $type . ' orders, only supported for $market orders'); - } - $amountEnum = 1; - $amount = $this->safe_number($params, 'quoteOrderQuantity'); - } - $sideEnum = ($side === 'buy') ? 0 : 1; - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $network = $this->safe_string($this->options, 'network', 'ETH'); - $orderVersion = $this->get_supported_mapping($network, array( - 'ETH' => 1, - 'BSC' => 2, - 'MATIC' => 4, - )); - $amountString = $this->amount_to_precision($symbol, $amount); - // https://docs.geniusyield.io/#time-in-force - $timeInForceEnums = array( - 'gtc' => 0, - 'ioc' => 2, - 'fok' => 3, - ); - $defaultTimeInForce = $this->safe_string($this->options, 'defaultTimeInForce', 'gtc'); - $timeInForce = $this->safe_string($params, 'timeInForce', $defaultTimeInForce); - $timeInForceEnum = null; - if (is_array($timeInForceEnums) && array_key_exists($timeInForce, $timeInForceEnums)) { - $timeInForceEnum = $timeInForceEnums[$timeInForce]; - } else { - $allOptions = is_array($timeInForceEnums) ? array_keys($timeInForceEnums) : array(); - $asString = implode(', ', $allOptions); - throw new BadRequest($this->id . ' ' . $timeInForce . ' is not a valid $timeInForce, please choose one of ' . $asString); - } - // https://docs.geniusyield.io/#self-trade-prevention - $selfTradePreventionEnums = array( - 'dc' => 0, - 'co' => 1, - 'cn' => 2, - 'cb' => 3, - ); - $defaultSelfTradePrevention = $this->safe_string($this->options, 'defaultSelfTradePrevention', 'cn'); - $selfTradePrevention = $this->safe_string($params, 'selfTradePrevention', $defaultSelfTradePrevention); - $selfTradePreventionEnum = null; - if (is_array($selfTradePreventionEnums) && array_key_exists($selfTradePrevention, $selfTradePreventionEnums)) { - $selfTradePreventionEnum = $selfTradePreventionEnums[$selfTradePrevention]; - } else { - $allOptions = is_array($selfTradePreventionEnums) ? array_keys($selfTradePreventionEnums) : array(); - $asString = implode(', ', $allOptions); - throw new BadRequest($this->id . ' ' . $selfTradePrevention . ' is not a valid $selfTradePrevention, please choose one of ' . $asString); - } - $byteArray = [ - $this->number_to_be($orderVersion, 1), - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - $this->encode($market['id']), - $this->number_to_be($typeEnum, 1), - $this->number_to_be($sideEnum, 1), - $this->encode($amountString), - $this->number_to_be($amountEnum, 1), - ]; - if ($limitOrder) { - $encodedPrice = $this->encode($priceString); - $byteArray[] = $encodedPrice; - } - if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { - $encodedPrice = $this->encode($stopPriceString || $priceString); - $byteArray[] = $encodedPrice; - } - $clientOrderId = $this->safe_string($params, 'clientOrderId'); - if ($clientOrderId !== null) { - $byteArray[] = $this->encode($clientOrderId); - } - $after = array( - $this->number_to_be($timeInForceEnum, 1), - $this->number_to_be($selfTradePreventionEnum, 1), - $this->number_to_be(0, 8), // unused - ); - $allBytes = $this->array_concat($byteArray, $after); - $binary = $this->binary_concat_array($allBytes); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'market' => $market['id'], - 'side' => $side, - 'type' => $type, - 'wallet' => $this->walletAddress, - 'selfTradePrevention' => $selfTradePrevention, - ), - 'signature' => $signature, - ); - if ($type !== 'market') { - $request['parameters']['timeInForce'] = $timeInForce; - } - if ($limitOrder) { - $request['parameters']['price'] = $priceString; - } - if (is_array($stopLossTypeEnums) && array_key_exists($type, $stopLossTypeEnums)) { - $request['parameters']['stopPrice'] = $stopPriceString || $priceString; - } - if ($amountEnum === 0) { - $request['parameters']['quantity'] = $amountString; - } else { - $request['parameters']['quoteOrderQuantity'] = $amountString; - } - if ($clientOrderId !== null) { - $request['parameters']['clientOrderId'] = $clientOrderId; - } - // { - // "market" => "DIL-ETH", - // "orderId" => "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - // "wallet" => "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - // "time" => 1598873478650, - // "status" => "filled", - // "type" => "limit", - // "side" => "buy", - // "originalQuantity" => "0.40000000", - // "executedQuantity" => "0.40000000", - // "cumulativeQuoteQuantity" => "0.03962396", - // "price" => "1.00000000", - // "fills" => array( - // { - // "fillId" => "48582d10-b9bb-3c4b-94d3-e67537cf2472", - // "price" => "0.09905990", - // "quantity" => "0.40000000", - // "quoteQuantity" => "0.03962396", - // "time" => 1598873478650, - // "makerSide" => "sell", - // "sequence" => 5053, - // "fee" => "0.00080000", - // "feeAsset" => "DIL", - // "gas" => "0.00857497", - // "liquidity" => "taker", - // "txStatus" => "pending" - // } - // ), - // "avgExecutionPrice" => "0.09905990" - // } - // we don't use extend here because it is a signed endpoint - $response = null; - if ($testOrder) { - $response = $this->privatePostOrdersTest ($request); - } else { - $response = $this->privatePostOrders ($request); - } - return $this->parse_order($response, $market); - } - - public function withdraw(string $code, float $amount, string $address, $tag = null, $params = array ()) { - /** - * make a withdrawal - * @see https://api-docs-v3.geniusyield.io/#withdraw-funds - * @param {string} $code unified $currency $code - * @param {float} $amount the $amount to withdraw - * @param {string} $address the $address to withdraw to - * @param {string} $tag - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~ - */ - list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params); - $this->check_required_credentials(); - $this->load_markets(); - $nonce = $this->uuidv1(); - $amountString = $this->currency_to_precision($code, $amount); - $currency = $this->currency($code); - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $byteArray = [ - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - $this->encode($currency['id']), - $this->encode($amountString), - $this->number_to_be(1, 1), // bool set to true - ]; - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $address, - 'asset' => $currency['id'], - 'quantity' => $amountString, - ), - 'signature' => $signature, - ); - $response = $this->privatePostWithdrawals ($request); - // - // { - // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset" => "ETH", - // "assetContractAddress" => "0x0000000000000000000000000000000000000000", - // "quantity" => "0.20000000", - // "time" => 1598962883190, - // "fee" => "0.00024000", - // "txStatus" => "pending", - // "txId" => null - // } - // - return $this->parse_transaction($response, $currency); - } - - public function cancel_all_orders(?string $symbol = null, $params = array ()) { - /** - * cancel all open orders - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are cancelled when $symbol is not null - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~ - */ - $this->check_required_credentials(); - $this->load_markets(); - $market = null; - if ($symbol !== null) { - $market = $this->market($symbol); - } - $nonce = $this->uuidv1(); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - ), - ); - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $byteArray = array( - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - ); - if ($market !== null) { - $byteArray[] = $this->encode($market['id']); - $request['parameters']['market'] = $market['id']; - } - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request['signature'] = $signature; - // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) - $response = $this->privateDeleteOrders ($this->extend($request, $params)); - return $this->parse_orders($response, $market); - } - - public function cancel_order(string $id, ?string $symbol = null, $params = array ()) { - /** - * cancels an open order - * @see https://api-docs-v3.geniusyield.io/#cancel-order - * @param {string} $id order $id - * @param {string} $symbol unified $symbol of the $market the order was made in - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~ - */ - $this->check_required_credentials(); - $this->load_markets(); - $market = null; - if ($symbol !== null) { - $market = $this->market($symbol); - } - $nonce = $this->uuidv1(); - $walletBytes = $this->remove0x_prefix($this->walletAddress); - $byteArray = array( - $this->base16_to_binary($nonce), - $this->base16_to_binary($walletBytes), - $this->encode($id), - ); - $binary = $this->binary_concat_array($byteArray); - $hash = $this->hash($binary, 'keccak', 'hex'); - $signature = $this->sign_message_string($hash, $this->privateKey); - $request = array( - 'parameters' => array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - 'orderId' => $id, - ), - 'signature' => $signature, - ); - // array( array( orderId => "688336f0-ec50-11ea-9842-b332f8a34d0e" ) ) - $response = $this->privateDeleteOrders ($this->extend($request, $params)); - $canceledOrder = $this->safe_dict($response, 0); - return $this->parse_order($canceledOrder, $market); - } - - public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) { - $errorCode = $this->safe_string($response, 'code'); - $message = $this->safe_string($response, 'message'); - if ($errorCode !== null) { - $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $message); - throw new ExchangeError($this->id . ' ' . $message); - } - return null; - } - - public function fetch_deposit(string $id, ?string $code = null, $params = array ()) { - /** - * fetch information on a deposit - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} $id deposit $id - * @param {string} $code not used by geniusyield fetchDeposit () - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ - */ - $this->load_markets(); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - 'depositId' => $id, - ); - $response = $this->privateGetDeposits ($this->extend($request, $params)); - return $this->parse_transaction($response); - } - - public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array { - /** - * fetch all deposits made to an account - * @see https://api-docs-v3.geniusyield.io/#get-deposits - * @param {string} $code unified currency $code - * @param {int} [$since] the earliest time in ms to fetch deposits for - * @param {int} [$limit] the maximum number of deposits structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ - */ - $params = $this->extend(array( - 'method' => 'privateGetDeposits', - ), $params); - return $this->fetch_transactions_helper($code, $since, $limit, $params); - } - - public function fetch_status($params = array ()) { - /** - * the latest known information on the availability of the exchange API - * @see https://api-docs-v3.geniusyield.io/#get-ping - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?id=exchange-status-structure status structure~ - */ - $response = $this->publicGetPing ($params); - return array( - 'status' => 'ok', // if there's no Errors, status = 'ok' - 'updated' => null, - 'eta' => null, - 'url' => null, - 'info' => $response, - ); - } - - public function fetch_time($params = array ()) { - /** - * fetches the current integer timestamp in milliseconds from the exchange server - * @see https://api-docs-v3.geniusyield.io/#get-time - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {int} the current integer timestamp in milliseconds from the exchange server - */ - $response = $this->publicGetTime ($params); - // - // array( serverTime => "1655258263236" ) - // - return $this->safe_integer($response, 'serverTime'); - } - - public function fetch_withdrawal(string $id, ?string $code = null, $params = array ()) { - /** - * fetch data on a currency withdrawal via the withdrawal $id - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} $id withdrawal $id - * @param {string} $code not used by geniusyield.fetchWithdrawal - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transaction-structure transaction structure~ - */ - $this->load_markets(); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - 'withdrawalId' => $id, - ); - $response = $this->privateGetWithdrawals ($this->extend($request, $params)); - return $this->parse_transaction($response); - } - - public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array { - /** - * fetch all withdrawals made from an account - * @see https://api-docs-v3.geniusyield.io/#get-withdrawals - * @param {string} $code unified currency $code - * @param {int} [$since] the earliest time in ms to fetch withdrawals for - * @param {int} [$limit] the maximum number of withdrawals structures to retrieve - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~ - */ - $params = $this->extend(array( - 'method' => 'privateGetWithdrawals', - ), $params); - return $this->fetch_transactions_helper($code, $since, $limit, $params); - } - - public function fetch_transactions_helper(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()) { - $this->load_markets(); - $nonce = $this->uuidv1(); - $request = array( - 'nonce' => $nonce, - 'wallet' => $this->walletAddress, - ); - $currency = null; - if ($code !== null) { - $currency = $this->currency($code); - $request['asset'] = $currency['id']; - } - if ($since !== null) { - $request['start'] = $since; - } - if ($limit !== null) { - $request['limit'] = $limit; - } - // array( - // { - // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - // "asset" => "ETH", - // "quantity" => "1.00000000", - // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime" => 1598865853000, - // "confirmationTime" => 1598865930231 - // } - // ) - $method = $params['method']; - $params = $this->omit($params, 'method'); - $response = null; - if ($method === 'privateGetDeposits') { - $response = $this->privateGetDeposits ($this->extend($request, $params)); - } elseif ($method === 'privateGetWithdrawals') { - $response = $this->privateGetWithdrawals ($this->extend($request, $params)); - } else { - throw new NotSupported($this->id . ' fetchTransactionsHelper() not support this method'); - } - return $this->parse_transactions($response, $currency, $since, $limit); - } - - public function parse_transaction_status(?string $status) { - $statuses = array( - 'mined' => 'ok', - ); - return $this->safe_string($statuses, $status, $status); - } - - public function parse_transaction(array $transaction, ?array $currency = null): array { - // - // fetchDeposits - // - // { - // "depositId" => "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - // "asset" => "ETH", - // "quantity" => "1.00000000", - // "txId" => "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - // "txTime" => 1598865853000, - // "confirmationTime" => 1598865930231 - // } - // - // fetchWithdrwalas - // - // { - // "withdrawalId" => "a62d8760-ec4d-11ea-9fa6-47904c19499b", - // "asset" => "ETH", - // "assetContractAddress" => "0x0000000000000000000000000000000000000000", - // "quantity" => "0.20000000", - // "time" => 1598962883288, - // "fee" => "0.00024000", - // "txId" => "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - // "txStatus" => "mined" - // } - // - // withdraw - // - // { - // "withdrawalId" => "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - // "asset" => "ETH", - // "assetContractAddress" => "0x0000000000000000000000000000000000000000", - // "quantity" => "0.20000000", - // "time" => 1598962883190, - // "fee" => "0.00024000", - // "txStatus" => "pending", - // "txId" => null - // } - // - $type = null; - if (is_array($transaction) && array_key_exists('depositId', $transaction)) { - $type = 'deposit'; - } elseif ((is_array($transaction) && array_key_exists('withdrawId', $transaction)) || (is_array($transaction) && array_key_exists('withdrawalId', $transaction))) { - $type = 'withdrawal'; - } - $id = $this->safe_string_2($transaction, 'depositId', 'withdrawId'); - $id = $this->safe_string($transaction, 'withdrawalId', $id); - $code = $this->safe_currency_code($this->safe_string($transaction, 'asset'), $currency); - $amount = $this->safe_number($transaction, 'quantity'); - $txid = $this->safe_string($transaction, 'txId'); - $timestamp = $this->safe_integer_2($transaction, 'txTime', 'time'); - $fee = null; - if (is_array($transaction) && array_key_exists('fee', $transaction)) { - $fee = array( - 'cost' => $this->safe_number($transaction, 'fee'), - 'currency' => 'ETH', - ); - } - $rawStatus = $this->safe_string($transaction, 'txStatus'); - $status = $this->parse_transaction_status($rawStatus); - $updated = $this->safe_integer($transaction, 'confirmationTime'); - return array( - 'info' => $transaction, - 'id' => $id, - 'txid' => $txid, - 'timestamp' => $timestamp, - 'datetime' => $this->iso8601($timestamp), - 'network' => null, - 'address' => null, - 'addressTo' => null, - 'addressFrom' => null, - 'tag' => null, - 'tagTo' => null, - 'tagFrom' => null, - 'type' => $type, - 'amount' => $amount, - 'currency' => $code, - 'status' => $status, - 'updated' => $updated, - 'comment' => null, - 'internal' => null, - 'fee' => $fee, - ); - } - - public function calculate_rate_limiter_cost($api, $method, $path, $params, $config = array ()) { - $hasApiKey = ($this->apiKey !== null); - $hasSecret = ($this->secret !== null); - $hasWalletAddress = ($this->walletAddress !== null); - $hasPrivateKey = ($this->privateKey !== null); - $defaultCost = $this->safe_value($config, 'cost', 1); - $authenticated = $hasApiKey && $hasSecret && $hasWalletAddress && $hasPrivateKey; - return $authenticated ? ($defaultCost / 2) : $defaultCost; - } - - public function fetch_deposit_address(?string $code = null, $params = array ()) { - /** - * fetch the Polygon address of the wallet - * @see https://api-docs-v3.geniusyield.io/#get-wallets - * @param {string} $code not used by geniusyield - * @param {array} [$params] extra parameters specific to the exchange API endpoint - * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~ - */ - $request = array(); - $request['nonce'] = $this->uuidv1(); - $response = $this->privateGetWallets ($this->extend($request, $params)); - // - // array( - // array( - // address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd => "0.00", - // time => "1678342148086" - // ), - // { - // address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd => "15.90", - // time => "1691697811659" - // } - // ) - // - return $this->parse_deposit_address($response); - } - - public function parse_deposit_address($depositAddress, ?array $currency = null) { - // - // array( - // array( - // $address => "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - // totalPortfolioValueUsd => "0.00", - // time => "1678342148086" - // ), - // { - // $address => "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - // totalPortfolioValueUsd => "15.90", - // time => "1691697811659" - // } - // ) - // - $length = count($depositAddress); - $entry = $this->safe_dict($depositAddress, $length - 1); - $address = $this->safe_string($entry, 'address'); - $this->check_address($address); - return array( - 'info' => $depositAddress, - 'currency' => null, - 'address' => $address, - 'tag' => null, - 'network' => 'MATIC', - ); - } - public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) { - $network = $this->safe_string($this->options, 'network', 'ETH'); - $version = $this->safe_string($this->options, 'version', 'v1'); + $network = $this->safe_string($this->options, 'network', 'mainnet'); + $version = $this->safe_string($this->options, 'version', 'v0'); $url = $this->urls['api'][$network] . '/' . $version . '/' . $path; $keys = is_array($params) ? array_keys($params) : array(); $length = count($keys); @@ -1804,52 +274,8 @@ public function sign($path, $api = 'public', $method = 'GET', $params = array () 'Content-Type' => 'application/json', ); if ($this->apiKey !== null) { - $headers['Genius Yield-API-Key'] = $this->apiKey; - } - if ($api === 'private') { - $payload = null; - if ($method === 'GET') { - $payload = $query; - } else { - $payload = $body; - } - $headers['Genius Yield-HMAC-Signature'] = $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha256', 'hex'); + $headers['api-key'] = $this->apiKey; } return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers ); } - - public function remove0x_prefix($hexData) { - if (mb_substr($hexData, 0, 2 - 0) === '0x') { - return mb_substr($hexData, 2); - } else { - return $hexData; - } - } - - public function hash_message($message) { - // takes a hex encoded $message - $binaryMessage = $this->base16_to_binary($this->remove0x_prefix($message)); - $prefix = $this->encode('\x19Ethereum Signed Message:\n' . $binaryMessage->byteLength); - return '0x' . $this->hash($this->binary_concat($prefix, $binaryMessage), 'keccak', 'hex'); - } - - public function sign_hash($hash, $privateKey) { - $signature = $this->ecdsa(mb_substr($hash, -64), mb_substr($privateKey, -64), 'secp256k1', null); - return array( - 'r' => '0x' . $signature['r'], - 's' => '0x' . $signature['s'], - 'v' => 27 . $signature['v'], - ); - } - - public function sign_message($message, $privateKey) { - return $this->sign_hash($this->hash_message($message), mb_substr($privateKey, -64)); - } - - public function sign_message_string($message, $privateKey) { - // still takes the input hex string - // same but returns a string instead of an object - $signature = $this->sign_message($message, $privateKey); - return $signature['r'] . $this->remove0x_prefix($signature['s']) . bin2hex($this->number_to_be($signature['v'], 1)); - } } diff --git a/python/ccxt/abstract/geniusyield.py b/python/ccxt/abstract/geniusyield.py index 6f58a8664e99..219f37e236db 100644 --- a/python/ccxt/abstract/geniusyield.py +++ b/python/ccxt/abstract/geniusyield.py @@ -2,5 +2,5 @@ class ImplicitAPI: - private_get_markets = privateGetMarkets = Entry('markets', 'private', 'GET', {'cost': 0}) - private_get_trading_fees = privateGetTradingFees = Entry('trading-fees', 'private', 'GET', {'cost': 0}) + private_get_markets = privateGetMarkets = Entry('markets', 'private', 'GET', {'cost': 10}) + private_get_trading_fees = privateGetTradingFees = Entry('trading-fees', 'private', 'GET', {'cost': 10}) diff --git a/python/ccxt/async_support/geniusyield.py b/python/ccxt/async_support/geniusyield.py index dec6eee0cbd3..cd1a67b0a6ff 100644 --- a/python/ccxt/async_support/geniusyield.py +++ b/python/ccxt/async_support/geniusyield.py @@ -5,28 +5,28 @@ from ccxt.async_support.base.exchange import Exchange from ccxt.abstract.geniusyield import ImplicitAPI -import hashlib -from ccxt.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction +from ccxt.base.types import Market, MarketInterface, Str from typing import List -from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError from ccxt.base.errors import BadRequest from ccxt.base.errors import InsufficientFunds from ccxt.base.errors import InvalidAddress from ccxt.base.errors import InvalidOrder -from ccxt.base.errors import NotSupported from ccxt.base.errors import DDoSProtection from ccxt.base.errors import ExchangeNotAvailable -from ccxt.base.decimal_to_precision import ROUND -from ccxt.base.decimal_to_precision import TRUNCATE -from ccxt.base.decimal_to_precision import DECIMAL_PLACES from ccxt.base.decimal_to_precision import TICK_SIZE from ccxt.base.decimal_to_precision import PAD_WITH_ZERO -from ccxt.base.precise import Precise class geniusyield(Exchange, ImplicitAPI): + def safe_market(self, marketId: Str = None, market: Market = None, delimiter: Str = None, marketType: Str = None) -> MarketInterface: + isOption = (marketId is not None) and ((marketId.find('-C') > -1) or (marketId.find('-P') > -1)) + if isOption and not (marketId in self.markets_by_id): + # handle expired option contracts + return self.create_expired_option_market(marketId) + return super(geniusyield, self).safe_market(marketId, market, delimiter, marketType) + def describe(self): return self.deep_extend(super(geniusyield, self).describe(), { 'id': 'geniusyield', @@ -46,7 +46,7 @@ def describe(self): 'future': False, 'option': False, 'addMargin': False, - 'cancelAllOrders': True, + 'cancelAllOrders': False, 'cancelOrder': True, 'cancelOrders': False, 'closeAllPositions': False, @@ -54,21 +54,21 @@ def describe(self): 'createDepositAddress': False, 'createOrder': True, 'createReduceOnlyOrder': False, - 'createStopLimitOrder': True, - 'createStopMarketOrder': True, - 'createStopOrder': True, + 'createStopLimitOrder': False, + 'createStopMarketOrder': False, + 'createStopOrder': False, 'fetchBalance': True, 'fetchBorrowRateHistories': False, 'fetchBorrowRateHistory': False, - 'fetchClosedOrders': True, + 'fetchClosedOrders': False, 'fetchCrossBorrowRate': False, 'fetchCrossBorrowRates': False, - 'fetchCurrencies': True, - 'fetchDeposit': True, - 'fetchDepositAddress': True, + 'fetchCurrencies': False, + 'fetchDeposit': False, + 'fetchDepositAddress': False, 'fetchDepositAddresses': False, 'fetchDepositAddressesByNetwork': False, - 'fetchDeposits': True, + 'fetchDeposits': False, 'fetchFundingHistory': False, 'fetchFundingRate': False, 'fetchFundingRateHistory': False, @@ -81,13 +81,13 @@ def describe(self): 'fetchMarginMode': False, 'fetchMarkets': True, 'fetchMarkOHLCV': False, - 'fetchMyTrades': True, - 'fetchOHLCV': True, + 'fetchMyTrades': False, + 'fetchOHLCV': False, 'fetchOpenInterestHistory': False, - 'fetchOpenOrders': True, - 'fetchOrder': True, - 'fetchOrderBook': True, - 'fetchOrders': False, + 'fetchOpenOrders': False, + 'fetchOrder': False, + 'fetchOrderBook': False, + 'fetchOrders': True, 'fetchPosition': False, 'fetchPositionHistory': False, 'fetchPositionMode': False, @@ -96,23 +96,23 @@ def describe(self): 'fetchPositionsHistory': False, 'fetchPositionsRisk': False, 'fetchPremiumIndexOHLCV': False, - 'fetchStatus': True, - 'fetchTicker': True, - 'fetchTickers': True, - 'fetchTime': True, - 'fetchTrades': True, + 'fetchStatus': False, + 'fetchTicker': False, + 'fetchTickers': False, + 'fetchTime': False, + 'fetchTrades': False, 'fetchTradingFee': False, - 'fetchTradingFees': True, + 'fetchTradingFees': False, 'fetchTransactions': False, - 'fetchWithdrawal': True, - 'fetchWithdrawals': True, + 'fetchWithdrawal': False, + 'fetchWithdrawals': False, 'reduceMargin': False, - 'sandbox': True, + 'sandbox': False, 'setLeverage': False, 'setMarginMode': False, 'setPositionMode': False, 'transfer': False, - 'withdraw': True, + 'withdraw': False, }, 'timeframes': { '1m': '1m', @@ -124,58 +124,34 @@ def describe(self): '1d': '1d', }, 'urls': { - 'test': { - 'MATIC': 'https://api-sandbox-matic.geniusyield.io', - }, - 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api': { - 'MATIC': 'https://api-matic.geniusyield.io', + 'preprod': 'http://localhost:8082', + 'mainnet': 'http://localhost:8082', }, - 'www': 'https://geniusyield.io', + 'www': 'https://www.geniusyield.co/', 'doc': [ - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ], }, 'api': { 'public': { 'get': { - 'ping': 1, - 'time': 1, - 'exchange': 1, - 'assets': 1, - 'markets': 1, - 'tickers': 1, - 'candles': 1, - 'trades': 1, - 'orderbook': 1, }, }, 'private': { 'get': { - 'user': 1, - 'wallets': 1, - 'balances': 1, - 'orders': 0.1, - 'fills': 0.1, - 'deposits': 1, - 'withdrawals': 1, - 'wsToken': 1, - }, - 'post': { - 'wallets': 1, - 'orders': 0.1, - 'orders/test': 0.1, - 'withdrawals': 1, - }, - 'delete': { - 'orders': 0.1, + 'markets': 10, + 'trading-fees': 10, }, }, }, + 'headers': { + 'X-Gate-Channel-Id': 'ccxt', + }, 'options': { - 'defaultTimeInForce': 'gtc', + 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', - 'network': 'MATIC', + 'network': 'mainnet', }, 'exceptions': { 'exact': { @@ -189,28 +165,16 @@ def describe(self): }, }, 'requiredCredentials': { - 'walletAddress': True, - 'privateKey': True, + 'walletAddress': False, + 'privateKey': False, 'apiKey': True, - 'secret': True, + 'secret': False, }, 'precisionMode': TICK_SIZE, 'paddingMode': PAD_WITH_ZERO, 'commonCurrencies': {}, }) - def price_to_precision(self, symbol, price): - # - # we override priceToPrecision to fix the following issue - # https://github.com/ccxt/ccxt/issues/13367 - # {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes"} - # - market = self.market(symbol) - info = self.safe_value(market, 'info', {}) - quoteAssetPrecision = self.safe_integer(info, 'quoteAssetPrecision') - price = self.decimal_to_precision(price, ROUND, market['precision']['price'], self.precisionMode) - return self.decimal_to_precision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO) - async def fetch_markets(self, params={}) -> List[Market]: """ retrieves data on all markets for geniusyield @@ -218,76 +182,34 @@ async def fetch_markets(self, params={}) -> List[Market]: :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: an array of objects representing market data """ - response = await self.publicGetMarkets(params) - # - # [ - # { - # "market": "ETH-USDC", - # "type": "hybrid", - # "status": "activeHybrid", - # "baseAsset": "ETH", - # "baseAssetPrecision": "8", - # "quoteAsset": "USDC", - # "quoteAssetPrecision": "8", - # "makerFeeRate": "0.0000", - # "takerFeeRate": "0.2500", - # "takerIdexFeeRate": "0.0500", - # "takerLiquidityProviderFeeRate": "0.2000", - # "tickSize": "0.01000000" - # }, - # ] - # - response2 = await self.publicGetExchange() - # + markets = await self.privateGetMarkets(params) + # [ # { - # "timeZone": "UTC", - # "serverTime": "1654460599952", - # "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - # "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - # "maticUsdPrice": "0.60", - # "gasPrice": "180", - # "volume24hUsd": "10015814.46", - # "totalVolumeUsd": "1589273533.28", - # "totalTrades": "1534904", - # "totalValueLockedUsd": "12041929.44", - # "geniusyieldStakingValueLockedUsd": "20133816.98", - # "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", - # "geniusyieldUsdPrice": "0.07", - # "geniusyieldMarketCapUsd": "48012346.00", - # "makerFeeRate": "0.0000", - # "takerFeeRate": "0.0025", - # "takerIdexFeeRate": "0.0005", - # "takerLiquidityProviderFeeRate": "0.0020", - # "makerTradeMinimum": "10.00000000", - # "takerTradeMinimum": "1.00000000", - # "withdrawMinimum": "0.50000000", - # "liquidityAdditionMinimum": "0.50000000", - # "liquidityRemovalMinimum": "0.40000000", - # "blockConfirmationDelay": "64" + # "market_id": "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + # "base_asset": "lovelace", + # "target_asset": "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" # } - # - maker = self.safe_number(response2, 'makerFeeRate') - taker = self.safe_number(response2, 'takerFeeRate') - makerMin = self.safe_string(response2, 'makerTradeMinimum') - takerMin = self.safe_string(response2, 'takerTradeMinimum') - minCostETH = self.parse_number(Precise.string_min(makerMin, takerMin)) + # ] + fees = await self.privateGetTradingFees() + # { + # "flat_maker_fee": "1000000", + # "flat_taker_fee": "1000000", + # "percentage_maker_fee": "0.3", + # "percentage_taker_fee": "0.3" + # } + maker = self.safe_number(fees, 'percentage_maker_fee') + taker = self.safe_number(fees, 'percentage_taker_fee') result = [] - for i in range(0, len(response)): - entry = response[i] - marketId = self.safe_string(entry, 'market') - baseId = self.safe_string(entry, 'baseAsset') - quoteId = self.safe_string(entry, 'quoteAsset') + for i in range(0, len(markets)): + entry = markets[i] + marketId = self.safe_string(entry, 'market_id') + baseId = self.safe_string(entry, 'base_asset') + quoteId = self.safe_string(entry, 'target_asset') base = self.safe_currency_code(baseId) quote = self.safe_currency_code(quoteId) - basePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'baseAssetPrecision'))) - quotePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'quoteAssetPrecision'))) - status = self.safe_string(entry, 'status') - minCost = None - if quote == 'ETH': - minCost = minCostETH result.append({ 'id': marketId, - 'symbol': base + '/' + quote, + 'symbol': marketId, 'base': base, 'quote': quote, 'settle': None, @@ -300,7 +222,7 @@ async def fetch_markets(self, params={}) -> List[Market]: 'swap': False, 'future': False, 'option': False, - 'active': (status != 'inactive'), + 'active': True, 'contract': False, 'linear': None, 'inverse': None, @@ -312,8 +234,8 @@ async def fetch_markets(self, params={}) -> List[Market]: 'strike': None, 'optionType': None, 'precision': { - 'amount': basePrecision, - 'price': self.safe_number(entry, 'tickSize'), + 'amount': None, + 'price': None, }, 'limits': { 'leverage': { @@ -321,15 +243,15 @@ async def fetch_markets(self, params={}) -> List[Market]: 'max': None, }, 'amount': { - 'min': basePrecision, + 'min': None, 'max': None, }, 'price': { - 'min': quotePrecision, + 'min': None, 'max': None, }, 'cost': { - 'min': minCost, + 'min': None, 'max': None, }, }, @@ -338,1380 +260,9 @@ async def fetch_markets(self, params={}) -> List[Market]: }) return result - async def fetch_ticker(self, symbol: str, params={}) -> Ticker: - """ - fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market - :see: https://api-docs-v3.geniusyield.io/#get-tickers - :param str symbol: unified symbol of the market to fetch the ticker for - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `ticker structure ` - """ - await self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - } - # [ - # { - # "market": "DIL-ETH", - # "time": 1598367493008, - # "open": "0.09695361", - # "high": "0.10245881", - # "low": "0.09572507", - # "close": "0.09917079", - # "closeQuantity": "0.71320950", - # "baseVolume": "309.17380612", - # "quoteVolume": "30.57633981", - # "percentChange": "2.28", - # "numTrades": 205, - # "ask": "0.09910476", - # "bid": "0.09688340", - # "sequence": 3902 - # } - # ] - response = await self.publicGetTickers(self.extend(request, params)) - ticker = self.safe_dict(response, 0) - return self.parse_ticker(ticker, market) - - async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers: - """ - fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - :see: https://api-docs-v3.geniusyield.io/#get-tickers - :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a dictionary of `ticker structures ` - """ - await self.load_markets() - # [ - # { - # "market": "DIL-ETH", - # "time": 1598367493008, - # "open": "0.09695361", - # "high": "0.10245881", - # "low": "0.09572507", - # "close": "0.09917079", - # "closeQuantity": "0.71320950", - # "baseVolume": "309.17380612", - # "quoteVolume": "30.57633981", - # "percentChange": "2.28", - # "numTrades": 205, - # "ask": "0.09910476", - # "bid": "0.09688340", - # "sequence": 3902 - # }, ... - # ] - response = await self.publicGetTickers(params) - return self.parse_tickers(response, symbols) - - def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker: - # { - # "market": "DIL-ETH", - # "time": 1598367493008, - # "open": "0.09695361", - # "high": "0.10245881", - # "low": "0.09572507", - # "close": "0.09917079", - # "closeQuantity": "0.71320950", - # "baseVolume": "309.17380612", - # "quoteVolume": "30.57633981", - # "percentChange": "2.28", - # "numTrades": 205, - # "ask": "0.09910476", - # "bid": "0.09688340", - # "sequence": 3902 - # } - marketId = self.safe_string(ticker, 'market') - market = self.safe_market(marketId, market, '-') - symbol = market['symbol'] - timestamp = self.safe_integer(ticker, 'time') - close = self.safe_string(ticker, 'close') - return self.safe_ticker({ - 'symbol': symbol, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'high': self.safe_string(ticker, 'high'), - 'low': self.safe_string(ticker, 'low'), - 'bid': self.safe_string(ticker, 'bid'), - 'bidVolume': None, - 'ask': self.safe_string(ticker, 'ask'), - 'askVolume': None, - 'vwap': None, - 'open': self.safe_string(ticker, 'open'), - 'close': close, - 'last': close, - 'previousClose': None, - 'change': None, - 'percentage': self.safe_string(ticker, 'percentChange'), - 'average': None, - 'baseVolume': self.safe_string(ticker, 'baseVolume'), - 'quoteVolume': self.safe_string(ticker, 'quoteVolume'), - 'info': ticker, - }, market) - - async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]: - """ - fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market - :see: https://api-docs-v3.geniusyield.io/#get-candles - :param str symbol: unified symbol of the market to fetch OHLCV data for - :param str timeframe: the length of time each candle represents - :param int [since]: timestamp in ms of the earliest candle to fetch - :param int [limit]: the maximum amount of candles to fetch - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns int[][]: A list of candles ordered, open, high, low, close, volume - """ - await self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - 'interval': timeframe, - } - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = min(limit, 1000) - response = await self.publicGetCandles(self.extend(request, params)) - if isinstance(response, list): - # [ - # { - # "start": 1598345580000, - # "open": "0.09771286", - # "high": "0.09771286", - # "low": "0.09771286", - # "close": "0.09771286", - # "volume": "1.45340410", - # "sequence": 3853 - # }, ... - # ] - return self.parse_ohlcvs(response, market, timeframe, since, limit) - else: - # {"nextTime":1595536440000} - return [] - - def parse_ohlcv(self, ohlcv, market: Market = None) -> list: - # { - # "start": 1598345580000, - # "open": "0.09771286", - # "high": "0.09771286", - # "low": "0.09771286", - # "close": "0.09771286", - # "volume": "1.45340410", - # "sequence": 3853 - # } - timestamp = self.safe_integer(ohlcv, 'start') - open = self.safe_number(ohlcv, 'open') - high = self.safe_number(ohlcv, 'high') - low = self.safe_number(ohlcv, 'low') - close = self.safe_number(ohlcv, 'close') - volume = self.safe_number(ohlcv, 'volume') - return [timestamp, open, high, low, close, volume] - - async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: - """ - get the list of most recent trades for a particular symbol - :see: https://api-docs-v3.geniusyield.io/#get-trades - :param str symbol: unified symbol of the market to fetch trades for - :param int [since]: timestamp in ms of the earliest trade to fetch - :param int [limit]: the maximum amount of trades to fetch - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Trade[]: a list of `trade structures ` - """ - await self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - } - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = min(limit, 1000) - # [ - # { - # "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", - # "price": "0.09771286", - # "quantity": "1.45340410", - # "quoteQuantity": "0.14201627", - # "time": 1598345638994, - # "makerSide": "buy", - # "sequence": 3853 - # }, ... - # ] - response = await self.publicGetTrades(self.extend(request, params)) - return self.parse_trades(response, market, since, limit) - - def parse_trade(self, trade: dict, market: Market = None) -> Trade: - # - # public trades - # { - # "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - # "price":"0.20377008", - # "quantity":"47.58448728", - # "quoteQuantity":"9.69629509", - # "time":1642091300873, - # "makerSide":"buy", - # "type":"hybrid", # one of either: "orderBook", "hybrid", or "pool" - # "sequence":31876 - # } - # - # private trades - # { - # "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - # "price":"0.20717368", - # "quantity":"15.00000000", - # "quoteQuantity":"3.10760523", - # "orderBookQuantity":"0.00000003", - # "orderBookQuoteQuantity":"0.00000001", - # "poolQuantity":"14.99999997", - # "poolQuoteQuantity":"3.10760522", - # "time":1642083351215, - # "makerSide":"sell", - # "sequence":31795, - # "market":"Genius Yield-USDC", - # "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - # "side":"buy", - # "fee":"0.03749989", - # "feeAsset":"Genius Yield", - # "gas":"0.40507261", - # "liquidity":"taker", - # "type":"hybrid", - # "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - # "txStatus":"mined" - # } - # - id = self.safe_string(trade, 'fillId') - priceString = self.safe_string(trade, 'price') - amountString = self.safe_string(trade, 'quantity') - costString = self.safe_string(trade, 'quoteQuantity') - timestamp = self.safe_integer(trade, 'time') - marketId = self.safe_string(trade, 'market') - symbol = self.safe_symbol(marketId, market, '-') - # self code handles the duality of public vs private trades - makerSide = self.safe_string(trade, 'makerSide') - oppositeSide = 'sell' if (makerSide == 'buy') else 'buy' - side = self.safe_string(trade, 'side', oppositeSide) - takerOrMaker = self.safe_string(trade, 'liquidity', 'taker') - feeCostString = self.safe_string(trade, 'fee') - fee = None - if feeCostString is not None: - feeCurrencyId = self.safe_string(trade, 'feeAsset') - fee = { - 'cost': feeCostString, - 'currency': self.safe_currency_code(feeCurrencyId), - } - orderId = self.safe_string(trade, 'orderId') - return self.safe_trade({ - 'info': trade, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'symbol': symbol, - 'id': id, - 'order': orderId, - 'type': 'limit', - 'side': side, - 'takerOrMaker': takerOrMaker, - 'price': priceString, - 'amount': amountString, - 'cost': costString, - 'fee': fee, - }, market) - - async def fetch_trading_fees(self, params={}) -> TradingFees: - """ - fetch the trading fees for multiple markets - :see: https://api-docs-v3.geniusyield.io/#get-api-account - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a dictionary of `fee structures ` indexed by market symbols - """ - self.check_required_credentials() - await self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - } - response = None - response = await self.privateGetUser(self.extend(request, params)) - # - # { - # "depositEnabled": True, - # "orderEnabled": True, - # "cancelEnabled": True, - # "withdrawEnabled": True, - # "totalPortfolioValueUsd": "0.00", - # "makerFeeRate": "0.0000", - # "takerFeeRate": "0.0025", - # "takerIdexFeeRate": "0.0005", - # "takerLiquidityProviderFeeRate": "0.0020" - # } - # - maker = self.safe_number(response, 'makerFeeRate') - taker = self.safe_number(response, 'takerFeeRate') - result: dict = {} - for i in range(0, len(self.symbols)): - symbol = self.symbols[i] - result[symbol] = { - 'info': response, - 'symbol': symbol, - 'maker': maker, - 'taker': taker, - 'percentage': True, - 'tierBased': False, - } - return result - - async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: - """ - fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data - :see: https://api-docs-v3.geniusyield.io/#get-order-books - :param str symbol: unified symbol of the market to fetch the order book for - :param int [limit]: the maximum amount of order book entries to return - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: A dictionary of `order book structures ` indexed by market symbols - """ - await self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - 'level': 2, - } - if limit is not None: - request['limit'] = limit - # { - # "sequence": 36416753, - # "bids": [ - # ['0.09672815', "8.22284267", 1], - # ['0.09672814', "1.83685554", 1], - # ['0.09672143', "4.10962617", 1], - # ['0.09658884', "4.03863759", 1], - # ['0.09653781', "3.35730684", 1], - # ['0.09624660', "2.54163586", 1], - # ['0.09617490', "1.93065030", 1] - # ], - # "asks": [ - # ['0.09910476', "3.22840154", 1], - # ['0.09940587', "3.39796593", 1], - # ['0.09948189', "4.25088898", 1], - # ['0.09958362', "2.42195784", 1], - # ['0.09974393', "4.25234367", 1], - # ['0.09995250', "3.40192141", 1] - # ] - # } - response = await self.publicGetOrderbook(self.extend(request, params)) - nonce = self.safe_integer(response, 'sequence') - return { - 'symbol': symbol, - 'timestamp': None, - 'datetime': None, - 'nonce': nonce, - 'bids': self.parse_side(response, 'bids'), - 'asks': self.parse_side(response, 'asks'), - } - - def parse_side(self, book, side): - bookSide = self.safe_value(book, side, []) - result = [] - for i in range(0, len(bookSide)): - order = bookSide[i] - price = self.safe_number(order, 0) - amount = self.safe_number(order, 1) - orderCount = self.safe_integer(order, 2) - result.append([price, amount, orderCount]) - descending = side == 'bids' - return self.sort_by(result, 0, descending) - - async def fetch_currencies(self, params={}) -> Currencies: - """ - fetches all available currencies on an exchange - :see: https://api-docs-v3.geniusyield.io/#get-assets - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: an associative dictionary of currencies - """ - response = await self.publicGetAssets(params) - # - # [ - # { - # "name": "Ethereum", - # "symbol": "ETH", - # "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - # "assetDecimals": "18", - # "exchangeDecimals": "8", - # "maticPrice": "3029.38503483" - # }, - # ] - # - result: dict = {} - for i in range(0, len(response)): - entry = response[i] - name = self.safe_string(entry, 'name') - currencyId = self.safe_string(entry, 'symbol') - code = self.safe_currency_code(currencyId) - precision = self.parse_number(self.parse_precision(self.safe_string(entry, 'exchangeDecimals'))) - result[code] = { - 'id': currencyId, - 'code': code, - 'info': entry, - 'type': None, - 'name': name, - 'active': None, - 'deposit': None, - 'withdraw': None, - 'fee': None, - 'precision': precision, - 'limits': { - 'amount': {'min': precision, 'max': None}, - 'withdraw': {'min': precision, 'max': None}, - }, - } - return result - - def parse_balance(self, response) -> Balances: - result: dict = { - 'info': response, - 'timestamp': None, - 'datetime': None, - } - for i in range(0, len(response)): - entry = response[i] - currencyId = self.safe_string(entry, 'asset') - code = self.safe_currency_code(currencyId) - account = self.account() - account['total'] = self.safe_string(entry, 'quantity') - account['free'] = self.safe_string(entry, 'availableForTrade') - account['used'] = self.safe_string(entry, 'locked') - result[code] = account - return self.safe_balance(result) - - async def fetch_balance(self, params={}) -> Balances: - """ - query for balance and get the amount of funds available for trading or funds locked in orders - :see: https://api-docs-v3.geniusyield.io/#get-balances - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `balance structure ` - """ - self.check_required_credentials() - await self.load_markets() - nonce1 = self.uuidv1() - request: dict = { - 'nonce': nonce1, - 'wallet': self.walletAddress, - } - # [ - # { - # "asset": "DIL", - # "quantity": "0.00000000", - # "availableForTrade": "0.00000000", - # "locked": "0.00000000", - # "usdValue": null - # }, ... - # ] - extendedRequest = self.extend(request, params) - if extendedRequest['wallet'] is None: - raise BadRequest(self.id + ' fetchBalance() wallet is None, set self.walletAddress or "address" in params') - response = None - try: - response = await self.privateGetBalances(extendedRequest) - except Exception as e: - if isinstance(e, InvalidAddress): - walletAddress = extendedRequest['wallet'] - await self.associate_wallet(walletAddress) - response = await self.privateGetBalances(extendedRequest) - else: - raise e - return self.parse_balance(response) - - async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): - """ - fetch all trades made by the user - :see: https://api-docs-v3.geniusyield.io/#get-fills - :param str symbol: unified market symbol - :param int [since]: the earliest time in ms to fetch trades for - :param int [limit]: the maximum number of trades structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Trade[]: a list of `trade structures ` - """ - self.check_required_credentials() - await self.load_markets() - market = None - request: dict = { - 'nonce': self.uuidv1(), - 'wallet': self.walletAddress, - } - if symbol is not None: - market = self.market(symbol) - request['market'] = market['id'] - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = limit - # [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478762, - # "makerSide": "sell", - # "sequence": 5053, - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "side": "buy", - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined" - # } - # ] - extendedRequest = self.extend(request, params) - if extendedRequest['wallet'] is None: - raise BadRequest(self.id + ' fetchMyTrades() walletAddress is None, set self.walletAddress or "address" in params') - response = None - try: - response = await self.privateGetFills(extendedRequest) - except Exception as e: - if isinstance(e, InvalidAddress): - walletAddress = extendedRequest['wallet'] - await self.associate_wallet(walletAddress) - response = await self.privateGetFills(extendedRequest) - else: - raise e - return self.parse_trades(response, market, since, limit) - - async def fetch_order(self, id: str, symbol: Str = None, params={}): - """ - fetches information on an order made by the user - :see: https://api-docs-v3.geniusyield.io/#get-orders - :param str symbol: unified symbol of the market the order was made in - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: An `order structure ` - """ - request: dict = { - 'orderId': id, - } - return await self.fetch_orders_helper(symbol, None, None, self.extend(request, params)) - - async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: - """ - fetch all unfilled currently open orders - :see: https://api-docs-v3.geniusyield.io/#get-orders - :param str symbol: unified market symbol - :param int [since]: the earliest time in ms to fetch open orders for - :param int [limit]: the maximum number of open orders structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Order[]: a list of `order structures ` - """ - request: dict = { - 'closed': False, - } - return await self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) - - async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: - """ - fetches information on multiple closed orders made by the user - :see: https://api-docs-v3.geniusyield.io/#get-orders - :param str symbol: unified market symbol of the market orders were made in - :param int [since]: the earliest time in ms to fetch orders for - :param int [limit]: the maximum number of order structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Order[]: a list of `order structures ` - """ - request: dict = { - 'closed': True, - } - return await self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) - - async def fetch_orders_helper(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): - await self.load_markets() - request: dict = { - 'nonce': self.uuidv1(), - 'wallet': self.walletAddress, - } - market = None - if symbol is not None: - market = self.market(symbol) - request['market'] = market['id'] - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = limit - response = await self.privateGetOrders(self.extend(request, params)) - # fetchClosedOrders / fetchOpenOrders - # [ - # { - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "avgExecutionPrice": "0.09905990", - # "price": "1.00000000", - # "fills": [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined" - # } - # ] - # } - # ] - # fetchOrder - # {market: "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "avgExecutionPrice": "0.09905990", - # "price": "1.00000000", - # "fills": - # [{fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined"}]} - if isinstance(response, list): - return self.parse_orders(response, market, since, limit) - else: - return self.parse_order(response, market) - - def parse_order_status(self, status: Str): - # https://docs.geniusyield.io/#order-states-amp-lifecycle - statuses: dict = { - 'active': 'open', - 'partiallyFilled': 'open', - 'rejected': 'canceled', - 'filled': 'closed', - } - return self.safe_string(statuses, status, status) - - def parse_order(self, order: dict, market: Market = None) -> Order: - # - # { - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "avgExecutionPrice": "0.09905990", - # "price": "1.00000000", - # "fills": [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined" - # } - # ] - # } - # - timestamp = self.safe_integer(order, 'time') - fills = self.safe_value(order, 'fills', []) - id = self.safe_string(order, 'orderId') - clientOrderId = self.safe_string(order, 'clientOrderId') - marketId = self.safe_string(order, 'market') - side = self.safe_string(order, 'side') - symbol = self.safe_symbol(marketId, market, '-') - type = self.safe_string(order, 'type') - amount = self.safe_string(order, 'originalQuantity') - filled = self.safe_string(order, 'executedQuantity') - average = self.safe_string(order, 'avgExecutionPrice') - price = self.safe_string(order, 'price') - rawStatus = self.safe_string(order, 'status') - timeInForce = self.safe_string_upper(order, 'timeInForce') - status = self.parse_order_status(rawStatus) - return self.safe_order({ - 'info': order, - 'id': id, - 'clientOrderId': clientOrderId, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'lastTradeTimestamp': None, - 'symbol': symbol, - 'type': type, - 'timeInForce': timeInForce, - 'postOnly': None, - 'side': side, - 'price': price, - 'stopPrice': None, - 'triggerPrice': None, - 'amount': amount, - 'cost': None, - 'average': average, - 'filled': filled, - 'remaining': None, - 'status': status, - 'fee': None, - 'trades': fills, - }, market) - - async def associate_wallet(self, walletAddress, params={}): - nonce = self.uuidv1() - noPrefix = self.remove0x_prefix(walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(noPrefix), - ] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - # { - # "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "totalPortfolioValueUsd": "0.00", - # "time": 1598468353626 - # } - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': walletAddress, - }, - 'signature': signature, - } - result = await self.privatePostWallets(request) - return result - - async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): - """ - create a trade order, https://docs.geniusyield.io/#create-order - :see: https://api-docs-v3.geniusyield.io/#create-order - :param str symbol: unified symbol of the market to create an order in - :param str type: 'market' or 'limit' - :param str side: 'buy' or 'sell' - :param float amount: how much of currency you want to trade in units of base currency - :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders - :param dict [params]: extra parameters specific to the exchange API endpoint - :param bool [params.test]: set to True to test an order, no order will be created but the request will be validated - :returns dict: an `order structure ` - """ - self.check_required_credentials() - await self.load_markets() - testOrder = self.safe_bool(params, 'test', False) - params = self.omit(params, 'test') - market = self.market(symbol) - nonce = self.uuidv1() - typeEnum = None - stopLossTypeEnums: dict = { - 'stopLoss': 3, - 'stopLossLimit': 4, - 'takeProfit': 5, - 'takeProfitLimit': 6, - } - stopPriceString = None - if (type == 'stopLossLimit') or (type == 'takeProfitLimit') or ('stopPrice' in params): - if not ('stopPrice' in params): - raise BadRequest(self.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders') - stopPriceString = self.price_to_precision(symbol, params['stopPrice']) - limitTypeEnums: dict = { - 'limit': 1, - 'limitMaker': 2, - } - priceString = None - typeLower = type.lower() - limitOrder = typeLower.find('limit') >= 0 - if type in limitTypeEnums: - typeEnum = limitTypeEnums[type] - priceString = self.price_to_precision(symbol, price) - elif type in stopLossTypeEnums: - typeEnum = stopLossTypeEnums[type] - priceString = self.price_to_precision(symbol, price) - elif type == 'market': - typeEnum = 0 - else: - raise BadRequest(self.id + ' ' + type + ' is not a valid order type') - amountEnum = 0 # base quantity - if 'quoteOrderQuantity' in params: - if type != 'market': - raise NotSupported(self.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders') - amountEnum = 1 - amount = self.safe_number(params, 'quoteOrderQuantity') - sideEnum = 0 if (side == 'buy') else 1 - walletBytes = self.remove0x_prefix(self.walletAddress) - network = self.safe_string(self.options, 'network', 'ETH') - orderVersion = self.get_supported_mapping(network, { - 'ETH': 1, - 'BSC': 2, - 'MATIC': 4, - }) - amountString = self.amount_to_precision(symbol, amount) - # https://docs.geniusyield.io/#time-in-force - timeInForceEnums: dict = { - 'gtc': 0, - 'ioc': 2, - 'fok': 3, - } - defaultTimeInForce = self.safe_string(self.options, 'defaultTimeInForce', 'gtc') - timeInForce = self.safe_string(params, 'timeInForce', defaultTimeInForce) - timeInForceEnum = None - if timeInForce in timeInForceEnums: - timeInForceEnum = timeInForceEnums[timeInForce] - else: - allOptions = list(timeInForceEnums.keys()) - asString = ', '.join(allOptions) - raise BadRequest(self.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString) - # https://docs.geniusyield.io/#self-trade-prevention - selfTradePreventionEnums: dict = { - 'dc': 0, - 'co': 1, - 'cn': 2, - 'cb': 3, - } - defaultSelfTradePrevention = self.safe_string(self.options, 'defaultSelfTradePrevention', 'cn') - selfTradePrevention = self.safe_string(params, 'selfTradePrevention', defaultSelfTradePrevention) - selfTradePreventionEnum = None - if selfTradePrevention in selfTradePreventionEnums: - selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention] - else: - allOptions = list(selfTradePreventionEnums.keys()) - asString = ', '.join(allOptions) - raise BadRequest(self.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString) - byteArray = [ - self.number_to_be(orderVersion, 1), - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - self.encode(market['id']), - self.number_to_be(typeEnum, 1), - self.number_to_be(sideEnum, 1), - self.encode(amountString), - self.number_to_be(amountEnum, 1), - ] - if limitOrder: - encodedPrice = self.encode(priceString) - byteArray.append(encodedPrice) - if type in stopLossTypeEnums: - encodedPrice = self.encode(stopPriceString or priceString) - byteArray.append(encodedPrice) - clientOrderId = self.safe_string(params, 'clientOrderId') - if clientOrderId is not None: - byteArray.append(self.encode(clientOrderId)) - after = [ - self.number_to_be(timeInForceEnum, 1), - self.number_to_be(selfTradePreventionEnum, 1), - self.number_to_be(0, 8), # unused - ] - allBytes = self.array_concat(byteArray, after) - binary = self.binary_concat_array(allBytes) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request: dict = { - 'parameters': { - 'nonce': nonce, - 'market': market['id'], - 'side': side, - 'type': type, - 'wallet': self.walletAddress, - 'selfTradePrevention': selfTradePrevention, - }, - 'signature': signature, - } - if type != 'market': - request['parameters']['timeInForce'] = timeInForce - if limitOrder: - request['parameters']['price'] = priceString - if type in stopLossTypeEnums: - request['parameters']['stopPrice'] = stopPriceString or priceString - if amountEnum == 0: - request['parameters']['quantity'] = amountString - else: - request['parameters']['quoteOrderQuantity'] = amountString - if clientOrderId is not None: - request['parameters']['clientOrderId'] = clientOrderId - # { - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "price": "1.00000000", - # "fills": [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txStatus": "pending" - # } - # ], - # "avgExecutionPrice": "0.09905990" - # } - # we don't use self.extend here because it is a signed endpoint - response = None - if testOrder: - response = await self.privatePostOrdersTest(request) - else: - response = await self.privatePostOrders(request) - return self.parse_order(response, market) - - async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}): - """ - make a withdrawal - :see: https://api-docs-v3.geniusyield.io/#withdraw-funds - :param str code: unified currency code - :param float amount: the amount to withdraw - :param str address: the address to withdraw to - :param str tag: - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `transaction structure ` - """ - tag, params = self.handle_withdraw_tag_and_params(tag, params) - self.check_required_credentials() - await self.load_markets() - nonce = self.uuidv1() - amountString = self.currency_to_precision(code, amount) - currency = self.currency(code) - walletBytes = self.remove0x_prefix(self.walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - self.encode(currency['id']), - self.encode(amountString), - self.number_to_be(1, 1), # bool set to True - ] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': address, - 'asset': currency['id'], - 'quantity': amountString, - }, - 'signature': signature, - } - response = await self.privatePostWithdrawals(request) - # - # { - # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - # "asset": "ETH", - # "assetContractAddress": "0x0000000000000000000000000000000000000000", - # "quantity": "0.20000000", - # "time": 1598962883190, - # "fee": "0.00024000", - # "txStatus": "pending", - # "txId": null - # } - # - return self.parse_transaction(response, currency) - - async def cancel_all_orders(self, symbol: Str = None, params={}): - """ - cancel all open orders - :see: https://api-docs-v3.geniusyield.io/#cancel-order - :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict[]: a list of `order structures ` - """ - self.check_required_credentials() - await self.load_markets() - market = None - if symbol is not None: - market = self.market(symbol) - nonce = self.uuidv1() - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': self.walletAddress, - }, - } - walletBytes = self.remove0x_prefix(self.walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - ] - if market is not None: - byteArray.append(self.encode(market['id'])) - request['parameters']['market'] = market['id'] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request['signature'] = signature - # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] - response = await self.privateDeleteOrders(self.extend(request, params)) - return self.parse_orders(response, market) - - async def cancel_order(self, id: str, symbol: Str = None, params={}): - """ - cancels an open order - :see: https://api-docs-v3.geniusyield.io/#cancel-order - :param str id: order id - :param str symbol: unified symbol of the market the order was made in - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: An `order structure ` - """ - self.check_required_credentials() - await self.load_markets() - market = None - if symbol is not None: - market = self.market(symbol) - nonce = self.uuidv1() - walletBytes = self.remove0x_prefix(self.walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - self.encode(id), - ] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': self.walletAddress, - 'orderId': id, - }, - 'signature': signature, - } - # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] - response = await self.privateDeleteOrders(self.extend(request, params)) - canceledOrder = self.safe_dict(response, 0) - return self.parse_order(canceledOrder, market) - - def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): - errorCode = self.safe_string(response, 'code') - message = self.safe_string(response, 'message') - if errorCode is not None: - self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, message) - raise ExchangeError(self.id + ' ' + message) - return None - - async def fetch_deposit(self, id: str, code: Str = None, params={}): - """ - fetch information on a deposit - :see: https://api-docs-v3.geniusyield.io/#get-deposits - :param str id: deposit id - :param str code: not used by geniusyield fetchDeposit() - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `transaction structure ` - """ - await self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - 'wallet': self.walletAddress, - 'depositId': id, - } - response = await self.privateGetDeposits(self.extend(request, params)) - return self.parse_transaction(response) - - async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: - """ - fetch all deposits made to an account - :see: https://api-docs-v3.geniusyield.io/#get-deposits - :param str code: unified currency code - :param int [since]: the earliest time in ms to fetch deposits for - :param int [limit]: the maximum number of deposits structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict[]: a list of `transaction structures ` - """ - params = self.extend({ - 'method': 'privateGetDeposits', - }, params) - return await self.fetch_transactions_helper(code, since, limit, params) - - async def fetch_status(self, params={}): - """ - the latest known information on the availability of the exchange API - :see: https://api-docs-v3.geniusyield.io/#get-ping - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `status structure ` - """ - response = await self.publicGetPing(params) - return { - 'status': 'ok', # if there's no Errors, status = 'ok' - 'updated': None, - 'eta': None, - 'url': None, - 'info': response, - } - - async def fetch_time(self, params={}): - """ - fetches the current integer timestamp in milliseconds from the exchange server - :see: https://api-docs-v3.geniusyield.io/#get-time - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns int: the current integer timestamp in milliseconds from the exchange server - """ - response = await self.publicGetTime(params) - # - # {serverTime: "1655258263236"} - # - return self.safe_integer(response, 'serverTime') - - async def fetch_withdrawal(self, id: str, code: Str = None, params={}): - """ - fetch data on a currency withdrawal via the withdrawal id - :see: https://api-docs-v3.geniusyield.io/#get-withdrawals - :param str id: withdrawal id - :param str code: not used by geniusyield.fetchWithdrawal - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `transaction structure ` - """ - await self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - 'wallet': self.walletAddress, - 'withdrawalId': id, - } - response = await self.privateGetWithdrawals(self.extend(request, params)) - return self.parse_transaction(response) - - async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: - """ - fetch all withdrawals made from an account - :see: https://api-docs-v3.geniusyield.io/#get-withdrawals - :param str code: unified currency code - :param int [since]: the earliest time in ms to fetch withdrawals for - :param int [limit]: the maximum number of withdrawals structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict[]: a list of `transaction structures ` - """ - params = self.extend({ - 'method': 'privateGetWithdrawals', - }, params) - return await self.fetch_transactions_helper(code, since, limit, params) - - async def fetch_transactions_helper(self, code: Str = None, since: Int = None, limit: Int = None, params={}): - await self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - 'wallet': self.walletAddress, - } - currency = None - if code is not None: - currency = self.currency(code) - request['asset'] = currency['id'] - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = limit - # [ - # { - # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - # "asset": "ETH", - # "quantity": "1.00000000", - # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - # "txTime": 1598865853000, - # "confirmationTime": 1598865930231 - # } - # ] - method = params['method'] - params = self.omit(params, 'method') - response = None - if method == 'privateGetDeposits': - response = await self.privateGetDeposits(self.extend(request, params)) - elif method == 'privateGetWithdrawals': - response = await self.privateGetWithdrawals(self.extend(request, params)) - else: - raise NotSupported(self.id + ' fetchTransactionsHelper() not support self method') - return self.parse_transactions(response, currency, since, limit) - - def parse_transaction_status(self, status: Str): - statuses: dict = { - 'mined': 'ok', - } - return self.safe_string(statuses, status, status) - - def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction: - # - # fetchDeposits - # - # { - # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - # "asset": "ETH", - # "quantity": "1.00000000", - # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - # "txTime": 1598865853000, - # "confirmationTime": 1598865930231 - # } - # - # fetchWithdrwalas - # - # { - # "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", - # "asset": "ETH", - # "assetContractAddress": "0x0000000000000000000000000000000000000000", - # "quantity": "0.20000000", - # "time": 1598962883288, - # "fee": "0.00024000", - # "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - # "txStatus": "mined" - # } - # - # withdraw - # - # { - # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - # "asset": "ETH", - # "assetContractAddress": "0x0000000000000000000000000000000000000000", - # "quantity": "0.20000000", - # "time": 1598962883190, - # "fee": "0.00024000", - # "txStatus": "pending", - # "txId": null - # } - # - type = None - if 'depositId' in transaction: - type = 'deposit' - elif ('withdrawId' in transaction) or ('withdrawalId' in transaction): - type = 'withdrawal' - id = self.safe_string_2(transaction, 'depositId', 'withdrawId') - id = self.safe_string(transaction, 'withdrawalId', id) - code = self.safe_currency_code(self.safe_string(transaction, 'asset'), currency) - amount = self.safe_number(transaction, 'quantity') - txid = self.safe_string(transaction, 'txId') - timestamp = self.safe_integer_2(transaction, 'txTime', 'time') - fee = None - if 'fee' in transaction: - fee = { - 'cost': self.safe_number(transaction, 'fee'), - 'currency': 'ETH', - } - rawStatus = self.safe_string(transaction, 'txStatus') - status = self.parse_transaction_status(rawStatus) - updated = self.safe_integer(transaction, 'confirmationTime') - return { - 'info': transaction, - 'id': id, - 'txid': txid, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'network': None, - 'address': None, - 'addressTo': None, - 'addressFrom': None, - 'tag': None, - 'tagTo': None, - 'tagFrom': None, - 'type': type, - 'amount': amount, - 'currency': code, - 'status': status, - 'updated': updated, - 'comment': None, - 'internal': None, - 'fee': fee, - } - - def calculate_rate_limiter_cost(self, api, method, path, params, config={}): - hasApiKey = (self.apiKey is not None) - hasSecret = (self.secret is not None) - hasWalletAddress = (self.walletAddress is not None) - hasPrivateKey = (self.privateKey is not None) - defaultCost = self.safe_value(config, 'cost', 1) - authenticated = hasApiKey and hasSecret and hasWalletAddress and hasPrivateKey - return(defaultCost / 2) if authenticated else defaultCost - - async def fetch_deposit_address(self, code: Str = None, params={}): - """ - fetch the Polygon address of the wallet - :see: https://api-docs-v3.geniusyield.io/#get-wallets - :param str code: not used by geniusyield - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: an `address structure ` - """ - request: dict = {} - request['nonce'] = self.uuidv1() - response = await self.privateGetWallets(self.extend(request, params)) - # - # [ - # { - # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - # totalPortfolioValueUsd: "0.00", - # time: "1678342148086" - # }, - # { - # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - # totalPortfolioValueUsd: "15.90", - # time: "1691697811659" - # } - # ] - # - return self.parse_deposit_address(response) - - def parse_deposit_address(self, depositAddress, currency: Currency = None): - # - # [ - # { - # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - # totalPortfolioValueUsd: "0.00", - # time: "1678342148086" - # }, - # { - # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - # totalPortfolioValueUsd: "15.90", - # time: "1691697811659" - # } - # ] - # - length = len(depositAddress) - entry = self.safe_dict(depositAddress, length - 1) - address = self.safe_string(entry, 'address') - self.check_address(address) - return { - 'info': depositAddress, - 'currency': None, - 'address': address, - 'tag': None, - 'network': 'MATIC', - } - def sign(self, path, api='public', method='GET', params={}, headers=None, body=None): - network = self.safe_string(self.options, 'network', 'ETH') - version = self.safe_string(self.options, 'version', 'v1') + network = self.safe_string(self.options, 'network', 'mainnet') + version = self.safe_string(self.options, 'version', 'v0') url = self.urls['api'][network] + '/' + version + '/' + path keys = list(params.keys()) length = len(keys) @@ -1726,41 +277,5 @@ def sign(self, path, api='public', method='GET', params={}, headers=None, body=N 'Content-Type': 'application/json', } if self.apiKey is not None: - headers['Genius Yield-API-Key'] = self.apiKey - if api == 'private': - payload = None - if method == 'GET': - payload = query - else: - payload = body - headers['Genius Yield-HMAC-Signature'] = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex') + headers['api-key'] = self.apiKey return {'url': url, 'method': method, 'body': body, 'headers': headers} - - def remove0x_prefix(self, hexData): - if hexData[0:2] == '0x': - return hexData[2:] - else: - return hexData - - def hash_message(self, message): - # takes a hex encoded message - binaryMessage = self.base16_to_binary(self.remove0x_prefix(message)) - prefix = self.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength) - return '0x' + self.hash(self.binary_concat(prefix, binaryMessage), 'keccak', 'hex') - - def sign_hash(self, hash, privateKey): - signature = self.ecdsa(hash[-64:], privateKey[-64:], 'secp256k1', None) - return { - 'r': '0x' + signature['r'], - 's': '0x' + signature['s'], - 'v': 27 + signature['v'], - } - - def sign_message(self, message, privateKey): - return self.sign_hash(self.hash_message(message), privateKey[-64:]) - - def sign_message_string(self, message, privateKey): - # still takes the input hex string - # same but returns a string instead of an object - signature = self.sign_message(message, privateKey) - return signature['r'] + self.remove0x_prefix(signature['s']) + self.binary_to_base16(self.number_to_be(signature['v'], 1)) diff --git a/python/ccxt/geniusyield.py b/python/ccxt/geniusyield.py index 5e3d66f84b65..7bb1c98d8e2f 100644 --- a/python/ccxt/geniusyield.py +++ b/python/ccxt/geniusyield.py @@ -5,28 +5,28 @@ from ccxt.base.exchange import Exchange from ccxt.abstract.geniusyield import ImplicitAPI -import hashlib -from ccxt.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction +from ccxt.base.types import Market, MarketInterface, Str from typing import List -from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError from ccxt.base.errors import BadRequest from ccxt.base.errors import InsufficientFunds from ccxt.base.errors import InvalidAddress from ccxt.base.errors import InvalidOrder -from ccxt.base.errors import NotSupported from ccxt.base.errors import DDoSProtection from ccxt.base.errors import ExchangeNotAvailable -from ccxt.base.decimal_to_precision import ROUND -from ccxt.base.decimal_to_precision import TRUNCATE -from ccxt.base.decimal_to_precision import DECIMAL_PLACES from ccxt.base.decimal_to_precision import TICK_SIZE from ccxt.base.decimal_to_precision import PAD_WITH_ZERO -from ccxt.base.precise import Precise class geniusyield(Exchange, ImplicitAPI): + def safe_market(self, marketId: Str = None, market: Market = None, delimiter: Str = None, marketType: Str = None) -> MarketInterface: + isOption = (marketId is not None) and ((marketId.find('-C') > -1) or (marketId.find('-P') > -1)) + if isOption and not (marketId in self.markets_by_id): + # handle expired option contracts + return self.create_expired_option_market(marketId) + return super(geniusyield, self).safe_market(marketId, market, delimiter, marketType) + def describe(self): return self.deep_extend(super(geniusyield, self).describe(), { 'id': 'geniusyield', @@ -46,7 +46,7 @@ def describe(self): 'future': False, 'option': False, 'addMargin': False, - 'cancelAllOrders': True, + 'cancelAllOrders': False, 'cancelOrder': True, 'cancelOrders': False, 'closeAllPositions': False, @@ -54,21 +54,21 @@ def describe(self): 'createDepositAddress': False, 'createOrder': True, 'createReduceOnlyOrder': False, - 'createStopLimitOrder': True, - 'createStopMarketOrder': True, - 'createStopOrder': True, + 'createStopLimitOrder': False, + 'createStopMarketOrder': False, + 'createStopOrder': False, 'fetchBalance': True, 'fetchBorrowRateHistories': False, 'fetchBorrowRateHistory': False, - 'fetchClosedOrders': True, + 'fetchClosedOrders': False, 'fetchCrossBorrowRate': False, 'fetchCrossBorrowRates': False, - 'fetchCurrencies': True, - 'fetchDeposit': True, - 'fetchDepositAddress': True, + 'fetchCurrencies': False, + 'fetchDeposit': False, + 'fetchDepositAddress': False, 'fetchDepositAddresses': False, 'fetchDepositAddressesByNetwork': False, - 'fetchDeposits': True, + 'fetchDeposits': False, 'fetchFundingHistory': False, 'fetchFundingRate': False, 'fetchFundingRateHistory': False, @@ -81,13 +81,13 @@ def describe(self): 'fetchMarginMode': False, 'fetchMarkets': True, 'fetchMarkOHLCV': False, - 'fetchMyTrades': True, - 'fetchOHLCV': True, + 'fetchMyTrades': False, + 'fetchOHLCV': False, 'fetchOpenInterestHistory': False, - 'fetchOpenOrders': True, - 'fetchOrder': True, - 'fetchOrderBook': True, - 'fetchOrders': False, + 'fetchOpenOrders': False, + 'fetchOrder': False, + 'fetchOrderBook': False, + 'fetchOrders': True, 'fetchPosition': False, 'fetchPositionHistory': False, 'fetchPositionMode': False, @@ -96,23 +96,23 @@ def describe(self): 'fetchPositionsHistory': False, 'fetchPositionsRisk': False, 'fetchPremiumIndexOHLCV': False, - 'fetchStatus': True, - 'fetchTicker': True, - 'fetchTickers': True, - 'fetchTime': True, - 'fetchTrades': True, + 'fetchStatus': False, + 'fetchTicker': False, + 'fetchTickers': False, + 'fetchTime': False, + 'fetchTrades': False, 'fetchTradingFee': False, - 'fetchTradingFees': True, + 'fetchTradingFees': False, 'fetchTransactions': False, - 'fetchWithdrawal': True, - 'fetchWithdrawals': True, + 'fetchWithdrawal': False, + 'fetchWithdrawals': False, 'reduceMargin': False, - 'sandbox': True, + 'sandbox': False, 'setLeverage': False, 'setMarginMode': False, 'setPositionMode': False, 'transfer': False, - 'withdraw': True, + 'withdraw': False, }, 'timeframes': { '1m': '1m', @@ -124,58 +124,34 @@ def describe(self): '1d': '1d', }, 'urls': { - 'test': { - 'MATIC': 'https://api-sandbox-matic.geniusyield.io', - }, - 'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg', 'api': { - 'MATIC': 'https://api-matic.geniusyield.io', + 'preprod': 'http://localhost:8082', + 'mainnet': 'http://localhost:8082', }, - 'www': 'https://geniusyield.io', + 'www': 'https://www.geniusyield.co/', 'doc': [ - 'https://api-docs-v3.geniusyield.io/', + 'https://github.com/geniusyield/dex-contracts-api?tab=readme-ov-file#geniusyield-dex', ], }, 'api': { 'public': { 'get': { - 'ping': 1, - 'time': 1, - 'exchange': 1, - 'assets': 1, - 'markets': 1, - 'tickers': 1, - 'candles': 1, - 'trades': 1, - 'orderbook': 1, }, }, 'private': { 'get': { - 'user': 1, - 'wallets': 1, - 'balances': 1, - 'orders': 0.1, - 'fills': 0.1, - 'deposits': 1, - 'withdrawals': 1, - 'wsToken': 1, - }, - 'post': { - 'wallets': 1, - 'orders': 0.1, - 'orders/test': 0.1, - 'withdrawals': 1, - }, - 'delete': { - 'orders': 0.1, + 'markets': 10, + 'trading-fees': 10, }, }, }, + 'headers': { + 'X-Gate-Channel-Id': 'ccxt', + }, 'options': { - 'defaultTimeInForce': 'gtc', + 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', - 'network': 'MATIC', + 'network': 'mainnet', }, 'exceptions': { 'exact': { @@ -189,28 +165,16 @@ def describe(self): }, }, 'requiredCredentials': { - 'walletAddress': True, - 'privateKey': True, + 'walletAddress': False, + 'privateKey': False, 'apiKey': True, - 'secret': True, + 'secret': False, }, 'precisionMode': TICK_SIZE, 'paddingMode': PAD_WITH_ZERO, 'commonCurrencies': {}, }) - def price_to_precision(self, symbol, price): - # - # we override priceToPrecision to fix the following issue - # https://github.com/ccxt/ccxt/issues/13367 - # {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided, and always require 4 decimals ending with 4 zeroes"} - # - market = self.market(symbol) - info = self.safe_value(market, 'info', {}) - quoteAssetPrecision = self.safe_integer(info, 'quoteAssetPrecision') - price = self.decimal_to_precision(price, ROUND, market['precision']['price'], self.precisionMode) - return self.decimal_to_precision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO) - def fetch_markets(self, params={}) -> List[Market]: """ retrieves data on all markets for geniusyield @@ -218,76 +182,34 @@ def fetch_markets(self, params={}) -> List[Market]: :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: an array of objects representing market data """ - response = self.publicGetMarkets(params) - # - # [ - # { - # "market": "ETH-USDC", - # "type": "hybrid", - # "status": "activeHybrid", - # "baseAsset": "ETH", - # "baseAssetPrecision": "8", - # "quoteAsset": "USDC", - # "quoteAssetPrecision": "8", - # "makerFeeRate": "0.0000", - # "takerFeeRate": "0.2500", - # "takerIdexFeeRate": "0.0500", - # "takerLiquidityProviderFeeRate": "0.2000", - # "tickSize": "0.01000000" - # }, - # ] - # - response2 = self.publicGetExchange() - # + markets = self.privateGetMarkets(params) + # [ # { - # "timeZone": "UTC", - # "serverTime": "1654460599952", - # "maticDepositContractAddress": "0x3253a7e75539edaeb1db608ce6ef9aa1ac9126b6", - # "maticCustodyContractAddress": "0x3bcc4eca0a40358558ca8d1bcd2d1dbde63eb468", - # "maticUsdPrice": "0.60", - # "gasPrice": "180", - # "volume24hUsd": "10015814.46", - # "totalVolumeUsd": "1589273533.28", - # "totalTrades": "1534904", - # "totalValueLockedUsd": "12041929.44", - # "geniusyieldStakingValueLockedUsd": "20133816.98", - # "geniusyieldTokenAddress": "0x9Cb74C8032b007466865f060ad2c46145d45553D", - # "geniusyieldUsdPrice": "0.07", - # "geniusyieldMarketCapUsd": "48012346.00", - # "makerFeeRate": "0.0000", - # "takerFeeRate": "0.0025", - # "takerIdexFeeRate": "0.0005", - # "takerLiquidityProviderFeeRate": "0.0020", - # "makerTradeMinimum": "10.00000000", - # "takerTradeMinimum": "1.00000000", - # "withdrawMinimum": "0.50000000", - # "liquidityAdditionMinimum": "0.50000000", - # "liquidityRemovalMinimum": "0.40000000", - # "blockConfirmationDelay": "64" + # "market_id": "lovelace_dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53", + # "base_asset": "lovelace", + # "target_asset": "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53" # } - # - maker = self.safe_number(response2, 'makerFeeRate') - taker = self.safe_number(response2, 'takerFeeRate') - makerMin = self.safe_string(response2, 'makerTradeMinimum') - takerMin = self.safe_string(response2, 'takerTradeMinimum') - minCostETH = self.parse_number(Precise.string_min(makerMin, takerMin)) + # ] + fees = self.privateGetTradingFees() + # { + # "flat_maker_fee": "1000000", + # "flat_taker_fee": "1000000", + # "percentage_maker_fee": "0.3", + # "percentage_taker_fee": "0.3" + # } + maker = self.safe_number(fees, 'percentage_maker_fee') + taker = self.safe_number(fees, 'percentage_taker_fee') result = [] - for i in range(0, len(response)): - entry = response[i] - marketId = self.safe_string(entry, 'market') - baseId = self.safe_string(entry, 'baseAsset') - quoteId = self.safe_string(entry, 'quoteAsset') + for i in range(0, len(markets)): + entry = markets[i] + marketId = self.safe_string(entry, 'market_id') + baseId = self.safe_string(entry, 'base_asset') + quoteId = self.safe_string(entry, 'target_asset') base = self.safe_currency_code(baseId) quote = self.safe_currency_code(quoteId) - basePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'baseAssetPrecision'))) - quotePrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'quoteAssetPrecision'))) - status = self.safe_string(entry, 'status') - minCost = None - if quote == 'ETH': - minCost = minCostETH result.append({ 'id': marketId, - 'symbol': base + '/' + quote, + 'symbol': marketId, 'base': base, 'quote': quote, 'settle': None, @@ -300,7 +222,7 @@ def fetch_markets(self, params={}) -> List[Market]: 'swap': False, 'future': False, 'option': False, - 'active': (status != 'inactive'), + 'active': True, 'contract': False, 'linear': None, 'inverse': None, @@ -312,8 +234,8 @@ def fetch_markets(self, params={}) -> List[Market]: 'strike': None, 'optionType': None, 'precision': { - 'amount': basePrecision, - 'price': self.safe_number(entry, 'tickSize'), + 'amount': None, + 'price': None, }, 'limits': { 'leverage': { @@ -321,15 +243,15 @@ def fetch_markets(self, params={}) -> List[Market]: 'max': None, }, 'amount': { - 'min': basePrecision, + 'min': None, 'max': None, }, 'price': { - 'min': quotePrecision, + 'min': None, 'max': None, }, 'cost': { - 'min': minCost, + 'min': None, 'max': None, }, }, @@ -338,1380 +260,9 @@ def fetch_markets(self, params={}) -> List[Market]: }) return result - def fetch_ticker(self, symbol: str, params={}) -> Ticker: - """ - fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market - :see: https://api-docs-v3.geniusyield.io/#get-tickers - :param str symbol: unified symbol of the market to fetch the ticker for - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `ticker structure ` - """ - self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - } - # [ - # { - # "market": "DIL-ETH", - # "time": 1598367493008, - # "open": "0.09695361", - # "high": "0.10245881", - # "low": "0.09572507", - # "close": "0.09917079", - # "closeQuantity": "0.71320950", - # "baseVolume": "309.17380612", - # "quoteVolume": "30.57633981", - # "percentChange": "2.28", - # "numTrades": 205, - # "ask": "0.09910476", - # "bid": "0.09688340", - # "sequence": 3902 - # } - # ] - response = self.publicGetTickers(self.extend(request, params)) - ticker = self.safe_dict(response, 0) - return self.parse_ticker(ticker, market) - - def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers: - """ - fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - :see: https://api-docs-v3.geniusyield.io/#get-tickers - :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a dictionary of `ticker structures ` - """ - self.load_markets() - # [ - # { - # "market": "DIL-ETH", - # "time": 1598367493008, - # "open": "0.09695361", - # "high": "0.10245881", - # "low": "0.09572507", - # "close": "0.09917079", - # "closeQuantity": "0.71320950", - # "baseVolume": "309.17380612", - # "quoteVolume": "30.57633981", - # "percentChange": "2.28", - # "numTrades": 205, - # "ask": "0.09910476", - # "bid": "0.09688340", - # "sequence": 3902 - # }, ... - # ] - response = self.publicGetTickers(params) - return self.parse_tickers(response, symbols) - - def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker: - # { - # "market": "DIL-ETH", - # "time": 1598367493008, - # "open": "0.09695361", - # "high": "0.10245881", - # "low": "0.09572507", - # "close": "0.09917079", - # "closeQuantity": "0.71320950", - # "baseVolume": "309.17380612", - # "quoteVolume": "30.57633981", - # "percentChange": "2.28", - # "numTrades": 205, - # "ask": "0.09910476", - # "bid": "0.09688340", - # "sequence": 3902 - # } - marketId = self.safe_string(ticker, 'market') - market = self.safe_market(marketId, market, '-') - symbol = market['symbol'] - timestamp = self.safe_integer(ticker, 'time') - close = self.safe_string(ticker, 'close') - return self.safe_ticker({ - 'symbol': symbol, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'high': self.safe_string(ticker, 'high'), - 'low': self.safe_string(ticker, 'low'), - 'bid': self.safe_string(ticker, 'bid'), - 'bidVolume': None, - 'ask': self.safe_string(ticker, 'ask'), - 'askVolume': None, - 'vwap': None, - 'open': self.safe_string(ticker, 'open'), - 'close': close, - 'last': close, - 'previousClose': None, - 'change': None, - 'percentage': self.safe_string(ticker, 'percentChange'), - 'average': None, - 'baseVolume': self.safe_string(ticker, 'baseVolume'), - 'quoteVolume': self.safe_string(ticker, 'quoteVolume'), - 'info': ticker, - }, market) - - def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]: - """ - fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market - :see: https://api-docs-v3.geniusyield.io/#get-candles - :param str symbol: unified symbol of the market to fetch OHLCV data for - :param str timeframe: the length of time each candle represents - :param int [since]: timestamp in ms of the earliest candle to fetch - :param int [limit]: the maximum amount of candles to fetch - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns int[][]: A list of candles ordered, open, high, low, close, volume - """ - self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - 'interval': timeframe, - } - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = min(limit, 1000) - response = self.publicGetCandles(self.extend(request, params)) - if isinstance(response, list): - # [ - # { - # "start": 1598345580000, - # "open": "0.09771286", - # "high": "0.09771286", - # "low": "0.09771286", - # "close": "0.09771286", - # "volume": "1.45340410", - # "sequence": 3853 - # }, ... - # ] - return self.parse_ohlcvs(response, market, timeframe, since, limit) - else: - # {"nextTime":1595536440000} - return [] - - def parse_ohlcv(self, ohlcv, market: Market = None) -> list: - # { - # "start": 1598345580000, - # "open": "0.09771286", - # "high": "0.09771286", - # "low": "0.09771286", - # "close": "0.09771286", - # "volume": "1.45340410", - # "sequence": 3853 - # } - timestamp = self.safe_integer(ohlcv, 'start') - open = self.safe_number(ohlcv, 'open') - high = self.safe_number(ohlcv, 'high') - low = self.safe_number(ohlcv, 'low') - close = self.safe_number(ohlcv, 'close') - volume = self.safe_number(ohlcv, 'volume') - return [timestamp, open, high, low, close, volume] - - def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: - """ - get the list of most recent trades for a particular symbol - :see: https://api-docs-v3.geniusyield.io/#get-trades - :param str symbol: unified symbol of the market to fetch trades for - :param int [since]: timestamp in ms of the earliest trade to fetch - :param int [limit]: the maximum amount of trades to fetch - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Trade[]: a list of `trade structures ` - """ - self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - } - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = min(limit, 1000) - # [ - # { - # "fillId": "b5467d00-b13e-3fa9-8216-dd66735550fc", - # "price": "0.09771286", - # "quantity": "1.45340410", - # "quoteQuantity": "0.14201627", - # "time": 1598345638994, - # "makerSide": "buy", - # "sequence": 3853 - # }, ... - # ] - response = self.publicGetTrades(self.extend(request, params)) - return self.parse_trades(response, market, since, limit) - - def parse_trade(self, trade: dict, market: Market = None) -> Trade: - # - # public trades - # { - # "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1", - # "price":"0.20377008", - # "quantity":"47.58448728", - # "quoteQuantity":"9.69629509", - # "time":1642091300873, - # "makerSide":"buy", - # "type":"hybrid", # one of either: "orderBook", "hybrid", or "pool" - # "sequence":31876 - # } - # - # private trades - # { - # "fillId":"83429066-9334-3582-b710-78858b2f0d6b", - # "price":"0.20717368", - # "quantity":"15.00000000", - # "quoteQuantity":"3.10760523", - # "orderBookQuantity":"0.00000003", - # "orderBookQuoteQuantity":"0.00000001", - # "poolQuantity":"14.99999997", - # "poolQuoteQuantity":"3.10760522", - # "time":1642083351215, - # "makerSide":"sell", - # "sequence":31795, - # "market":"Genius Yield-USDC", - # "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c", - # "side":"buy", - # "fee":"0.03749989", - # "feeAsset":"Genius Yield", - # "gas":"0.40507261", - # "liquidity":"taker", - # "type":"hybrid", - # "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815", - # "txStatus":"mined" - # } - # - id = self.safe_string(trade, 'fillId') - priceString = self.safe_string(trade, 'price') - amountString = self.safe_string(trade, 'quantity') - costString = self.safe_string(trade, 'quoteQuantity') - timestamp = self.safe_integer(trade, 'time') - marketId = self.safe_string(trade, 'market') - symbol = self.safe_symbol(marketId, market, '-') - # self code handles the duality of public vs private trades - makerSide = self.safe_string(trade, 'makerSide') - oppositeSide = 'sell' if (makerSide == 'buy') else 'buy' - side = self.safe_string(trade, 'side', oppositeSide) - takerOrMaker = self.safe_string(trade, 'liquidity', 'taker') - feeCostString = self.safe_string(trade, 'fee') - fee = None - if feeCostString is not None: - feeCurrencyId = self.safe_string(trade, 'feeAsset') - fee = { - 'cost': feeCostString, - 'currency': self.safe_currency_code(feeCurrencyId), - } - orderId = self.safe_string(trade, 'orderId') - return self.safe_trade({ - 'info': trade, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'symbol': symbol, - 'id': id, - 'order': orderId, - 'type': 'limit', - 'side': side, - 'takerOrMaker': takerOrMaker, - 'price': priceString, - 'amount': amountString, - 'cost': costString, - 'fee': fee, - }, market) - - def fetch_trading_fees(self, params={}) -> TradingFees: - """ - fetch the trading fees for multiple markets - :see: https://api-docs-v3.geniusyield.io/#get-api-account - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a dictionary of `fee structures ` indexed by market symbols - """ - self.check_required_credentials() - self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - } - response = None - response = self.privateGetUser(self.extend(request, params)) - # - # { - # "depositEnabled": True, - # "orderEnabled": True, - # "cancelEnabled": True, - # "withdrawEnabled": True, - # "totalPortfolioValueUsd": "0.00", - # "makerFeeRate": "0.0000", - # "takerFeeRate": "0.0025", - # "takerIdexFeeRate": "0.0005", - # "takerLiquidityProviderFeeRate": "0.0020" - # } - # - maker = self.safe_number(response, 'makerFeeRate') - taker = self.safe_number(response, 'takerFeeRate') - result: dict = {} - for i in range(0, len(self.symbols)): - symbol = self.symbols[i] - result[symbol] = { - 'info': response, - 'symbol': symbol, - 'maker': maker, - 'taker': taker, - 'percentage': True, - 'tierBased': False, - } - return result - - def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: - """ - fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data - :see: https://api-docs-v3.geniusyield.io/#get-order-books - :param str symbol: unified symbol of the market to fetch the order book for - :param int [limit]: the maximum amount of order book entries to return - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: A dictionary of `order book structures ` indexed by market symbols - """ - self.load_markets() - market = self.market(symbol) - request: dict = { - 'market': market['id'], - 'level': 2, - } - if limit is not None: - request['limit'] = limit - # { - # "sequence": 36416753, - # "bids": [ - # ['0.09672815', "8.22284267", 1], - # ['0.09672814', "1.83685554", 1], - # ['0.09672143', "4.10962617", 1], - # ['0.09658884', "4.03863759", 1], - # ['0.09653781', "3.35730684", 1], - # ['0.09624660', "2.54163586", 1], - # ['0.09617490', "1.93065030", 1] - # ], - # "asks": [ - # ['0.09910476', "3.22840154", 1], - # ['0.09940587', "3.39796593", 1], - # ['0.09948189', "4.25088898", 1], - # ['0.09958362', "2.42195784", 1], - # ['0.09974393', "4.25234367", 1], - # ['0.09995250', "3.40192141", 1] - # ] - # } - response = self.publicGetOrderbook(self.extend(request, params)) - nonce = self.safe_integer(response, 'sequence') - return { - 'symbol': symbol, - 'timestamp': None, - 'datetime': None, - 'nonce': nonce, - 'bids': self.parse_side(response, 'bids'), - 'asks': self.parse_side(response, 'asks'), - } - - def parse_side(self, book, side): - bookSide = self.safe_value(book, side, []) - result = [] - for i in range(0, len(bookSide)): - order = bookSide[i] - price = self.safe_number(order, 0) - amount = self.safe_number(order, 1) - orderCount = self.safe_integer(order, 2) - result.append([price, amount, orderCount]) - descending = side == 'bids' - return self.sort_by(result, 0, descending) - - def fetch_currencies(self, params={}) -> Currencies: - """ - fetches all available currencies on an exchange - :see: https://api-docs-v3.geniusyield.io/#get-assets - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: an associative dictionary of currencies - """ - response = self.publicGetAssets(params) - # - # [ - # { - # "name": "Ethereum", - # "symbol": "ETH", - # "contractAddress": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - # "assetDecimals": "18", - # "exchangeDecimals": "8", - # "maticPrice": "3029.38503483" - # }, - # ] - # - result: dict = {} - for i in range(0, len(response)): - entry = response[i] - name = self.safe_string(entry, 'name') - currencyId = self.safe_string(entry, 'symbol') - code = self.safe_currency_code(currencyId) - precision = self.parse_number(self.parse_precision(self.safe_string(entry, 'exchangeDecimals'))) - result[code] = { - 'id': currencyId, - 'code': code, - 'info': entry, - 'type': None, - 'name': name, - 'active': None, - 'deposit': None, - 'withdraw': None, - 'fee': None, - 'precision': precision, - 'limits': { - 'amount': {'min': precision, 'max': None}, - 'withdraw': {'min': precision, 'max': None}, - }, - } - return result - - def parse_balance(self, response) -> Balances: - result: dict = { - 'info': response, - 'timestamp': None, - 'datetime': None, - } - for i in range(0, len(response)): - entry = response[i] - currencyId = self.safe_string(entry, 'asset') - code = self.safe_currency_code(currencyId) - account = self.account() - account['total'] = self.safe_string(entry, 'quantity') - account['free'] = self.safe_string(entry, 'availableForTrade') - account['used'] = self.safe_string(entry, 'locked') - result[code] = account - return self.safe_balance(result) - - def fetch_balance(self, params={}) -> Balances: - """ - query for balance and get the amount of funds available for trading or funds locked in orders - :see: https://api-docs-v3.geniusyield.io/#get-balances - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `balance structure ` - """ - self.check_required_credentials() - self.load_markets() - nonce1 = self.uuidv1() - request: dict = { - 'nonce': nonce1, - 'wallet': self.walletAddress, - } - # [ - # { - # "asset": "DIL", - # "quantity": "0.00000000", - # "availableForTrade": "0.00000000", - # "locked": "0.00000000", - # "usdValue": null - # }, ... - # ] - extendedRequest = self.extend(request, params) - if extendedRequest['wallet'] is None: - raise BadRequest(self.id + ' fetchBalance() wallet is None, set self.walletAddress or "address" in params') - response = None - try: - response = self.privateGetBalances(extendedRequest) - except Exception as e: - if isinstance(e, InvalidAddress): - walletAddress = extendedRequest['wallet'] - self.associate_wallet(walletAddress) - response = self.privateGetBalances(extendedRequest) - else: - raise e - return self.parse_balance(response) - - def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): - """ - fetch all trades made by the user - :see: https://api-docs-v3.geniusyield.io/#get-fills - :param str symbol: unified market symbol - :param int [since]: the earliest time in ms to fetch trades for - :param int [limit]: the maximum number of trades structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Trade[]: a list of `trade structures ` - """ - self.check_required_credentials() - self.load_markets() - market = None - request: dict = { - 'nonce': self.uuidv1(), - 'wallet': self.walletAddress, - } - if symbol is not None: - market = self.market(symbol) - request['market'] = market['id'] - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = limit - # [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478762, - # "makerSide": "sell", - # "sequence": 5053, - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "side": "buy", - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined" - # } - # ] - extendedRequest = self.extend(request, params) - if extendedRequest['wallet'] is None: - raise BadRequest(self.id + ' fetchMyTrades() walletAddress is None, set self.walletAddress or "address" in params') - response = None - try: - response = self.privateGetFills(extendedRequest) - except Exception as e: - if isinstance(e, InvalidAddress): - walletAddress = extendedRequest['wallet'] - self.associate_wallet(walletAddress) - response = self.privateGetFills(extendedRequest) - else: - raise e - return self.parse_trades(response, market, since, limit) - - def fetch_order(self, id: str, symbol: Str = None, params={}): - """ - fetches information on an order made by the user - :see: https://api-docs-v3.geniusyield.io/#get-orders - :param str symbol: unified symbol of the market the order was made in - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: An `order structure ` - """ - request: dict = { - 'orderId': id, - } - return self.fetch_orders_helper(symbol, None, None, self.extend(request, params)) - - def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: - """ - fetch all unfilled currently open orders - :see: https://api-docs-v3.geniusyield.io/#get-orders - :param str symbol: unified market symbol - :param int [since]: the earliest time in ms to fetch open orders for - :param int [limit]: the maximum number of open orders structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Order[]: a list of `order structures ` - """ - request: dict = { - 'closed': False, - } - return self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) - - def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: - """ - fetches information on multiple closed orders made by the user - :see: https://api-docs-v3.geniusyield.io/#get-orders - :param str symbol: unified market symbol of the market orders were made in - :param int [since]: the earliest time in ms to fetch orders for - :param int [limit]: the maximum number of order structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns Order[]: a list of `order structures ` - """ - request: dict = { - 'closed': True, - } - return self.fetch_orders_helper(symbol, since, limit, self.extend(request, params)) - - def fetch_orders_helper(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): - self.load_markets() - request: dict = { - 'nonce': self.uuidv1(), - 'wallet': self.walletAddress, - } - market = None - if symbol is not None: - market = self.market(symbol) - request['market'] = market['id'] - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = limit - response = self.privateGetOrders(self.extend(request, params)) - # fetchClosedOrders / fetchOpenOrders - # [ - # { - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "avgExecutionPrice": "0.09905990", - # "price": "1.00000000", - # "fills": [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined" - # } - # ] - # } - # ] - # fetchOrder - # {market: "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "avgExecutionPrice": "0.09905990", - # "price": "1.00000000", - # "fills": - # [{fillId: "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined"}]} - if isinstance(response, list): - return self.parse_orders(response, market, since, limit) - else: - return self.parse_order(response, market) - - def parse_order_status(self, status: Str): - # https://docs.geniusyield.io/#order-states-amp-lifecycle - statuses: dict = { - 'active': 'open', - 'partiallyFilled': 'open', - 'rejected': 'canceled', - 'filled': 'closed', - } - return self.safe_string(statuses, status, status) - - def parse_order(self, order: dict, market: Market = None) -> Order: - # - # { - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "avgExecutionPrice": "0.09905990", - # "price": "1.00000000", - # "fills": [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65", - # "txStatus": "mined" - # } - # ] - # } - # - timestamp = self.safe_integer(order, 'time') - fills = self.safe_value(order, 'fills', []) - id = self.safe_string(order, 'orderId') - clientOrderId = self.safe_string(order, 'clientOrderId') - marketId = self.safe_string(order, 'market') - side = self.safe_string(order, 'side') - symbol = self.safe_symbol(marketId, market, '-') - type = self.safe_string(order, 'type') - amount = self.safe_string(order, 'originalQuantity') - filled = self.safe_string(order, 'executedQuantity') - average = self.safe_string(order, 'avgExecutionPrice') - price = self.safe_string(order, 'price') - rawStatus = self.safe_string(order, 'status') - timeInForce = self.safe_string_upper(order, 'timeInForce') - status = self.parse_order_status(rawStatus) - return self.safe_order({ - 'info': order, - 'id': id, - 'clientOrderId': clientOrderId, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'lastTradeTimestamp': None, - 'symbol': symbol, - 'type': type, - 'timeInForce': timeInForce, - 'postOnly': None, - 'side': side, - 'price': price, - 'stopPrice': None, - 'triggerPrice': None, - 'amount': amount, - 'cost': None, - 'average': average, - 'filled': filled, - 'remaining': None, - 'status': status, - 'fee': None, - 'trades': fills, - }, market) - - def associate_wallet(self, walletAddress, params={}): - nonce = self.uuidv1() - noPrefix = self.remove0x_prefix(walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(noPrefix), - ] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - # { - # "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "totalPortfolioValueUsd": "0.00", - # "time": 1598468353626 - # } - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': walletAddress, - }, - 'signature': signature, - } - result = self.privatePostWallets(request) - return result - - def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): - """ - create a trade order, https://docs.geniusyield.io/#create-order - :see: https://api-docs-v3.geniusyield.io/#create-order - :param str symbol: unified symbol of the market to create an order in - :param str type: 'market' or 'limit' - :param str side: 'buy' or 'sell' - :param float amount: how much of currency you want to trade in units of base currency - :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders - :param dict [params]: extra parameters specific to the exchange API endpoint - :param bool [params.test]: set to True to test an order, no order will be created but the request will be validated - :returns dict: an `order structure ` - """ - self.check_required_credentials() - self.load_markets() - testOrder = self.safe_bool(params, 'test', False) - params = self.omit(params, 'test') - market = self.market(symbol) - nonce = self.uuidv1() - typeEnum = None - stopLossTypeEnums: dict = { - 'stopLoss': 3, - 'stopLossLimit': 4, - 'takeProfit': 5, - 'takeProfitLimit': 6, - } - stopPriceString = None - if (type == 'stopLossLimit') or (type == 'takeProfitLimit') or ('stopPrice' in params): - if not ('stopPrice' in params): - raise BadRequest(self.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders') - stopPriceString = self.price_to_precision(symbol, params['stopPrice']) - limitTypeEnums: dict = { - 'limit': 1, - 'limitMaker': 2, - } - priceString = None - typeLower = type.lower() - limitOrder = typeLower.find('limit') >= 0 - if type in limitTypeEnums: - typeEnum = limitTypeEnums[type] - priceString = self.price_to_precision(symbol, price) - elif type in stopLossTypeEnums: - typeEnum = stopLossTypeEnums[type] - priceString = self.price_to_precision(symbol, price) - elif type == 'market': - typeEnum = 0 - else: - raise BadRequest(self.id + ' ' + type + ' is not a valid order type') - amountEnum = 0 # base quantity - if 'quoteOrderQuantity' in params: - if type != 'market': - raise NotSupported(self.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders') - amountEnum = 1 - amount = self.safe_number(params, 'quoteOrderQuantity') - sideEnum = 0 if (side == 'buy') else 1 - walletBytes = self.remove0x_prefix(self.walletAddress) - network = self.safe_string(self.options, 'network', 'ETH') - orderVersion = self.get_supported_mapping(network, { - 'ETH': 1, - 'BSC': 2, - 'MATIC': 4, - }) - amountString = self.amount_to_precision(symbol, amount) - # https://docs.geniusyield.io/#time-in-force - timeInForceEnums: dict = { - 'gtc': 0, - 'ioc': 2, - 'fok': 3, - } - defaultTimeInForce = self.safe_string(self.options, 'defaultTimeInForce', 'gtc') - timeInForce = self.safe_string(params, 'timeInForce', defaultTimeInForce) - timeInForceEnum = None - if timeInForce in timeInForceEnums: - timeInForceEnum = timeInForceEnums[timeInForce] - else: - allOptions = list(timeInForceEnums.keys()) - asString = ', '.join(allOptions) - raise BadRequest(self.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString) - # https://docs.geniusyield.io/#self-trade-prevention - selfTradePreventionEnums: dict = { - 'dc': 0, - 'co': 1, - 'cn': 2, - 'cb': 3, - } - defaultSelfTradePrevention = self.safe_string(self.options, 'defaultSelfTradePrevention', 'cn') - selfTradePrevention = self.safe_string(params, 'selfTradePrevention', defaultSelfTradePrevention) - selfTradePreventionEnum = None - if selfTradePrevention in selfTradePreventionEnums: - selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention] - else: - allOptions = list(selfTradePreventionEnums.keys()) - asString = ', '.join(allOptions) - raise BadRequest(self.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString) - byteArray = [ - self.number_to_be(orderVersion, 1), - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - self.encode(market['id']), - self.number_to_be(typeEnum, 1), - self.number_to_be(sideEnum, 1), - self.encode(amountString), - self.number_to_be(amountEnum, 1), - ] - if limitOrder: - encodedPrice = self.encode(priceString) - byteArray.append(encodedPrice) - if type in stopLossTypeEnums: - encodedPrice = self.encode(stopPriceString or priceString) - byteArray.append(encodedPrice) - clientOrderId = self.safe_string(params, 'clientOrderId') - if clientOrderId is not None: - byteArray.append(self.encode(clientOrderId)) - after = [ - self.number_to_be(timeInForceEnum, 1), - self.number_to_be(selfTradePreventionEnum, 1), - self.number_to_be(0, 8), # unused - ] - allBytes = self.array_concat(byteArray, after) - binary = self.binary_concat_array(allBytes) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request: dict = { - 'parameters': { - 'nonce': nonce, - 'market': market['id'], - 'side': side, - 'type': type, - 'wallet': self.walletAddress, - 'selfTradePrevention': selfTradePrevention, - }, - 'signature': signature, - } - if type != 'market': - request['parameters']['timeInForce'] = timeInForce - if limitOrder: - request['parameters']['price'] = priceString - if type in stopLossTypeEnums: - request['parameters']['stopPrice'] = stopPriceString or priceString - if amountEnum == 0: - request['parameters']['quantity'] = amountString - else: - request['parameters']['quoteOrderQuantity'] = amountString - if clientOrderId is not None: - request['parameters']['clientOrderId'] = clientOrderId - # { - # "market": "DIL-ETH", - # "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63", - # "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1", - # "time": 1598873478650, - # "status": "filled", - # "type": "limit", - # "side": "buy", - # "originalQuantity": "0.40000000", - # "executedQuantity": "0.40000000", - # "cumulativeQuoteQuantity": "0.03962396", - # "price": "1.00000000", - # "fills": [ - # { - # "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472", - # "price": "0.09905990", - # "quantity": "0.40000000", - # "quoteQuantity": "0.03962396", - # "time": 1598873478650, - # "makerSide": "sell", - # "sequence": 5053, - # "fee": "0.00080000", - # "feeAsset": "DIL", - # "gas": "0.00857497", - # "liquidity": "taker", - # "txStatus": "pending" - # } - # ], - # "avgExecutionPrice": "0.09905990" - # } - # we don't use self.extend here because it is a signed endpoint - response = None - if testOrder: - response = self.privatePostOrdersTest(request) - else: - response = self.privatePostOrders(request) - return self.parse_order(response, market) - - def withdraw(self, code: str, amount: float, address: str, tag=None, params={}): - """ - make a withdrawal - :see: https://api-docs-v3.geniusyield.io/#withdraw-funds - :param str code: unified currency code - :param float amount: the amount to withdraw - :param str address: the address to withdraw to - :param str tag: - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `transaction structure ` - """ - tag, params = self.handle_withdraw_tag_and_params(tag, params) - self.check_required_credentials() - self.load_markets() - nonce = self.uuidv1() - amountString = self.currency_to_precision(code, amount) - currency = self.currency(code) - walletBytes = self.remove0x_prefix(self.walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - self.encode(currency['id']), - self.encode(amountString), - self.number_to_be(1, 1), # bool set to True - ] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': address, - 'asset': currency['id'], - 'quantity': amountString, - }, - 'signature': signature, - } - response = self.privatePostWithdrawals(request) - # - # { - # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - # "asset": "ETH", - # "assetContractAddress": "0x0000000000000000000000000000000000000000", - # "quantity": "0.20000000", - # "time": 1598962883190, - # "fee": "0.00024000", - # "txStatus": "pending", - # "txId": null - # } - # - return self.parse_transaction(response, currency) - - def cancel_all_orders(self, symbol: Str = None, params={}): - """ - cancel all open orders - :see: https://api-docs-v3.geniusyield.io/#cancel-order - :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict[]: a list of `order structures ` - """ - self.check_required_credentials() - self.load_markets() - market = None - if symbol is not None: - market = self.market(symbol) - nonce = self.uuidv1() - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': self.walletAddress, - }, - } - walletBytes = self.remove0x_prefix(self.walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - ] - if market is not None: - byteArray.append(self.encode(market['id'])) - request['parameters']['market'] = market['id'] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request['signature'] = signature - # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] - response = self.privateDeleteOrders(self.extend(request, params)) - return self.parse_orders(response, market) - - def cancel_order(self, id: str, symbol: Str = None, params={}): - """ - cancels an open order - :see: https://api-docs-v3.geniusyield.io/#cancel-order - :param str id: order id - :param str symbol: unified symbol of the market the order was made in - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: An `order structure ` - """ - self.check_required_credentials() - self.load_markets() - market = None - if symbol is not None: - market = self.market(symbol) - nonce = self.uuidv1() - walletBytes = self.remove0x_prefix(self.walletAddress) - byteArray = [ - self.base16_to_binary(nonce), - self.base16_to_binary(walletBytes), - self.encode(id), - ] - binary = self.binary_concat_array(byteArray) - hash = self.hash(binary, 'keccak', 'hex') - signature = self.sign_message_string(hash, self.privateKey) - request: dict = { - 'parameters': { - 'nonce': nonce, - 'wallet': self.walletAddress, - 'orderId': id, - }, - 'signature': signature, - } - # [{orderId: "688336f0-ec50-11ea-9842-b332f8a34d0e"}] - response = self.privateDeleteOrders(self.extend(request, params)) - canceledOrder = self.safe_dict(response, 0) - return self.parse_order(canceledOrder, market) - - def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): - errorCode = self.safe_string(response, 'code') - message = self.safe_string(response, 'message') - if errorCode is not None: - self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, message) - raise ExchangeError(self.id + ' ' + message) - return None - - def fetch_deposit(self, id: str, code: Str = None, params={}): - """ - fetch information on a deposit - :see: https://api-docs-v3.geniusyield.io/#get-deposits - :param str id: deposit id - :param str code: not used by geniusyield fetchDeposit() - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `transaction structure ` - """ - self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - 'wallet': self.walletAddress, - 'depositId': id, - } - response = self.privateGetDeposits(self.extend(request, params)) - return self.parse_transaction(response) - - def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: - """ - fetch all deposits made to an account - :see: https://api-docs-v3.geniusyield.io/#get-deposits - :param str code: unified currency code - :param int [since]: the earliest time in ms to fetch deposits for - :param int [limit]: the maximum number of deposits structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict[]: a list of `transaction structures ` - """ - params = self.extend({ - 'method': 'privateGetDeposits', - }, params) - return self.fetch_transactions_helper(code, since, limit, params) - - def fetch_status(self, params={}): - """ - the latest known information on the availability of the exchange API - :see: https://api-docs-v3.geniusyield.io/#get-ping - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `status structure ` - """ - response = self.publicGetPing(params) - return { - 'status': 'ok', # if there's no Errors, status = 'ok' - 'updated': None, - 'eta': None, - 'url': None, - 'info': response, - } - - def fetch_time(self, params={}): - """ - fetches the current integer timestamp in milliseconds from the exchange server - :see: https://api-docs-v3.geniusyield.io/#get-time - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns int: the current integer timestamp in milliseconds from the exchange server - """ - response = self.publicGetTime(params) - # - # {serverTime: "1655258263236"} - # - return self.safe_integer(response, 'serverTime') - - def fetch_withdrawal(self, id: str, code: Str = None, params={}): - """ - fetch data on a currency withdrawal via the withdrawal id - :see: https://api-docs-v3.geniusyield.io/#get-withdrawals - :param str id: withdrawal id - :param str code: not used by geniusyield.fetchWithdrawal - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: a `transaction structure ` - """ - self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - 'wallet': self.walletAddress, - 'withdrawalId': id, - } - response = self.privateGetWithdrawals(self.extend(request, params)) - return self.parse_transaction(response) - - def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: - """ - fetch all withdrawals made from an account - :see: https://api-docs-v3.geniusyield.io/#get-withdrawals - :param str code: unified currency code - :param int [since]: the earliest time in ms to fetch withdrawals for - :param int [limit]: the maximum number of withdrawals structures to retrieve - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict[]: a list of `transaction structures ` - """ - params = self.extend({ - 'method': 'privateGetWithdrawals', - }, params) - return self.fetch_transactions_helper(code, since, limit, params) - - def fetch_transactions_helper(self, code: Str = None, since: Int = None, limit: Int = None, params={}): - self.load_markets() - nonce = self.uuidv1() - request: dict = { - 'nonce': nonce, - 'wallet': self.walletAddress, - } - currency = None - if code is not None: - currency = self.currency(code) - request['asset'] = currency['id'] - if since is not None: - request['start'] = since - if limit is not None: - request['limit'] = limit - # [ - # { - # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e", - # "asset": "ETH", - # "quantity": "1.00000000", - # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - # "txTime": 1598865853000, - # "confirmationTime": 1598865930231 - # } - # ] - method = params['method'] - params = self.omit(params, 'method') - response = None - if method == 'privateGetDeposits': - response = self.privateGetDeposits(self.extend(request, params)) - elif method == 'privateGetWithdrawals': - response = self.privateGetWithdrawals(self.extend(request, params)) - else: - raise NotSupported(self.id + ' fetchTransactionsHelper() not support self method') - return self.parse_transactions(response, currency, since, limit) - - def parse_transaction_status(self, status: Str): - statuses: dict = { - 'mined': 'ok', - } - return self.safe_string(statuses, status, status) - - def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction: - # - # fetchDeposits - # - # { - # "depositId": "e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f", - # "asset": "ETH", - # "quantity": "1.00000000", - # "txId": "0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142", - # "txTime": 1598865853000, - # "confirmationTime": 1598865930231 - # } - # - # fetchWithdrwalas - # - # { - # "withdrawalId": "a62d8760-ec4d-11ea-9fa6-47904c19499b", - # "asset": "ETH", - # "assetContractAddress": "0x0000000000000000000000000000000000000000", - # "quantity": "0.20000000", - # "time": 1598962883288, - # "fee": "0.00024000", - # "txId": "0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce", - # "txStatus": "mined" - # } - # - # withdraw - # - # { - # "withdrawalId": "a61dcff0-ec4d-11ea-8b83-c78a6ecb3180", - # "asset": "ETH", - # "assetContractAddress": "0x0000000000000000000000000000000000000000", - # "quantity": "0.20000000", - # "time": 1598962883190, - # "fee": "0.00024000", - # "txStatus": "pending", - # "txId": null - # } - # - type = None - if 'depositId' in transaction: - type = 'deposit' - elif ('withdrawId' in transaction) or ('withdrawalId' in transaction): - type = 'withdrawal' - id = self.safe_string_2(transaction, 'depositId', 'withdrawId') - id = self.safe_string(transaction, 'withdrawalId', id) - code = self.safe_currency_code(self.safe_string(transaction, 'asset'), currency) - amount = self.safe_number(transaction, 'quantity') - txid = self.safe_string(transaction, 'txId') - timestamp = self.safe_integer_2(transaction, 'txTime', 'time') - fee = None - if 'fee' in transaction: - fee = { - 'cost': self.safe_number(transaction, 'fee'), - 'currency': 'ETH', - } - rawStatus = self.safe_string(transaction, 'txStatus') - status = self.parse_transaction_status(rawStatus) - updated = self.safe_integer(transaction, 'confirmationTime') - return { - 'info': transaction, - 'id': id, - 'txid': txid, - 'timestamp': timestamp, - 'datetime': self.iso8601(timestamp), - 'network': None, - 'address': None, - 'addressTo': None, - 'addressFrom': None, - 'tag': None, - 'tagTo': None, - 'tagFrom': None, - 'type': type, - 'amount': amount, - 'currency': code, - 'status': status, - 'updated': updated, - 'comment': None, - 'internal': None, - 'fee': fee, - } - - def calculate_rate_limiter_cost(self, api, method, path, params, config={}): - hasApiKey = (self.apiKey is not None) - hasSecret = (self.secret is not None) - hasWalletAddress = (self.walletAddress is not None) - hasPrivateKey = (self.privateKey is not None) - defaultCost = self.safe_value(config, 'cost', 1) - authenticated = hasApiKey and hasSecret and hasWalletAddress and hasPrivateKey - return(defaultCost / 2) if authenticated else defaultCost - - def fetch_deposit_address(self, code: Str = None, params={}): - """ - fetch the Polygon address of the wallet - :see: https://api-docs-v3.geniusyield.io/#get-wallets - :param str code: not used by geniusyield - :param dict [params]: extra parameters specific to the exchange API endpoint - :returns dict: an `address structure ` - """ - request: dict = {} - request['nonce'] = self.uuidv1() - response = self.privateGetWallets(self.extend(request, params)) - # - # [ - # { - # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - # totalPortfolioValueUsd: "0.00", - # time: "1678342148086" - # }, - # { - # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - # totalPortfolioValueUsd: "15.90", - # time: "1691697811659" - # } - # ] - # - return self.parse_deposit_address(response) - - def parse_deposit_address(self, depositAddress, currency: Currency = None): - # - # [ - # { - # address: "0x37A1827CA64C94A26028bDCb43FBDCB0bf6DAf5B", - # totalPortfolioValueUsd: "0.00", - # time: "1678342148086" - # }, - # { - # address: "0x0Ef3456E616552238B0c562d409507Ed6051A7b3", - # totalPortfolioValueUsd: "15.90", - # time: "1691697811659" - # } - # ] - # - length = len(depositAddress) - entry = self.safe_dict(depositAddress, length - 1) - address = self.safe_string(entry, 'address') - self.check_address(address) - return { - 'info': depositAddress, - 'currency': None, - 'address': address, - 'tag': None, - 'network': 'MATIC', - } - def sign(self, path, api='public', method='GET', params={}, headers=None, body=None): - network = self.safe_string(self.options, 'network', 'ETH') - version = self.safe_string(self.options, 'version', 'v1') + network = self.safe_string(self.options, 'network', 'mainnet') + version = self.safe_string(self.options, 'version', 'v0') url = self.urls['api'][network] + '/' + version + '/' + path keys = list(params.keys()) length = len(keys) @@ -1726,41 +277,5 @@ def sign(self, path, api='public', method='GET', params={}, headers=None, body=N 'Content-Type': 'application/json', } if self.apiKey is not None: - headers['Genius Yield-API-Key'] = self.apiKey - if api == 'private': - payload = None - if method == 'GET': - payload = query - else: - payload = body - headers['Genius Yield-HMAC-Signature'] = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex') + headers['api-key'] = self.apiKey return {'url': url, 'method': method, 'body': body, 'headers': headers} - - def remove0x_prefix(self, hexData): - if hexData[0:2] == '0x': - return hexData[2:] - else: - return hexData - - def hash_message(self, message): - # takes a hex encoded message - binaryMessage = self.base16_to_binary(self.remove0x_prefix(message)) - prefix = self.encode('\x19Ethereum Signed Message:\n' + binaryMessage.byteLength) - return '0x' + self.hash(self.binary_concat(prefix, binaryMessage), 'keccak', 'hex') - - def sign_hash(self, hash, privateKey): - signature = self.ecdsa(hash[-64:], privateKey[-64:], 'secp256k1', None) - return { - 'r': '0x' + signature['r'], - 's': '0x' + signature['s'], - 'v': 27 + signature['v'], - } - - def sign_message(self, message, privateKey): - return self.sign_hash(self.hash_message(message), privateKey[-64:]) - - def sign_message_string(self, message, privateKey): - # still takes the input hex string - # same but returns a string instead of an object - signature = self.sign_message(message, privateKey) - return signature['r'] + self.remove0x_prefix(signature['s']) + self.binary_to_base16(self.number_to_be(signature['v'], 1)) diff --git a/ts/src/geniusyield.ts b/ts/src/geniusyield.ts index 412364e573c2..e65c17bf0e1d 100644 --- a/ts/src/geniusyield.ts +++ b/ts/src/geniusyield.ts @@ -4,7 +4,7 @@ import Exchange from './abstract/geniusyield.js'; import { TICK_SIZE, PAD_WITH_ZERO } from './base/functions/number.js'; import { InvalidOrder, InsufficientFunds, ExchangeNotAvailable, DDoSProtection, BadRequest, InvalidAddress, AuthenticationError } from './base/errors.js'; -import type { Market } from './base/types.js'; +import type { Market, Str, MarketInterface } from './base/types.js'; // --------------------------------------------------------------------------- @@ -13,6 +13,15 @@ import type { Market } from './base/types.js'; * @augments Exchange */ export default class geniusyield extends Exchange { + safeMarket (marketId: Str = undefined, market: Market = undefined, delimiter: Str = undefined, marketType: Str = undefined): MarketInterface { + const isOption = (marketId !== undefined) && ((marketId.indexOf ('-C') > -1) || (marketId.indexOf ('-P') > -1)); + if (isOption && !(marketId in this.markets_by_id)) { + // handle expired option contracts + return this.createExpiredOptionMarket (marketId); + } + return super.safeMarket (marketId, market, delimiter, marketType); + } + describe () { return this.deepExtend (super.describe (), { 'id': 'geniusyield', @@ -33,17 +42,17 @@ export default class geniusyield extends Exchange { 'option': false, 'addMargin': false, 'cancelAllOrders': false, - 'cancelOrder': false, + 'cancelOrder': true, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': false, + 'createOrder': true, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, 'createStopOrder': false, - 'fetchBalance': false, + 'fetchBalance': true, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchClosedOrders': false, @@ -73,7 +82,7 @@ export default class geniusyield extends Exchange { 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': false, - 'fetchOrders': false, + 'fetchOrders': true, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, @@ -111,8 +120,8 @@ export default class geniusyield extends Exchange { }, 'urls': { 'api': { - 'preprod': 'https://localhost:8082/v0/', - 'mainnet': 'https://localhost:8082/v0/', + 'preprod': 'http://localhost:8082', + 'mainnet': 'http://localhost:8082', }, 'www': 'https://www.geniusyield.co/', 'doc': [ @@ -126,11 +135,14 @@ export default class geniusyield extends Exchange { }, 'private': { 'get': { - 'markets': 0, - 'trading-fees': 0, + 'markets': 10, + 'trading-fees': 10, }, }, }, + 'headers': { + 'X-Gate-Channel-Id': 'ccxt', + }, 'options': { 'defaultTimeInForce': 'utc', 'defaultSelfTradePrevention': 'cn', @@ -247,4 +259,28 @@ export default class geniusyield extends Exchange { } return result; } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const network = this.safeString (this.options, 'network', 'mainnet'); + const version = this.safeString (this.options, 'version', 'v0'); + let url = this.urls['api'][network] + '/' + version + '/' + path; + const keys = Object.keys (params); + const length = keys.length; + let query = undefined; + if (length > 0) { + if (method === 'GET') { + query = this.urlencode (params); + url = url + '?' + query; + } else { + body = this.json (params); + } + } + headers = { + 'Content-Type': 'application/json', + }; + if (this.apiKey !== undefined) { + headers['api-key'] = this.apiKey; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } } From 11b1283743897843fa0e0d73af899fc2c933539c Mon Sep 17 00:00:00 2001 From: 4TT1L4 <2914096+4TT1L4@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:02:36 +0200 Subject: [PATCH 5/5] =?UTF-8?q?Initia=C3=83l=20fetchBalance=20implementati?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cs/ccxt/api/geniusyield.cs | 10 + dist/cjs/src/geniusyield.js | 51 ++- js/src/abstract/geniusyield.d.ts | 2 + js/src/geniusyield.d.ts | 4 +- js/src/geniusyield.js | 51 ++- php/abstract/geniusyield.php | 12 + php/async/abstract/geniusyield.php | 12 + php/async/geniusyield.php | 54 ++- php/geniusyield.php | 52 ++- python/ccxt/abstract/geniusyield.py | 2 + python/ccxt/async_support/geniusyield.py | 49 ++- python/ccxt/geniusyield.py | 49 ++- ts/src/abstract/geniusyield.ts | 2 + ts/src/geniusyield.ts | 54 ++- wiki/exchanges/geniusyield.md | 528 ----------------------- 15 files changed, 297 insertions(+), 635 deletions(-) diff --git a/cs/ccxt/api/geniusyield.cs b/cs/ccxt/api/geniusyield.cs index 15b7260e72d6..3e83fc45c879 100644 --- a/cs/ccxt/api/geniusyield.cs +++ b/cs/ccxt/api/geniusyield.cs @@ -11,6 +11,11 @@ public partial class geniusyield : Exchange { public geniusyield (object args = null): base(args) {} + public async Task privateGetBalancesAddress (object parameters = null) + { + return await this.callAsync ("privateGetBalancesAddress",parameters); + } + public async Task privateGetMarkets (object parameters = null) { return await this.callAsync ("privateGetMarkets",parameters); @@ -21,4 +26,9 @@ public async Task privateGetTradingFees (object parameters = null) return await this.callAsync ("privateGetTradingFees",parameters); } + public async Task privateGetSettings (object parameters = null) + { + return await this.callAsync ("privateGetSettings",parameters); + } + } \ No newline at end of file diff --git a/dist/cjs/src/geniusyield.js b/dist/cjs/src/geniusyield.js index ce8b11b314ee..790edbc8a554 100644 --- a/dist/cjs/src/geniusyield.js +++ b/dist/cjs/src/geniusyield.js @@ -39,12 +39,12 @@ class geniusyield extends geniusyield$1 { 'option': false, 'addMargin': false, 'cancelAllOrders': false, - 'cancelOrder': true, + 'cancelOrder': false, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': true, + 'createOrder': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, @@ -79,7 +79,7 @@ class geniusyield extends geniusyield$1 { 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': false, - 'fetchOrders': true, + 'fetchOrders': false, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, @@ -131,8 +131,10 @@ class geniusyield extends geniusyield$1 { }, 'private': { 'get': { + 'balances/{address}': 10, 'markets': 10, 'trading-fees': 10, + 'settings': 10, }, }, }, @@ -167,6 +169,7 @@ class geniusyield extends geniusyield$1 { }); } async fetchMarkets(params = {}) { + this.checkRequiredCredentials(); /** * @method * @name geniusyield#fetchMarkets @@ -254,22 +257,38 @@ class geniusyield extends geniusyield$1 { } return result; } + parseBalance(response) { + const result = { + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + }; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const currencyId = this.safeString(entry, 'asset'); + const code = this.safeCurrencyCode(currencyId); + const account = this.account(); + account['total'] = this.safeString(entry, 'quantity'); + account['free'] = this.safeString(entry, 'availableForTrade'); + account['used'] = this.safeString(entry, 'locked'); + result[code] = account; + } + return this.safeBalance(result); + } + async fetchBalance(params = {}) { + const settings = await this.privateGetSettings(params); + const address = this.safeString(settings, 'address'); + const request = { + 'address': address, + }; + const balances = await this.privateGetBalancesAddress(this.extend(request, params)); + return this.safeBalance(balances); + } sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + this.checkRequiredCredentials(); const network = this.safeString(this.options, 'network', 'mainnet'); const version = this.safeString(this.options, 'version', 'v0'); - let url = this.urls['api'][network] + '/' + version + '/' + path; - const keys = Object.keys(params); - const length = keys.length; - let query = undefined; - if (length > 0) { - if (method === 'GET') { - query = this.urlencode(params); - url = url + '?' + query; - } - else { - body = this.json(params); - } - } + const url = this.urls['api'][network] + '/' + version + '/' + this.implodeParams(path, params); headers = { 'Content-Type': 'application/json', }; diff --git a/js/src/abstract/geniusyield.d.ts b/js/src/abstract/geniusyield.d.ts index fed72de5d6de..62fd2c4c8bd3 100644 --- a/js/src/abstract/geniusyield.d.ts +++ b/js/src/abstract/geniusyield.d.ts @@ -1,8 +1,10 @@ import { implicitReturnType } from '../base/types.js'; import { Exchange as _Exchange } from '../base/Exchange.js'; interface Exchange { + privateGetBalancesAddress(params?: {}): Promise; privateGetMarkets(params?: {}): Promise; privateGetTradingFees(params?: {}): Promise; + privateGetSettings(params?: {}): Promise; } declare abstract class Exchange extends _Exchange { } diff --git a/js/src/geniusyield.d.ts b/js/src/geniusyield.d.ts index c6160cb8faa0..8c0eae7653fd 100644 --- a/js/src/geniusyield.d.ts +++ b/js/src/geniusyield.d.ts @@ -1,5 +1,5 @@ import Exchange from './abstract/geniusyield.js'; -import type { Market, Str, MarketInterface } from './base/types.js'; +import type { Balances, Market, MarketInterface, Str } from './base/types.js'; /** * @class geniusyield * @augments Exchange @@ -8,6 +8,8 @@ export default class geniusyield extends Exchange { safeMarket(marketId?: Str, market?: Market, delimiter?: Str, marketType?: Str): MarketInterface; describe(): any; fetchMarkets(params?: {}): Promise; + parseBalance(response: any): Balances; + fetchBalance(params?: {}): Promise; sign(path: any, api?: string, method?: string, params?: {}, headers?: any, body?: any): { url: string; method: string; diff --git a/js/src/geniusyield.js b/js/src/geniusyield.js index e01f2e08371f..e84a49dd8dd2 100644 --- a/js/src/geniusyield.js +++ b/js/src/geniusyield.js @@ -42,12 +42,12 @@ export default class geniusyield extends Exchange { 'option': false, 'addMargin': false, 'cancelAllOrders': false, - 'cancelOrder': true, + 'cancelOrder': false, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': true, + 'createOrder': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, @@ -82,7 +82,7 @@ export default class geniusyield extends Exchange { 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': false, - 'fetchOrders': true, + 'fetchOrders': false, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, @@ -134,8 +134,10 @@ export default class geniusyield extends Exchange { }, 'private': { 'get': { + 'balances/{address}': 10, 'markets': 10, 'trading-fees': 10, + 'settings': 10, }, }, }, @@ -170,6 +172,7 @@ export default class geniusyield extends Exchange { }); } async fetchMarkets(params = {}) { + this.checkRequiredCredentials(); /** * @method * @name geniusyield#fetchMarkets @@ -257,22 +260,38 @@ export default class geniusyield extends Exchange { } return result; } + parseBalance(response) { + const result = { + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + }; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const currencyId = this.safeString(entry, 'asset'); + const code = this.safeCurrencyCode(currencyId); + const account = this.account(); + account['total'] = this.safeString(entry, 'quantity'); + account['free'] = this.safeString(entry, 'availableForTrade'); + account['used'] = this.safeString(entry, 'locked'); + result[code] = account; + } + return this.safeBalance(result); + } + async fetchBalance(params = {}) { + const settings = await this.privateGetSettings(params); + const address = this.safeString(settings, 'address'); + const request = { + 'address': address, + }; + const balances = await this.privateGetBalancesAddress(this.extend(request, params)); + return this.safeBalance(balances); + } sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + this.checkRequiredCredentials(); const network = this.safeString(this.options, 'network', 'mainnet'); const version = this.safeString(this.options, 'version', 'v0'); - let url = this.urls['api'][network] + '/' + version + '/' + path; - const keys = Object.keys(params); - const length = keys.length; - let query = undefined; - if (length > 0) { - if (method === 'GET') { - query = this.urlencode(params); - url = url + '?' + query; - } - else { - body = this.json(params); - } - } + const url = this.urls['api'][network] + '/' + version + '/' + this.implodeParams(path, params); headers = { 'Content-Type': 'application/json', }; diff --git a/php/abstract/geniusyield.php b/php/abstract/geniusyield.php index bbe02f2545fb..2680e5a36369 100644 --- a/php/abstract/geniusyield.php +++ b/php/abstract/geniusyield.php @@ -7,16 +7,28 @@ abstract class geniusyield extends \ccxt\Exchange { + public function private_get_balances_address($params = array()) { + return $this->request('balances/{address}', 'private', 'GET', $params, null, null, array("cost" => 10)); + } public function private_get_markets($params = array()) { return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function private_get_trading_fees($params = array()) { return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } + public function private_get_settings($params = array()) { + return $this->request('settings', 'private', 'GET', $params, null, null, array("cost" => 10)); + } + public function privateGetBalancesAddress($params = array()) { + return $this->request('balances/{address}', 'private', 'GET', $params, null, null, array("cost" => 10)); + } public function privateGetMarkets($params = array()) { return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function privateGetTradingFees($params = array()) { return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } + public function privateGetSettings($params = array()) { + return $this->request('settings', 'private', 'GET', $params, null, null, array("cost" => 10)); + } } diff --git a/php/async/abstract/geniusyield.php b/php/async/abstract/geniusyield.php index 7eb7857705ff..9bf752c51074 100644 --- a/php/async/abstract/geniusyield.php +++ b/php/async/abstract/geniusyield.php @@ -7,16 +7,28 @@ abstract class geniusyield extends \ccxt\async\Exchange { + public function private_get_balances_address($params = array()) { + return $this->request('balances/{address}', 'private', 'GET', $params, null, null, array("cost" => 10)); + } public function private_get_markets($params = array()) { return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function private_get_trading_fees($params = array()) { return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } + public function private_get_settings($params = array()) { + return $this->request('settings', 'private', 'GET', $params, null, null, array("cost" => 10)); + } + public function privateGetBalancesAddress($params = array()) { + return $this->request('balances/{address}', 'private', 'GET', $params, null, null, array("cost" => 10)); + } public function privateGetMarkets($params = array()) { return $this->request('markets', 'private', 'GET', $params, null, null, array("cost" => 10)); } public function privateGetTradingFees($params = array()) { return $this->request('trading-fees', 'private', 'GET', $params, null, null, array("cost" => 10)); } + public function privateGetSettings($params = array()) { + return $this->request('settings', 'private', 'GET', $params, null, null, array("cost" => 10)); + } } diff --git a/php/async/geniusyield.php b/php/async/geniusyield.php index bb300a55c442..666829fb9504 100644 --- a/php/async/geniusyield.php +++ b/php/async/geniusyield.php @@ -41,12 +41,12 @@ public function describe() { 'option' => false, 'addMargin' => false, 'cancelAllOrders' => false, - 'cancelOrder' => true, + 'cancelOrder' => false, 'cancelOrders' => false, 'closeAllPositions' => false, 'closePosition' => false, 'createDepositAddress' => false, - 'createOrder' => true, + 'createOrder' => false, 'createReduceOnlyOrder' => false, 'createStopLimitOrder' => false, 'createStopMarketOrder' => false, @@ -81,7 +81,7 @@ public function describe() { 'fetchOpenOrders' => false, 'fetchOrder' => false, 'fetchOrderBook' => false, - 'fetchOrders' => true, + 'fetchOrders' => false, 'fetchPosition' => false, 'fetchPositionHistory' => false, 'fetchPositionMode' => false, @@ -134,8 +134,10 @@ public function describe() { ), 'private' => array( 'get' => array( + 'balances/{address}' => 10, 'markets' => 10, 'trading-fees' => 10, + 'settings' => 10, ), ), ), @@ -172,6 +174,7 @@ public function describe() { public function fetch_markets($params = array ()): PromiseInterface { return Async\async(function () use ($params) { + $this->check_required_credentials(); /** * retrieves data on all $markets for geniusyield * @see https://api-docs-v3.geniusyield.io/#get-$markets @@ -259,21 +262,42 @@ public function fetch_markets($params = array ()): PromiseInterface { }) (); } + public function parse_balance($response): array { + $result = array( + 'info' => $response, + 'timestamp' => null, + 'datetime' => null, + ); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $currencyId = $this->safe_string($entry, 'asset'); + $code = $this->safe_currency_code($currencyId); + $account = $this->account(); + $account['total'] = $this->safe_string($entry, 'quantity'); + $account['free'] = $this->safe_string($entry, 'availableForTrade'); + $account['used'] = $this->safe_string($entry, 'locked'); + $result[$code] = $account; + } + return $this->safe_balance($result); + } + + public function fetch_balance($params = array ()): PromiseInterface { + return Async\async(function () use ($params) { + $settings = Async\await($this->privateGetSettings ($params)); + $address = $this->safe_string($settings, 'address'); + $request = array( + 'address' => $address, + ); + $balances = Async\await($this->privateGetBalancesAddress ($this->extend($request, $params))); + return $this->safe_balance($balances); + }) (); + } + public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) { + $this->check_required_credentials(); $network = $this->safe_string($this->options, 'network', 'mainnet'); $version = $this->safe_string($this->options, 'version', 'v0'); - $url = $this->urls['api'][$network] . '/' . $version . '/' . $path; - $keys = is_array($params) ? array_keys($params) : array(); - $length = count($keys); - $query = null; - if ($length > 0) { - if ($method === 'GET') { - $query = $this->urlencode($params); - $url = $url . '?' . $query; - } else { - $body = $this->json($params); - } - } + $url = $this->urls['api'][$network] . '/' . $version . '/' . $this->implode_params($path, $params); $headers = array( 'Content-Type' => 'application/json', ); diff --git a/php/geniusyield.php b/php/geniusyield.php index fe54ecf34ac0..9c4cf97d5b94 100644 --- a/php/geniusyield.php +++ b/php/geniusyield.php @@ -39,12 +39,12 @@ public function describe() { 'option' => false, 'addMargin' => false, 'cancelAllOrders' => false, - 'cancelOrder' => true, + 'cancelOrder' => false, 'cancelOrders' => false, 'closeAllPositions' => false, 'closePosition' => false, 'createDepositAddress' => false, - 'createOrder' => true, + 'createOrder' => false, 'createReduceOnlyOrder' => false, 'createStopLimitOrder' => false, 'createStopMarketOrder' => false, @@ -79,7 +79,7 @@ public function describe() { 'fetchOpenOrders' => false, 'fetchOrder' => false, 'fetchOrderBook' => false, - 'fetchOrders' => true, + 'fetchOrders' => false, 'fetchPosition' => false, 'fetchPositionHistory' => false, 'fetchPositionMode' => false, @@ -132,8 +132,10 @@ public function describe() { ), 'private' => array( 'get' => array( + 'balances/{address}' => 10, 'markets' => 10, 'trading-fees' => 10, + 'settings' => 10, ), ), ), @@ -169,6 +171,7 @@ public function describe() { } public function fetch_markets($params = array ()): array { + $this->check_required_credentials(); /** * retrieves data on all $markets for geniusyield * @see https://api-docs-v3.geniusyield.io/#get-$markets @@ -255,21 +258,40 @@ public function fetch_markets($params = array ()): array { return $result; } + public function parse_balance($response): array { + $result = array( + 'info' => $response, + 'timestamp' => null, + 'datetime' => null, + ); + for ($i = 0; $i < count($response); $i++) { + $entry = $response[$i]; + $currencyId = $this->safe_string($entry, 'asset'); + $code = $this->safe_currency_code($currencyId); + $account = $this->account(); + $account['total'] = $this->safe_string($entry, 'quantity'); + $account['free'] = $this->safe_string($entry, 'availableForTrade'); + $account['used'] = $this->safe_string($entry, 'locked'); + $result[$code] = $account; + } + return $this->safe_balance($result); + } + + public function fetch_balance($params = array ()): array { + $settings = $this->privateGetSettings ($params); + $address = $this->safe_string($settings, 'address'); + $request = array( + 'address' => $address, + ); + $balances = $this->privateGetBalancesAddress ($this->extend($request, $params)); + return $this->safe_balance($balances); + } + public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) { + $this->check_required_credentials(); $network = $this->safe_string($this->options, 'network', 'mainnet'); $version = $this->safe_string($this->options, 'version', 'v0'); - $url = $this->urls['api'][$network] . '/' . $version . '/' . $path; - $keys = is_array($params) ? array_keys($params) : array(); - $length = count($keys); - $query = null; - if ($length > 0) { - if ($method === 'GET') { - $query = $this->urlencode($params); - $url = $url . '?' . $query; - } else { - $body = $this->json($params); - } - } + $url = $this->urls['api'][$network] . '/' . $version . '/' . $this->implode_params($path, $params); $headers = array( 'Content-Type' => 'application/json', ); diff --git a/python/ccxt/abstract/geniusyield.py b/python/ccxt/abstract/geniusyield.py index 219f37e236db..6d62bee80860 100644 --- a/python/ccxt/abstract/geniusyield.py +++ b/python/ccxt/abstract/geniusyield.py @@ -2,5 +2,7 @@ class ImplicitAPI: + private_get_balances_address = privateGetBalancesAddress = Entry('balances/{address}', 'private', 'GET', {'cost': 10}) private_get_markets = privateGetMarkets = Entry('markets', 'private', 'GET', {'cost': 10}) private_get_trading_fees = privateGetTradingFees = Entry('trading-fees', 'private', 'GET', {'cost': 10}) + private_get_settings = privateGetSettings = Entry('settings', 'private', 'GET', {'cost': 10}) diff --git a/python/ccxt/async_support/geniusyield.py b/python/ccxt/async_support/geniusyield.py index cd1a67b0a6ff..ad2a4f2f48b2 100644 --- a/python/ccxt/async_support/geniusyield.py +++ b/python/ccxt/async_support/geniusyield.py @@ -5,7 +5,7 @@ from ccxt.async_support.base.exchange import Exchange from ccxt.abstract.geniusyield import ImplicitAPI -from ccxt.base.types import Market, MarketInterface, Str +from ccxt.base.types import Balances, Market, MarketInterface, Str from typing import List from ccxt.base.errors import AuthenticationError from ccxt.base.errors import BadRequest @@ -47,12 +47,12 @@ def describe(self): 'option': False, 'addMargin': False, 'cancelAllOrders': False, - 'cancelOrder': True, + 'cancelOrder': False, 'cancelOrders': False, 'closeAllPositions': False, 'closePosition': False, 'createDepositAddress': False, - 'createOrder': True, + 'createOrder': False, 'createReduceOnlyOrder': False, 'createStopLimitOrder': False, 'createStopMarketOrder': False, @@ -87,7 +87,7 @@ def describe(self): 'fetchOpenOrders': False, 'fetchOrder': False, 'fetchOrderBook': False, - 'fetchOrders': True, + 'fetchOrders': False, 'fetchPosition': False, 'fetchPositionHistory': False, 'fetchPositionMode': False, @@ -140,8 +140,10 @@ def describe(self): }, 'private': { 'get': { + 'balances/{address}': 10, 'markets': 10, 'trading-fees': 10, + 'settings': 10, }, }, }, @@ -176,6 +178,7 @@ def describe(self): }) async def fetch_markets(self, params={}) -> List[Market]: + self.check_required_credentials() """ retrieves data on all markets for geniusyield :see: https://api-docs-v3.geniusyield.io/#get-markets @@ -260,19 +263,37 @@ async def fetch_markets(self, params={}) -> List[Market]: }) return result + def parse_balance(self, response) -> Balances: + result: dict = { + 'info': response, + 'timestamp': None, + 'datetime': None, + } + for i in range(0, len(response)): + entry = response[i] + currencyId = self.safe_string(entry, 'asset') + code = self.safe_currency_code(currencyId) + account = self.account() + account['total'] = self.safe_string(entry, 'quantity') + account['free'] = self.safe_string(entry, 'availableForTrade') + account['used'] = self.safe_string(entry, 'locked') + result[code] = account + return self.safe_balance(result) + + async def fetch_balance(self, params={}) -> Balances: + settings = await self.privateGetSettings(params) + address = self.safe_string(settings, 'address') + request: dict = { + 'address': address, + } + balances = await self.privateGetBalancesAddress(self.extend(request, params)) + return self.safe_balance(balances) + def sign(self, path, api='public', method='GET', params={}, headers=None, body=None): + self.check_required_credentials() network = self.safe_string(self.options, 'network', 'mainnet') version = self.safe_string(self.options, 'version', 'v0') - url = self.urls['api'][network] + '/' + version + '/' + path - keys = list(params.keys()) - length = len(keys) - query = None - if length > 0: - if method == 'GET': - query = self.urlencode(params) - url = url + '?' + query - else: - body = self.json(params) + url = self.urls['api'][network] + '/' + version + '/' + self.implode_params(path, params) headers = { 'Content-Type': 'application/json', } diff --git a/python/ccxt/geniusyield.py b/python/ccxt/geniusyield.py index 7bb1c98d8e2f..00ff29ded5d6 100644 --- a/python/ccxt/geniusyield.py +++ b/python/ccxt/geniusyield.py @@ -5,7 +5,7 @@ from ccxt.base.exchange import Exchange from ccxt.abstract.geniusyield import ImplicitAPI -from ccxt.base.types import Market, MarketInterface, Str +from ccxt.base.types import Balances, Market, MarketInterface, Str from typing import List from ccxt.base.errors import AuthenticationError from ccxt.base.errors import BadRequest @@ -47,12 +47,12 @@ def describe(self): 'option': False, 'addMargin': False, 'cancelAllOrders': False, - 'cancelOrder': True, + 'cancelOrder': False, 'cancelOrders': False, 'closeAllPositions': False, 'closePosition': False, 'createDepositAddress': False, - 'createOrder': True, + 'createOrder': False, 'createReduceOnlyOrder': False, 'createStopLimitOrder': False, 'createStopMarketOrder': False, @@ -87,7 +87,7 @@ def describe(self): 'fetchOpenOrders': False, 'fetchOrder': False, 'fetchOrderBook': False, - 'fetchOrders': True, + 'fetchOrders': False, 'fetchPosition': False, 'fetchPositionHistory': False, 'fetchPositionMode': False, @@ -140,8 +140,10 @@ def describe(self): }, 'private': { 'get': { + 'balances/{address}': 10, 'markets': 10, 'trading-fees': 10, + 'settings': 10, }, }, }, @@ -176,6 +178,7 @@ def describe(self): }) def fetch_markets(self, params={}) -> List[Market]: + self.check_required_credentials() """ retrieves data on all markets for geniusyield :see: https://api-docs-v3.geniusyield.io/#get-markets @@ -260,19 +263,37 @@ def fetch_markets(self, params={}) -> List[Market]: }) return result + def parse_balance(self, response) -> Balances: + result: dict = { + 'info': response, + 'timestamp': None, + 'datetime': None, + } + for i in range(0, len(response)): + entry = response[i] + currencyId = self.safe_string(entry, 'asset') + code = self.safe_currency_code(currencyId) + account = self.account() + account['total'] = self.safe_string(entry, 'quantity') + account['free'] = self.safe_string(entry, 'availableForTrade') + account['used'] = self.safe_string(entry, 'locked') + result[code] = account + return self.safe_balance(result) + + def fetch_balance(self, params={}) -> Balances: + settings = self.privateGetSettings(params) + address = self.safe_string(settings, 'address') + request: dict = { + 'address': address, + } + balances = self.privateGetBalancesAddress(self.extend(request, params)) + return self.safe_balance(balances) + def sign(self, path, api='public', method='GET', params={}, headers=None, body=None): + self.check_required_credentials() network = self.safe_string(self.options, 'network', 'mainnet') version = self.safe_string(self.options, 'version', 'v0') - url = self.urls['api'][network] + '/' + version + '/' + path - keys = list(params.keys()) - length = len(keys) - query = None - if length > 0: - if method == 'GET': - query = self.urlencode(params) - url = url + '?' + query - else: - body = self.json(params) + url = self.urls['api'][network] + '/' + version + '/' + self.implode_params(path, params) headers = { 'Content-Type': 'application/json', } diff --git a/ts/src/abstract/geniusyield.ts b/ts/src/abstract/geniusyield.ts index 1d0e32cb9e77..1d6a8062086a 100644 --- a/ts/src/abstract/geniusyield.ts +++ b/ts/src/abstract/geniusyield.ts @@ -9,8 +9,10 @@ import { implicitReturnType } from '../base/types.js'; import { Exchange as _Exchange } from '../base/Exchange.js'; interface Exchange { + privateGetBalancesAddress (params?: {}): Promise; privateGetMarkets (params?: {}): Promise; privateGetTradingFees (params?: {}): Promise; + privateGetSettings (params?: {}): Promise; } abstract class Exchange extends _Exchange {} diff --git a/ts/src/geniusyield.ts b/ts/src/geniusyield.ts index e65c17bf0e1d..236fba13cfbf 100644 --- a/ts/src/geniusyield.ts +++ b/ts/src/geniusyield.ts @@ -4,7 +4,7 @@ import Exchange from './abstract/geniusyield.js'; import { TICK_SIZE, PAD_WITH_ZERO } from './base/functions/number.js'; import { InvalidOrder, InsufficientFunds, ExchangeNotAvailable, DDoSProtection, BadRequest, InvalidAddress, AuthenticationError } from './base/errors.js'; -import type { Market, Str, MarketInterface } from './base/types.js'; +import type { Balances, Dict, Market, MarketInterface, Str } from './base/types.js'; // --------------------------------------------------------------------------- @@ -42,12 +42,12 @@ export default class geniusyield extends Exchange { 'option': false, 'addMargin': false, 'cancelAllOrders': false, - 'cancelOrder': true, + 'cancelOrder': false, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, - 'createOrder': true, + 'createOrder': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopMarketOrder': false, @@ -82,7 +82,7 @@ export default class geniusyield extends Exchange { 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': false, - 'fetchOrders': true, + 'fetchOrders': false, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, @@ -135,8 +135,10 @@ export default class geniusyield extends Exchange { }, 'private': { 'get': { + 'balances/{address}': 10, 'markets': 10, 'trading-fees': 10, + 'settings': 10, }, }, }, @@ -172,6 +174,7 @@ export default class geniusyield extends Exchange { } async fetchMarkets (params = {}): Promise { + this.checkRequiredCredentials (); /** * @method * @name geniusyield#fetchMarkets @@ -260,21 +263,40 @@ export default class geniusyield extends Exchange { return result; } + parseBalance (response): Balances { + const result: Dict = { + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + }; + for (let i = 0; i < response.length; i++) { + const entry = response[i]; + const currencyId = this.safeString (entry, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeString (entry, 'quantity'); + account['free'] = this.safeString (entry, 'availableForTrade'); + account['used'] = this.safeString (entry, 'locked'); + result[code] = account; + } + return this.safeBalance (result); + } + + async fetchBalance (params = {}): Promise { + const settings = await this.privateGetSettings (params); + const address = this.safeString (settings, 'address'); + const request: Dict = { + 'address': address, + }; + const balances = await this.privateGetBalancesAddress (this.extend (request, params)); + return this.safeBalance (balances); + } + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + this.checkRequiredCredentials (); const network = this.safeString (this.options, 'network', 'mainnet'); const version = this.safeString (this.options, 'version', 'v0'); - let url = this.urls['api'][network] + '/' + version + '/' + path; - const keys = Object.keys (params); - const length = keys.length; - let query = undefined; - if (length > 0) { - if (method === 'GET') { - query = this.urlencode (params); - url = url + '?' + query; - } else { - body = this.json (params); - } - } + const url = this.urls['api'][network] + '/' + version + '/' + this.implodeParams (path, params); headers = { 'Content-Type': 'application/json', }; diff --git a/wiki/exchanges/geniusyield.md b/wiki/exchanges/geniusyield.md index 477d4ffb389d..d15034974284 100644 --- a/wiki/exchanges/geniusyield.md +++ b/wiki/exchanges/geniusyield.md @@ -6,29 +6,6 @@ **Extends**: Exchange * [fetchMarkets](#fetchmarkets) -* [fetchTicker](#fetchticker) -* [fetchTickers](#fetchtickers) -* [fetchOHLCV](#fetchohlcv) -* [fetchTrades](#fetchtrades) -* [fetchTradingFees](#fetchtradingfees) -* [fetchOrderBook](#fetchorderbook) -* [fetchCurrencies](#fetchcurrencies) -* [fetchBalance](#fetchbalance) -* [fetchMyTrades](#fetchmytrades) -* [fetchOrder](#fetchorder) -* [fetchOpenOrders](#fetchopenorders) -* [fetchClosedOrders](#fetchclosedorders) -* [createOrder](#createorder) -* [withdraw](#withdraw) -* [cancelAllOrders](#cancelallorders) -* [cancelOrder](#cancelorder) -* [fetchDeposit](#fetchdeposit) -* [fetchDeposits](#fetchdeposits) -* [fetchStatus](#fetchstatus) -* [fetchTime](#fetchtime) -* [fetchWithdrawal](#fetchwithdrawal) -* [fetchWithdrawals](#fetchwithdrawals) -* [fetchDepositAddress](#fetchdepositaddress) @@ -49,508 +26,3 @@ retrieves data on all markets for geniusyield geniusyield.fetchMarkets ([params]) ``` - - - -### fetchTicker{docsify-ignore} -fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a [ticker structure](https://docs.ccxt.com/#/?id=ticker-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-tickers - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified symbol of the market to fetch the ticker for | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchTicker (symbol[, params]) -``` - - - - -### fetchTickers{docsify-ignore} -fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a dictionary of [ticker structures](https://docs.ccxt.com/#/?id=ticker-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-tickers - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbols | Array<string>, undefined | Yes | unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchTickers (symbols[, params]) -``` - - - - -### fetchOHLCV{docsify-ignore} -fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<Array<int>> - A list of candles ordered as timestamp, open, high, low, close, volume - -**See**: https://api-docs-v3.geniusyield.io/#get-candles - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified symbol of the market to fetch OHLCV data for | -| timeframe | string | Yes | the length of time each candle represents | -| since | int | No | timestamp in ms of the earliest candle to fetch | -| limit | int | No | the maximum amount of candles to fetch | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchOHLCV (symbol, timeframe[, since, limit, params]) -``` - - - - -### fetchTrades{docsify-ignore} -get the list of most recent trades for a particular symbol - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<Trade> - a list of [trade structures](https://docs.ccxt.com/#/?id=public-trades) - -**See**: https://api-docs-v3.geniusyield.io/#get-trades - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified symbol of the market to fetch trades for | -| since | int | No | timestamp in ms of the earliest trade to fetch | -| limit | int | No | the maximum amount of trades to fetch | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchTrades (symbol[, since, limit, params]) -``` - - - - -### fetchTradingFees{docsify-ignore} -fetch the trading fees for multiple markets - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a dictionary of [fee structures](https://docs.ccxt.com/#/?id=fee-structure) indexed by market symbols - -**See**: https://api-docs-v3.geniusyield.io/#get-api-account - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchTradingFees ([params]) -``` - - - - -### fetchOrderBook{docsify-ignore} -fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - A dictionary of [order book structures](https://docs.ccxt.com/#/?id=order-book-structure) indexed by market symbols - -**See**: https://api-docs-v3.geniusyield.io/#get-order-books - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified symbol of the market to fetch the order book for | -| limit | int | No | the maximum amount of order book entries to return | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchOrderBook (symbol[, limit, params]) -``` - - - - -### fetchCurrencies{docsify-ignore} -fetches all available currencies on an exchange - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - an associative dictionary of currencies - -**See**: https://api-docs-v3.geniusyield.io/#get-assets - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchCurrencies ([params]) -``` - - - - -### fetchBalance{docsify-ignore} -query for balance and get the amount of funds available for trading or funds locked in orders - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a [balance structure](https://docs.ccxt.com/#/?id=balance-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-balances - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchBalance ([params]) -``` - - - - -### fetchMyTrades{docsify-ignore} -fetch all trades made by the user - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<Trade> - a list of [trade structures](https://docs.ccxt.com/#/?id=trade-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-fills - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified market symbol | -| since | int | No | the earliest time in ms to fetch trades for | -| limit | int | No | the maximum number of trades structures to retrieve | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchMyTrades (symbol[, since, limit, params]) -``` - - - - -### fetchOrder{docsify-ignore} -fetches information on an order made by the user - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - An [order structure](https://docs.ccxt.com/#/?id=order-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-orders - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified symbol of the market the order was made in | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchOrder (symbol[, params]) -``` - - - - -### fetchOpenOrders{docsify-ignore} -fetch all unfilled currently open orders - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<Order> - a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-orders - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified market symbol | -| since | int | No | the earliest time in ms to fetch open orders for | -| limit | int | No | the maximum number of open orders structures to retrieve | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchOpenOrders (symbol[, since, limit, params]) -``` - - - - -### fetchClosedOrders{docsify-ignore} -fetches information on multiple closed orders made by the user - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<Order> - a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-orders - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified market symbol of the market orders were made in | -| since | int | No | the earliest time in ms to fetch orders for | -| limit | int | No | the maximum number of order structures to retrieve | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchClosedOrders (symbol[, since, limit, params]) -``` - - - - -### createOrder{docsify-ignore} -create a trade order, https://docs.geniusyield.io/#create-order - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - an [order structure](https://docs.ccxt.com/#/?id=order-structure) - -**See**: https://api-docs-v3.geniusyield.io/#create-order - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified symbol of the market to create an order in | -| type | string | Yes | 'market' or 'limit' | -| side | string | Yes | 'buy' or 'sell' | -| amount | float | Yes | how much of currency you want to trade in units of base currency | -| price | float | No | the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders | -| params | object | No | extra parameters specific to the exchange API endpoint | -| params.test | bool | No | set to true to test an order, no order will be created but the request will be validated | - - -```javascript -geniusyield.createOrder (symbol, type, side, amount[, price, params]) -``` - - - - -### withdraw{docsify-ignore} -make a withdrawal - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a [transaction structure](https://docs.ccxt.com/#/?id=transaction-structure) - -**See**: https://api-docs-v3.geniusyield.io/#withdraw-funds - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| code | string | Yes | unified currency code | -| amount | float | Yes | the amount to withdraw | -| address | string | Yes | the address to withdraw to | -| tag | string | Yes | | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.withdraw (code, amount, address, tag[, params]) -``` - - - - -### cancelAllOrders{docsify-ignore} -cancel all open orders - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<object> - a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) - -**See**: https://api-docs-v3.geniusyield.io/#cancel-order - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| symbol | string | Yes | unified market symbol, only orders in the market of this symbol are cancelled when symbol is not undefined | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.cancelAllOrders (symbol[, params]) -``` - - - - -### cancelOrder{docsify-ignore} -cancels an open order - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - An [order structure](https://docs.ccxt.com/#/?id=order-structure) - -**See**: https://api-docs-v3.geniusyield.io/#cancel-order - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| id | string | Yes | order id | -| symbol | string | Yes | unified symbol of the market the order was made in | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.cancelOrder (id, symbol[, params]) -``` - - - - -### fetchDeposit{docsify-ignore} -fetch information on a deposit - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a [transaction structure](https://docs.ccxt.com/#/?id=transaction-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-deposits - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| id | string | Yes | deposit id | -| code | string | Yes | not used by geniusyield fetchDeposit () | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchDeposit (id, code[, params]) -``` - - - - -### fetchDeposits{docsify-ignore} -fetch all deposits made to an account - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<object> - a list of [transaction structures](https://docs.ccxt.com/#/?id=transaction-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-deposits - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| code | string | Yes | unified currency code | -| since | int | No | the earliest time in ms to fetch deposits for | -| limit | int | No | the maximum number of deposits structures to retrieve | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchDeposits (code[, since, limit, params]) -``` - - - - -### fetchStatus{docsify-ignore} -the latest known information on the availability of the exchange API - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a [status structure](https://docs.ccxt.com/#/?id=exchange-status-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-ping - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchStatus ([params]) -``` - - - - -### fetchTime{docsify-ignore} -fetches the current integer timestamp in milliseconds from the exchange server - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: int - the current integer timestamp in milliseconds from the exchange server - -**See**: https://api-docs-v3.geniusyield.io/#get-time - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchTime ([params]) -``` - - - - -### fetchWithdrawal{docsify-ignore} -fetch data on a currency withdrawal via the withdrawal id - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - a [transaction structure](https://docs.ccxt.com/#/?id=transaction-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-withdrawals - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| id | string | Yes | withdrawal id | -| code | string | Yes | not used by geniusyield.fetchWithdrawal | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchWithdrawal (id, code[, params]) -``` - - - - -### fetchWithdrawals{docsify-ignore} -fetch all withdrawals made from an account - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: Array<object> - a list of [transaction structures](https://docs.ccxt.com/#/?id=transaction-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-withdrawals - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| code | string | Yes | unified currency code | -| since | int | No | the earliest time in ms to fetch withdrawals for | -| limit | int | No | the maximum number of withdrawals structures to retrieve | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchWithdrawals (code[, since, limit, params]) -``` - - - - -### fetchDepositAddress{docsify-ignore} -fetch the Polygon address of the wallet - -**Kind**: instance method of [geniusyield](#geniusyield) -**Returns**: object - an [address structure](https://docs.ccxt.com/#/?id=address-structure) - -**See**: https://api-docs-v3.geniusyield.io/#get-wallets - -| Param | Type | Required | Description | -| --- | --- | --- | --- | -| code | string | Yes | not used by geniusyield | -| params | object | No | extra parameters specific to the exchange API endpoint | - - -```javascript -geniusyield.fetchDepositAddress (code[, params]) -``` -