Skip to content

Commit b0ce488

Browse files
Borrow rate types and parsers (#22205)
* chore: new type IsolatedBorrowRate * refactor: changed BorrowRate type to CrossBorrowRate * feat(base/exchange): parseIsolatedBorrowRates, parseIsolatedBorrowRate * feat: new types - CrossBorrowRate, IsolatedBorrowRate * fetchCrossBorrowRate(s) return type * fetch(Cross|Isolated)BorrowRate(s) return types * base/Exchange import CrossBorrowRate * Exchange.ts removed BorrowRate type * types.py linting * fix crossborrowRates transpiling * bybit minor fix --------- Co-authored-by: carlosmiei <[email protected]>
1 parent dcd92ad commit b0ce488

File tree

13 files changed

+215
-61
lines changed

13 files changed

+215
-61
lines changed

build/transpile.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -994,13 +994,16 @@ class Transpiler {
994994
'Balances': /-> Balances:/,
995995
'Bool': /: Bool =/,
996996
'Conversion': /-> Conversion:/,
997+
'CrossBorrowRate': /-> CrossBorrowRate:/,
998+
'CrossBorrowRates': /-> CrossBorrowRates:/,
997999
'Currencies': /-> Currencies:/,
9981000
'Currency': /(-> Currency:|: Currency)/,
9991001
'FundingHistory': /\[FundingHistory/,
10001002
'Greeks': /-> Greeks:/,
10011003
'IndexType': /: IndexType/,
10021004
'Int': /: Int =/,
1003-
'Liquidation': /-> (?:List\[)?Liquidation/,
1005+
'IsolatedBorrowRate': /-> IsolatedBorrowRate:/,
1006+
'IsolatedBorrowRates': /-> IsolatedBorrowRates:/,
10041007
'LastPrice': /-> LastPrice:/,
10051008
'LastPrices': /-> LastPrices:/,
10061009
'Leverage': /-> Leverage:/,
@@ -1683,7 +1686,7 @@ class Transpiler {
16831686
'Dictionary<any>': 'array',
16841687
'Dict': 'array',
16851688
}
1686-
const phpArrayRegex = /^(?:Market|Currency|Account|AccountStructure|BalanceAccount|object|OHLCV|Order|OrderBook|Tickers?|Trade|Transaction|Balances?|MarketInterface|TransferEntry|Leverages|Leverage|Greeks|MarginModes|MarginMode|MarginModification|LastPrice|LastPrices|TradingFeeInterface|Currencies|TradingFees)( \| undefined)?$|\w+\[\]/
1689+
const phpArrayRegex = /^(?:Market|Currency|Account|AccountStructure|BalanceAccount|object|OHLCV|Order|OrderBook|Tickers?|Trade|Transaction|Balances?|MarketInterface|TransferEntry|Leverages|Leverage|Greeks|MarginModes|MarginMode|MarginModification|LastPrice|LastPrices|TradingFeeInterface|Currencies|TradingFees|CrossBorrowRate|IsolatedBorrowRate)( \| undefined)?$|\w+\[\]/
16871690
let phpArgs = args.map (x => {
16881691
const parts = x.split (':')
16891692
if (parts.length === 1) {

cs/ccxt/base/Exchange.Types.cs

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -758,25 +758,131 @@ public DepositAddressResponse(object depositAddressResponse2)
758758
}
759759
}
760760

761-
public struct BorrowRate
761+
public struct CrossBorrowRate
762762
{
763763
public string? currency;
764764
public double? rate;
765765
public Int64? timestamp;
766766
public string? datetime;
767767
public Dictionary<string, object> info;
768768

769-
public BorrowRate(object borrowRate)
769+
public CrossBorrowRate(object crossBorrowRate)
770770
{
771-
var borrowRate2 = (Dictionary<string, object>)borrowRate;
772-
currency = Exchange.SafeString(borrowRate2, "currency");
773-
rate = Exchange.SafeFloat(borrowRate2, "rate");
774-
timestamp = Exchange.SafeInteger(borrowRate2, "timestamp");
775-
datetime = Exchange.SafeString(borrowRate2, "datetime");
776-
info = borrowRate2.ContainsKey("info") ? (Dictionary<string, object>)borrowRate2["info"] : null;
771+
var crossBorrowRate2 = (Dictionary<string, object>)crossBorrowRate;
772+
currency = Exchange.SafeString(crossBorrowRate2, "currency");
773+
rate = Exchange.SafeFloat(crossBorrowRate2, "rate");
774+
timestamp = Exchange.SafeInteger(crossBorrowRate2, "timestamp");
775+
datetime = Exchange.SafeString(crossBorrowRate2, "datetime");
776+
info = crossBorrowRate2.ContainsKey("info") ? (Dictionary<string, object>)crossBorrowRate2["info"] : null;
777777
}
778778
}
779779

780+
public struct CrossBorrowRates
781+
{
782+
public Dictionary<string, object> info;
783+
public Dictionary<string, CrossBorrowRate> crossBorrowRates;
784+
785+
public CrossBorrowRates(object crossBorrowRates2)
786+
{
787+
var crossBorrowRates = (Dictionary<string, object>)crossBorrowRates2;
788+
789+
info = crossBorrowRates.ContainsKey("info") ? (Dictionary<string, object>)crossBorrowRates["info"] : null;
790+
this.crossBorrowRates = new Dictionary<string, CrossBorrowRate>();
791+
foreach (var crossBorrowRate in crossBorrowRates)
792+
{
793+
if (crossBorrowRate.Key != "info")
794+
this.crossBorrowRates.Add(crossBorrowRate.Key, new CrossBorrowRate(crossBorrowRate.Value));
795+
}
796+
}
797+
798+
// Indexer
799+
public CrossBorrowRate this[string key]
800+
{
801+
get
802+
{
803+
if (crossBorrowRates.ContainsKey(key))
804+
{
805+
return crossBorrowRates[key];
806+
}
807+
else
808+
{
809+
throw new KeyNotFoundException($"The key '{key}' was not found in the isolatedBorrowRates.");
810+
}
811+
}
812+
set
813+
{
814+
crossBorrowRates[key] = value;
815+
}
816+
}
817+
}
818+
819+
public struct IsolatedBorrowRate
820+
{
821+
public string symbol;
822+
// public string base;
823+
public double? baseRate;
824+
public string quote;
825+
public double? quoteRate;
826+
public double? rate;
827+
public Int64? timestamp;
828+
public string? datetime;
829+
public Dictionary<string, object> info;
830+
831+
public IsolatedBorrowRate(object isolatedBorrowRate)
832+
{
833+
var isolatedBorrowRate2 = (Dictionary<string, object>)isolatedBorrowRate;
834+
symbol = Exchange.SafeString (isolatedBorrowRate2, "symbol");
835+
// base = Exchange.SafeString (isolatedBorrowRate2, "base");
836+
baseRate = Exchange.SafeFloat (isolatedBorrowRate2, "baseRate");
837+
quote = Exchange.SafeString (isolatedBorrowRate2, "quote");
838+
quoteRate = Exchange.SafeFloat (isolatedBorrowRate2, "quoteRate");
839+
rate = Exchange.SafeFloat(isolatedBorrowRate2, "rate");
840+
timestamp = Exchange.SafeInteger(isolatedBorrowRate2, "timestamp");
841+
datetime = Exchange.SafeString(isolatedBorrowRate2, "datetime");
842+
info = isolatedBorrowRate2.ContainsKey("info") ? (Dictionary<string, object>)isolatedBorrowRate2["info"] : null;
843+
}
844+
}
845+
846+
public struct IsolatedBorrowRates
847+
{
848+
public Dictionary<string, object> info;
849+
public Dictionary<string, IsolatedBorrowRate> isolatedBorrowRates;
850+
851+
public IsolatedBorrowRates(object isolatedBorrowRates2)
852+
{
853+
var isolatedBorrowRates = (Dictionary<string, object>)isolatedBorrowRates2;
854+
855+
info = isolatedBorrowRates.ContainsKey("info") ? (Dictionary<string, object>)isolatedBorrowRates["info"] : null;
856+
this.isolatedBorrowRates = new Dictionary<string, IsolatedBorrowRate>();
857+
foreach (var isolatedBorrowRate in isolatedBorrowRates)
858+
{
859+
if (isolatedBorrowRate.Key != "info")
860+
this.isolatedBorrowRates.Add(isolatedBorrowRate.Key, new IsolatedBorrowRate(isolatedBorrowRate.Value));
861+
}
862+
}
863+
864+
// Indexer
865+
public IsolatedBorrowRate this[string key]
866+
{
867+
get
868+
{
869+
if (isolatedBorrowRates.ContainsKey(key))
870+
{
871+
return isolatedBorrowRates[key];
872+
}
873+
else
874+
{
875+
throw new KeyNotFoundException($"The key '{key}' was not found in the isolatedBorrowRates.");
876+
}
877+
}
878+
set
879+
{
880+
isolatedBorrowRates[key] = value;
881+
}
882+
}
883+
}
884+
885+
780886
public struct BorrowInterest
781887
{
782888
public string? account;

python/ccxt/base/types.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,13 @@ class OrderRequest(TypedDict):
148148
price: Union[None, float]
149149
params: Dict[str, Any]
150150

151+
151152
class CancellationRequest(TypedDict):
152153
id: Str
153154
symbol: Str
154155
clientOrderId: Str
155156

157+
156158
class Order(TypedDict):
157159
info: Dict[str, Any]
158160
id: Str
@@ -437,9 +439,32 @@ class MarginModification(TypedDict):
437439
datetime: Str
438440

439441

442+
class CrossBorrowRate(TypedDict):
443+
info: Dict[str, any]
444+
currency: Str
445+
rate: float
446+
period: Optional[float]
447+
timestamp: Int
448+
datetime: Str
449+
450+
451+
class IsolatedBorrowRate(TypedDict):
452+
info: Dict[str, any]
453+
symbol: str
454+
base: str
455+
baseRate: float
456+
quote: str
457+
quoteRate: float
458+
period: Int
459+
timestamp: Int
460+
datetime: Str
461+
462+
440463
LastPrices = Dict[Str, LastPrice]
441464
Currencies = Dict[Str, CurrencyInterface]
442465
TradingFees = Dict[Str, TradingFeeInterface]
466+
IsolatedBorrowRates = Dict[Str, IsolatedBorrowRate]
467+
CrossBorrowRates = Dict[Str, CrossBorrowRate]
443468

444469
Market = Optional[MarketInterface]
445470
Currency = Optional[CurrencyInterface]

ts/src/base/Exchange.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ import { OrderBook as WsOrderBook, IndexedOrderBook, CountedOrderBook } from './
146146
//
147147
import { axolotl } from './functions/crypto.js';
148148
// import types
149-
import type { Market, Trade, Fee, Ticker, OHLCV, OHLCVC, Order, OrderBook, Balance, Balances, Dictionary, Transaction, DepositAddressResponse, Currency, MinMax, IndexType, Int, OrderType, OrderSide, Position, FundingRate, DepositWithdrawFeeNetwork, LedgerEntry, BorrowInterest, OpenInterest, LeverageTier, TransferEntry, BorrowRate, FundingRateHistory, Liquidation, FundingHistory, OrderRequest, MarginMode, Tickers, Greeks, Option, OptionChain, Str, Num, MarketInterface, CurrencyInterface, BalanceAccount, MarginModes, MarketType, Leverage, Leverages, LastPrice, LastPrices, Account, Strings, MarginModification, TradingFeeInterface, Currencies, TradingFees, Conversion, CancellationRequest } from './types.js';
149+
import type { Market, Trade, Fee, Ticker, OHLCV, OHLCVC, Order, OrderBook, Balance, Balances, Dictionary, Transaction, DepositAddressResponse, Currency, MinMax, IndexType, Int, OrderType, OrderSide, Position, FundingRate, DepositWithdrawFeeNetwork, LedgerEntry, BorrowInterest, OpenInterest, LeverageTier, TransferEntry, FundingRateHistory, Liquidation, FundingHistory, OrderRequest, MarginMode, Tickers, Greeks, Option, OptionChain, Str, Num, MarketInterface, CurrencyInterface, BalanceAccount, MarginModes, MarketType, Leverage, Leverages, LastPrice, LastPrices, Account, Strings, MarginModification, TradingFeeInterface, Currencies, TradingFees, Conversion, CancellationRequest, IsolatedBorrowRate, IsolatedBorrowRates, CrossBorrowRates, CrossBorrowRate } from './types.js';
150150
// export {Market, Trade, Fee, Ticker, OHLCV, OHLCVC, Order, OrderBook, Balance, Balances, Dictionary, Transaction, DepositAddressResponse, Currency, MinMax, IndexType, Int, OrderType, OrderSide, Position, FundingRateHistory, Liquidation, FundingHistory} from './types.js'
151151
// import { Market, Trade, Fee, Ticker, OHLCV, OHLCVC, Order, OrderBook, Balance, Balances, Dictionary, Transaction, DepositAddressResponse, Currency, MinMax, IndexType, Int, OrderType, OrderSide, Position, FundingRateHistory, OpenInterest, Liquidation, OrderRequest, FundingHistory, MarginMode, Tickers, Greeks, Str, Num, MarketInterface, CurrencyInterface, Account } from './types.js';
152-
export type { Market, Trade, Fee, Ticker, OHLCV, OHLCVC, Order, OrderBook, Balance, Balances, Dictionary, Transaction, DepositAddressResponse, Currency, MinMax, IndexType, Int, OrderType, OrderSide, Position, LedgerEntry, BorrowInterest, OpenInterest, LeverageTier, TransferEntry, BorrowRate, FundingRateHistory, Liquidation, FundingHistory, OrderRequest, MarginMode, Tickers, Greeks, Option, OptionChain, Str, Num, MarketInterface, CurrencyInterface, BalanceAccount, MarginModes, MarketType, Leverage, Leverages, LastPrice, LastPrices, Account, Strings, Conversion } from './types.js'
152+
export type { Market, Trade, Fee, Ticker, OHLCV, OHLCVC, Order, OrderBook, Balance, Balances, Dictionary, Transaction, DepositAddressResponse, Currency, MinMax, IndexType, Int, OrderType, OrderSide, Position, LedgerEntry, BorrowInterest, OpenInterest, LeverageTier, TransferEntry, CrossBorrowRate, FundingRateHistory, Liquidation, FundingHistory, OrderRequest, MarginMode, Tickers, Greeks, Option, OptionChain, Str, Num, MarketInterface, CurrencyInterface, BalanceAccount, MarginModes, MarketType, Leverage, Leverages, LastPrice, LastPrices, Account, Strings, Conversion } from './types.js'
153153

154154
// ----------------------------------------------------------------------------
155155
// move this elsewhere.
@@ -2337,11 +2337,11 @@ export default class Exchange {
23372337
throw new NotSupported (this.id + ' parseOrder() is not supported yet');
23382338
}
23392339

2340-
async fetchCrossBorrowRates (params = {}): Promise<{}> {
2340+
async fetchCrossBorrowRates (params = {}): Promise<CrossBorrowRates> {
23412341
throw new NotSupported (this.id + ' fetchCrossBorrowRates() is not supported yet');
23422342
}
23432343

2344-
async fetchIsolatedBorrowRates (params = {}): Promise<{}> {
2344+
async fetchIsolatedBorrowRates (params = {}): Promise<IsolatedBorrowRates> {
23452345
throw new NotSupported (this.id + ' fetchIsolatedBorrowRates() is not supported yet');
23462346
}
23472347

@@ -2365,6 +2365,10 @@ export default class Exchange {
23652365
throw new NotSupported (this.id + ' parseBorrowInterest() is not supported yet');
23662366
}
23672367

2368+
parseIsolatedBorrowRate (info, market: Market = undefined): IsolatedBorrowRate {
2369+
throw new NotSupported (this.id + ' parseIsolatedBorrowRate() is not supported yet');
2370+
}
2371+
23682372
parseWsTrade (trade, market: Market = undefined): Trade {
23692373
throw new NotSupported (this.id + ' parseWsTrade() is not supported yet');
23702374
}
@@ -4455,7 +4459,7 @@ export default class Exchange {
44554459
}
44564460
}
44574461

4458-
async fetchCrossBorrowRate (code: string, params = {}): Promise<{}> {
4462+
async fetchCrossBorrowRate (code: string, params = {}): Promise<CrossBorrowRate> {
44594463
await this.loadMarkets ();
44604464
if (!this.has['fetchBorrowRates']) {
44614465
throw new NotSupported (this.id + ' fetchCrossBorrowRate() is not supported yet');
@@ -4468,13 +4472,13 @@ export default class Exchange {
44684472
return rate;
44694473
}
44704474

4471-
async fetchIsolatedBorrowRate (symbol: string, params = {}): Promise<{}> {
4475+
async fetchIsolatedBorrowRate (symbol: string, params = {}): Promise<IsolatedBorrowRate> {
44724476
await this.loadMarkets ();
44734477
if (!this.has['fetchBorrowRates']) {
44744478
throw new NotSupported (this.id + ' fetchIsolatedBorrowRate() is not supported yet');
44754479
}
44764480
const borrowRates = await this.fetchIsolatedBorrowRates (params);
4477-
const rate = this.safeDict (borrowRates, symbol);
4481+
const rate = this.safeDict (borrowRates, symbol) as IsolatedBorrowRate;
44784482
if (rate === undefined) {
44794483
throw new ExchangeError (this.id + ' fetchIsolatedBorrowRate() could not find the borrow rate for market symbol ' + symbol);
44804484
}
@@ -5834,6 +5838,17 @@ export default class Exchange {
58345838
return interests;
58355839
}
58365840

5841+
parseIsolatedBorrowRates (info: any): IsolatedBorrowRates {
5842+
const result = {};
5843+
for (let i = 0; i < info.length; i++) {
5844+
const item = info[i];
5845+
const borrowRate = this.parseIsolatedBorrowRate (item);
5846+
const symbol = this.safeString (borrowRate, 'symbol');
5847+
result[symbol] = borrowRate;
5848+
}
5849+
return result as any;
5850+
}
5851+
58375852
parseFundingRateHistories (response, market = undefined, since: Int = undefined, limit: Int = undefined): FundingRateHistory[] {
58385853
const rates = [];
58395854
for (let i = 0; i < response.length; i++) {

ts/src/base/types.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,13 +389,25 @@ export interface TransferEntry {
389389
status?: Str;
390390
}
391391

392-
export interface BorrowRate {
392+
export interface CrossBorrowRate {
393+
info: any;
393394
currency?: Str;
394-
rate?: number;
395+
rate: number;
395396
period?: number;
396397
timestamp?: number;
397398
datetime?: Str;
398-
info: any;
399+
}
400+
401+
export interface IsolatedBorrowRate {
402+
info: any,
403+
symbol: string,
404+
base: string,
405+
baseRate: number,
406+
quote: string,
407+
quoteRate: number,
408+
period?: Int,
409+
timestamp?: Int,
410+
datetime?: Str,
399411
}
400412

401413
export interface FundingRateHistory {
@@ -560,6 +572,12 @@ export interface MarginModes extends Dictionary<MarginMode> {
560572
export interface OptionChain extends Dictionary<Option> {
561573
}
562574

575+
export interface IsolatedBorrowRates extends Dictionary<IsolatedBorrowRates> {
576+
}
577+
578+
export interface CrossBorrowRates extends Dictionary<CrossBorrowRates> {
579+
}
580+
563581
/** [ timestamp, open, high, low, close, volume ] */
564582
export type OHLCV = [Num, Num, Num, Num, Num, Num];
565583

ts/src/binance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import Exchange from './abstract/binance.js';
55
import { ExchangeError, ArgumentsRequired, OperationFailed, OperationRejected, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, NotSupported, BadRequest, BadSymbol, AccountSuspended, OrderImmediatelyFillable, OnMaintenance, BadResponse, RequestTimeout, OrderNotFillable, MarginModeAlreadySet } from './base/errors.js';
66
import { Precise } from './base/Precise.js';
7-
import type { TransferEntry, Int, OrderSide, Balances, OrderType, Trade, OHLCV, Order, FundingRateHistory, OpenInterest, Liquidation, OrderRequest, Str, Transaction, Ticker, OrderBook, Tickers, Market, Greeks, Strings, Currency, MarketInterface, MarginMode, MarginModes, Leverage, Leverages, Num, Option, MarginModification, TradingFeeInterface, Currencies, TradingFees, Conversion } from './base/types.js';
7+
import type { TransferEntry, Int, OrderSide, Balances, OrderType, Trade, OHLCV, Order, FundingRateHistory, OpenInterest, Liquidation, OrderRequest, Str, Transaction, Ticker, OrderBook, Tickers, Market, Greeks, Strings, Currency, MarketInterface, MarginMode, MarginModes, Leverage, Leverages, Num, Option, MarginModification, TradingFeeInterface, Currencies, TradingFees, Conversion, CrossBorrowRate } from './base/types.js';
88
import { TRUNCATE, DECIMAL_PLACES } from './base/functions/number.js';
99
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
1010
import { rsa } from './base/functions/rsa.js';
@@ -11133,7 +11133,7 @@ export default class binance extends Exchange {
1113311133
return await this.modifyMarginHelper (symbol, amount, 1, params);
1113411134
}
1113511135

11136-
async fetchCrossBorrowRate (code: string, params = {}) {
11136+
async fetchCrossBorrowRate (code: string, params = {}): Promise<CrossBorrowRate> {
1113711137
/**
1113811138
* @method
1113911139
* @name binance#fetchCrossBorrowRate

ts/src/bitget.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ExchangeError, ExchangeNotAvailable, NotSupported, OnMaintenance, Argum
66
import { Precise } from './base/Precise.js';
77
import { TICK_SIZE } from './base/functions/number.js';
88
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
9-
import type { Int, OrderSide, OrderType, Trade, OHLCV, Order, FundingRateHistory, OrderRequest, FundingHistory, Balances, Str, Transaction, Ticker, OrderBook, Tickers, Market, Strings, Currency, Position, Liquidation, TransferEntry, Leverage, MarginMode, Num, MarginModification, TradingFeeInterface, Currencies, TradingFees, Conversion } from './base/types.js';
9+
import type { Int, OrderSide, OrderType, Trade, OHLCV, Order, FundingRateHistory, OrderRequest, FundingHistory, Balances, Str, Transaction, Ticker, OrderBook, Tickers, Market, Strings, Currency, Position, Liquidation, TransferEntry, Leverage, MarginMode, Num, MarginModification, TradingFeeInterface, Currencies, TradingFees, Conversion, CrossBorrowRate, IsolatedBorrowRate } from './base/types.js';
1010

1111
// ---------------------------------------------------------------------------
1212

@@ -7863,7 +7863,7 @@ export default class bitget extends Exchange {
78637863
});
78647864
}
78657865

7866-
async fetchIsolatedBorrowRate (symbol: string, params = {}) {
7866+
async fetchIsolatedBorrowRate (symbol: string, params = {}): Promise<IsolatedBorrowRate> {
78677867
/**
78687868
* @method
78697869
* @name bitget#fetchIsolatedBorrowRate
@@ -7927,7 +7927,7 @@ export default class bitget extends Exchange {
79277927
return this.parseIsolatedBorrowRate (first, market);
79287928
}
79297929

7930-
parseIsolatedBorrowRate (info, market: Market = undefined) {
7930+
parseIsolatedBorrowRate (info, market: Market = undefined): IsolatedBorrowRate {
79317931
//
79327932
// {
79337933
// "symbol": "BTCUSDT",
@@ -7980,7 +7980,7 @@ export default class bitget extends Exchange {
79807980
};
79817981
}
79827982

7983-
async fetchCrossBorrowRate (code: string, params = {}) {
7983+
async fetchCrossBorrowRate (code: string, params = {}): Promise<CrossBorrowRate> {
79847984
/**
79857985
* @method
79867986
* @name bitget#fetchCrossBorrowRate

0 commit comments

Comments
 (0)