@drift-labs/sdk 0.2.0-temp.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -29
- package/lib/accounts/bulkAccountLoader.d.ts +2 -0
- package/lib/accounts/bulkAccountLoader.js +36 -29
- package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserStatsSubscription.js +21 -0
- package/lib/accounts/bulkUserSubscription.d.ts +2 -2
- package/lib/accounts/bulkUserSubscription.js +0 -1
- package/lib/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
- package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
- package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
- package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
- package/lib/accounts/types.d.ts +26 -15
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
- package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/marketAddresses.d.ts +1 -3
- package/lib/addresses/marketAddresses.js +1 -1
- package/lib/addresses/pda.d.ts +15 -9
- package/lib/addresses/pda.js +73 -35
- package/lib/adminClient.d.ts +65 -0
- package/lib/adminClient.js +637 -0
- package/lib/config.d.ts +10 -10
- package/lib/config.js +26 -22
- package/lib/constants/numericConstants.d.ts +29 -12
- package/lib/constants/numericConstants.js +40 -21
- package/lib/constants/perpMarkets.d.ts +18 -0
- package/lib/constants/{markets.js → perpMarkets.js} +20 -9
- package/lib/constants/spotMarkets.d.ts +19 -0
- package/lib/constants/spotMarkets.js +63 -0
- package/lib/dlob/DLOB.d.ts +82 -0
- package/lib/dlob/DLOB.js +694 -0
- package/lib/dlob/DLOBNode.d.ts +54 -0
- package/lib/dlob/DLOBNode.js +77 -0
- package/lib/dlob/NodeList.d.ts +27 -0
- package/lib/dlob/NodeList.js +144 -0
- package/lib/driftClient.d.ts +234 -0
- package/lib/driftClient.js +2108 -0
- package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
- package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
- package/lib/events/eventList.d.ts +0 -1
- package/lib/events/eventList.js +0 -7
- package/lib/events/eventSubscriber.d.ts +5 -2
- package/lib/events/eventSubscriber.js +25 -11
- package/lib/events/fetchLogs.d.ts +13 -2
- package/lib/events/fetchLogs.js +45 -14
- package/lib/events/pollingLogProvider.d.ts +2 -1
- package/lib/events/pollingLogProvider.js +7 -3
- package/lib/events/sort.js +8 -11
- package/lib/events/txEventCache.d.ts +0 -2
- package/lib/events/txEventCache.js +0 -14
- package/lib/events/types.d.ts +11 -3
- package/lib/events/types.js +8 -0
- package/lib/events/webSocketLogProvider.js +1 -1
- package/lib/examples/makeTradeExample.js +30 -18
- package/lib/factory/bigNum.d.ts +8 -4
- package/lib/factory/bigNum.js +109 -19
- package/lib/idl/drift.json +8392 -0
- package/lib/index.d.ts +29 -12
- package/lib/index.js +29 -12
- package/lib/math/amm.d.ts +9 -6
- package/lib/math/amm.js +91 -38
- package/lib/math/conversion.js +1 -1
- package/lib/math/exchangeStatus.d.ts +4 -0
- package/lib/math/exchangeStatus.js +18 -0
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +23 -21
- package/lib/math/insurance.d.ts +4 -0
- package/lib/math/insurance.js +27 -0
- package/lib/math/margin.d.ts +11 -0
- package/lib/math/margin.js +82 -0
- package/lib/math/market.d.ts +14 -9
- package/lib/math/market.js +70 -10
- package/lib/math/oracles.d.ts +4 -0
- package/lib/math/oracles.js +36 -8
- package/lib/math/orders.d.ts +16 -6
- package/lib/math/orders.js +97 -17
- package/lib/math/position.d.ts +27 -13
- package/lib/math/position.js +91 -37
- package/lib/math/repeg.js +17 -8
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +192 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/trade.d.ts +10 -10
- package/lib/math/trade.js +27 -31
- package/lib/oracles/pythClient.js +1 -1
- package/lib/oracles/quoteAssetOracleClient.js +1 -1
- package/lib/oracles/switchboardClient.js +1 -1
- package/lib/orderParams.d.ts +4 -4
- package/lib/orderParams.js +12 -4
- package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
- package/lib/serum/serumFulfillmentConfigMap.js +17 -0
- package/lib/serum/serumSubscriber.d.ts +27 -0
- package/lib/serum/serumSubscriber.js +56 -0
- package/lib/serum/types.d.ts +11 -0
- package/{src/oracles → lib/serum}/types.js +0 -0
- package/lib/tokenFaucet.d.ts +1 -0
- package/lib/tokenFaucet.js +23 -12
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +13 -4
- package/lib/tx/types.d.ts +1 -1
- package/lib/types.d.ts +631 -222
- package/lib/types.js +137 -24
- package/lib/user.d.ts +228 -0
- package/lib/user.js +959 -0
- package/lib/userConfig.d.ts +14 -0
- package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
- package/lib/userMap/userMap.d.ts +41 -0
- package/lib/userMap/userMap.js +85 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/lib/userName.d.ts +1 -0
- package/lib/userName.js +3 -2
- package/lib/userStats.d.ts +18 -0
- package/lib/userStats.js +49 -0
- package/lib/userStatsConfig.d.ts +14 -0
- package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
- package/lib/util/getTokenAddress.d.ts +2 -0
- package/lib/util/getTokenAddress.js +9 -0
- package/package.json +12 -5
- package/src/accounts/bulkAccountLoader.ts +44 -34
- package/src/accounts/bulkUserStatsSubscription.ts +33 -0
- package/src/accounts/bulkUserSubscription.ts +2 -3
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
- package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
- package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
- package/src/accounts/types.ts +35 -15
- package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/marketAddresses.ts +3 -4
- package/src/addresses/pda.ts +105 -33
- package/src/adminClient.ts +1207 -0
- package/src/config.ts +41 -34
- package/src/constants/numericConstants.ts +59 -26
- package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
- package/src/constants/spotMarkets.ts +83 -0
- package/src/dlob/DLOB.ts +1120 -0
- package/src/dlob/DLOBNode.ts +155 -0
- package/src/dlob/NodeList.ts +195 -0
- package/src/driftClient.ts +3594 -0
- package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
- package/src/events/eventList.ts +1 -8
- package/src/events/eventSubscriber.ts +36 -14
- package/src/events/fetchLogs.ts +60 -15
- package/src/events/pollingLogProvider.ts +11 -3
- package/src/events/sort.ts +11 -15
- package/src/events/txEventCache.ts +0 -16
- package/src/events/types.ts +27 -2
- package/src/events/webSocketLogProvider.ts +1 -1
- package/src/examples/makeTradeExample.js +152 -75
- package/src/examples/makeTradeExample.ts +44 -28
- package/src/factory/bigNum.ts +150 -22
- package/src/idl/drift.json +8392 -0
- package/src/idl/pyth.json +98 -2
- package/src/index.ts +29 -12
- package/src/math/amm.ts +161 -48
- package/src/math/conversion.ts +2 -2
- package/src/math/exchangeStatus.ts +31 -0
- package/src/math/funding.ts +41 -31
- package/src/math/insurance.ts +35 -0
- package/src/math/margin.ts +133 -0
- package/src/math/market.ts +143 -14
- package/src/math/oracles.ts +63 -9
- package/src/math/orders.ts +168 -26
- package/src/math/position.ts +133 -59
- package/src/math/repeg.ts +19 -9
- package/src/math/spotBalance.ts +319 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/trade.ts +33 -37
- package/src/oracles/pythClient.ts +2 -2
- package/src/oracles/quoteAssetOracleClient.ts +2 -2
- package/src/oracles/switchboardClient.ts +2 -2
- package/src/orderParams.ts +16 -8
- package/src/serum/serumFulfillmentConfigMap.ts +26 -0
- package/src/serum/serumSubscriber.ts +99 -0
- package/src/serum/types.ts +13 -0
- package/src/tokenFaucet.ts +38 -15
- package/src/tx/retryTxSender.ts +16 -5
- package/src/tx/types.ts +2 -1
- package/src/types.ts +594 -195
- package/src/user.ts +1599 -0
- package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
- package/src/userMap/userMap.ts +124 -0
- package/src/userMap/userStatsMap.ts +108 -0
- package/src/userName.ts +2 -1
- package/src/userStats.ts +75 -0
- package/src/userStatsConfig.ts +18 -0
- package/src/util/getTokenAddress.ts +18 -0
- package/tests/bn/test.ts +46 -11
- package/tests/dlob/helpers.ts +619 -0
- package/tests/dlob/test.ts +4586 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
- package/lib/admin.d.ts +0 -44
- package/lib/admin.js +0 -438
- package/lib/clearingHouse.d.ts +0 -146
- package/lib/clearingHouse.js +0 -1154
- package/lib/clearingHouseUser.d.ts +0 -187
- package/lib/clearingHouseUser.js +0 -634
- package/lib/clearingHouseUserConfig.d.ts +0 -14
- package/lib/constants/banks.d.ts +0 -16
- package/lib/constants/banks.js +0 -41
- package/lib/constants/markets.d.ts +0 -19
- package/lib/idl/clearing_house.json +0 -4464
- package/lib/math/bankBalance.d.ts +0 -9
- package/lib/math/bankBalance.js +0 -75
- package/lib/math/state.d.ts +0 -8
- package/lib/math/state.js +0 -15
- package/lib/orders.d.ts +0 -8
- package/lib/orders.js +0 -134
- package/src/accounts/bulkAccountLoader.js +0 -197
- package/src/accounts/bulkUserSubscription.js +0 -33
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
- package/src/accounts/pollingOracleSubscriber.js +0 -93
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
- package/src/accounts/pollingUserAccountSubscriber.js +0 -132
- package/src/accounts/types.js +0 -10
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -93
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
- package/src/addresses/marketAddresses.js +0 -26
- package/src/admin.js +0 -517
- package/src/admin.ts +0 -730
- package/src/clearingHouse.ts +0 -1828
- package/src/clearingHouseUser.ts +0 -978
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/config.js +0 -67
- package/src/constants/banks.js +0 -42
- package/src/constants/banks.ts +0 -50
- package/src/constants/markets.js +0 -42
- package/src/constants/numericConstants.js +0 -41
- package/src/events/eventSubscriber.js +0 -139
- package/src/events/fetchLogs.js +0 -50
- package/src/events/pollingLogProvider.js +0 -64
- package/src/events/sort.js +0 -44
- package/src/events/txEventCache.js +0 -71
- package/src/events/types.js +0 -20
- package/src/events/webSocketLogProvider.js +0 -41
- package/src/factory/bigNum.js +0 -390
- package/src/factory/oracleClient.js +0 -20
- package/src/idl/clearing_house.json +0 -4464
- package/src/index.js +0 -69
- package/src/math/amm.js +0 -369
- package/src/math/auction.js +0 -42
- package/src/math/bankBalance.ts +0 -112
- package/src/math/conversion.js +0 -11
- package/src/math/funding.js +0 -248
- package/src/math/oracles.js +0 -26
- package/src/math/repeg.js +0 -128
- package/src/math/state.js +0 -15
- package/src/math/state.ts +0 -14
- package/src/math/trade.js +0 -253
- package/src/math/utils.js +0 -26
- package/src/math/utils.js.map +0 -1
- package/src/mockUSDCFaucet.js +0 -280
- package/src/oracles/oracleClientCache.js +0 -19
- package/src/oracles/pythClient.js +0 -46
- package/src/oracles/quoteAssetOracleClient.js +0 -32
- package/src/oracles/switchboardClient.js +0 -69
- package/src/orderParams.js +0 -20
- package/src/orders.ts +0 -245
- package/src/slot/SlotSubscriber.js +0 -39
- package/src/tokenFaucet.js +0 -189
- package/src/types.js +0 -125
- package/src/userName.js +0 -20
- package/src/wallet.js +0 -35
package/src/idl/pyth.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.1.0",
|
|
3
3
|
"name": "pyth",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -41,6 +41,102 @@
|
|
|
41
41
|
"type": "i64"
|
|
42
42
|
}
|
|
43
43
|
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "setPriceInfo",
|
|
47
|
+
"accounts": [
|
|
48
|
+
{
|
|
49
|
+
"name": "price",
|
|
50
|
+
"isMut": true,
|
|
51
|
+
"isSigner": false
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"args": [
|
|
55
|
+
{
|
|
56
|
+
"name": "price",
|
|
57
|
+
"type": "i64"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "conf",
|
|
61
|
+
"type": "u64"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "slot",
|
|
65
|
+
"type": "u64"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "setTwap",
|
|
71
|
+
"accounts": [
|
|
72
|
+
{
|
|
73
|
+
"name": "price",
|
|
74
|
+
"isMut": true,
|
|
75
|
+
"isSigner": false
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"args": [
|
|
79
|
+
{
|
|
80
|
+
"name": "twap",
|
|
81
|
+
"type": "i64"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"types": [
|
|
87
|
+
{
|
|
88
|
+
"name": "PriceStatus",
|
|
89
|
+
"type": {
|
|
90
|
+
"kind": "enum",
|
|
91
|
+
"variants": [
|
|
92
|
+
{
|
|
93
|
+
"name": "Unknown"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "Trading"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "Halted"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "Auction"
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "CorpAction",
|
|
109
|
+
"type": {
|
|
110
|
+
"kind": "enum",
|
|
111
|
+
"variants": [
|
|
112
|
+
{
|
|
113
|
+
"name": "NoCorpAct"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "PriceType",
|
|
120
|
+
"type": {
|
|
121
|
+
"kind": "enum",
|
|
122
|
+
"variants": [
|
|
123
|
+
{
|
|
124
|
+
"name": "Unknown"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "Price"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "TWAP"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "Volatility"
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
44
137
|
}
|
|
45
|
-
]
|
|
138
|
+
],
|
|
139
|
+
"metadata": {
|
|
140
|
+
"address": "gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"
|
|
141
|
+
}
|
|
46
142
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,26 +7,34 @@ export * from './oracles/types';
|
|
|
7
7
|
export * from './oracles/pythClient';
|
|
8
8
|
export * from './oracles/switchboardClient';
|
|
9
9
|
export * from './types';
|
|
10
|
-
export * from './constants/
|
|
10
|
+
export * from './constants/perpMarkets';
|
|
11
11
|
export * from './accounts/fetch';
|
|
12
|
-
export * from './accounts/
|
|
12
|
+
export * from './accounts/webSocketDriftClientAccountSubscriber';
|
|
13
13
|
export * from './accounts/bulkAccountLoader';
|
|
14
14
|
export * from './accounts/bulkUserSubscription';
|
|
15
|
-
export * from './accounts/
|
|
16
|
-
export * from './accounts/
|
|
15
|
+
export * from './accounts/bulkUserStatsSubscription';
|
|
16
|
+
export * from './accounts/pollingDriftClientAccountSubscriber';
|
|
17
|
+
export * from './accounts/pollingOracleAccountSubscriber';
|
|
17
18
|
export * from './accounts/pollingTokenAccountSubscriber';
|
|
19
|
+
export * from './accounts/pollingUserAccountSubscriber';
|
|
20
|
+
export * from './accounts/pollingUserStatsAccountSubscriber';
|
|
18
21
|
export * from './accounts/types';
|
|
19
22
|
export * from './addresses/pda';
|
|
20
|
-
export * from './
|
|
21
|
-
export * from './
|
|
22
|
-
export * from './
|
|
23
|
-
export * from './
|
|
23
|
+
export * from './adminClient';
|
|
24
|
+
export * from './user';
|
|
25
|
+
export * from './userConfig';
|
|
26
|
+
export * from './userStats';
|
|
27
|
+
export * from './userStatsConfig';
|
|
28
|
+
export * from './driftClient';
|
|
24
29
|
export * from './factory/oracleClient';
|
|
25
30
|
export * from './factory/bigNum';
|
|
26
31
|
export * from './events/types';
|
|
27
32
|
export * from './events/eventSubscriber';
|
|
33
|
+
export * from './events/fetchLogs';
|
|
28
34
|
export * from './math/auction';
|
|
35
|
+
export * from './math/spotMarket';
|
|
29
36
|
export * from './math/conversion';
|
|
37
|
+
export * from './math/exchangeStatus';
|
|
30
38
|
export * from './math/funding';
|
|
31
39
|
export * from './math/market';
|
|
32
40
|
export * from './math/position';
|
|
@@ -35,7 +43,8 @@ export * from './math/amm';
|
|
|
35
43
|
export * from './math/trade';
|
|
36
44
|
export * from './math/orders';
|
|
37
45
|
export * from './math/repeg';
|
|
38
|
-
export * from './
|
|
46
|
+
export * from './math/margin';
|
|
47
|
+
export * from './math/insurance';
|
|
39
48
|
export * from './orderParams';
|
|
40
49
|
export * from './slot/SlotSubscriber';
|
|
41
50
|
export * from './wallet';
|
|
@@ -43,11 +52,19 @@ export * from './types';
|
|
|
43
52
|
export * from './math/utils';
|
|
44
53
|
export * from './config';
|
|
45
54
|
export * from './constants/numericConstants';
|
|
55
|
+
export * from './serum/serumSubscriber';
|
|
56
|
+
export * from './serum/serumFulfillmentConfigMap';
|
|
46
57
|
export * from './tx/retryTxSender';
|
|
47
58
|
export * from './util/computeUnits';
|
|
48
59
|
export * from './util/tps';
|
|
49
|
-
export * from './
|
|
50
|
-
export * from './
|
|
51
|
-
export * from './
|
|
60
|
+
export * from './util/promiseTimeout';
|
|
61
|
+
export * from './math/spotBalance';
|
|
62
|
+
export * from './constants/spotMarkets';
|
|
63
|
+
export * from './driftClientConfig';
|
|
64
|
+
export * from './dlob/DLOB';
|
|
65
|
+
export * from './dlob/DLOBNode';
|
|
66
|
+
export * from './dlob/NodeList';
|
|
67
|
+
export * from './userMap/userMap';
|
|
68
|
+
export * from './userMap/userStatsMap';
|
|
52
69
|
|
|
53
70
|
export { BN, PublicKey, pyth };
|
package/src/math/amm.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BN } from '@project-serum/anchor';
|
|
2
2
|
import {
|
|
3
3
|
AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO,
|
|
4
|
-
|
|
4
|
+
PRICE_PRECISION,
|
|
5
5
|
PEG_PRECISION,
|
|
6
6
|
ZERO,
|
|
7
7
|
BID_ASK_SPREAD_PRECISION,
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
AMM,
|
|
16
16
|
PositionDirection,
|
|
17
17
|
SwapDirection,
|
|
18
|
-
|
|
18
|
+
PerpMarketAccount,
|
|
19
19
|
isVariant,
|
|
20
20
|
} from '../types';
|
|
21
21
|
import { assert } from '../assert/assert';
|
|
22
|
-
import { squareRootBN } from '..';
|
|
22
|
+
import { squareRootBN, standardizeBaseAssetAmount } from '..';
|
|
23
23
|
|
|
24
24
|
import { OraclePriceData } from '../oracles/types';
|
|
25
25
|
import {
|
|
@@ -33,18 +33,21 @@ export function calculatePegFromTargetPrice(
|
|
|
33
33
|
baseAssetReserve: BN,
|
|
34
34
|
quoteAssetReserve: BN
|
|
35
35
|
): BN {
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
return BN.max(
|
|
37
|
+
targetPrice
|
|
38
|
+
.mul(baseAssetReserve)
|
|
39
|
+
.div(quoteAssetReserve)
|
|
40
|
+
.add(PRICE_DIV_PEG.div(new BN(2)))
|
|
41
|
+
.div(PRICE_DIV_PEG),
|
|
42
|
+
ONE
|
|
43
|
+
);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export function calculateOptimalPegAndBudget(
|
|
44
47
|
amm: AMM,
|
|
45
48
|
oraclePriceData: OraclePriceData
|
|
46
49
|
): [BN, BN, BN, boolean] {
|
|
47
|
-
const
|
|
50
|
+
const reservePriceBefore = calculatePrice(
|
|
48
51
|
amm.baseAssetReserve,
|
|
49
52
|
amm.quoteAssetReserve,
|
|
50
53
|
amm.pegMultiplier
|
|
@@ -67,15 +70,15 @@ export function calculateOptimalPegAndBudget(
|
|
|
67
70
|
let newTargetPrice: BN;
|
|
68
71
|
let newOptimalPeg: BN;
|
|
69
72
|
let newBudget: BN;
|
|
70
|
-
const targetPriceGap =
|
|
73
|
+
const targetPriceGap = reservePriceBefore.sub(targetPrice);
|
|
71
74
|
|
|
72
75
|
if (targetPriceGap.abs().gt(maxPriceSpread)) {
|
|
73
76
|
const markAdj = targetPriceGap.abs().sub(maxPriceSpread);
|
|
74
77
|
|
|
75
78
|
if (targetPriceGap.lt(new BN(0))) {
|
|
76
|
-
newTargetPrice =
|
|
79
|
+
newTargetPrice = reservePriceBefore.add(markAdj);
|
|
77
80
|
} else {
|
|
78
|
-
newTargetPrice =
|
|
81
|
+
newTargetPrice = reservePriceBefore.sub(markAdj);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
newOptimalPeg = calculatePegFromTargetPrice(
|
|
@@ -114,7 +117,7 @@ export function calculateNewAmm(
|
|
|
114
117
|
newAmm.sqrtK = newAmm.sqrtK.mul(pKNumer).div(pKDenom);
|
|
115
118
|
const invariant = newAmm.sqrtK.mul(newAmm.sqrtK);
|
|
116
119
|
newAmm.quoteAssetReserve = invariant.div(newAmm.baseAssetReserve);
|
|
117
|
-
const directionToClose = amm.
|
|
120
|
+
const directionToClose = amm.baseAssetAmountWithAmm.gt(ZERO)
|
|
118
121
|
? PositionDirection.SHORT
|
|
119
122
|
: PositionDirection.LONG;
|
|
120
123
|
|
|
@@ -122,7 +125,7 @@ export function calculateNewAmm(
|
|
|
122
125
|
calculateAmmReservesAfterSwap(
|
|
123
126
|
newAmm,
|
|
124
127
|
'base',
|
|
125
|
-
amm.
|
|
128
|
+
amm.baseAssetAmountWithAmm.abs(),
|
|
126
129
|
getSwapDirection('base', directionToClose)
|
|
127
130
|
);
|
|
128
131
|
|
|
@@ -154,7 +157,7 @@ export function calculateUpdatedAMM(
|
|
|
154
157
|
newAmm.quoteAssetReserve = invariant.div(newAmm.baseAssetReserve);
|
|
155
158
|
newAmm.pegMultiplier = newPeg;
|
|
156
159
|
|
|
157
|
-
const directionToClose = amm.
|
|
160
|
+
const directionToClose = amm.baseAssetAmountWithAmm.gt(ZERO)
|
|
158
161
|
? PositionDirection.SHORT
|
|
159
162
|
: PositionDirection.LONG;
|
|
160
163
|
|
|
@@ -162,7 +165,7 @@ export function calculateUpdatedAMM(
|
|
|
162
165
|
calculateAmmReservesAfterSwap(
|
|
163
166
|
newAmm,
|
|
164
167
|
'base',
|
|
165
|
-
amm.
|
|
168
|
+
amm.baseAssetAmountWithAmm.abs(),
|
|
166
169
|
getSwapDirection('base', directionToClose)
|
|
167
170
|
);
|
|
168
171
|
|
|
@@ -239,7 +242,7 @@ export function calculateBidAskPrice(
|
|
|
239
242
|
* @param baseAssetReserves
|
|
240
243
|
* @param quoteAssetReserves
|
|
241
244
|
* @param pegMultiplier
|
|
242
|
-
* @returns price : Precision
|
|
245
|
+
* @returns price : Precision PRICE_PRECISION
|
|
243
246
|
*/
|
|
244
247
|
export function calculatePrice(
|
|
245
248
|
baseAssetReserves: BN,
|
|
@@ -251,7 +254,7 @@ export function calculatePrice(
|
|
|
251
254
|
}
|
|
252
255
|
|
|
253
256
|
return quoteAssetReserves
|
|
254
|
-
.mul(
|
|
257
|
+
.mul(PRICE_PRECISION)
|
|
255
258
|
.mul(pegMultiplier)
|
|
256
259
|
.div(PEG_PRECISION)
|
|
257
260
|
.div(baseAssetReserves);
|
|
@@ -305,13 +308,62 @@ export function calculateAmmReservesAfterSwap(
|
|
|
305
308
|
return [newQuoteAssetReserve, newBaseAssetReserve];
|
|
306
309
|
}
|
|
307
310
|
|
|
311
|
+
export function calculateMarketOpenBidAsk(
|
|
312
|
+
baseAssetReserve: BN,
|
|
313
|
+
minBaseAssetReserve: BN,
|
|
314
|
+
maxBaseAssetReserve: BN
|
|
315
|
+
): [BN, BN] {
|
|
316
|
+
// open orders
|
|
317
|
+
let openAsks;
|
|
318
|
+
if (maxBaseAssetReserve > baseAssetReserve) {
|
|
319
|
+
openAsks = maxBaseAssetReserve.sub(baseAssetReserve).mul(new BN(-1));
|
|
320
|
+
} else {
|
|
321
|
+
openAsks = ZERO;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
let openBids;
|
|
325
|
+
if (minBaseAssetReserve < baseAssetReserve) {
|
|
326
|
+
openBids = baseAssetReserve.sub(minBaseAssetReserve);
|
|
327
|
+
} else {
|
|
328
|
+
openBids = ZERO;
|
|
329
|
+
}
|
|
330
|
+
return [openBids, openAsks];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function calculateInventoryScale(
|
|
334
|
+
netBaseAssetAmount: BN,
|
|
335
|
+
baseAssetReserve: BN,
|
|
336
|
+
minBaseAssetReserve: BN,
|
|
337
|
+
maxBaseAssetReserve: BN
|
|
338
|
+
): number {
|
|
339
|
+
const maxScale = BID_ASK_SPREAD_PRECISION.mul(new BN(10));
|
|
340
|
+
// inventory skew
|
|
341
|
+
const [openBids, openAsks] = calculateMarketOpenBidAsk(
|
|
342
|
+
baseAssetReserve,
|
|
343
|
+
minBaseAssetReserve,
|
|
344
|
+
maxBaseAssetReserve
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const minSideLiquidity = BN.max(
|
|
348
|
+
new BN(1),
|
|
349
|
+
BN.min(openBids.abs(), openAsks.abs())
|
|
350
|
+
);
|
|
351
|
+
const inventoryScale =
|
|
352
|
+
BN.min(
|
|
353
|
+
maxScale,
|
|
354
|
+
netBaseAssetAmount.mul(maxScale).div(minSideLiquidity).abs()
|
|
355
|
+
).toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
356
|
+
|
|
357
|
+
return inventoryScale;
|
|
358
|
+
}
|
|
359
|
+
|
|
308
360
|
export function calculateEffectiveLeverage(
|
|
309
361
|
baseSpread: number,
|
|
310
362
|
quoteAssetReserve: BN,
|
|
311
363
|
terminalQuoteAssetReserve: BN,
|
|
312
364
|
pegMultiplier: BN,
|
|
313
365
|
netBaseAssetAmount: BN,
|
|
314
|
-
|
|
366
|
+
reservePrice: BN,
|
|
315
367
|
totalFeeMinusDistributions: BN
|
|
316
368
|
): number {
|
|
317
369
|
// inventory skew
|
|
@@ -321,8 +373,8 @@ export function calculateEffectiveLeverage(
|
|
|
321
373
|
.div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
|
|
322
374
|
|
|
323
375
|
const localBaseAssetValue = netBaseAssetAmount
|
|
324
|
-
.mul(
|
|
325
|
-
.div(AMM_TO_QUOTE_PRECISION_RATIO.mul(
|
|
376
|
+
.mul(reservePrice)
|
|
377
|
+
.div(AMM_TO_QUOTE_PRECISION_RATIO.mul(PRICE_PRECISION));
|
|
326
378
|
|
|
327
379
|
const effectiveLeverage =
|
|
328
380
|
localBaseAssetValue.sub(netBaseAssetValue).toNumber() /
|
|
@@ -342,34 +394,59 @@ export function calculateMaxSpread(marginRatioInitial: number): number {
|
|
|
342
394
|
|
|
343
395
|
export function calculateSpreadBN(
|
|
344
396
|
baseSpread: number,
|
|
345
|
-
|
|
397
|
+
lastOracleReservePriceSpreadPct: BN,
|
|
346
398
|
lastOracleConfPct: BN,
|
|
347
399
|
maxSpread: number,
|
|
348
400
|
quoteAssetReserve: BN,
|
|
349
401
|
terminalQuoteAssetReserve: BN,
|
|
350
402
|
pegMultiplier: BN,
|
|
351
403
|
netBaseAssetAmount: BN,
|
|
352
|
-
|
|
353
|
-
totalFeeMinusDistributions: BN
|
|
404
|
+
reservePrice: BN,
|
|
405
|
+
totalFeeMinusDistributions: BN,
|
|
406
|
+
baseAssetReserve: BN,
|
|
407
|
+
minBaseAssetReserve: BN,
|
|
408
|
+
maxBaseAssetReserve: BN
|
|
354
409
|
): [number, number] {
|
|
355
410
|
let longSpread = baseSpread / 2;
|
|
356
411
|
let shortSpread = baseSpread / 2;
|
|
357
412
|
|
|
358
|
-
if (
|
|
413
|
+
if (lastOracleReservePriceSpreadPct.gt(ZERO)) {
|
|
359
414
|
shortSpread = Math.max(
|
|
360
415
|
shortSpread,
|
|
361
|
-
|
|
416
|
+
lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
417
|
+
lastOracleConfPct.toNumber()
|
|
362
418
|
);
|
|
363
|
-
} else if (
|
|
419
|
+
} else if (lastOracleReservePriceSpreadPct.lt(ZERO)) {
|
|
364
420
|
longSpread = Math.max(
|
|
365
421
|
longSpread,
|
|
366
|
-
|
|
422
|
+
lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
423
|
+
lastOracleConfPct.toNumber()
|
|
367
424
|
);
|
|
368
425
|
}
|
|
369
426
|
|
|
370
|
-
const maxTargetSpread: number =
|
|
427
|
+
const maxTargetSpread: number = Math.max(
|
|
428
|
+
maxSpread,
|
|
429
|
+
lastOracleReservePriceSpreadPct.abs().toNumber()
|
|
430
|
+
);
|
|
371
431
|
|
|
372
|
-
const
|
|
432
|
+
const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = 10;
|
|
433
|
+
|
|
434
|
+
const inventoryScale = calculateInventoryScale(
|
|
435
|
+
netBaseAssetAmount,
|
|
436
|
+
baseAssetReserve,
|
|
437
|
+
minBaseAssetReserve,
|
|
438
|
+
maxBaseAssetReserve
|
|
439
|
+
);
|
|
440
|
+
const inventorySpreadScale = Math.min(
|
|
441
|
+
MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
|
|
442
|
+
1 + inventoryScale
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
if (netBaseAssetAmount.gt(ZERO)) {
|
|
446
|
+
longSpread *= inventorySpreadScale;
|
|
447
|
+
} else if (netBaseAssetAmount.lt(ZERO)) {
|
|
448
|
+
shortSpread *= inventorySpreadScale;
|
|
449
|
+
}
|
|
373
450
|
|
|
374
451
|
const effectiveLeverage = calculateEffectiveLeverage(
|
|
375
452
|
baseSpread,
|
|
@@ -377,20 +454,23 @@ export function calculateSpreadBN(
|
|
|
377
454
|
terminalQuoteAssetReserve,
|
|
378
455
|
pegMultiplier,
|
|
379
456
|
netBaseAssetAmount,
|
|
380
|
-
|
|
457
|
+
reservePrice,
|
|
381
458
|
totalFeeMinusDistributions
|
|
382
459
|
);
|
|
383
460
|
|
|
384
461
|
if (totalFeeMinusDistributions.gt(ZERO)) {
|
|
385
|
-
const spreadScale = Math.min(
|
|
462
|
+
const spreadScale = Math.min(
|
|
463
|
+
MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
|
|
464
|
+
1 + effectiveLeverage
|
|
465
|
+
);
|
|
386
466
|
if (netBaseAssetAmount.gt(ZERO)) {
|
|
387
467
|
longSpread *= spreadScale;
|
|
388
468
|
} else {
|
|
389
469
|
shortSpread *= spreadScale;
|
|
390
470
|
}
|
|
391
471
|
} else {
|
|
392
|
-
longSpread *=
|
|
393
|
-
shortSpread *=
|
|
472
|
+
longSpread *= MAX_BID_ASK_INVENTORY_SKEW_FACTOR;
|
|
473
|
+
shortSpread *= MAX_BID_ASK_INVENTORY_SKEW_FACTOR;
|
|
394
474
|
}
|
|
395
475
|
|
|
396
476
|
const totalSpread = longSpread + shortSpread;
|
|
@@ -416,23 +496,23 @@ export function calculateSpread(
|
|
|
416
496
|
return amm.baseSpread / 2;
|
|
417
497
|
}
|
|
418
498
|
|
|
419
|
-
const
|
|
499
|
+
const reservePrice = calculatePrice(
|
|
420
500
|
amm.baseAssetReserve,
|
|
421
501
|
amm.quoteAssetReserve,
|
|
422
502
|
amm.pegMultiplier
|
|
423
503
|
);
|
|
424
504
|
|
|
425
|
-
const targetPrice = oraclePriceData?.price ||
|
|
505
|
+
const targetPrice = oraclePriceData?.price || reservePrice;
|
|
426
506
|
const confInterval = oraclePriceData.confidence || ZERO;
|
|
427
507
|
|
|
428
|
-
const targetMarkSpreadPct =
|
|
508
|
+
const targetMarkSpreadPct = reservePrice
|
|
429
509
|
.sub(targetPrice)
|
|
430
510
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
431
|
-
.div(
|
|
511
|
+
.div(reservePrice);
|
|
432
512
|
|
|
433
513
|
const confIntervalPct = confInterval
|
|
434
514
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
435
|
-
.div(
|
|
515
|
+
.div(reservePrice);
|
|
436
516
|
|
|
437
517
|
const [longSpread, shortSpread] = calculateSpreadBN(
|
|
438
518
|
amm.baseSpread,
|
|
@@ -442,9 +522,12 @@ export function calculateSpread(
|
|
|
442
522
|
amm.quoteAssetReserve,
|
|
443
523
|
amm.terminalQuoteAssetReserve,
|
|
444
524
|
amm.pegMultiplier,
|
|
445
|
-
amm.
|
|
446
|
-
|
|
447
|
-
amm.totalFeeMinusDistributions
|
|
525
|
+
amm.baseAssetAmountWithAmm,
|
|
526
|
+
reservePrice,
|
|
527
|
+
amm.totalFeeMinusDistributions,
|
|
528
|
+
amm.baseAssetReserve,
|
|
529
|
+
amm.minBaseAssetReserve,
|
|
530
|
+
amm.maxBaseAssetReserve
|
|
448
531
|
);
|
|
449
532
|
|
|
450
533
|
let spread: number;
|
|
@@ -543,10 +626,10 @@ export function getSwapDirection(
|
|
|
543
626
|
* Helper function calculating terminal price of amm
|
|
544
627
|
*
|
|
545
628
|
* @param market
|
|
546
|
-
* @returns cost : Precision
|
|
629
|
+
* @returns cost : Precision PRICE_PRECISION
|
|
547
630
|
*/
|
|
548
|
-
export function calculateTerminalPrice(market:
|
|
549
|
-
const directionToClose = market.amm.
|
|
631
|
+
export function calculateTerminalPrice(market: PerpMarketAccount) {
|
|
632
|
+
const directionToClose = market.amm.baseAssetAmountWithAmm.gt(ZERO)
|
|
550
633
|
? PositionDirection.SHORT
|
|
551
634
|
: PositionDirection.LONG;
|
|
552
635
|
|
|
@@ -554,12 +637,12 @@ export function calculateTerminalPrice(market: MarketAccount) {
|
|
|
554
637
|
calculateAmmReservesAfterSwap(
|
|
555
638
|
market.amm,
|
|
556
639
|
'base',
|
|
557
|
-
market.amm.
|
|
640
|
+
market.amm.baseAssetAmountWithAmm.abs(),
|
|
558
641
|
getSwapDirection('base', directionToClose)
|
|
559
642
|
);
|
|
560
643
|
|
|
561
644
|
const terminalPrice = newQuoteAssetReserve
|
|
562
|
-
.mul(
|
|
645
|
+
.mul(PRICE_PRECISION)
|
|
563
646
|
.mul(market.amm.pegMultiplier)
|
|
564
647
|
.div(PEG_PRECISION)
|
|
565
648
|
.div(newBaseAssetReserve);
|
|
@@ -576,7 +659,7 @@ export function calculateMaxBaseAssetAmountToTrade(
|
|
|
576
659
|
const invariant = amm.sqrtK.mul(amm.sqrtK);
|
|
577
660
|
|
|
578
661
|
const newBaseAssetReserveSquared = invariant
|
|
579
|
-
.mul(
|
|
662
|
+
.mul(PRICE_PRECISION)
|
|
580
663
|
.mul(amm.pegMultiplier)
|
|
581
664
|
.div(limit_price)
|
|
582
665
|
.div(PEG_PRECISION);
|
|
@@ -610,6 +693,10 @@ export function calculateQuoteAssetAmountSwapped(
|
|
|
610
693
|
pegMultiplier: BN,
|
|
611
694
|
swapDirection: SwapDirection
|
|
612
695
|
): BN {
|
|
696
|
+
if (isVariant(swapDirection, 'remove')) {
|
|
697
|
+
quoteAssetReserves = quoteAssetReserves.add(ONE);
|
|
698
|
+
}
|
|
699
|
+
|
|
613
700
|
let quoteAssetAmount = quoteAssetReserves
|
|
614
701
|
.mul(pegMultiplier)
|
|
615
702
|
.div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
|
|
@@ -620,3 +707,29 @@ export function calculateQuoteAssetAmountSwapped(
|
|
|
620
707
|
|
|
621
708
|
return quoteAssetAmount;
|
|
622
709
|
}
|
|
710
|
+
|
|
711
|
+
export function calculateMaxBaseAssetAmountFillable(
|
|
712
|
+
amm: AMM,
|
|
713
|
+
orderDirection: PositionDirection
|
|
714
|
+
): BN {
|
|
715
|
+
const maxFillSize = amm.baseAssetReserve.div(
|
|
716
|
+
new BN(amm.maxFillReserveFraction)
|
|
717
|
+
);
|
|
718
|
+
let maxBaseAssetAmountOnSide: BN;
|
|
719
|
+
if (isVariant(orderDirection, 'long')) {
|
|
720
|
+
maxBaseAssetAmountOnSide = BN.max(
|
|
721
|
+
ZERO,
|
|
722
|
+
amm.baseAssetReserve.sub(amm.minBaseAssetReserve)
|
|
723
|
+
);
|
|
724
|
+
} else {
|
|
725
|
+
maxBaseAssetAmountOnSide = BN.max(
|
|
726
|
+
ZERO,
|
|
727
|
+
amm.maxBaseAssetReserve.sub(amm.baseAssetReserve)
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return standardizeBaseAssetAmount(
|
|
732
|
+
BN.min(maxFillSize, maxBaseAssetAmountOnSide),
|
|
733
|
+
amm.orderStepSize
|
|
734
|
+
);
|
|
735
|
+
}
|
package/src/math/conversion.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BN } from '../';
|
|
2
|
-
import {
|
|
2
|
+
import { PRICE_PRECISION } from '../constants/numericConstants';
|
|
3
3
|
|
|
4
4
|
export const convertToNumber = (
|
|
5
5
|
bigNumber: BN,
|
|
6
|
-
precision: BN =
|
|
6
|
+
precision: BN = PRICE_PRECISION
|
|
7
7
|
) => {
|
|
8
8
|
if (!bigNumber) return 0;
|
|
9
9
|
return (
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isOneOfVariant,
|
|
3
|
+
isVariant,
|
|
4
|
+
PerpMarketAccount,
|
|
5
|
+
SpotMarketAccount,
|
|
6
|
+
StateAccount,
|
|
7
|
+
} from '../types';
|
|
8
|
+
|
|
9
|
+
export function exchangePaused(state: StateAccount): boolean {
|
|
10
|
+
return isVariant(state.exchangeStatus, 'paused');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function fillPaused(
|
|
14
|
+
state: StateAccount,
|
|
15
|
+
market: PerpMarketAccount | SpotMarketAccount
|
|
16
|
+
): boolean {
|
|
17
|
+
return (
|
|
18
|
+
isOneOfVariant(state.exchangeStatus, ['paused', 'fillPaused']) ||
|
|
19
|
+
isOneOfVariant(market.status, ['paused', 'fillPaused'])
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function ammPaused(
|
|
24
|
+
state: StateAccount,
|
|
25
|
+
market: PerpMarketAccount | SpotMarketAccount
|
|
26
|
+
): boolean {
|
|
27
|
+
return (
|
|
28
|
+
isOneOfVariant(state.exchangeStatus, ['paused', 'ammPaused']) ||
|
|
29
|
+
isOneOfVariant(market.status, ['paused', 'ammPaused'])
|
|
30
|
+
);
|
|
31
|
+
}
|