Fix styling
This commit is contained in:
parent
0996c5fc27
commit
49c1633f52
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example usage of Change Leverage functionality
|
* Example usage of Change Leverage functionality
|
||||||
*
|
*
|
||||||
* This example demonstrates how to use the LaravelBitunixApi package
|
* This example demonstrates how to use the LaravelBitunixApi package
|
||||||
* to change leverage for a trading pair on Bitunix exchange.
|
* to change leverage for a trading pair on Bitunix exchange.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
require_once __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
|
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
|
||||||
|
|
||||||
|
|
@ -22,39 +22,39 @@ config([
|
||||||
try {
|
try {
|
||||||
// Get the API instance
|
// Get the API instance
|
||||||
$api = app(ChangeLeverageRequestContract::class);
|
$api = app(ChangeLeverageRequestContract::class);
|
||||||
|
|
||||||
// Change leverage for BTCUSDT pair
|
// Change leverage for BTCUSDT pair
|
||||||
$symbol = 'BTCUSDT';
|
$symbol = 'BTCUSDT';
|
||||||
$marginCoin = 'USDT';
|
$marginCoin = 'USDT';
|
||||||
$leverage = 12;
|
$leverage = 12;
|
||||||
|
|
||||||
echo "Changing leverage for {$symbol} to {$leverage}x...\n";
|
echo "Changing leverage for {$symbol} to {$leverage}x...\n";
|
||||||
|
|
||||||
$response = $api->changeLeverage($symbol, $marginCoin, $leverage);
|
$response = $api->changeLeverage($symbol, $marginCoin, $leverage);
|
||||||
|
|
||||||
// Check response status
|
// Check response status
|
||||||
if ($response->getStatusCode() === 200) {
|
if ($response->getStatusCode() === 200) {
|
||||||
$data = json_decode($response->getBody()->getContents(), true);
|
$data = json_decode($response->getBody()->getContents(), true);
|
||||||
|
|
||||||
if ($data['code'] === 0) {
|
if ($data['code'] === 0) {
|
||||||
echo "✅ Leverage changed successfully!\n";
|
echo "✅ Leverage changed successfully!\n";
|
||||||
echo "Symbol: " . $data['data'][0]['symbol'] . "\n";
|
echo 'Symbol: '.$data['data'][0]['symbol']."\n";
|
||||||
echo "Margin Coin: " . $data['data'][0]['marginCoin'] . "\n";
|
echo 'Margin Coin: '.$data['data'][0]['marginCoin']."\n";
|
||||||
echo "New Leverage: " . $data['data'][0]['leverage'] . "\n";
|
echo 'New Leverage: '.$data['data'][0]['leverage']."\n";
|
||||||
} else {
|
} else {
|
||||||
echo "❌ API Error: " . $data['msg'] . "\n";
|
echo '❌ API Error: '.$data['msg']."\n";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
echo "❌ HTTP Error: " . $response->getStatusCode() . "\n";
|
echo '❌ HTTP Error: '.$response->getStatusCode()."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo "❌ Exception: " . $e->getMessage() . "\n";
|
echo '❌ Exception: '.$e->getMessage()."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Environment Variables Required:
|
* Environment Variables Required:
|
||||||
*
|
*
|
||||||
* BITUNIX_API_KEY=your-api-key
|
* BITUNIX_API_KEY=your-api-key
|
||||||
* BITUNIX_API_SECRET=your-api-secret
|
* BITUNIX_API_SECRET=your-api-secret
|
||||||
* BITUNIX_LANGUAGE=en-US
|
* BITUNIX_LANGUAGE=en-US
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
namespace Msr\LaravelBitunixApi;
|
namespace Msr\LaravelBitunixApi;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
|
|
||||||
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
|
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
|
||||||
|
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
|
||||||
use Msr\LaravelBitunixApi\Requests\Header;
|
use Msr\LaravelBitunixApi\Requests\Header;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
class LaravelBitunixApi implements FutureKLineRequestContract, ChangeLeverageRequestContract
|
class LaravelBitunixApi implements ChangeLeverageRequestContract, FutureKLineRequestContract
|
||||||
{
|
{
|
||||||
private Client $publicFutureClient;
|
private Client $publicFutureClient;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,9 @@ interface ChangeLeverageRequestContract
|
||||||
/**
|
/**
|
||||||
* Change leverage for a trading pair
|
* Change leverage for a trading pair
|
||||||
*
|
*
|
||||||
* @param string $symbol Trading pair (e.g., 'BTCUSDT')
|
* @param string $symbol Trading pair (e.g., 'BTCUSDT')
|
||||||
* @param string $marginCoin Margin coin (e.g., 'USDT')
|
* @param string $marginCoin Margin coin (e.g., 'USDT')
|
||||||
* @param int $leverage Leverage value
|
* @param int $leverage Leverage value
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
*/
|
||||||
public function changeLeverage(string $symbol, string $marginCoin, int $leverage): ResponseInterface;
|
public function changeLeverage(string $symbol, string $marginCoin, int $leverage): ResponseInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,6 @@ class Header
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Sort query parameters in ascending ASCII order by Key
|
* Sort query parameters in ascending ASCII order by Key
|
||||||
*
|
|
||||||
* @param array $parameters
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function sortQueryParameters(array $parameters): array
|
public static function sortQueryParameters(array $parameters): array
|
||||||
{
|
{
|
||||||
|
|
@ -17,15 +14,13 @@ class Header
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($parameters, SORT_STRING);
|
ksort($parameters, SORT_STRING);
|
||||||
|
|
||||||
return $parameters;
|
return $parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert sorted parameters to string format
|
* Convert sorted parameters to string format
|
||||||
* Example: ["id" => "1", "uid" => "200"] becomes "id1uid200"
|
* Example: ["id" => "1", "uid" => "200"] becomes "id1uid200"
|
||||||
*
|
|
||||||
* @param array $parameters
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public static function digestQueryParameters(array $parameters): string
|
public static function digestQueryParameters(array $parameters): string
|
||||||
{
|
{
|
||||||
|
|
@ -35,18 +30,16 @@ class Header
|
||||||
|
|
||||||
$sortedParameters = self::sortQueryParameters($parameters);
|
$sortedParameters = self::sortQueryParameters($parameters);
|
||||||
$result = '';
|
$result = '';
|
||||||
|
|
||||||
foreach ($sortedParameters as $key => $value) {
|
foreach ($sortedParameters as $key => $value) {
|
||||||
$result .= $key . $value;
|
$result .= $key.$value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random 32-bit nonce string
|
* Generate a random 32-bit nonce string
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public static function generateNonce(): string
|
public static function generateNonce(): string
|
||||||
{
|
{
|
||||||
|
|
@ -55,8 +48,6 @@ class Header
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate current timestamp in milliseconds
|
* Generate current timestamp in milliseconds
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public static function generateTimestamp(): string
|
public static function generateTimestamp(): string
|
||||||
{
|
{
|
||||||
|
|
@ -65,58 +56,48 @@ class Header
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate signature according to Bitunix API documentation
|
* Generate signature according to Bitunix API documentation
|
||||||
*
|
*
|
||||||
* Steps:
|
* Steps:
|
||||||
* 1. Sort all queryParams in ascending ASCII order by Key
|
* 1. Sort all queryParams in ascending ASCII order by Key
|
||||||
* 2. Remove all spaces from body string
|
* 2. Remove all spaces from body string
|
||||||
* 3. Create digest: SHA256(nonce + timestamp + api-key + queryParams + body)
|
* 3. Create digest: SHA256(nonce + timestamp + api-key + queryParams + body)
|
||||||
* 4. Create sign: SHA256(digest + secretKey)
|
* 4. Create sign: SHA256(digest + secretKey)
|
||||||
*
|
|
||||||
* @param array $queryParams
|
|
||||||
* @param string $body
|
|
||||||
* @param string $nonce
|
|
||||||
* @param string $timestamp
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public static function generateSignValue(array $queryParams = [], string $body = '', string $nonce = '', string $timestamp = ''): string
|
public static function generateSignValue(array $queryParams = [], string $body = '', string $nonce = '', string $timestamp = ''): string
|
||||||
{
|
{
|
||||||
$apiKey = config('bitunix-api.api_key');
|
$apiKey = config('bitunix-api.api_key');
|
||||||
$apiSecret = config('bitunix-api.api_secret');
|
$apiSecret = config('bitunix-api.api_secret');
|
||||||
|
|
||||||
if (empty($apiKey) || empty($apiSecret)) {
|
if (empty($apiKey) || empty($apiSecret)) {
|
||||||
throw new \InvalidArgumentException('API key and secret must be configured');
|
throw new \InvalidArgumentException('API key and secret must be configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Sort query parameters in ascending ASCII order
|
// Step 1: Sort query parameters in ascending ASCII order
|
||||||
$queryParamsString = self::digestQueryParameters($queryParams);
|
$queryParamsString = self::digestQueryParameters($queryParams);
|
||||||
|
|
||||||
// Step 2: Remove all spaces from body (already done if JSON encoded properly)
|
// Step 2: Remove all spaces from body (already done if JSON encoded properly)
|
||||||
$bodyString = trim($body);
|
$bodyString = trim($body);
|
||||||
|
|
||||||
// Step 3: Create digest: SHA256(nonce + timestamp + api-key + queryParams + body)
|
// Step 3: Create digest: SHA256(nonce + timestamp + api-key + queryParams + body)
|
||||||
$digestInput = $nonce . $timestamp . $apiKey . $queryParamsString . $bodyString;
|
$digestInput = $nonce.$timestamp.$apiKey.$queryParamsString.$bodyString;
|
||||||
$digest = hash('sha256', $digestInput);
|
$digest = hash('sha256', $digestInput);
|
||||||
|
|
||||||
// Step 4: Create sign: SHA256(digest + secretKey)
|
// Step 4: Create sign: SHA256(digest + secretKey)
|
||||||
$signInput = $digest . $apiSecret;
|
$signInput = $digest.$apiSecret;
|
||||||
$sign = hash('sha256', $signInput);
|
$sign = hash('sha256', $signInput);
|
||||||
|
|
||||||
return $sign;
|
return $sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate complete headers for authenticated requests
|
* Generate complete headers for authenticated requests
|
||||||
*
|
|
||||||
* @param array $queryParams
|
|
||||||
* @param string $body
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function generateHeaders(array $queryParams = [], string $body = ''): array
|
public static function generateHeaders(array $queryParams = [], string $body = ''): array
|
||||||
{
|
{
|
||||||
$nonce = self::generateNonce();
|
$nonce = self::generateNonce();
|
||||||
$timestamp = self::generateTimestamp();
|
$timestamp = self::generateTimestamp();
|
||||||
$sign = self::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
$sign = self::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'api-key' => config('bitunix-api.api_key'),
|
'api-key' => config('bitunix-api.api_key'),
|
||||||
'sign' => $sign,
|
'sign' => $sign,
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
|
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
|
|
||||||
it('can change leverage successfully', function () {
|
it('can change leverage successfully', function () {
|
||||||
$api = app(ChangeLeverageRequestContract::class);
|
$api = app(ChangeLeverageRequestContract::class);
|
||||||
// This test will make a real API call if credentials are valid
|
// This test will make a real API call if credentials are valid
|
||||||
// For testing purposes, we'll just verify the method exists and can be called
|
// For testing purposes, we'll just verify the method exists and can be called
|
||||||
expect(fn() => $api->changeLeverage('BTCUSDT', 'USDT', 12))
|
expect(fn () => $api->changeLeverage('BTCUSDT', 'USDT', 12))
|
||||||
->not->toThrow(Exception::class);
|
->not->toThrow(Exception::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('validates required parameters for change leverage', function () {
|
it('validates required parameters for change leverage', function () {
|
||||||
$api = app(ChangeLeverageRequestContract::class);
|
$api = app(ChangeLeverageRequestContract::class);
|
||||||
|
|
||||||
expect(fn() => $api->changeLeverage('BTCUSDT', 'USDT', 10))
|
expect(fn () => $api->changeLeverage('BTCUSDT', 'USDT', 10))
|
||||||
->not->toThrow(Exception::class)
|
->not->toThrow(Exception::class)
|
||||||
->and(fn() => $api->changeLeverage('BTCUSDT', 'USDT', 0))
|
->and(fn () => $api->changeLeverage('BTCUSDT', 'USDT', 0))
|
||||||
->not->toThrow(Exception::class);
|
->not->toThrow(Exception::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,33 +14,33 @@ it('can sort query parameters in ascending ASCII order', function () {
|
||||||
$parameters = [
|
$parameters = [
|
||||||
'uid' => '200',
|
'uid' => '200',
|
||||||
'id' => '1',
|
'id' => '1',
|
||||||
'name' => 'test'
|
'name' => 'test',
|
||||||
];
|
];
|
||||||
|
|
||||||
$sorted = Header::sortQueryParameters($parameters);
|
$sorted = Header::sortQueryParameters($parameters);
|
||||||
|
|
||||||
expect(array_keys($sorted))->toBe(['id', 'name', 'uid']);
|
expect(array_keys($sorted))->toBe(['id', 'name', 'uid']);
|
||||||
expect($sorted)->toBe([
|
expect($sorted)->toBe([
|
||||||
'id' => '1',
|
'id' => '1',
|
||||||
'name' => 'test',
|
'name' => 'test',
|
||||||
'uid' => '200'
|
'uid' => '200',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can digest query parameters to string format', function () {
|
it('can digest query parameters to string format', function () {
|
||||||
$parameters = [
|
$parameters = [
|
||||||
'id' => '1',
|
'id' => '1',
|
||||||
'uid' => '200'
|
'uid' => '200',
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = Header::digestQueryParameters($parameters);
|
$result = Header::digestQueryParameters($parameters);
|
||||||
|
|
||||||
expect($result)->toBe('id1uid200');
|
expect($result)->toBe('id1uid200');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can generate a valid nonce', function () {
|
it('can generate a valid nonce', function () {
|
||||||
$nonce = Header::generateNonce();
|
$nonce = Header::generateNonce();
|
||||||
|
|
||||||
expect($nonce)
|
expect($nonce)
|
||||||
->toBeString()
|
->toBeString()
|
||||||
->toHaveLength(32)
|
->toHaveLength(32)
|
||||||
|
|
@ -49,7 +49,7 @@ it('can generate a valid nonce', function () {
|
||||||
|
|
||||||
it('can generate timestamp in milliseconds', function () {
|
it('can generate timestamp in milliseconds', function () {
|
||||||
$timestamp = Header::generateTimestamp();
|
$timestamp = Header::generateTimestamp();
|
||||||
|
|
||||||
expect($timestamp)
|
expect($timestamp)
|
||||||
->toBeString()
|
->toBeString()
|
||||||
->toMatch('/^\d{13}$/'); // 13 digits for milliseconds
|
->toMatch('/^\d{13}$/'); // 13 digits for milliseconds
|
||||||
|
|
@ -60,9 +60,9 @@ it('can generate signature according to Bitunix documentation', function () {
|
||||||
$timestamp = '20241120123045';
|
$timestamp = '20241120123045';
|
||||||
$queryParams = ['id' => '1', 'uid' => '200'];
|
$queryParams = ['id' => '1', 'uid' => '200'];
|
||||||
$body = '{"uid":"2899","arr":[{"id":1,"name":"maple"},{"id":2,"name":"lily"}]}';
|
$body = '{"uid":"2899","arr":[{"id":1,"name":"maple"},{"id":2,"name":"lily"}]}';
|
||||||
|
|
||||||
$sign = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
$sign = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
||||||
|
|
||||||
expect($sign)
|
expect($sign)
|
||||||
->toBeString()
|
->toBeString()
|
||||||
->toHaveLength(64) // SHA256 hex length
|
->toHaveLength(64) // SHA256 hex length
|
||||||
|
|
@ -72,9 +72,9 @@ it('can generate signature according to Bitunix documentation', function () {
|
||||||
it('can generate complete headers for authenticated requests', function () {
|
it('can generate complete headers for authenticated requests', function () {
|
||||||
$queryParams = ['symbol' => 'BTCUSDT'];
|
$queryParams = ['symbol' => 'BTCUSDT'];
|
||||||
$body = '{"symbol":"BTCUSDT","marginCoin":"USDT","leverage":12}';
|
$body = '{"symbol":"BTCUSDT","marginCoin":"USDT","leverage":12}';
|
||||||
|
|
||||||
$headers = Header::generateHeaders($queryParams, $body);
|
$headers = Header::generateHeaders($queryParams, $body);
|
||||||
|
|
||||||
expect($headers)
|
expect($headers)
|
||||||
->toHaveKeys(['api-key', 'sign', 'nonce', 'timestamp', 'language', 'Content-Type'])
|
->toHaveKeys(['api-key', 'sign', 'nonce', 'timestamp', 'language', 'Content-Type'])
|
||||||
->and($headers['api-key'])->toBe('test-api-key')
|
->and($headers['api-key'])->toBe('test-api-key')
|
||||||
|
|
@ -90,23 +90,23 @@ it('throws exception when API credentials are missing', function () {
|
||||||
'bitunix-api.api_key' => '',
|
'bitunix-api.api_key' => '',
|
||||||
'bitunix-api.api_secret' => '',
|
'bitunix-api.api_secret' => '',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(fn() => Header::generateSignValue([], '', 'nonce', 'timestamp'))
|
expect(fn () => Header::generateSignValue([], '', 'nonce', 'timestamp'))
|
||||||
->toThrow(InvalidArgumentException::class, 'API key and secret must be configured');
|
->toThrow(InvalidArgumentException::class, 'API key and secret must be configured');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty query parameters correctly', function () {
|
it('handles empty query parameters correctly', function () {
|
||||||
$result = Header::digestQueryParameters([]);
|
$result = Header::digestQueryParameters([]);
|
||||||
|
|
||||||
expect($result)->toBe('');
|
expect($result)->toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty body correctly', function () {
|
it('handles empty body correctly', function () {
|
||||||
$nonce = '123456';
|
$nonce = '123456';
|
||||||
$timestamp = '20241120123045';
|
$timestamp = '20241120123045';
|
||||||
|
|
||||||
$sign = Header::generateSignValue([], '', $nonce, $timestamp);
|
$sign = Header::generateSignValue([], '', $nonce, $timestamp);
|
||||||
|
|
||||||
expect($sign)
|
expect($sign)
|
||||||
->toBeString()
|
->toBeString()
|
||||||
->toHaveLength(64);
|
->toHaveLength(64);
|
||||||
|
|
@ -117,19 +117,19 @@ it('generates consistent signature for same inputs', function () {
|
||||||
$timestamp = '20241120123045';
|
$timestamp = '20241120123045';
|
||||||
$queryParams = ['id' => '1'];
|
$queryParams = ['id' => '1'];
|
||||||
$body = '{"test":"value"}';
|
$body = '{"test":"value"}';
|
||||||
|
|
||||||
$sign1 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
$sign1 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
||||||
$sign2 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
$sign2 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
|
||||||
|
|
||||||
expect($sign1)->toBe($sign2);
|
expect($sign1)->toBe($sign2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates different signatures for different inputs', function () {
|
it('generates different signatures for different inputs', function () {
|
||||||
$nonce = '123456';
|
$nonce = '123456';
|
||||||
$timestamp = '20241120123045';
|
$timestamp = '20241120123045';
|
||||||
|
|
||||||
$sign1 = Header::generateSignValue(['id' => '1'], '{"test":"value1"}', $nonce, $timestamp);
|
$sign1 = Header::generateSignValue(['id' => '1'], '{"test":"value1"}', $nonce, $timestamp);
|
||||||
$sign2 = Header::generateSignValue(['id' => '2'], '{"test":"value2"}', $nonce, $timestamp);
|
$sign2 = Header::generateSignValue(['id' => '2'], '{"test":"value2"}', $nonce, $timestamp);
|
||||||
|
|
||||||
expect($sign1)->not->toBe($sign2);
|
expect($sign1)->not->toBe($sign2);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue