Ccxt: fetchOrder (bitstamp) not working anymore -> "invalid nonce"

Created on 14 Feb 2018  路  52Comments  路  Source: ccxt/ccxt

  • OS: Win 7 64
  • Programming Language version: python 3.6
  • CCXT version: 10.1104
  • Exchange: bitstamp
  • Method: fetchOrder

Method used to work, now it does not.
"raise ExchangeError(self.id + ' ' + self.json(response))
ccxt.base.errors.ExchangeError: bitstamp {"status":"error","reason":"Invalid nonce","code":"API0004"}"

I issued a brand new API key from bitstamp -> same problem. All other private API Key methods still work (I can place order, etc. but to fetch the order based on orderID creates the error.
Any idea why?
Thanks
Urs

All 52 comments

Traceback (most recent call last):
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\ccxt\base\exchange.py", line 364, in fetch
    response.raise_for_status()
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\requests\models.py", line 935, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Authentication Failed for url: https://www.bitstamp.net/api/v2/open_orders/all/

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "\\DARTH3-V03\RedirectedFolders\us\My Documents\python\crypto\bitstamp-004.py", line 712, in <module>
    observeOpeningOrderPlaced(exchange,symbol)
  File "\\DARTH3-V03\RedirectedFolders\us\My Documents\python\crypto\bitstamp-004.py", line 528, in observeOpeningOrderPlaced
    orderInfo=exchange.fetchOrder(exchange.openOpeningOrders[symbol][side][0]) #fetchorder by orderid
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\ccxt\bitstamp.py", line 425, in fetch_order
    orders = self.privatePostOpenOrdersAll()
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\ccxt\base\exchange.py", line 311, in request
    return self.fetch2(path, api, method, params, headers, body)
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\ccxt\base\exchange.py", line 308, in fetch2
    return self.fetch(request['url'], request['method'], request['headers'], request['body'])
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\ccxt\base\exchange.py", line 376, in fetch
    self.handle_errors(response.status_code, response.reason, url, method, self.last_response_headers, self.last_http_response)
  File "C:\Users\us\.thonny\BundledPython36\lib\site-packages\ccxt\bitstamp.py", line 504, in handle_errors
    raise ExchangeError(self.id + ' ' + self.json(response))
ccxt.base.errors.ExchangeError: bitstamp {"status":"error","reason":"Invalid nonce","code":"API0004"}
>>> 

@stabilus can you please show a minimal (shortest possible, but complete) snippet of your code to reproduce the problem?

This will create the error:

def instantiateExchanges():
    # instantiate exchange(s)

    #***bitstamp****
    global bitstamp_ccxt
    global g_minProfit
    bitstamp_ccxt = ccxt.bitstamp()
    bitstamp_ccxt.uid='xxxx'
    bitstamp_ccxt.password='xxxx'
    bitstamp_ccxt.apiKey='xxxx'
    bitstamp_ccxt.secret='xxxx'
    bitstamp_ccxt.loadMarkets()

instantiateExchanges()
exchange=bitstamp_ccxt
symbol='BTC/USD'

time.sleep(1)
result=exchange.createLimitBuyOrder(symbol,0.01,4100)
orderID=result['id']
print (orderID)

time.sleep(1)
print(exchange.fetchOrder(orderID))

Ok, can you please add verbose mode and paste the full verbose output (without the key) ?

# ...
exchange.verbose = True # add this before the last line
print(exchange.fetchOrder(orderID))

Standing by for more info from you. Thx!

This is the verbose output, followed by the error messages:

960016270
POST https://www.bitstamp.net/api/v2/order_status/
Request: {'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'python-requests/2.18.4', 'Accept-Encoding': 'gzip, deflate'}
key=XXXX&signature=XXXXXX&nonce=1518644639&id=960016270
POST https://www.bitstamp.net/api/v2/order_status/ 200
Response: {'Access-Control-Allow-Headers': 'x-requested-with, Content-Type, origin, accept, cache-control', 'Access-Control-Allow-Methods': 'POST, GET', 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'max-age=0', 'Content-Language': 'en', 'Content-Type': 'application/json', 'Date': 'Wed, 14 Feb 2018 21:44:00 GMT', 'Expires': 'Wed, 14 Feb 2018 21:44:00 GMT', 'Last-Modified': 'Wed, 14 Feb 2018 21:44:00 GMT', 'Server': 'Apache', 'Strict-Transport-Security': 'max-age=63072000; includeSubDomains', 'Vary': 'Accept-Language', 'X-Frame-Options': 'SAMEORIGIN', 'transfer-encoding': 'chunked', 'Connection': 'keep-alive', 'Set-Cookie': 'visid_incap_99025=50kIyr46SE+B0q2suQdttp+thFoAAAAAQUIPAAAAAACzzKMVkcPqA//eGaSaraPq; expires=Thu, 14 Feb 2019 15:06:31 GMT; path=/; Domain=.bitstamp.net, incap_ses_189_99025=iEgUBEAfJAXWb/IC3XifAp+thFoAAAAAnMkVCwDaOsce3XpHpKGNjQ==; path=/; Domain=.bitstamp.net', 'X-Iinfo': '10-2657481-2657575 SNNN RT(1518644636526 2533) q(0 0 0 -1) r(0 0) U6', 'X-CDN': 'Incapsula', 'Content-Encoding': 'gzip'}
{"status": "Open", "id": 960016270, "transactions": []}
POST https://www.bitstamp.net/api/v2/open_orders/all/
Request: {'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'python-requests/2.18.4', 'Accept-Encoding': 'gzip, deflate'}
key=6pgmGQJtGcUaZulmSYmVxP9HT9b7XYLD&signature=XXXX&nonce=1518644639
Traceback (most recent call last):
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbaseexchange.py", line 364, in fetch
response.raise_for_status()
File "C:Usersus.thonnyBundledPython36libsite-packagesrequestsmodels.py", line 935, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Authentication Failed for url: https://www.bitstamp.net/api/v2/open_orders/all/

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "\DARTH3-V03RedirectedFoldersusMy Documentspythoncryptobitstamp-004-t1.py", line 35, in
print(exchange.fetchOrder(orderID))
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbitstamp.py", line 425, in fetch_order
orders = self.privatePostOpenOrdersAll()
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbaseexchange.py", line 311, in request
return self.fetch2(path, api, method, params, headers, body)
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbaseexchange.py", line 308, in fetch2
return self.fetch(request['url'], request['method'], request['headers'], request['body'])
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbaseexchange.py", line 376, in fetch
self.handle_errors(response.status_code, response.reason, url, method, self.last_response_headers, self.last_http_response)
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbitstamp.py", line 504, in handle_errors
raise ExchangeError(self.id + ' ' + self.json(response))
ccxt.base.errors.ExchangeError: bitstamp {"status":"error","reason":"Invalid nonce","code":"API0004"}

Hi Kroitor
Do you have all the info you need from me to look into this? What I find strange is that the method used to work just fine and then suddenly started to throw this nonce error.

Was about to post the same.

Two problems:

fetchOrder on bitstamp is 2 calls: first we check the orderstatus (open or closed), and then poll the according endpoint, depending on order status. (or so it should be, right now we check status and then we poll the "open" endpoint anyway, but that's an easy fix)

Anyhow, first call goes perfectly, second call gives a nonce error.
When calling them seperately, all goes well.

In the verbose output, we can see the nonce is reused, I believe this causes the problem.

fetch:
 bitstamp POST https://www.bitstamp.net/api/v2/order_status/
Request:
 { 'Content-Type': 'application/x-www-form-urlencoded' }
 key=xxx&signature=xxx&nonce=1518686391&id=962455719

handleRestResponse:
 bitstamp POST https://www.bitstamp.net/api/v2/order_status/ 200 OK
Response:
 { 'access-control-allow-headers': 'x-requested-with, Content-Type, origin, accept, cache-control',
  'access-control-allow-methods': 'POST, GET',
  'access-control-allow-origin': '*',
  'cache-control': 'max-age=0',
  'content-language': 'en',
  'content-type': 'application/json',
  date: 'Thu, 15 Feb 2018 09:19:51 GMT',
  expires: 'Thu, 15 Feb 2018 09:19:51 GMT',
  'last-modified': 'Thu, 15 Feb 2018 09:19:51 GMT',
  server: 'Apache',
  'strict-transport-security': 'max-age=63072000; includeSubDomains',
  vary: 'Accept-Language',
  'x-frame-options': 'SAMEORIGIN',
  'transfer-encoding': 'chunked',
  connection: 'Close',
  'set-cookie': 'incap_ses_767_99025=0QFGaFjglGiWXciQp+6kCrZQhVoAAAAAxIrWQDe9KAbjpI79FozUVQ==; path=
/; Domain=.bitstamp.net',
  'x-iinfo': '12-9910999-9911000 NNNN CT(17 17 0) RT(1518686390399 13) q(0 0 0 -1) r(0 0) U6',
  'x-cdn': 'Incapsula',
  'content-encoding': 'gzip' }
 {"status": "Finished", "id": 962455719, "transactions": [{"fee": "1.18", "price": "187.99000000", "
datetime": "2018-02-15 06:50:34.427688", "ltc": "2.50625000", "tid": 54805885, "type": 2, "eur": "47
1.1499375000000000"}]}

fetch:
 bitstamp POST https://www.bitstamp.net/api/v2/open_orders/all/
Request:
 { 'Content-Type': 'application/x-www-form-urlencoded' }
 key=xxx&signature=xxx&nonce=1518686391

handleRestResponse:
 bitstamp POST https://www.bitstamp.net/api/v2/open_orders/all/ 403 Authentication Failed
Response:
 { 'access-control-allow-headers': 'x-requested-with, Content-Type, origin, accept, cache-control',
  'access-control-allow-methods': 'POST, GET',
  'access-control-allow-origin': '*',
  'content-language': 'en',
  'content-type': 'application/json',
  date: 'Thu, 15 Feb 2018 09:19:51 GMT',
  server: 'Apache',
  'strict-transport-security': 'max-age=63072000; includeSubDomains',
  vary: 'Accept-Language',
  'x-frame-options': 'SAMEORIGIN',
  'transfer-encoding': 'chunked',
  connection: 'Close',
  'set-cookie': 'incap_ses_767_99025=PfW/HILr8gmiXciQp+6kCrZQhVoAAAAAFEUdOmQ1qzRZv0WUKrhIBg==; path=
/; Domain=.bitstamp.net',
  'x-iinfo': '14-16391799-16391801 NNNN CT(17 18 0) RT(1518686390551 19) q(0 0 1 -1) r(1 1) U6',
  'x-cdn': 'Incapsula',
  'content-encoding': 'gzip' }
 {"status": "error", "reason": "Invalid nonce", "code": "API0004"}

(node:6004) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error:
bitstamp {"status":"error","reason":"Invalid nonce","code":"API0004"}
(node:6004) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future
, promise rejections that are not handled will terminate the Node.js process with a non-zero exit co
de.

fetchOrder on bitstamp is 2 calls

This is the main problem, we should remove the second call from there.

However, in your particular case, a workaround would be to enable rate limiting (set 'enableRateLimit': true on your instance).

We're going to remove the fetchOrder for bitstamp to the emulated part, as current implementation is flawed (requires two calls instead of just one). Bitstamp doesn't have a method for fetching a single order at all, they only have open orders. And we're going to emulate other methods with cache.

nevermind this:

first we check the orderstatus (open or closed), and then poll the according endpoint, depending on order status. (or so it should be, right now we check status and then we poll the "open" endpoint anyway, but that's an easy fix)

All the info for closed orders is in the "status" call.
I will make a temp order that will remain open, and see if I can get that in the status call too.
That should make it possible to go to one call.

There's no fetchOrder for bitstamp anymore, there's just the fetchOpenOrders now. We will add emulation for the fetchOrder shortly.

I will make a temp order that will remain open, and see if I can get that in the status call too.
That should make it possible to go to one call.

so far, fetching openorders and closed orders had not been implemented in ccxt. If you implement that, that would already be a great feature to work with
sorry, I can't be of active help for coding on your end - I'm a python newbie...

According to bitstamps API openorders are cached for 10 seconds while there is no cache for order status. In order to react quickly to order status changes, it might be advisable to continue using order status calls when implementing the fetchOrder method on the ccxt side.

@stabilus

it might be advisable to continue using order status calls when implementing the fetchOrder method on the ccxt side

If it requires two calls, this is highly discouraged. Instead we propose to use the existing fetchOrderStatus with fetchOpenOrders and leave the order of sequential calls up to the user (won't decide on the order of sequential calls in the library).

orderStatus output for a closed order:

{ status: 'Finished',
  id: 962455719,
  transactions:
   [ { fee: '1.18',
       price: '187.99000000',
       datetime: '2018-02-15 06:50:34.427688',
       ltc: '2.50625000',
       tid: 54805885,
       type: 2,
       eur: '471.1499375000000000' } ] }

for Open order:
{ status: 'Open', id: 963180354, transactions: [] }

Could we make fetchOrder just this single call?
It's not perfect as it does not return a lot of info for open orders, but it's better than deleting fetchOrder altogether.

Perfect! This is all we need! I would like to contribute to your great effort. Please state your BTC address for a small donation!

Not affiliated, just a happy "customer" like you.
For donations to CCXT team: https://opencollective.com/ccxt/donate

404... ?

Could we make fetchOrder just this single call?

Yes, we will reimplement it using the orders cache. For now we offer fetchOrderStatus + fetchOpenOrders (which is effectively the same as the former fetchOrder implementation).

It's not perfect as it does not return a lot of info for open orders, but it's better than deleting fetchOrder altogether.

We are not deleting it forever, just making calls explicit, and we're going to add a caching layer to bitstamp shortly as well.

https://opencollective.com/ccxt )

Please state your BTC address for a small donation!

https://github.com/ccxt/ccxt#crypto ) Thx )

ETH 0xa7c2b18b7c8b86984560cad3b1bc3224b388ded0
BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva
BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze
LTC LbT8mkAqQBphc4yxLXEDgYDfEax74et3bP

sent it to the opencollective by BTC. Thank YOU!

For now we offer fetchOrderStatus + fetchOpenOrders (which is effectively the same as the former fetchOrder implementation).

Respectfully, it is not.
fetchOrderStatus only returns "closed" or "open".
fetchOpenOrders only returns open orders.
with the newest update, we have NO way to check closed orders.

I need details on closed orders, and the info is readily available in the same call used for fetchOrderStatus. Raw output:

{ status: 'Finished',
  id: 962455719,
  transactions:
   [ { fee: '1.18',
       price: '187.99000000',
       datetime: '2018-02-15 06:50:34.427688',
       ltc: '2.50625000',
       tid: 54805885,
       type: 2,
       eur: '471.1499375000000000' } ] }

I can make a fetchOrder() out of this call (single call now, not double as it was), the only drawback being that the info for open orders will be very limited.

When we have a caching implementation, we can ofcourse use that instead.

@stabilus

sent it to the opencollective by BTC. Thank YOU!

Thx! However, opencollective does not accept BTC %)) They accept fiat payments... if you want to contribute with crypto, your contributions are welcome, and the crypto addresses are:

ETH 0xa7c2b18b7c8b86984560cad3b1bc3224b388ded0
BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva
BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze
LTC LbT8mkAqQBphc4yxLXEDgYDfEax74et3bP

Thx again)

@wannesdemaeght I've restored the following for fetchOrder (for now):

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let market = undefined;
        if (typeof symbol !== 'undefined')
            market = this.market (symbol);
        let response = await this.privatePostOrderStatus ({ 'id': id });
        return this.parseOrder (response, market);
    }

This looks like what you need, right? )

sorry, I'm limping behind...
.orderStatus now only returns "open" or "closed" but none of the trade information. How would you now get the remaining or the filled portion respectively - without having to call openOrders (which caches for 10 seconds)?
.parseOrder(orderid) returns an error:

Traceback (most recent call last):
File "", line 1, in
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbitstamp.py", line 378, in parse_order
datetimeString = self.safe_string(order, 'datetime')
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbaseexchange.py", line 447, in safe_string
return str(dictionary[key]) if key is not None and (key in dictionary) and dictionary[key] is not None else default_value
TypeError: argument of type 'int' is not iterable

@stabilus

  1. update ccxt to version 1.10.1113
  2. call fetchOrder (id) like you did before (use your initial code without changing anything in it)

I'm on 1.10.1113.
exchange.fetchOrder(963439610)
Traceback (most recent call last):
File "", line 1, in
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbitstamp.py", line 362, in fetch_order
return self.parse_order(response, market)
File "C:Usersus.thonnyBundledPython36libsite-packagesccxtbitstamp.py", line 402, in parse_order
filled += trade['amount']
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

@stabilus ok, one more fix is coming up... hold on

@wannesdemaeght I've restored the following for fetchOrder (for now):

async fetchOrder (id, symbol = undefined, params = {}) {
    await this.loadMarkets ();
    let market = undefined;
    if (typeof symbol !== 'undefined')
        market = this.market (symbol);
    let response = await this.privatePostOrderStatus ({ 'id': id });
    return this.parseOrder (response, market);
}

This looks like what you need, right? )

Great, thanks.
Now there are some underlying problems with parsing that response:

RangeError: Invalid time value

This has to do with encoding and decoding the datetimestring.
When I comment these out for testing, I get this as response:

{ id: '962455719',
  timestamp: undefined,
  status: 'closed',
  symbol: undefined,
  type: undefined,
  side: undefined,
  price: undefined,
  cost: undefined,
  amount: undefined,
  filled: NaN,
  remaining: undefined,
  trades:
   [ { id: '54805885',
       info: [Object],
       timestamp: 1518677434427,
       datetime: '2018-02-15T06:50:34.427Z',
       symbol: undefined,
       order: undefined,
       type: undefined,
       side: 'sell',
       price: 187.99,
       amount: undefined,
       fee: [Object] } ],
  fee: undefined,
  info: { status: 'Finished', id: 962455719, transactions: [ [Object] ] } }

any idea regarding the timestamp? raw format is this:

datetime: '2018-02-15 06:50:34.427688'

regarding parsing, I don't know what would be better:
Changing parseOrder to work with fetchOrder,
or make a new, temporary, parseFetchOrder (seeing as how the whole implementation of fetchOrder is temporary too).

Please let me know how you want me to proceed.

any idea regarding the timestamp?

It's easily explained: no timestamp data for the order... The timestamp you're posting is the trade timestamp (gets parsed correctly). Nothing to do with encoding at all. The data is just missing for the order in this particular reply. Will fix for that as well.

Please let me know how you want me to proceed.

No worries for now, I'm fixing the parser to output the trade amounts + order filled value at the very least.

It's easily explained: no timestamp data for the order... The timestamp you're posting is the trade timestamp.

Right, makes sense. Anyhow, timestamp is not important for me :-)

No worries for now, I'm fixing the parser to output the trade amounts + order filled value at the very least.

Ideally we should return the following info:
price,
cost,
amount,
fee

that info should be readily available from orderstatus.

that info should be readily available from orderstatus.

Not really, it isn't... the total amount of the order is still unknown.

Ideally we should return the following info:
price,
cost,
amount,
fee

We can only return that info for trades inside the order, but not for the order itself. For the order itself we can only return the filled amount, but not the total amount.

{ status: 'Finished',
  id: 962455719,
  transactions:
   [ { fee: '1.18',
       price: '187.99000000',
       datetime: '2018-02-15 06:50:34.427688',
       ltc: '2.50625000',
       tid: 54805885,
       type: 2,
       eur: '471.1499375000000000' } ] }

if result == finished, amount total = amount filled ( in this case 2.50625000 LTC)

@wannesdemaeght right, but only if it is finished (it can be partially filled)

correct, but when it's not finished, it will come back as this:

{ status: 'Open', id: 963180354, transactions: [] }

so doesn't really matter

if status == finished --> amount = sum of all transaction.amount (sucks that they put the amount as ltc / eth / ...)

correct, but when it's not finished, it will come back as this:
{ status: 'Open', id: 963180354, transactions: [] }

This isn't always the case. In case of a partial fill it will return status = 'Open' with non-empty transactions (not sure about that, tho, they may be caching it up to the moment when the order gets 'Finished'). And in that situation we can't deduce the total amount of the order from trades, unfortunately. Their API is too poor.

the sum of amounts in transactions corresponds to the filled portion of an open partially executed order. so, perhaps the attribute "filled" could be provided in case of a partially filled order

@stabilus yep, this is what I'm saying, we can provide filled, but not total amount for the order in that case.

Will upload a fix and will update you here soon.

makes sense.
Open --> filled = sum of amounts, total amount = undefined
Closed --> filled = total amount = sum of amounts

@kroitor
"Thx! However, opencollective does not accept BTC %)) They accept fiat payments... if you want to contribute with crypto, your contributions are welcome, and the crypto addresses are:"

opencollective seems to have accepted BTC (they have a payment gateway for BTC). Next time I'll know your direct BTC address. :)

@stabilus wow, didn't know that %) Thx again )

https://twitter.com/opencollect/status/943620141430067208

Ok, this is fixed in ccxt 1.10.1114 and fetchOrder should work normally now, as described above with respect to amount and filled.

@stabilus , @wannesdemaeght let me know if you still have any difficulties with it. Thank you for your involvement!

{ id: '962455719',
  datetime: undefined,
  timestamp: undefined,
  status: 'closed',
  symbol: 'LTC/EUR',
  type: undefined,
  side: undefined,
  price: undefined,
  cost: undefined,
  amount: 2.50625,
  filled: 2.50625,
  remaining: 0,
  trades:
   [ { id: '54805885',
       info: [Object],
       timestamp: 1518677434427,
       datetime: '2018-02-15T06:50:34.427Z',
       symbol: 'LTC/EUR',
       order: '962455719',
       type: undefined,
       side: undefined,
       price: 187.99,
       amount: 2.50625,
       fee: [Object] } ],
  fee: undefined,
  info: { status: 'Finished', id: 962455719, transactions: [ [Object] ] } }

price: if status == closed --> (price per trade * amount per trade) / total amount
cost: if status == closed --> price * amount
fee: if status == closed --> sum of fees per trade

@wannesdemaeght yep, you're right, will add that as well.

I really love telling people what to do :rofl:

works like a charm mate! For my understanding: fetchOrder makes 1 call to the exchange - not 2, correct? And it calls the bitstamp orderStatus method (which does not cache for 10 seconds). Correct?

@stabilus

fetchOrder makes 1 call to the exchange - not 2, correct?

Exactly.

great, and what about " it calls the bitstamp orderStatus method (which does not cache for 10 seconds). Correct?"

it calls the bitstamp orderStatus method (which does not cache for 10 seconds). Correct?"

Yes.

Added price, cost and fee to fetchOrder return in ccxt 1.10.1115

Thanks man! Works perfectly.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marinsokol5 picture marinsokol5  路  60Comments

cklester picture cklester  路  346Comments

JovianMoon picture JovianMoon  路  44Comments

Fcl69 picture Fcl69  路  45Comments

npomfret picture npomfret  路  60Comments