├── .gitignore ├── LICENSE ├── README.md ├── rest ├── c# │ ├── ExmoAPI.csproj │ ├── ExmoAPI.sln │ ├── ExmoApi.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── csc_build.bat ├── golang │ └── exmo.go ├── java │ ├── jv.iml │ ├── lib │ │ ├── commons-codec-1.8.jar │ │ ├── okhttp-3.1.2.jar │ │ └── okio-1.6.0.jar │ └── src │ │ ├── Exmo.java │ │ └── Main.java ├── js │ ├── README.md │ ├── jquery_version.html │ ├── native_js.html │ ├── native_js_promise.html │ ├── server.bat │ └── web-server.js ├── nodejs │ ├── README.md │ ├── exmo.js │ └── test_exmo.js ├── objectivec │ ├── api.xcodeproj │ │ └── project.pbxproj │ └── api │ │ ├── ExmoApiHandler.h │ │ ├── ExmoApiHandler.m │ │ └── main.m ├── php │ └── exmo.php ├── python │ ├── exmo2.py │ └── exmo3.py ├── r │ └── exmo.r ├── ruby │ └── exmo.rb ├── swift │ ├── api-Bridging-Header.h │ ├── api.xcodeproj │ │ └── project.pbxproj │ └── api │ │ ├── ApiHandler.swift │ │ ├── NSData+Extensions.swift │ │ └── main.swift └── с++ │ ├── README.md │ ├── connection.hpp │ ├── curl_object.hpp │ ├── exmo.cpp │ ├── exmo_api.hpp │ └── hmac_sha512.hpp └── ws ├── .net ├── Program.cs ├── WebSocketClient.csproj └── exmo.sln ├── golang └── client.go ├── java ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── exmo │ └── App.java ├── js ├── WebSocketsPolyfill.js ├── client.js ├── package-lock.json ├── package.json └── start.js ├── python2 ├── client.py └── requirements.txt └── python3 ├── client.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.suo 3 | node_modules 4 | dist 5 | .tmp 6 | .sass-cache 7 | bower_components 8 | target 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 exmo-dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # exmo.com API 2 | ### API documentation 3 | https://exmo.com/api_doc 4 | 5 | ### Supported languages: 6 | - php. 7 | - js. 8 | - nodejs. 9 | - c#. 10 | - c++11. 11 | - python. 12 | - objective c. 13 | - swift. 14 | - java. 15 | - ruby. 16 | - golang. 17 | - R. 18 | -------------------------------------------------------------------------------- /rest/c#/ExmoAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A868170E-E9E0-4120-AF21-82341D07CD5E} 8 | Exe 9 | Properties 10 | ExmoAPI 11 | ExmoAPI 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 59 | -------------------------------------------------------------------------------- /rest/c#/ExmoAPI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExmoAPI", "ExmoAPI.csproj", "{A868170E-E9E0-4120-AF21-82341D07CD5E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A868170E-E9E0-4120-AF21-82341D07CD5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A868170E-E9E0-4120-AF21-82341D07CD5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A868170E-E9E0-4120-AF21-82341D07CD5E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A868170E-E9E0-4120-AF21-82341D07CD5E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /rest/c#/ExmoApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Security.Cryptography; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using System.Web; 12 | 13 | namespace ExmoAPI 14 | { 15 | public class ExmoApi 16 | { 17 | private static long _nounce; 18 | // API settings 19 | private string _key; 20 | private string _secret; 21 | private string _url = "https://api.exmo.com/v1/{0}"; 22 | 23 | static ExmoApi() 24 | { 25 | _nounce = Helpers.GetTimestamp(); 26 | } 27 | 28 | public ExmoApi(string key, string secret) 29 | { 30 | _key = key; 31 | _secret = secret; 32 | } 33 | 34 | public async Task ApiQueryAsync(string apiName, IDictionary req) 35 | { 36 | using (var client = new HttpClient()) 37 | { 38 | var n = Interlocked.Increment(ref _nounce); 39 | req.Add("nonce", Convert.ToString(n)); 40 | var message = ToQueryString(req); 41 | 42 | var sign = Sign(_secret, message); 43 | 44 | var content = new FormUrlEncodedContent(req); 45 | content.Headers.Add("Sign", sign); 46 | content.Headers.Add("Key", _key); 47 | 48 | var response = await client.PostAsync(string.Format(_url, apiName), content); 49 | 50 | return await response.Content.ReadAsStringAsync(); 51 | } 52 | } 53 | 54 | public async Task ApiQueryAsyncEx(string apiName, IDictionary req) 55 | { 56 | using (var client = new HttpClient()) 57 | { 58 | var n = Interlocked.Increment(ref _nounce); 59 | req.Add("nonce", Convert.ToString(n)); 60 | var message = ToQueryString(req); 61 | 62 | var sign = Sign(_secret, message); 63 | 64 | var content = new FormUrlEncodedContent(req); 65 | content.Headers.Add("Sign", sign); 66 | content.Headers.Add("Key", _key); 67 | 68 | var response = await client.PostAsync(string.Format(_url, apiName), content); 69 | await Task.Factory.StartNew(async () => 70 | { 71 | var data = await response.Content.ReadAsStringAsync(); 72 | Console.WriteLine(data); 73 | }); 74 | 75 | return response.StatusCode; 76 | } 77 | } 78 | 79 | public string ApiQuery(string apiName, IDictionary req) 80 | { 81 | using (var wb = new WebClient()) 82 | { 83 | req.Add("nonce", Convert.ToString(_nounce++)); 84 | var message = ToQueryString(req); 85 | 86 | var sign = Sign(_secret, message); 87 | 88 | wb.Headers.Add("Sign", sign); 89 | wb.Headers.Add("Key", _key); 90 | 91 | var data = req.ToNameValueCollection(); 92 | 93 | var response = wb.UploadValues(string.Format(_url, apiName), "POST", data); 94 | return Encoding.UTF8.GetString(response); 95 | } 96 | } 97 | 98 | private string ToQueryString(IDictionary dic) 99 | { 100 | var array = (from key in dic.Keys 101 | select string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(dic[key]))) 102 | .ToArray(); 103 | return string.Join("&", array); 104 | } 105 | 106 | public static string Sign(string key, string message) 107 | { 108 | using (HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes(key))) 109 | { 110 | byte[] b = hmac.ComputeHash(Encoding.UTF8.GetBytes(message)); 111 | return ByteToString(b); 112 | } 113 | } 114 | 115 | public static string ByteToString(byte[] buff) 116 | { 117 | string sbinary = ""; 118 | 119 | for (int i = 0; i < buff.Length; i++) 120 | { 121 | sbinary += buff[i].ToString("X2"); // hex format 122 | } 123 | return (sbinary).ToLowerInvariant(); 124 | } 125 | 126 | } 127 | 128 | public static class Helpers 129 | { 130 | public static NameValueCollection ToNameValueCollection(this IDictionary dict) 131 | { 132 | var nameValueCollection = new NameValueCollection(); 133 | 134 | foreach (var kvp in dict) 135 | { 136 | string value = string.Empty; 137 | if (kvp.Value != null) 138 | value = kvp.Value.ToString(); 139 | 140 | nameValueCollection.Add(kvp.Key.ToString(), value); 141 | } 142 | 143 | return nameValueCollection; 144 | } 145 | 146 | public static long GetTimestamp() 147 | { 148 | var d = (DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; 149 | return (long)d; 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /rest/c#/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace ExmoAPI 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | //Init 12 | var api = new ExmoApi("your_key", "your_secret"); 13 | 14 | //sync query 15 | var result = api.ApiQuery("user_info", new Dictionary()); 16 | Console.WriteLine("sync result"); 17 | Console.WriteLine(result); 18 | var result2 = api.ApiQuery("user_cancelled_orders", new Dictionary { { "limit", "100" }, { "offset", "0"} }); 19 | Console.WriteLine("sync result2"); 20 | Console.WriteLine(result2); 21 | 22 | 23 | //async query 24 | var task = api.ApiQueryAsync("user_info", new Dictionary ()); 25 | Console.WriteLine("async result3"); 26 | Console.WriteLine(task.Result); 27 | 28 | Task.Factory.StartNew(async () => 29 | { 30 | var result3 = await api.ApiQueryAsync("user_cancelled_orders", new Dictionary { { "limit", "2" }, { "offset", "0" } }); 31 | Console.WriteLine("async result4"); 32 | Console.WriteLine(result3); 33 | }); 34 | 35 | Console.ReadLine(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rest/c#/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExmoAPI")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ExmoAPI")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a868170e-e9e0-4120-af21-82341d07cd5e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /rest/c#/csc_build.bat: -------------------------------------------------------------------------------- 1 | "c:\Program Files (x86)\MSBuild\12.0\Bin\csc.exe" Program.cs ExmoApi.cs /reference:System.dll,System.Net.Http.dll -------------------------------------------------------------------------------- /rest/golang/exmo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/hmac" 6 | "crypto/sha512" 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "io/ioutil" 11 | "net/http" 12 | "net/url" 13 | "strconv" 14 | "time" 15 | ) 16 | 17 | type ApiResponse map[string]interface{} 18 | type ApiParams map[string]string 19 | 20 | func api_query(method string, params ApiParams) (ApiResponse, error) { 21 | key := "" // TODO replace with your api key from profile page 22 | secret := "" // TODO replace with your api secret from profile page 23 | 24 | post_params := url.Values{} 25 | post_params.Add("nonce", nonce()) 26 | if params != nil { 27 | for key, value := range params { 28 | post_params.Add(key, value) 29 | } 30 | } 31 | post_content := post_params.Encode() 32 | 33 | sign := do_sign(post_content, secret) 34 | 35 | req, _ := http.NewRequest("POST", "https://api.exmo.com/v1/"+method, bytes.NewBuffer([]byte(post_content))) 36 | req.Header.Set("Key", key) 37 | req.Header.Set("Sign", sign) 38 | req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 39 | req.Header.Add("Content-Length", strconv.Itoa(len(post_content))) 40 | 41 | client := &http.Client{} 42 | resp, err := client.Do(req) 43 | if err != nil { 44 | return nil, err 45 | } 46 | defer resp.Body.Close() 47 | 48 | if resp.Status != "200 OK" { 49 | return nil, errors.New("http status: " + resp.Status) 50 | } 51 | 52 | body, err1 := ioutil.ReadAll(resp.Body) 53 | if err1 != nil { 54 | return nil, err1 55 | } 56 | 57 | var dat map[string]interface{} 58 | err2 := json.Unmarshal([]byte(body), &dat) 59 | if err2 != nil { 60 | return nil, err2 61 | } 62 | 63 | if result, ok := dat["result"]; ok && result.(bool) != true { 64 | return nil, errors.New(dat["error"].(string)) 65 | } 66 | 67 | return dat, nil 68 | } 69 | 70 | func nonce() string { 71 | return fmt.Sprintf("%d", time.Now().UnixNano()) 72 | } 73 | 74 | func do_sign(message string, secret string) string { 75 | mac := hmac.New(sha512.New, []byte(secret)) 76 | mac.Write([]byte(message)) 77 | return fmt.Sprintf("%x", mac.Sum(nil)) 78 | } 79 | 80 | func main() { 81 | result, err := api_query("user_info", nil) 82 | if err != nil { 83 | fmt.Printf("api error: %s\n", err.Error()) 84 | } else { 85 | fmt.Printf("api result: %v\n", result) 86 | } 87 | 88 | fmt.Printf("-------------\n") 89 | 90 | result1, err1 := api_query("user_trades", ApiParams{"pair": "BTC_USD"}) 91 | if err1 != nil { 92 | fmt.Printf("api error: %s\n", err1.Error()) 93 | } else { 94 | fmt.Printf("api result: %v\n", result1) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /rest/java/jv.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /rest/java/lib/commons-codec-1.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exmo-dev/exmo_api_lib/251baceb5fa736395dcc4b4d82f47f118b262f80/rest/java/lib/commons-codec-1.8.jar -------------------------------------------------------------------------------- /rest/java/lib/okhttp-3.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exmo-dev/exmo_api_lib/251baceb5fa736395dcc4b4d82f47f118b262f80/rest/java/lib/okhttp-3.1.2.jar -------------------------------------------------------------------------------- /rest/java/lib/okio-1.6.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exmo-dev/exmo_api_lib/251baceb5fa736395dcc4b4d82f47f118b262f80/rest/java/lib/okio-1.6.0.jar -------------------------------------------------------------------------------- /rest/java/src/Exmo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Admin on 2/18/2016. 3 | */ 4 | 5 | import okhttp3.*; 6 | import org.apache.commons.codec.binary.Hex; 7 | 8 | import javax.crypto.Mac; 9 | import javax.crypto.spec.SecretKeySpec; 10 | import java.io.IOException; 11 | import java.io.UnsupportedEncodingException; 12 | import java.security.InvalidKeyException; 13 | import java.security.NoSuchAlgorithmException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | 18 | public class Exmo { 19 | private static long _nonce; 20 | private String _key; 21 | private String _secret; 22 | 23 | public Exmo(String key, String secret) { 24 | _nonce = System.nanoTime(); 25 | _key = key; 26 | _secret = secret; 27 | } 28 | 29 | public final String Request(String method, Map arguments) { 30 | Mac mac; 31 | SecretKeySpec key; 32 | String sign; 33 | 34 | if (arguments == null) { // If the user provided no arguments, just create an empty argument array. 35 | arguments = new HashMap<>(); 36 | } 37 | 38 | arguments.put("nonce", "" + ++_nonce); // Add the dummy nonce. 39 | 40 | String postData = ""; 41 | 42 | for (Map.Entry stringStringEntry : arguments.entrySet()) { 43 | Map.Entry argument = (Map.Entry) stringStringEntry; 44 | 45 | if (postData.length() > 0) { 46 | postData += "&"; 47 | } 48 | postData += argument.getKey() + "=" + argument.getValue(); 49 | } 50 | 51 | // Create a new secret key 52 | try { 53 | key = new SecretKeySpec(_secret.getBytes("UTF-8"), "HmacSHA512"); 54 | } catch (UnsupportedEncodingException uee) { 55 | System.err.println("Unsupported encoding exception: " + uee.toString()); 56 | return null; 57 | } 58 | 59 | // Create a new mac 60 | try { 61 | mac = Mac.getInstance("HmacSHA512"); 62 | } catch (NoSuchAlgorithmException nsae) { 63 | System.err.println("No such algorithm exception: " + nsae.toString()); 64 | return null; 65 | } 66 | 67 | // Init mac with key. 68 | try { 69 | mac.init(key); 70 | } catch (InvalidKeyException ike) { 71 | System.err.println("Invalid key exception: " + ike.toString()); 72 | return null; 73 | } 74 | 75 | 76 | // Encode the post data by the secret and encode the result as base64. 77 | try { 78 | sign = Hex.encodeHexString(mac.doFinal(postData.getBytes("UTF-8"))); 79 | } catch (UnsupportedEncodingException uee) { 80 | System.err.println("Unsupported encoding exception: " + uee.toString()); 81 | return null; 82 | } 83 | 84 | // Now do the actual request 85 | MediaType form = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8"); 86 | 87 | OkHttpClient client = new OkHttpClient(); 88 | try { 89 | 90 | RequestBody body = RequestBody.create(form, postData); 91 | Request request = new Request.Builder() 92 | .url("https://api.exmo.com/v1/" + method) 93 | .addHeader("Key", _key) 94 | .addHeader("Sign", sign) 95 | .post(body) 96 | .build(); 97 | 98 | Response response = client.newCall(request).execute(); 99 | return response.body().string(); 100 | } catch (IOException e) { 101 | System.err.println("Request fail: " + e.toString()); 102 | return null; // An error occured... 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /rest/java/src/Main.java: -------------------------------------------------------------------------------- 1 | import java.util.HashMap; 2 | 3 | public class Main { 4 | 5 | public static void main(String[] args) { 6 | 7 | Exmo e = new Exmo("your_key","your_secret"); 8 | String result = e.Request("user_info", null); 9 | System.out.println(result); 10 | String result2 = e.Request("user_cancelled_orders", new HashMap() {{ 11 | put("limit", "2"); 12 | put("offset", "0"); 13 | }}); 14 | System.out.println(result2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rest/js/README.md: -------------------------------------------------------------------------------- 1 | https://www.npmjs.com/package/crypto-js 2 | bower install crypto-js 3 | 4 | http://jquery.com/ 5 | bower install jquery 6 | 7 | -------------------------------------------------------------------------------- /rest/js/jquery_version.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exmo API jquery 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /rest/js/native_js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exmo API 6 |
7 | 8 | 9 | 10 | 11 | 68 | 69 | -------------------------------------------------------------------------------- /rest/js/native_js_promise.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exmo API 6 |
7 | 8 | 9 | 10 | 11 | 91 | 92 | -------------------------------------------------------------------------------- /rest/js/server.bat: -------------------------------------------------------------------------------- 1 | node web-server.js 2 | PAUSE -------------------------------------------------------------------------------- /rest/js/web-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var util = require('util'), 4 | http = require('http'), 5 | fs = require('fs'), 6 | url = require('url'), 7 | events = require('events'); 8 | 9 | var DEFAULT_PORT = 8000; 10 | 11 | function main(argv) { 12 | new HttpServer({ 13 | 'GET': createServlet(StaticServlet), 14 | 'HEAD': createServlet(StaticServlet) 15 | }).start(Number(argv[2]) || DEFAULT_PORT); 16 | } 17 | 18 | function escapeHtml(value) { 19 | return value.toString(). 20 | replace('<', '<'). 21 | replace('>', '>'). 22 | replace('"', '"'); 23 | } 24 | 25 | function createServlet(Class) { 26 | var servlet = new Class(); 27 | return servlet.handleRequest.bind(servlet); 28 | } 29 | 30 | /** 31 | * An Http server implementation that uses a map of methods to decide 32 | * action routing. 33 | * 34 | * @param {Object} Map of method => Handler function 35 | */ 36 | function HttpServer(handlers) { 37 | this.handlers = handlers; 38 | this.server = http.createServer(this.handleRequest_.bind(this)); 39 | } 40 | 41 | HttpServer.prototype.start = function(port) { 42 | this.port = port; 43 | this.server.listen(port); 44 | util.puts('Http Server running at http://localhost:' + port + '/'); 45 | }; 46 | 47 | HttpServer.prototype.parseUrl_ = function(urlString) { 48 | var parsed = url.parse(urlString); 49 | parsed.pathname = url.resolve('/', parsed.pathname); 50 | return url.parse(url.format(parsed), true); 51 | }; 52 | 53 | HttpServer.prototype.handleRequest_ = function(req, res) { 54 | var logEntry = req.method + ' ' + req.url; 55 | if (req.headers['user-agent']) { 56 | logEntry += ' ' + req.headers['user-agent']; 57 | } 58 | util.puts(logEntry); 59 | req.url = this.parseUrl_(req.url); 60 | var handler = this.handlers[req.method]; 61 | if (!handler) { 62 | res.writeHead(501); 63 | res.end(); 64 | } else { 65 | handler.call(this, req, res); 66 | } 67 | }; 68 | 69 | /** 70 | * Handles static content. 71 | */ 72 | function StaticServlet() {} 73 | 74 | StaticServlet.MimeMap = { 75 | 'txt': 'text/plain', 76 | 'html': 'text/html', 77 | 'css': 'text/css', 78 | 'xml': 'application/xml', 79 | 'json': 'application/json', 80 | 'js': 'application/javascript', 81 | 'jpg': 'image/jpeg', 82 | 'jpeg': 'image/jpeg', 83 | 'gif': 'image/gif', 84 | 'png': 'image/png', 85 |   'svg': 'image/svg+xml' 86 | }; 87 | 88 | StaticServlet.prototype.handleRequest = function(req, res) { 89 | var self = this; 90 | var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){ 91 | return String.fromCharCode(parseInt(hex, 16)); 92 | }); 93 | var parts = path.split('/'); 94 | if (parts[parts.length-1].charAt(0) === '.') 95 | return self.sendForbidden_(req, res, path); 96 | fs.stat(path, function(err, stat) { 97 | if (err) 98 | return self.sendMissing_(req, res, path); 99 | if (stat.isDirectory()) 100 | return self.sendDirectory_(req, res, path); 101 | return self.sendFile_(req, res, path); 102 | }); 103 | } 104 | 105 | StaticServlet.prototype.sendError_ = function(req, res, error) { 106 | res.writeHead(500, { 107 | 'Content-Type': 'text/html' 108 | }); 109 | res.write('\n'); 110 | res.write('Internal Server Error\n'); 111 | res.write('

Internal Server Error

'); 112 | res.write('
' + escapeHtml(util.inspect(error)) + '
'); 113 | util.puts('500 Internal Server Error'); 114 | util.puts(util.inspect(error)); 115 | }; 116 | 117 | StaticServlet.prototype.sendMissing_ = function(req, res, path) { 118 | path = path.substring(1); 119 | res.writeHead(404, { 120 | 'Content-Type': 'text/html' 121 | }); 122 | res.write('\n'); 123 | res.write('404 Not Found\n'); 124 | res.write('

Not Found

'); 125 | res.write( 126 | '

The requested URL ' + 127 | escapeHtml(path) + 128 | ' was not found on this server.

' 129 | ); 130 | res.end(); 131 | util.puts('404 Not Found: ' + path); 132 | }; 133 | 134 | StaticServlet.prototype.sendForbidden_ = function(req, res, path) { 135 | path = path.substring(1); 136 | res.writeHead(403, { 137 | 'Content-Type': 'text/html' 138 | }); 139 | res.write('\n'); 140 | res.write('403 Forbidden\n'); 141 | res.write('

Forbidden

'); 142 | res.write( 143 | '

You do not have permission to access ' + 144 | escapeHtml(path) + ' on this server.

' 145 | ); 146 | res.end(); 147 | util.puts('403 Forbidden: ' + path); 148 | }; 149 | 150 | StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) { 151 | res.writeHead(301, { 152 | 'Content-Type': 'text/html', 153 | 'Location': redirectUrl 154 | }); 155 | res.write('\n'); 156 | res.write('301 Moved Permanently\n'); 157 | res.write('

Moved Permanently

'); 158 | res.write( 159 | '

The document has moved here.

' 162 | ); 163 | res.end(); 164 | util.puts('301 Moved Permanently: ' + redirectUrl); 165 | }; 166 | 167 | StaticServlet.prototype.sendFile_ = function(req, res, path) { 168 | var self = this; 169 | var file = fs.createReadStream(path); 170 | res.writeHead(200, { 171 | 'Content-Type': StaticServlet. 172 | MimeMap[path.split('.').pop()] || 'text/plain' 173 | }); 174 | if (req.method === 'HEAD') { 175 | res.end(); 176 | } else { 177 | file.on('data', res.write.bind(res)); 178 | file.on('close', function() { 179 | res.end(); 180 | }); 181 | file.on('error', function(error) { 182 | self.sendError_(req, res, error); 183 | }); 184 | } 185 | }; 186 | 187 | StaticServlet.prototype.sendDirectory_ = function(req, res, path) { 188 | var self = this; 189 | if (path.match(/[^\/]$/)) { 190 | req.url.pathname += '/'; 191 | var redirectUrl = url.format(url.parse(url.format(req.url))); 192 | return self.sendRedirect_(req, res, redirectUrl); 193 | } 194 | fs.readdir(path, function(err, files) { 195 | if (err) 196 | return self.sendError_(req, res, error); 197 | 198 | if (!files.length) 199 | return self.writeDirectoryIndex_(req, res, path, []); 200 | 201 | var remaining = files.length; 202 | files.forEach(function(fileName, index) { 203 | fs.stat(path + '/' + fileName, function(err, stat) { 204 | if (err) 205 | return self.sendError_(req, res, err); 206 | if (stat.isDirectory()) { 207 | files[index] = fileName + '/'; 208 | } 209 | if (!(--remaining)) 210 | return self.writeDirectoryIndex_(req, res, path, files); 211 | }); 212 | }); 213 | }); 214 | }; 215 | 216 | StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) { 217 | path = path.substring(1); 218 | res.writeHead(200, { 219 | 'Content-Type': 'text/html' 220 | }); 221 | if (req.method === 'HEAD') { 222 | res.end(); 223 | return; 224 | } 225 | res.write('\n'); 226 | res.write('' + escapeHtml(path) + '\n'); 227 | res.write('\n'); 230 | res.write('

Directory: ' + escapeHtml(path) + '

'); 231 | res.write('
    '); 232 | files.forEach(function(fileName) { 233 | if (fileName.charAt(0) !== '.') { 234 | res.write('
  1. ' + 236 | escapeHtml(fileName) + '
  2. '); 237 | } 238 | }); 239 | res.write('
'); 240 | res.end(); 241 | }; 242 | 243 | // Must be last, 244 | main(process.argv); 245 | -------------------------------------------------------------------------------- /rest/nodejs/README.md: -------------------------------------------------------------------------------- 1 | https://www.npmjs.com/package/crypto-js 2 | npm install crypto-js 3 | 4 | https://www.npmjs.com/package/querystring 5 | npm install querystring 6 | 7 | https://www.npmjs.com/package/request 8 | npm install request 9 | -------------------------------------------------------------------------------- /rest/nodejs/exmo.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto') 2 | http = require('http'), 3 | querystring = require('querystring'), 4 | request = require('request'), 5 | config = { 6 | url: 'https://api.exmo.com/v1/' 7 | }; 8 | 9 | 10 | function sign(message){ 11 | return crypto.createHmac('sha512', config.secret).update(message).digest('hex'); 12 | } 13 | 14 | exports.init_exmo = function (cfg) { 15 | config.key = cfg.key; 16 | config.secret = cfg.secret; 17 | config.nonce = Math.floor(new Date().getTime()); 18 | }; 19 | 20 | exports.api_query = function(method_name, data, callback){ 21 | data.nonce = config.nonce++; 22 | var post_data = querystring.stringify(data); 23 | 24 | var options = { 25 | url: config.url + method_name, 26 | method: 'POST', 27 | headers: { 28 | 'Key': config.key, 29 | 'Sign': sign(post_data) 30 | }, 31 | form:data 32 | }; 33 | 34 | request(options, function (error, response, body) { 35 | if (!error && response.statusCode == 200) { 36 | callback(body); 37 | }else{ 38 | callback(error); 39 | } 40 | }); 41 | }; 42 | 43 | exports.api_query2 = function(method_name, data, callback){ 44 | data.nonce = config.nonce++; 45 | var post_data = querystring.stringify(data); 46 | 47 | var post_options = { 48 | host: 'api.exmo.com', 49 | port: '80', 50 | path: '/v1/' + method_name, 51 | method: 'POST', 52 | headers: { 53 | 'Key': config.key, 54 | 'Sign': sign(post_data), 55 | 'Content-Type': 'application/x-www-form-urlencoded', 56 | 'Content-Length': Buffer.byteLength(post_data) 57 | } 58 | }; 59 | var post_req = http.request(post_options, function(res) { 60 | res.setEncoding('utf8'); 61 | res.on('data', function (chunk) { 62 | callback(chunk); 63 | }); 64 | }); 65 | 66 | post_req.write(post_data); 67 | post_req.end(); 68 | }; 69 | 70 | 71 | 72 | exports.test = function(){ 73 | return config.key; 74 | }; 75 | -------------------------------------------------------------------------------- /rest/nodejs/test_exmo.js: -------------------------------------------------------------------------------- 1 | var exmo = require("./exmo"); 2 | 3 | exmo.init_exmo({key:'your_key', secret:'your_secret'}); 4 | //request version 5 | exmo.api_query("user_info", { }, function(result){ 6 | console.log(result); 7 | exmo.api_query("user_cancelled_orders", { "limit":1, "offset":0 }, function(result){ 8 | console.log(result); 9 | 10 | //http nodejs version 11 | exmo.api_query2("user_info", { }, function(result){ 12 | console.log(result); 13 | exmo.api_query2("user_cancelled_orders", { "limit":2, "offset":0 }, function(result){ 14 | console.log(result); 15 | }); 16 | }); 17 | }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /rest/objectivec/api.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 84BFEEA71C70827F009C9544 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BFEEA61C70827F009C9544 /* main.m */; }; 11 | 84BFEEAF1C7084A8009C9544 /* ExmoApiHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BFEEAE1C7084A8009C9544 /* ExmoApiHandler.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 84BFEEA11C70827F009C9544 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 84BFEEA31C70827F009C9544 /* api */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = api; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 84BFEEA61C70827F009C9544 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 84BFEEAD1C7084A0009C9544 /* ExmoApiHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExmoApiHandler.h; sourceTree = ""; }; 30 | 84BFEEAE1C7084A8009C9544 /* ExmoApiHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExmoApiHandler.m; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 84BFEEA01C70827F009C9544 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 84BFEE9A1C70827F009C9544 = { 45 | isa = PBXGroup; 46 | children = ( 47 | 84BFEEA51C70827F009C9544 /* api */, 48 | 84BFEEA41C70827F009C9544 /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 84BFEEA41C70827F009C9544 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 84BFEEA31C70827F009C9544 /* api */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 84BFEEA51C70827F009C9544 /* api */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 84BFEEAE1C7084A8009C9544 /* ExmoApiHandler.m */, 64 | 84BFEEAD1C7084A0009C9544 /* ExmoApiHandler.h */, 65 | 84BFEEA61C70827F009C9544 /* main.m */, 66 | ); 67 | path = api; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | 84BFEEA21C70827F009C9544 /* api */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = 84BFEEAA1C70827F009C9544 /* Build configuration list for PBXNativeTarget "api" */; 76 | buildPhases = ( 77 | 84BFEE9F1C70827F009C9544 /* Sources */, 78 | 84BFEEA01C70827F009C9544 /* Frameworks */, 79 | 84BFEEA11C70827F009C9544 /* CopyFiles */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = api; 86 | productName = api; 87 | productReference = 84BFEEA31C70827F009C9544 /* api */; 88 | productType = "com.apple.product-type.tool"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | 84BFEE9B1C70827F009C9544 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastUpgradeCheck = 0720; 97 | ORGANIZATIONNAME = exmo; 98 | TargetAttributes = { 99 | 84BFEEA21C70827F009C9544 = { 100 | CreatedOnToolsVersion = 7.2.1; 101 | }; 102 | }; 103 | }; 104 | buildConfigurationList = 84BFEE9E1C70827F009C9544 /* Build configuration list for PBXProject "api" */; 105 | compatibilityVersion = "Xcode 3.2"; 106 | developmentRegion = English; 107 | hasScannedForEncodings = 0; 108 | knownRegions = ( 109 | en, 110 | ); 111 | mainGroup = 84BFEE9A1C70827F009C9544; 112 | productRefGroup = 84BFEEA41C70827F009C9544 /* Products */; 113 | projectDirPath = ""; 114 | projectRoot = ""; 115 | targets = ( 116 | 84BFEEA21C70827F009C9544 /* api */, 117 | ); 118 | }; 119 | /* End PBXProject section */ 120 | 121 | /* Begin PBXSourcesBuildPhase section */ 122 | 84BFEE9F1C70827F009C9544 /* Sources */ = { 123 | isa = PBXSourcesBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | 84BFEEA71C70827F009C9544 /* main.m in Sources */, 127 | 84BFEEAF1C7084A8009C9544 /* ExmoApiHandler.m in Sources */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXSourcesBuildPhase section */ 132 | 133 | /* Begin XCBuildConfiguration section */ 134 | 84BFEEA81C70827F009C9544 /* Debug */ = { 135 | isa = XCBuildConfiguration; 136 | buildSettings = { 137 | ALWAYS_SEARCH_USER_PATHS = NO; 138 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 139 | CLANG_CXX_LIBRARY = "libc++"; 140 | CLANG_ENABLE_MODULES = YES; 141 | CLANG_ENABLE_OBJC_ARC = YES; 142 | CLANG_WARN_BOOL_CONVERSION = YES; 143 | CLANG_WARN_CONSTANT_CONVERSION = YES; 144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 145 | CLANG_WARN_EMPTY_BODY = YES; 146 | CLANG_WARN_ENUM_CONVERSION = YES; 147 | CLANG_WARN_INT_CONVERSION = YES; 148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 149 | CLANG_WARN_UNREACHABLE_CODE = YES; 150 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 151 | CODE_SIGN_IDENTITY = "-"; 152 | COPY_PHASE_STRIP = NO; 153 | DEBUG_INFORMATION_FORMAT = dwarf; 154 | ENABLE_STRICT_OBJC_MSGSEND = YES; 155 | ENABLE_TESTABILITY = YES; 156 | GCC_C_LANGUAGE_STANDARD = gnu99; 157 | GCC_DYNAMIC_NO_PIC = NO; 158 | GCC_NO_COMMON_BLOCKS = YES; 159 | GCC_OPTIMIZATION_LEVEL = 0; 160 | GCC_PREPROCESSOR_DEFINITIONS = ( 161 | "DEBUG=1", 162 | "$(inherited)", 163 | ); 164 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 165 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 166 | GCC_WARN_UNDECLARED_SELECTOR = YES; 167 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 168 | GCC_WARN_UNUSED_FUNCTION = YES; 169 | GCC_WARN_UNUSED_VARIABLE = YES; 170 | MACOSX_DEPLOYMENT_TARGET = 10.11; 171 | MTL_ENABLE_DEBUG_INFO = YES; 172 | ONLY_ACTIVE_ARCH = YES; 173 | SDKROOT = macosx; 174 | }; 175 | name = Debug; 176 | }; 177 | 84BFEEA91C70827F009C9544 /* Release */ = { 178 | isa = XCBuildConfiguration; 179 | buildSettings = { 180 | ALWAYS_SEARCH_USER_PATHS = NO; 181 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 182 | CLANG_CXX_LIBRARY = "libc++"; 183 | CLANG_ENABLE_MODULES = YES; 184 | CLANG_ENABLE_OBJC_ARC = YES; 185 | CLANG_WARN_BOOL_CONVERSION = YES; 186 | CLANG_WARN_CONSTANT_CONVERSION = YES; 187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 188 | CLANG_WARN_EMPTY_BODY = YES; 189 | CLANG_WARN_ENUM_CONVERSION = YES; 190 | CLANG_WARN_INT_CONVERSION = YES; 191 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 192 | CLANG_WARN_UNREACHABLE_CODE = YES; 193 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 194 | CODE_SIGN_IDENTITY = "-"; 195 | COPY_PHASE_STRIP = NO; 196 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 197 | ENABLE_NS_ASSERTIONS = NO; 198 | ENABLE_STRICT_OBJC_MSGSEND = YES; 199 | GCC_C_LANGUAGE_STANDARD = gnu99; 200 | GCC_NO_COMMON_BLOCKS = YES; 201 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 202 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 203 | GCC_WARN_UNDECLARED_SELECTOR = YES; 204 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 205 | GCC_WARN_UNUSED_FUNCTION = YES; 206 | GCC_WARN_UNUSED_VARIABLE = YES; 207 | MACOSX_DEPLOYMENT_TARGET = 10.11; 208 | MTL_ENABLE_DEBUG_INFO = NO; 209 | SDKROOT = macosx; 210 | }; 211 | name = Release; 212 | }; 213 | 84BFEEAB1C70827F009C9544 /* Debug */ = { 214 | isa = XCBuildConfiguration; 215 | buildSettings = { 216 | PRODUCT_NAME = "$(TARGET_NAME)"; 217 | }; 218 | name = Debug; 219 | }; 220 | 84BFEEAC1C70827F009C9544 /* Release */ = { 221 | isa = XCBuildConfiguration; 222 | buildSettings = { 223 | PRODUCT_NAME = "$(TARGET_NAME)"; 224 | }; 225 | name = Release; 226 | }; 227 | /* End XCBuildConfiguration section */ 228 | 229 | /* Begin XCConfigurationList section */ 230 | 84BFEE9E1C70827F009C9544 /* Build configuration list for PBXProject "api" */ = { 231 | isa = XCConfigurationList; 232 | buildConfigurations = ( 233 | 84BFEEA81C70827F009C9544 /* Debug */, 234 | 84BFEEA91C70827F009C9544 /* Release */, 235 | ); 236 | defaultConfigurationIsVisible = 0; 237 | defaultConfigurationName = Release; 238 | }; 239 | 84BFEEAA1C70827F009C9544 /* Build configuration list for PBXNativeTarget "api" */ = { 240 | isa = XCConfigurationList; 241 | buildConfigurations = ( 242 | 84BFEEAB1C70827F009C9544 /* Debug */, 243 | 84BFEEAC1C70827F009C9544 /* Release */, 244 | ); 245 | defaultConfigurationIsVisible = 0; 246 | }; 247 | /* End XCConfigurationList section */ 248 | }; 249 | rootObject = 84BFEE9B1C70827F009C9544 /* Project object */; 250 | } 251 | -------------------------------------------------------------------------------- /rest/objectivec/api/ExmoApiHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // BtceApiHandler.h 3 | // 4 | // 5 | 6 | #import 7 | 8 | @interface ExmoApiHandler : NSObject 9 | 10 | @property (nonatomic,strong) NSString *api_key; 11 | @property (nonatomic,strong) NSString *secret_key; 12 | 13 | - (NSData *)getResponseFromServerForPost:(NSDictionary *)postDictionary method:(NSString *)methodName; 14 | 15 | - (NSData *)getResponseFromPublicServerUrl:(NSString *)urlString; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /rest/objectivec/api/ExmoApiHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExmoApiHandler.m 3 | // 4 | // 5 | 6 | #import "ExmoApiHandler.h" 7 | #import 8 | 9 | @implementation ExmoApiHandler 10 | 11 | @synthesize api_key; 12 | @synthesize secret_key; 13 | 14 | - (id)init { 15 | self = [super init]; 16 | if (self) { 17 | [self setupInitialValues]; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)setupInitialValues { 23 | api_key = @"your_key"; 24 | secret_key = @"your_secret"; 25 | } 26 | 27 | // 28 | 29 | - (NSData *)getResponseFromServerForPost:(NSDictionary *)postDictionary method:(NSString *)methodName{ 30 | NSString *post; 31 | int i = 0; 32 | for (NSString *key in [postDictionary allKeys]) { 33 | NSString *value = [postDictionary objectForKey:key]; 34 | if (i==0) 35 | post = [NSString stringWithFormat:@"%@=%@", key, value]; 36 | else 37 | post = [NSString stringWithFormat:@"%@&%@=%@", post, key, value]; 38 | i++; 39 | } 40 | post = [NSString stringWithFormat:@"%@&nonce=%@", post, getNonce()]; 41 | 42 | 43 | NSString *signedPost = hmacForKeyAndData(secret_key, post); 44 | NSString *url = [@"https://api.exmo.com/v1/" stringByAppendingString:methodName]; 45 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] 46 | initWithURL: 47 | [NSURL URLWithString: url]]; 48 | [request setHTTPMethod:@"POST"]; 49 | [request setValue:api_key forHTTPHeaderField:@"Key"]; 50 | [request setValue:signedPost forHTTPHeaderField:@"Sign"]; 51 | [request setHTTPBody:[post dataUsingEncoding: NSUTF8StringEncoding]]; 52 | 53 | NSURLResponse *theResponse = NULL; 54 | NSError *theError = NULL; 55 | NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&theError]; 56 | return responseData; 57 | } 58 | 59 | - (NSData *)getResponseFromPublicServerUrl:(NSString *)urlString { 60 | NSURL *url = [NSURL URLWithString:urlString]; 61 | NSData *data = [NSData dataWithContentsOfURL:url]; 62 | return data; 63 | } 64 | 65 | NSString *getNonce() { 66 | NSTimeInterval timeStamp = [[NSDate date] timeIntervalSinceDate:[NSDate dateWithString:@"2012-04-18 00:00:01 +0600"]]; 67 | int currentNonce = [NSNumber numberWithDouble: timeStamp].intValue; 68 | NSString *nonceString = [NSString stringWithFormat:@"%i",currentNonce]; 69 | return nonceString; 70 | } 71 | 72 | NSString *hmacForKeyAndData(NSString *key, NSString *data) { 73 | const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding]; 74 | const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding]; 75 | unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH]; 76 | CCHmac(kCCHmacAlgSHA512, cKey, strlen(cKey), cData, strlen(cData), cHMAC); 77 | NSMutableString *hashString = [NSMutableString stringWithCapacity:sizeof(cHMAC) * 2]; 78 | for (int i = 0; i < sizeof(cHMAC); i++) { 79 | [hashString appendFormat:@"%02x", cHMAC[i]]; 80 | } 81 | return hashString; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /rest/objectivec/api/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // api 4 | // 5 | // Created by Admin on 14.02.16. 6 | // Copyright © 2016 exmo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ExmoApiHandler.h" 11 | 12 | ExmoApiHandler *apiHandler; 13 | 14 | int main(int argc, const char * argv[]) { 15 | @autoreleasepool { 16 | apiHandler = [[ExmoApiHandler alloc] init]; 17 | 18 | NSMutableDictionary *post = [[NSMutableDictionary alloc] init]; 19 | 20 | NSLog(@"user_info:\n"); 21 | NSData *response = [apiHandler getResponseFromServerForPost:post method:@"user_info"]; 22 | NSLog(@"%@\n",[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]); 23 | 24 | NSLog(@"user_cancelled_orders:\n"); 25 | NSMutableDictionary *post2 = [[NSMutableDictionary alloc] init]; 26 | 27 | [post2 setObject:@"limit" forKey:@"100"]; 28 | [post2 setObject:@"offset" forKey:@"0"]; 29 | 30 | NSData *response2 = [apiHandler getResponseFromServerForPost:post2 method:@"user_cancelled_orders"]; 31 | NSLog(@"%@\n",[[NSString alloc] initWithData:response2 encoding:NSUTF8StringEncoding]); 32 | int key; 33 | // insert code here... 34 | 35 | while (1) 36 | { 37 | NSLog(@"Press any key (q to quit):"); 38 | fpurge(stdin); 39 | key = getc(stdin); 40 | if (key == 'q') 41 | { 42 | break; 43 | } 44 | 45 | NSLog(@"\nYou pressed: %c", (char)key); 46 | } 47 | 48 | } 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /rest/php/exmo.php: -------------------------------------------------------------------------------- 1 | function api_query($api_name, array $req = array()) 2 | { 3 | $mt = explode(' ', microtime()); 4 | $NONCE = $mt[1] . substr($mt[0], 2, 6); 5 | 6 | // API settings 7 | $key = ""; //TODO replace with your api key from profile page 8 | $secret = ""; //TODO replace with your api secret from profile page 9 | 10 | $url = "https://api.exmo.com/v1/$api_name"; 11 | 12 | $req['nonce'] = $NONCE; 13 | 14 | // generate the POST data string 15 | $post_data = http_build_query($req, '', '&'); 16 | 17 | $sign = hash_hmac('sha512', $post_data, $secret); 18 | 19 | // generate the extra headers 20 | $headers = array( 21 | 'Sign: ' . $sign, 22 | 'Key: ' . $key, 23 | ); 24 | 25 | // our curl handle (initialize if required) 26 | static $ch = null; 27 | if (is_null($ch)) { 28 | $ch = curl_init(); 29 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 30 | curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; PHP client; ' . php_uname('s') . '; PHP/' . phpversion() . ')'); 31 | } 32 | curl_setopt($ch, CURLOPT_URL, $url); 33 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); 34 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 35 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); 36 | 37 | // run the query 38 | $res = curl_exec($ch); 39 | if ($res === false) throw new Exception('Could not get reply: ' . curl_error($ch)); 40 | 41 | $dec = json_decode($res, true); 42 | if ($dec === null) 43 | throw new Exception('Invalid data received, please make sure connection is working and requested API exists'); 44 | 45 | return $dec; 46 | } 47 | -------------------------------------------------------------------------------- /rest/python/exmo2.py: -------------------------------------------------------------------------------- 1 | import httplib 2 | import urllib 3 | import json 4 | import hashlib 5 | import hmac 6 | import time 7 | 8 | api_key = "your_key" 9 | api_secret = "your_secret" 10 | 11 | nonce = int(round(time.time()*1000)) 12 | 13 | params = {"nonce": nonce} 14 | params = urllib.urlencode(params) 15 | 16 | H = hmac.new(api_secret, digestmod=hashlib.sha512) 17 | H.update(params) 18 | sign = H.hexdigest() 19 | 20 | headers = {"Content-type": "application/x-www-form-urlencoded", 21 | "Key":api_key, 22 | "Sign":sign} 23 | conn = httplib.HTTPSConnection("api.exmo.com") 24 | conn.request("POST", "/v1/user_info", params, headers) 25 | response = conn.getresponse() 26 | 27 | print response.status, response.reason 28 | print json.load(response) 29 | 30 | conn.close() -------------------------------------------------------------------------------- /rest/python/exmo3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import http.client 3 | import urllib 4 | import json 5 | import hashlib 6 | import hmac 7 | import time 8 | 9 | class ExmoAPI: 10 | def __init__(self, API_KEY, API_SECRET, API_URL = 'api.exmo.com', API_VERSION = 'v1'): 11 | self.API_URL = API_URL 12 | self.API_VERSION = API_VERSION 13 | self.API_KEY = API_KEY 14 | self.API_SECRET = bytes(API_SECRET, encoding='utf-8') 15 | 16 | def sha512(self, data): 17 | H = hmac.new(key = self.API_SECRET, digestmod = hashlib.sha512) 18 | H.update(data.encode('utf-8')) 19 | return H.hexdigest() 20 | 21 | def api_query(self, api_method, params = {}): 22 | params['nonce'] = int(round(time.time() * 1000)) 23 | params = urllib.parse.urlencode(params) 24 | 25 | sign = self.sha512(params) 26 | headers = { 27 | "Content-type": "application/x-www-form-urlencoded", 28 | "Key": self.API_KEY, 29 | "Sign": sign 30 | } 31 | conn = http.client.HTTPSConnection(self.API_URL) 32 | conn.request("POST", "/" + self.API_VERSION + "/" + api_method, params, headers) 33 | response = conn.getresponse().read() 34 | 35 | conn.close() 36 | 37 | try: 38 | obj = json.loads(response.decode('utf-8')) 39 | if 'error' in obj and obj['error']: 40 | print(obj['error']) 41 | raise sys.exit() 42 | return obj 43 | except json.decoder.JSONDecodeError: 44 | print('Error while parsing response:', response) 45 | raise sys.exit() 46 | 47 | # Example 48 | ExmoAPI_instance = ExmoAPI('YOUR API KEY', 'YOUR API SECRET') 49 | print(ExmoAPI_instance.api_query('user_info')) 50 | 51 | # print(ExmoAPI_instance.api_query('order_create', { 52 | # "pair": 'DOGE_BTC', 53 | # "quantity":100, 54 | # "price":0.00001, 55 | # "type":"sell" 56 | # }) 57 | # ) 58 | 59 | # print(ExmoAPI_instance.api_query('order_cancel', { 60 | # "order_id": 3063120293 61 | # }) 62 | # ) 63 | -------------------------------------------------------------------------------- /rest/r/exmo.r: -------------------------------------------------------------------------------- 1 | library(httr) 2 | library(jsonlite) 3 | library(nanotime) 4 | library(digest) 5 | 6 | api_url <- "https://api.exmo.com/v1/" 7 | api_key <- "K-..." 8 | api_secret <- "S-..." 9 | 10 | api_query <- function(method, key, secret, params = list()){ 11 | 12 | nonce <- (as.numeric(as.POSIXct(Sys.time()))*10000000)%/%1 13 | 14 | params <- c(params, nonce = nonce) 15 | 16 | data <- paste(names(params), params, sep = "=", collapse = "&") 17 | 18 | signature <- 19 | hmac( 20 | key = secret, 21 | object = data, 22 | algo = "sha512" 23 | ) 24 | 25 | response <- POST( 26 | url = paste(api_url, method, sep = ""), 27 | accept_json(), 28 | add_headers( 29 | "Key" = key, 30 | "Sign" = signature, 31 | "Content-Type" = "application/x-www-form-urlencoded" 32 | ), 33 | body = data 34 | ) 35 | 36 | exmo_content <- content(response, as="text") 37 | json_exmo_content <- fromJSON(exmo_content) 38 | return(json_exmo_content) 39 | } 40 | 41 | params <- list( 42 | pair = "ETH_BTC", 43 | limit = 100, 44 | offset = 0 45 | 46 | ) 47 | 48 | api_query("user_trades", api_key, api_secret, params) 49 | api_query("user_info", api_key, api_secret) -------------------------------------------------------------------------------- /rest/ruby/exmo.rb: -------------------------------------------------------------------------------- 1 | require 'net/https' 2 | require "json" 3 | 4 | module Exmo 5 | class Error < StandardError 6 | attr_reader :object 7 | 8 | def initialize(object) 9 | @object = object 10 | end 11 | end 12 | 13 | class API 14 | class << self 15 | KEY = "K-b123456" # TODO replace with your api key from settings page 16 | SECRET = "S-e123456" # TODO replace with your api secret from settings page 17 | 18 | def api_query(method, params = nil) 19 | raise ArgumentError unless method.is_a?(String) || method.is_a?(Symbol) 20 | 21 | params = {} if params.nil? 22 | params['nonce'] = nonce 23 | 24 | uri = URI.parse(['https://api.exmo.com/v1', method].join('/')) 25 | 26 | post_data = URI.encode_www_form(params) 27 | 28 | digest = OpenSSL::Digest.new('sha512') 29 | sign = OpenSSL::HMAC.hexdigest(digest, SECRET, post_data) 30 | 31 | headers = { 32 | 'Sign' => sign, 33 | 'Key' => KEY 34 | } 35 | 36 | req = Net::HTTP::Post.new(uri.path, headers) 37 | req.body = post_data 38 | http = Net::HTTP.new(uri.host, uri.port) 39 | http.use_ssl = true if uri.scheme == 'https' 40 | response = http.request(req) 41 | 42 | unless response.code == '200' 43 | raise Exmo::Error.new(__method__), ['http error:', response.code].join(' ') 44 | end 45 | 46 | result = response.body.to_s 47 | 48 | unless result.is_a?(String) && valid_json?(result) 49 | raise Exmo::Error.new(__method__), "Invalid json" 50 | end 51 | 52 | JSON.load result 53 | end 54 | 55 | private 56 | 57 | def valid_json?(json) 58 | JSON.parse(json) 59 | true 60 | rescue 61 | false 62 | end 63 | 64 | def nonce 65 | Time.now.strftime("%s%6N") 66 | end 67 | end 68 | end 69 | end 70 | 71 | 72 | 73 | puts "%s" % Exmo::API.api_query('user_info').inspect 74 | 75 | 76 | 77 | # puts "%s" % Exmo::API.api_query('trades', pair: 'DOGE_USD').inspect 78 | # puts "%s" % Exmo::API.api_query('order_trades', order_id: 12345).inspect 79 | # puts "%s" % Exmo::API.api_query('order_book', pair: 'DOGE_USD', limit: 10).inspect 80 | # puts "%s" % Exmo::API.api_query('ticker').inspect 81 | # puts "%s" % Exmo::API.api_query('pair_settings').inspect 82 | # puts "%s" % Exmo::API.api_query('currency').inspect 83 | 84 | # order_params = { 85 | # pair: 'DOGE_USD', 86 | # quantity: 100, 87 | # price: 99, 88 | # type: 'sell' 89 | # } 90 | # puts "%s" % Exmo::API.api_query('order_create', order_params).inspect 91 | 92 | # puts "%s" % Exmo::API.api_query('user_open_orders').inspect 93 | # puts "%s" % Exmo::API.api_query('order_cancel', order_id: 12345).inspect 94 | # puts "%s" % Exmo::API.api_query('user_cancelled_orders', limit: 100, offset: 0).inspect 95 | 96 | # puts "%s" % Exmo::API.api_query('order_trades', order_id: 12345).inspect 97 | # puts "%s" % Exmo::API.api_query('required_amount', pair: 'BTC_USD', quantity: 10).inspect 98 | # puts "%s" % Exmo::API.api_query('deposit_address').inspect 99 | -------------------------------------------------------------------------------- /rest/swift/api-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // api-Bridging-Header.h 3 | // api 4 | // 5 | // Created by Admin on 14.02.16. 6 | // Copyright © 2016 exmo. All rights reserved. 7 | // 8 | 9 | #ifndef api_Bridging_Header_h 10 | #define api_Bridging_Header_h 11 | 12 | #import 13 | 14 | #endif /* api_Bridging_Header_h */ 15 | -------------------------------------------------------------------------------- /rest/swift/api.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 84BFEEBD1C70911E009C9544 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BFEEBC1C70911E009C9544 /* main.swift */; }; 11 | 84BFEEC41C709199009C9544 /* ApiHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BFEEC31C709199009C9544 /* ApiHandler.swift */; }; 12 | 84BFEEC61C7095A6009C9544 /* NSData+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BFEEC51C7095A6009C9544 /* NSData+Extensions.swift */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | 84BFEEB71C70911E009C9544 /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 84BFEEB91C70911E009C9544 /* api */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = api; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 84BFEEBC1C70911E009C9544 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 30 | 84BFEEC31C709199009C9544 /* ApiHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiHandler.swift; sourceTree = ""; }; 31 | 84BFEEC51C7095A6009C9544 /* NSData+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSData+Extensions.swift"; sourceTree = ""; }; 32 | 84BFEEC71C709A1A009C9544 /* api-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "api-Bridging-Header.h"; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 84BFEEB61C70911E009C9544 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 84BFEEB01C70911E009C9544 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 84BFEEC71C709A1A009C9544 /* api-Bridging-Header.h */, 50 | 84BFEEBB1C70911E009C9544 /* api */, 51 | 84BFEEBA1C70911E009C9544 /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 84BFEEBA1C70911E009C9544 /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 84BFEEB91C70911E009C9544 /* api */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 84BFEEBB1C70911E009C9544 /* api */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 84BFEEBC1C70911E009C9544 /* main.swift */, 67 | 84BFEEC31C709199009C9544 /* ApiHandler.swift */, 68 | 84BFEEC51C7095A6009C9544 /* NSData+Extensions.swift */, 69 | ); 70 | path = api; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | 84BFEEB81C70911E009C9544 /* api */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = 84BFEEC01C70911E009C9544 /* Build configuration list for PBXNativeTarget "api" */; 79 | buildPhases = ( 80 | 84BFEEB51C70911E009C9544 /* Sources */, 81 | 84BFEEB61C70911E009C9544 /* Frameworks */, 82 | 84BFEEB71C70911E009C9544 /* CopyFiles */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = api; 89 | productName = api; 90 | productReference = 84BFEEB91C70911E009C9544 /* api */; 91 | productType = "com.apple.product-type.tool"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 84BFEEB11C70911E009C9544 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastSwiftUpdateCheck = 0720; 100 | LastUpgradeCheck = 0720; 101 | ORGANIZATIONNAME = exmo; 102 | TargetAttributes = { 103 | 84BFEEB81C70911E009C9544 = { 104 | CreatedOnToolsVersion = 7.2.1; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = 84BFEEB41C70911E009C9544 /* Build configuration list for PBXProject "api" */; 109 | compatibilityVersion = "Xcode 3.2"; 110 | developmentRegion = English; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | ); 115 | mainGroup = 84BFEEB01C70911E009C9544; 116 | productRefGroup = 84BFEEBA1C70911E009C9544 /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | 84BFEEB81C70911E009C9544 /* api */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXSourcesBuildPhase section */ 126 | 84BFEEB51C70911E009C9544 /* Sources */ = { 127 | isa = PBXSourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 84BFEEBD1C70911E009C9544 /* main.swift in Sources */, 131 | 84BFEEC41C709199009C9544 /* ApiHandler.swift in Sources */, 132 | 84BFEEC61C7095A6009C9544 /* NSData+Extensions.swift in Sources */, 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | /* End PBXSourcesBuildPhase section */ 137 | 138 | /* Begin XCBuildConfiguration section */ 139 | 84BFEEBE1C70911E009C9544 /* Debug */ = { 140 | isa = XCBuildConfiguration; 141 | buildSettings = { 142 | ALWAYS_SEARCH_USER_PATHS = NO; 143 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 144 | CLANG_CXX_LIBRARY = "libc++"; 145 | CLANG_ENABLE_MODULES = YES; 146 | CLANG_ENABLE_OBJC_ARC = YES; 147 | CLANG_WARN_BOOL_CONVERSION = YES; 148 | CLANG_WARN_CONSTANT_CONVERSION = YES; 149 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 150 | CLANG_WARN_EMPTY_BODY = YES; 151 | CLANG_WARN_ENUM_CONVERSION = YES; 152 | CLANG_WARN_INT_CONVERSION = YES; 153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 154 | CLANG_WARN_UNREACHABLE_CODE = YES; 155 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 156 | CODE_SIGN_IDENTITY = "-"; 157 | COPY_PHASE_STRIP = NO; 158 | DEBUG_INFORMATION_FORMAT = dwarf; 159 | ENABLE_STRICT_OBJC_MSGSEND = YES; 160 | ENABLE_TESTABILITY = YES; 161 | GCC_C_LANGUAGE_STANDARD = gnu99; 162 | GCC_DYNAMIC_NO_PIC = NO; 163 | GCC_NO_COMMON_BLOCKS = YES; 164 | GCC_OPTIMIZATION_LEVEL = 0; 165 | GCC_PREPROCESSOR_DEFINITIONS = ( 166 | "DEBUG=1", 167 | "$(inherited)", 168 | ); 169 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 170 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 171 | GCC_WARN_UNDECLARED_SELECTOR = YES; 172 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 173 | GCC_WARN_UNUSED_FUNCTION = YES; 174 | GCC_WARN_UNUSED_VARIABLE = YES; 175 | MACOSX_DEPLOYMENT_TARGET = 10.11; 176 | MTL_ENABLE_DEBUG_INFO = YES; 177 | ONLY_ACTIVE_ARCH = YES; 178 | SDKROOT = macosx; 179 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 180 | }; 181 | name = Debug; 182 | }; 183 | 84BFEEBF1C70911E009C9544 /* Release */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 188 | CLANG_CXX_LIBRARY = "libc++"; 189 | CLANG_ENABLE_MODULES = YES; 190 | CLANG_ENABLE_OBJC_ARC = YES; 191 | CLANG_WARN_BOOL_CONVERSION = YES; 192 | CLANG_WARN_CONSTANT_CONVERSION = YES; 193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 194 | CLANG_WARN_EMPTY_BODY = YES; 195 | CLANG_WARN_ENUM_CONVERSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_UNREACHABLE_CODE = YES; 199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 200 | CODE_SIGN_IDENTITY = "-"; 201 | COPY_PHASE_STRIP = NO; 202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 203 | ENABLE_NS_ASSERTIONS = NO; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | GCC_C_LANGUAGE_STANDARD = gnu99; 206 | GCC_NO_COMMON_BLOCKS = YES; 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | MACOSX_DEPLOYMENT_TARGET = 10.11; 214 | MTL_ENABLE_DEBUG_INFO = NO; 215 | SDKROOT = macosx; 216 | }; 217 | name = Release; 218 | }; 219 | 84BFEEC11C70911E009C9544 /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | PRODUCT_NAME = "$(TARGET_NAME)"; 223 | SWIFT_OBJC_BRIDGING_HEADER = "api-Bridging-Header.h"; 224 | }; 225 | name = Debug; 226 | }; 227 | 84BFEEC21C70911E009C9544 /* Release */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | PRODUCT_NAME = "$(TARGET_NAME)"; 231 | SWIFT_OBJC_BRIDGING_HEADER = "api-Bridging-Header.h"; 232 | }; 233 | name = Release; 234 | }; 235 | /* End XCBuildConfiguration section */ 236 | 237 | /* Begin XCConfigurationList section */ 238 | 84BFEEB41C70911E009C9544 /* Build configuration list for PBXProject "api" */ = { 239 | isa = XCConfigurationList; 240 | buildConfigurations = ( 241 | 84BFEEBE1C70911E009C9544 /* Debug */, 242 | 84BFEEBF1C70911E009C9544 /* Release */, 243 | ); 244 | defaultConfigurationIsVisible = 0; 245 | defaultConfigurationName = Release; 246 | }; 247 | 84BFEEC01C70911E009C9544 /* Build configuration list for PBXNativeTarget "api" */ = { 248 | isa = XCConfigurationList; 249 | buildConfigurations = ( 250 | 84BFEEC11C70911E009C9544 /* Debug */, 251 | 84BFEEC21C70911E009C9544 /* Release */, 252 | ); 253 | defaultConfigurationIsVisible = 0; 254 | }; 255 | /* End XCConfigurationList section */ 256 | }; 257 | rootObject = 84BFEEB11C70911E009C9544 /* Project object */; 258 | } 259 | -------------------------------------------------------------------------------- /rest/swift/api/ApiHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiHandler.swift 3 | // api 4 | // 5 | // Created by Admin on 14.02.16. 6 | // Copyright © 2016 exmo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | public class ApiHandler: NSObject { 13 | private enum Config: String{ 14 | case API_URL = "https://api.exmo.com/v1/" 15 | case API_KEY = "your_key" 16 | case API_SECRET = "your_secret" 17 | case NONCE = "Nonce" 18 | } 19 | 20 | 21 | 22 | private var api_key: String! 23 | private var secret_key: String! 24 | 25 | 26 | 27 | private var nonce: Int{ 28 | get{ 29 | let value = NSUserDefaults.standardUserDefaults().integerForKey(Config.NONCE.rawValue) 30 | return (value == 0) ? calculateInitialNonce(): value 31 | } 32 | set{ 33 | NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: Config.NONCE.rawValue) 34 | } 35 | } 36 | 37 | override init() { 38 | super.init() 39 | setupInitValues() 40 | } 41 | 42 | internal func setupInitValues(){ 43 | self.api_key = Config.API_KEY.rawValue 44 | self.secret_key = Config.API_SECRET.rawValue 45 | } 46 | 47 | public func userInfo()-> NSData?{ 48 | NSLog("start user_info") 49 | let post = NSMutableDictionary() 50 | return self.getResponseFromServerForPost(post, method:"user_info") 51 | } 52 | 53 | public func canceledOrders(limit:Int, offset:Int)-> NSData?{ 54 | NSLog("start user_cancelled_orders") 55 | let post = NSMutableDictionary() 56 | post.setObject(limit, forKey: "limit") 57 | post.setObject(offset, forKey: "offset") 58 | return self.getResponseFromServerForPost(post, method:"user_cancelled_orders") 59 | } 60 | 61 | private func getResponseFromServerForPost(postDictionary: NSDictionary, method: String) -> NSData?{ 62 | var post: String! 63 | var i: Int = 0 64 | for (key, value) in postDictionary { 65 | if (i==0){ 66 | post = "\(key)=\(value)" 67 | }else{ 68 | post = "\(post)&\(key)=\(value)" 69 | } 70 | i++; 71 | } 72 | post = "\(post)&nonce=\(nonce)" 73 | nonce++ 74 | print(post) 75 | let signedPost = hmacForKeyAndData(secret_key, data: post) as String 76 | let request = NSMutableURLRequest(URL: NSURL(string: Config.API_URL.rawValue as String + method)!) 77 | request.HTTPMethod = "POST" 78 | request.setValue(api_key, forHTTPHeaderField: "Key") 79 | request.setValue(signedPost, forHTTPHeaderField: "Sign") 80 | 81 | let requestBodyData = (post as NSString).dataUsingEncoding(NSUTF8StringEncoding) 82 | request.HTTPBody = requestBodyData 83 | 84 | var error: NSError? 85 | let theResponse: AutoreleasingUnsafeMutablePointer =nil 86 | let responseData = try! NSURLConnection.sendSynchronousRequest(request, returningResponse: theResponse) as NSData! 87 | if (error != nil){ 88 | return nil 89 | } 90 | 91 | return responseData 92 | } 93 | 94 | private func calculateInitialNonce()->Int{ 95 | let dataFormat = NSDateFormatter() 96 | dataFormat.dateFormat = "yyyy-MM-dd HH:mm:ss xxxx" 97 | let timeStamp = NSDate().timeIntervalSinceDate(dataFormat.dateFromString("2012-04-18 00:00:03 +0600")!) 98 | let currentNonce = NSNumber(double: timeStamp) as Int 99 | return currentNonce 100 | } 101 | 102 | 103 | private func hmacForKeyAndData(key: NSString, data: NSString)->NSString{ 104 | let cKey = key.cStringUsingEncoding(NSASCIIStringEncoding) 105 | let cData = data.cStringUsingEncoding(NSASCIIStringEncoding) 106 | let _ = [CUnsignedChar](count: Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0) 107 | let digestLen = Int(CC_SHA512_DIGEST_LENGTH) 108 | let result = UnsafeMutablePointer.alloc(digestLen) 109 | print("CCHmac") 110 | CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA512), cKey, Int(key.length), cData, Int(data.length), result) 111 | let hashString = NSMutableString(capacity: Int(CC_SHA512_DIGEST_LENGTH)) 112 | for i in 0.. NSDictionary?{ 12 | if let json = try! NSJSONSerialization.JSONObjectWithData(self, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary{ 13 | return json 14 | }else{ 15 | return nil 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /rest/swift/api/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // api 4 | // 5 | // Created by Admin on 14.02.16. 6 | // Copyright © 2016 exmo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let api = ApiHandler() 12 | let result = api.userInfo() 13 | let dataString = String(data: result!, encoding: NSUTF8StringEncoding) 14 | NSLog(dataString!) 15 | 16 | let result2 = api.canceledOrders(100, offset: 0) 17 | let dataString2 = String(data: result2!, encoding: NSUTF8StringEncoding) 18 | NSLog(dataString2!) -------------------------------------------------------------------------------- /rest/с++/README.md: -------------------------------------------------------------------------------- 1 | Dependencies: CURL, OpenSSL 2 | 3 | -------------------------------------------------------------------------------- /rest/с++/connection.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_CONNECTION_HPP 2 | #define HTTP_CONNECTION_HPP 3 | 4 | #include "curl_object.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using json_data = std::string; 11 | using headers_t = std::map; 12 | 13 | namespace http { 14 | 15 | class request { 16 | public: 17 | request() {} 18 | virtual ~request() {} 19 | 20 | virtual curl_slist* prepare(const headers_t&) const = 0; 21 | }; 22 | 23 | class post : public request { 24 | public: 25 | post() {} 26 | virtual ~post() {} 27 | 28 | virtual curl_slist* prepare(const headers_t& headers) const { 29 | curl_slist* list = nullptr; 30 | for (auto h : headers) { 31 | std::string s{ std::string(h.first + ": " + h.second) }; 32 | list = curl_slist_append(list, s.c_str()); 33 | } 34 | return list; 35 | } 36 | }; 37 | 38 | class connection { 39 | public: 40 | connection(const std::string url = "") 41 | {} 42 | ~connection() {} 43 | 44 | void request(const std::string url, const request& r, const std::string& params = "", const headers_t& headers = headers_t()) { 45 | recv_data_.clear(); 46 | recv_header_.clear(); 47 | //curl_easy_setopt(curl_object::get_instance(), CURLOPT_SSL_VERIFYHOST, 0); 48 | //curl_easy_setopt(curl_object::get_instance(), CURLOPT_SSL_VERIFYPEER, 0); 49 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_URL, url.c_str()); 50 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_WRITEDATA, &recv_data_); 51 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_HEADERDATA, &recv_header_); 52 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_WRITEFUNCTION, write_received_data_to_string); 53 | 54 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_POSTFIELDS, params.c_str()); 55 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_POSTFIELDSIZE, params.size()); 56 | 57 | curl_slist* list = r.prepare(headers); 58 | curl_easy_setopt(curl_object::get_instance(), CURLOPT_HTTPHEADER, list); 59 | 60 | CURLcode rescode = curl_easy_perform(curl_object::get_instance()); 61 | if (rescode != CURLE_OK) { 62 | std::string msg{ "!> curl_easy_perform failed with error: " }; 63 | msg += curl_easy_strerror(rescode); 64 | throw std::runtime_error(msg); 65 | } 66 | } 67 | 68 | json_data get_response() { 69 | return recv_data_; 70 | } 71 | 72 | private: 73 | static std::size_t write_received_data_to_string(char *ptr, std::size_t size, std::size_t nmemb, void *buffer) { 74 | if (buffer) { 75 | std::string *tstr = reinterpret_cast(buffer); 76 | tstr->append(ptr, size * nmemb); 77 | } 78 | return size * nmemb; 79 | } 80 | 81 | private: 82 | std::string recv_data_; 83 | std::string recv_header_; 84 | }; 85 | 86 | } 87 | 88 | #endif -------------------------------------------------------------------------------- /rest/с++/curl_object.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CURL_OBJECT_HPP 2 | #define CURL_OBJECT_HPP 3 | 4 | #include 5 | 6 | class curl_object { 7 | static CURL* ch; 8 | curl_object() { } 9 | public: 10 | ~curl_object() { 11 | curl_easy_cleanup(ch); 12 | ch = nullptr; 13 | } 14 | 15 | static CURL* get_instance() { 16 | if (ch == nullptr) { 17 | ch = curl_easy_init(); 18 | } 19 | return ch; 20 | } 21 | }; 22 | 23 | CURL* curl_object::ch = nullptr; 24 | 25 | #endif // CURL_OBJECT_HPP -------------------------------------------------------------------------------- /rest/с++/exmo.cpp: -------------------------------------------------------------------------------- 1 | #include "exmo_api.hpp" 2 | #include 3 | 4 | int main() 5 | { 6 | exmo_api api("your_key", "your_secret"); 7 | 8 | json_data response = api.call("user_info", ""); 9 | std::clog << ">> user_info: " << response << "\n\n"; 10 | 11 | response = api.call("user_cancelled_orders", api.build({ "limit=100", "offset=0" })); 12 | std::clog << ">> user_cancelled_orders: " << response << "\n\n"; 13 | 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /rest/с++/exmo_api.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EXMO_API_HPP 2 | #define EXMO_API_HPP 3 | 4 | #include "hmac_sha512.hpp" 5 | #include "connection.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | class exmo_api { 13 | public: 14 | exmo_api(const std::string& key, const std::string& secret) 15 | { 16 | key_ = key; 17 | secret_ = secret; 18 | url_ = "api.exmo.com/v1/"; 19 | connection_ = http::connection(); 20 | nonce_ = ::time(nullptr); 21 | } 22 | 23 | json_data call(const std::string& method, const std::string& p) { 24 | std::string params = "nonce="; 25 | nonce_++; 26 | params.append(std::to_string(nonce_)); 27 | 28 | if (p.size() != 0) { 29 | params.append("&"); 30 | } 31 | params.append(p); 32 | 33 | std::map headers; 34 | headers["Content-type"] = "application/x-www-form-urlencoded"; 35 | headers["Key"] = key_; 36 | headers["Sign"] = this->signature(params); 37 | 38 | connection_.request(url_ + method, http::post(), params, headers); 39 | return connection_.get_response(); 40 | } 41 | 42 | std::string build(std::vector params_) { 43 | 44 | std::string params = ""; 45 | for (auto i : params_) { 46 | params += "&" + i; 47 | } 48 | return params; 49 | } 50 | 51 | private: 52 | std::string signature(const std::string& params) { 53 | HMAC_SHA512 hmac_sha512(secret_, params); 54 | return hmac_sha512.hex_digest(); 55 | } 56 | 57 | private: 58 | unsigned long nonce_; 59 | std::string key_; 60 | std::string secret_; 61 | std::string url_; 62 | http::connection connection_; 63 | }; 64 | #endif -------------------------------------------------------------------------------- /rest/с++/hmac_sha512.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HMAC_SHA512_HPP 2 | #define HMAC_SHA512_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class HMAC_SHA512 { 9 | public: 10 | HMAC_SHA512(const std::string& key, const std::string& msg) { 11 | HMAC_CTX ctx; 12 | HMAC_CTX_init(&ctx); 13 | 14 | // Set HMAC key. 15 | HMAC_Init_ex(&ctx, reinterpret_cast(key.c_str()), key.size(), EVP_sha512(), NULL); 16 | 17 | // May be called repeatedly to insert all your data. 18 | HMAC_Update(&ctx, reinterpret_cast(msg.c_str()), msg.size()); 19 | 20 | // Finish HMAC computation and fetch result. 21 | unsigned char* result = new unsigned char[129]; 22 | unsigned int result_len = 129; 23 | HMAC_Final(&ctx, result, &result_len); 24 | for (int i = 0; i < result_len; i++) { 25 | digest_.push_back(int(result[i])); 26 | } 27 | HMAC_CTX_cleanup(&ctx); 28 | } 29 | 30 | std::string hex_digest() { 31 | std::string str; 32 | const char hex_chars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 33 | for (auto i : digest_) { 34 | const char byte = i; 35 | str.push_back(hex_chars[(byte & 0xF0) >> 4]); 36 | str.push_back(hex_chars[(byte & 0x0F) >> 0]); 37 | } 38 | return str; 39 | } 40 | 41 | private: 42 | std::vector digest_; 43 | }; 44 | 45 | #endif // HMAC_SHA512_HPP -------------------------------------------------------------------------------- /ws/.net/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.WebSockets; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Security.Cryptography; 8 | 9 | namespace WebSocketClient 10 | { 11 | class Program 12 | { 13 | private static readonly string Connection = "wss://ws-api.exmo.com:443/v1/public"; 14 | private static readonly string ConnectionPrivate = "wss://ws-api.exmo.com:443/v1/private"; 15 | 16 | static async Task Main(string[] args) 17 | { 18 | TestPublic(); 19 | TestPrivate(); 20 | 21 | Console.ReadLine(); 22 | } 23 | 24 | static void TestPublic() 25 | { 26 | Task.Factory.StartNew(async () => 27 | { 28 | using (var socket = new ClientWebSocket()) 29 | try 30 | { 31 | await socket.ConnectAsync(new Uri(Connection), CancellationToken.None); 32 | await SendPublic(socket); 33 | await Receive(socket); 34 | 35 | } 36 | catch (Exception ex) 37 | { 38 | Console.WriteLine($"ERROR - {ex.Message}"); 39 | } 40 | 41 | }); 42 | } 43 | 44 | static void TestPrivate() 45 | { 46 | Task.Factory.StartNew(async () => 47 | { 48 | using (var socket = new ClientWebSocket()) 49 | try 50 | { 51 | await socket.ConnectAsync(new Uri(ConnectionPrivate), CancellationToken.None); 52 | await SendPrivate(socket); 53 | await Receive(socket); 54 | 55 | } 56 | catch (Exception ex) 57 | { 58 | Console.WriteLine($"ERROR - {ex.Message}"); 59 | } 60 | 61 | }); 62 | } 63 | 64 | static async Task SendPublic(ClientWebSocket socket) => await Send(socket, "{\"id\":1,\"method\":\"subscribe\",\"topics\":[\"spot/trades:BTC_USD\",\"spot/ticker:LTC_USD\"]}"); 65 | static async Task SendPrivate(ClientWebSocket socket) { 66 | var apiKey = "K-"; 67 | var secretKey = "S-"; 68 | var nonce = (long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; 69 | var sign = ""; 70 | using (HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes(secretKey))) 71 | { 72 | byte[] b = hmac.ComputeHash(Encoding.UTF8.GetBytes(apiKey + Convert.ToString(nonce))); 73 | sign = Convert.ToBase64String(b); 74 | } 75 | 76 | var loginCommand = string.Format("{{\"id\":1,\"method\":\"login\",\"api_key\":\"{0}\",\"sign\":\"{1}\",\"nonce\":{2:D}}}", apiKey, sign, nonce); 77 | await Send(socket, loginCommand); 78 | 79 | var subCommand = "{ \"id\":2,\"method\":\"subscribe\",\"topics\":[\"spot/orders\",\"spot/user_trades\"]}"; 80 | await Send(socket, subCommand); 81 | return; 82 | } 83 | 84 | static async Task Send(ClientWebSocket socket, string data) => 85 | await socket.SendAsync(Encoding.UTF8.GetBytes(data), WebSocketMessageType.Text, true, CancellationToken.None); 86 | 87 | static async Task Receive(ClientWebSocket socket) 88 | { 89 | var buffer = new ArraySegment(new byte[2048]); 90 | do 91 | { 92 | WebSocketReceiveResult result; 93 | using (var ms = new MemoryStream()) 94 | { 95 | do 96 | { 97 | result = await socket.ReceiveAsync(buffer, CancellationToken.None); 98 | ms.Write(buffer.Array, buffer.Offset, result.Count); 99 | } while (!result.EndOfMessage); 100 | 101 | if (result.MessageType == WebSocketMessageType.Close) 102 | break; 103 | 104 | ms.Seek(0, SeekOrigin.Begin); 105 | using (var reader = new StreamReader(ms, Encoding.UTF8)) 106 | Console.WriteLine(await reader.ReadToEndAsync()); 107 | } 108 | } while (true); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /ws/.net/WebSocketClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | _net 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /ws/.net/exmo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSocketClient", "WebSocketClient.csproj", "{072E516B-1D22-4ED9-9AD0-3178A27626D7}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {072E516B-1D22-4ED9-9AD0-3178A27626D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {072E516B-1D22-4ED9-9AD0-3178A27626D7}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {072E516B-1D22-4ED9-9AD0-3178A27626D7}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {072E516B-1D22-4ED9-9AD0-3178A27626D7}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /ws/golang/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha512" 6 | "encoding/base64" 7 | "fmt" 8 | "log" 9 | "os" 10 | "os/signal" 11 | "strconv" 12 | "time" 13 | 14 | "github.com/gorilla/websocket" 15 | ) 16 | 17 | func main() { 18 | publicApiUsageExample() 19 | } 20 | 21 | func publicApiUsageExample() { 22 | startExmoClient("wss://ws-api.exmo.com:443/v1/public", []string{`{"id":1,"method":"subscribe","topics":["spot/trades:BTC_USD","spot/ticker:LTC_USD"]}`}) 23 | } 24 | 25 | func privateApiUsageExample() { 26 | apiKey := "" 27 | secretKey := "" 28 | nonce := time.Now().In(time.UTC).Unix() 29 | 30 | mac := hmac.New(sha512.New, []byte(secretKey)) 31 | _, _ = mac.Write([]byte(apiKey + strconv.Itoa(int(nonce)))) 32 | sign := base64.StdEncoding.EncodeToString(mac.Sum(nil)) 33 | loginCommand := fmt.Sprintf(`{"id":1,"method":"login","api_key":"%s","sign":"%s","nonce":%d}`, apiKey, sign, nonce) 34 | 35 | startExmoClient("wss://ws-api.exmo.com:443/v1/private", []string{loginCommand, `{"id":2,"method":"subscribe","topics":["spot/orders","spot/user_trades"]}`}) 36 | } 37 | 38 | func startExmoClient(url string, initMessages []string) { 39 | interrupt := make(chan os.Signal, 1) 40 | signal.Notify(interrupt, os.Interrupt) 41 | 42 | c, _, err := websocket.DefaultDialer.Dial(url, nil) 43 | if err != nil { 44 | log.Fatal("dial:", err) 45 | } 46 | defer c.Close() 47 | 48 | done := make(chan struct{}) 49 | 50 | go func() { 51 | defer close(done) 52 | for { 53 | _, message, err := c.ReadMessage() 54 | if err != nil { 55 | log.Println("read:", err) 56 | return 57 | } 58 | log.Printf("recv: %s", message) 59 | } 60 | }() 61 | 62 | for _, initMessage := range initMessages { 63 | err = c.WriteMessage(websocket.TextMessage, []byte(initMessage)) 64 | if err != nil { 65 | log.Printf("fail to write init message: %v", err) 66 | return 67 | } 68 | log.Printf("sent: %s", initMessage) 69 | } 70 | 71 | select { 72 | case <-interrupt: 73 | log.Println("interrupt") 74 | 75 | // Cleanly close the connection by sending a close message and then 76 | // waiting (with timeout) for the server to close the connection. 77 | err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) 78 | if err != nil { 79 | log.Println("write close:", err) 80 | return 81 | } 82 | select { 83 | case <-done: 84 | case <-time.After(time.Second): 85 | } 86 | case <-done: 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ws/java/README.md: -------------------------------------------------------------------------------- 1 | To run this example use IDE like Intellij / Eclipse / NetBeans 2 | 3 | You can also follow these CLI commands: 4 | ``` 5 | mvn clean compile assembly:single 6 | java -jar target/ws-client-example-1.0-SNAPSHOT-jar-with-dependencies.jar 7 | ``` 8 | -------------------------------------------------------------------------------- /ws/java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.exmo 8 | ws-client-example 9 | 1.0-SNAPSHOT 10 | 11 | ws-client-example 12 | https://github.com/exmo-dev/exmo_api_lib 13 | 14 | 15 | UTF-8 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | 22 | org.java-websocket 23 | Java-WebSocket 24 | 1.5.1 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.11 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | maven-assembly-plugin 39 | 40 | 41 | 42 | com.exmo.App 43 | 44 | 45 | 46 | jar-with-dependencies 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ws/java/src/main/java/com/exmo/App.java: -------------------------------------------------------------------------------- 1 | package com.exmo; 2 | 3 | import java.net.URI; 4 | import java.net.URISyntaxException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.util.Base64; 7 | 8 | import org.java_websocket.client.WebSocketClient; 9 | import org.java_websocket.handshake.ServerHandshake; 10 | 11 | import javax.crypto.Mac; 12 | import javax.crypto.spec.SecretKeySpec; 13 | 14 | public class App { 15 | private static final String HMAC_SHA512 = "HmacSHA512"; 16 | 17 | public static void main(String[] args) { 18 | publicApiUsageExample(); 19 | } 20 | 21 | public static void publicApiUsageExample() { 22 | WebSocketClient ws = null; 23 | try { 24 | ws = startExmoClient("wss://ws-api.exmo.com:443/v1/public", new String[]{"{\"id\":1,\"method\":\"subscribe\",\"topics\":[\"spot/trades:BTC_USD\",\"spot/ticker:LTC_USD\"]}"}); 25 | // wait for pressing any key 26 | System.in.read(); 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } finally { 30 | if (ws != null) { 31 | try { 32 | ws.close(); 33 | } catch (Exception e) { 34 | 35 | } 36 | } 37 | } 38 | } 39 | 40 | public static void privateApiUsageExample() { 41 | WebSocketClient ws = null; 42 | try { 43 | final String apiKey = ""; 44 | final String secretKey = ""; 45 | final long nonce = System.currentTimeMillis(); 46 | 47 | SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA512); 48 | Mac mac = Mac.getInstance(HMAC_SHA512); 49 | mac.init(keySpec); 50 | byte[] macData = mac.doFinal((apiKey + nonce).getBytes(StandardCharsets.UTF_8)); 51 | String sign = Base64.getEncoder().encodeToString(macData); 52 | 53 | String loginCommand = String.format("{\"id\":1,\"method\":\"login\",\"api_key\":\"%s\",\"sign\":\"%s\",\"nonce\":%d}", apiKey, sign, nonce); 54 | 55 | ws = startExmoClient("wss://ws-api.exmo.com:443/v1/private", new String[]{ 56 | loginCommand, 57 | "{\"id\":2,\"method\":\"subscribe\",\"topics\":[\"spot/orders\",\"spot/user_trades\"]}" 58 | }); 59 | 60 | // wait for pressing any key 61 | System.in.read(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } finally { 65 | if (ws != null) { 66 | try { 67 | ws.close(); 68 | } catch (Exception e) { 69 | 70 | } 71 | } 72 | } 73 | } 74 | 75 | public static WebSocketClient startExmoClient(String url, String[] initMessages) throws URISyntaxException, InterruptedException { 76 | WebSocketClient ws = newWsClient(url); 77 | ws.connectBlocking(); 78 | for (String message : initMessages) { 79 | ws.send(message); 80 | System.out.println("sent: " + message); 81 | } 82 | return ws; 83 | } 84 | 85 | private static WebSocketClient newWsClient(String url) throws URISyntaxException { 86 | return new WebSocketClient(new URI(url)) { 87 | 88 | @Override 89 | public void onOpen(ServerHandshake serverHandshake) { 90 | } 91 | 92 | @Override 93 | public void onMessage(String s) { 94 | System.out.println("read: " + s); 95 | } 96 | 97 | @Override 98 | public void onClose(int i, String s, boolean b) { 99 | System.out.println("close: " + i + " " + s); 100 | } 101 | 102 | @Override 103 | public void onError(Exception e) { 104 | System.out.println("error: " + e.toString()); 105 | } 106 | }; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /ws/js/WebSocketsPolyfill.js: -------------------------------------------------------------------------------- 1 | const WebSocketLib = require('ws'); 2 | 3 | function createWSConnection(props) { 4 | return typeof WebSocket !== 'undefined' ? new WebSocket(props) : new WebSocketLib(props); 5 | } 6 | 7 | module.exports = { 8 | createWSConnection, 9 | }; 10 | -------------------------------------------------------------------------------- /ws/js/client.js: -------------------------------------------------------------------------------- 1 | const CryptoJS = require('crypto-js'); 2 | const { createWSConnection } = require('./WebSocketsPolyfill'); 3 | const EXMO_WS_BASE_URL = `wss://ws-api.exmo.com:443/v1`; 4 | const EXMO_WS_PUBLIC_URL = `${EXMO_WS_BASE_URL}/public`; 5 | const EXMO_WS_PRIVATE_URL = `${EXMO_WS_BASE_URL}/private`; 6 | 7 | function createExmoWSConnection(url, messages) { 8 | const socket = createWSConnection(url); 9 | const onMessage = (event) => console.log('message:', event); 10 | const onClose = (event) => console.log('connection closed:', event); 11 | const onError = (error) => console.log('connection error:', error); 12 | const onInitialize = () => { 13 | console.log('connection opened'); 14 | 15 | for (let message of messages) { 16 | console.log('sending:', message); 17 | socket.send(message); 18 | } 19 | }; 20 | 21 | socket.on('open', onInitialize); 22 | socket.on("message", onMessage); 23 | socket.on('close', onClose); 24 | socket.on('error', onError); 25 | } 26 | 27 | function connectExmoWSPublicApi() { 28 | const data = [ 29 | '{"id":1,"method":"subscribe","topics":["spot/trades:BTC_USD","spot/ticker:LTC_USD"]}', 30 | ]; 31 | 32 | createExmoWSConnection(EXMO_WS_PUBLIC_URL, data); 33 | } 34 | 35 | function connectExmoWSPrivateApi(apiKey) { 36 | const secretKey = ''; 37 | const nonce = Date.now(); 38 | const sign = CryptoJS.HmacSHA512(apiKey + nonce, secretKey).toString(CryptoJS.enc.Base64); 39 | const data = [ 40 | `{"id":1,"method":"login","api_key":"${apiKey}","sign":"${sign}","nonce":${nonce}}`, 41 | '{"id":2,"method":"subscribe","topics":["spot/orders","spot/user_trades"]}', 42 | ]; 43 | 44 | createExmoWSConnection(EXMO_WS_PRIVATE_URL, data); 45 | } 46 | 47 | module.exports = { 48 | connectExmoWSPublicApi, 49 | connectExmoWSPrivateApi, 50 | }; 51 | -------------------------------------------------------------------------------- /ws/js/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exmo_api_lib_demo", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "crypto-js": { 8 | "version": "4.0.0", 9 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", 10 | "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==" 11 | }, 12 | "ws": { 13 | "version": "7.3.0", 14 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", 15 | "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ws/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exmo_api_lib_demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "client.js", 6 | "scripts": { 7 | "start": "node ./start.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "exmo", 11 | "license": "ISC", 12 | "dependencies": { 13 | "crypto-js": "^4.0.0", 14 | "ws": "^7.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ws/js/start.js: -------------------------------------------------------------------------------- 1 | const { connectExmoWSPublicApi } = require('./client'); 2 | 3 | connectExmoWSPublicApi(); 4 | -------------------------------------------------------------------------------- /ws/python2/client.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import hmac 4 | import time 5 | 6 | from websocket import create_connection 7 | from websocket._exceptions import WebSocketConnectionClosedException 8 | 9 | 10 | def start_exmo_client(url, init_messages): 11 | ws = create_connection(url, connection='Connection: Upgrade') 12 | 13 | try: 14 | for init_message in init_messages: 15 | ws.send(init_message) 16 | print "sent:", init_message 17 | while True: 18 | print "read:", ws.recv() 19 | 20 | except WebSocketConnectionClosedException as ex: 21 | print "connection closed", ex 22 | except KeyboardInterrupt: 23 | pass 24 | finally: 25 | ws.close() 26 | 27 | 28 | def public_api_usage_example(): 29 | start_exmo_client( 30 | "wss://ws-api.exmo.com:443/v1/public", 31 | ["""{"id":1,"method":"subscribe","topics":["spot/trades:BTC_USD","spot/ticker:LTC_USD"]}"""], 32 | ) 33 | 34 | 35 | def private_api_usage_example(): 36 | api_key = "" 37 | secret = "" 38 | nonce = str(int(time.time())) 39 | 40 | sign = hmac.new(secret.encode('utf8'), (api_key + nonce).encode('utf8'), hashlib.sha512).digest() 41 | sign = base64.b64encode(sign).decode('utf8') 42 | login_command = '{"id":1,"method":"login","api_key":"%s","sign":"%s","nonce":%s}' % (api_key, sign, nonce) 43 | 44 | start_exmo_client( 45 | "wss://ws-api.exmo.com:443/v1/private", 46 | [login_command, """{"id":2,"method":"subscribe","topics":["spot/orders","spot/user_trades"]}"""], 47 | ) 48 | 49 | 50 | if __name__ == "__main__": 51 | private_api_usage_example() -------------------------------------------------------------------------------- /ws/python2/requirements.txt: -------------------------------------------------------------------------------- 1 | websocket-client==0.57.0 -------------------------------------------------------------------------------- /ws/python3/client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import base64 3 | import hashlib 4 | import hmac 5 | import time 6 | 7 | import websockets 8 | 9 | 10 | def start_exmo_client(url, init_messages): 11 | async def ws_loop(): 12 | async with websockets.connect(url) as websocket: 13 | for init_message in init_messages: 14 | await websocket.send(init_message) 15 | print("sent:", init_message) 16 | while True: 17 | print("read:", await websocket.recv()) 18 | 19 | try: 20 | asyncio.get_event_loop().run_until_complete(ws_loop()) 21 | except websockets.exceptions.ConnectionClosed as ex: 22 | print("connection closed", ex) 23 | except KeyboardInterrupt: 24 | pass 25 | 26 | 27 | def public_api_usage_example(): 28 | start_exmo_client( 29 | "wss://ws-api.exmo.com:443/v1/public", 30 | ["""{"id":1,"method":"subscribe","topics":["spot/trades:BTC_USD","spot/ticker:LTC_USD"]}"""], 31 | ) 32 | 33 | 34 | def private_api_usage_example(): 35 | api_key = "" 36 | secret = "" 37 | nonce = str(int(time.time())) 38 | 39 | sign = hmac.new(secret.encode('utf8'), (api_key + nonce).encode('utf8'), hashlib.sha512).digest() 40 | sign = base64.b64encode(sign).decode('utf8') 41 | login_command = '{"id":1,"method":"login","api_key":"%s","sign":"%s","nonce":%s}' % (api_key, sign, nonce) 42 | 43 | start_exmo_client( 44 | "wss://ws-api.exmo.com:443/v1/private", 45 | [login_command, """{"id":2,"method":"subscribe","topics":["spot/orders","spot/user_trades"]}"""], 46 | ) 47 | 48 | 49 | if __name__ == "__main__": 50 | public_api_usage_example() 51 | -------------------------------------------------------------------------------- /ws/python3/requirements.txt: -------------------------------------------------------------------------------- 1 | websockets==8.1 --------------------------------------------------------------------------------