To start working with us, the partner needs to contact us via email. Further communication can be carried out in the agreed communication channel.
There, we have to agree on the points of cooperation, i.e., which pairs, fees we will work with etc. Іmportant - clarify the type of fees (fixed/floating) for which we will work for a specific pair.
The partner needs to exchange keys with our service and implement signature formation and verification of our signature in the code: the partner, on their side, generate a pair of keys (private and public keys) and send us a public key signed with their private key.
And we, in turn, also generate a pair of keys for the partner and send them our public key signed with our private key.
With the help of these public keys, a check is made that the request came from specific recipients.
After the configuration by our team, the partner can call the API and fully work with crypto processing.
SWAPPLE API uses RSA-1024 PKCS1 keypair to secure requests.
First of all you need to generate RSA-1024 keys and send us your public key. We will send you our public key so that you can verify our response.
How to sign requests
You need to generate SHA512 hash sign based on request body.
Authentication parameters are transmitted in Headers:
public-key
signature
timestamp - current time in milliseconds (used in sign generation)
An example of generating a signature.
import requests
import binascii
import time
import collections
from decimal import Decimal
from Crypto.Hash import SHA512
from Crypto.Signature.pkcs1_15 import PKCS115_SigScheme
from Crypto.PublicKey import RSA
private_key = b'-----BEGIN RSA PRIVATE KEY-----\nMIICXAIagdfgadfJBdBhOb5mkijXoA/VeGqiIPVjdWth25uI\n369z434y8VMYAcmQ9a44vdutn2Zwq9dVtM35cK+etbtVo/GSDGSDGSD\n8QOIlZn+18+Hd4kWESCiycllJBsjwsNZK3PdnM7s2c6UmFuqesY3MDD/4QIDAQAB\nAoGagdfgadfgadfgadfgadfgdK//VD6lxaKvM5RNz8\nAOTc3WFefN4AnULh1T4vxq1zaPwDWhXj+y3SZjUZs6un8dZ5P6c7vqlILThy7ihJ\nCS04GRTzzybPHDU5c6eMlrm55jBpub3ajyhGgSyWgynY+RECQQC5Y/HSI18kEDWp\nerdbdc6vPs1TkhULAZOzoQXXZhDoEZjF9bRhEcbUHdiKs4+QLMjEP2CVhG/AtZ1r\nibw2A6XdAkEA8I/YzDUr1ns9jI9ZZrshblCdYu3F0Bs01Uwub36ro6LK3aVv8unz\ndxMuJQcsk+hQwnheXGsNi2/qWyC5aW2L1QJAMhEWxpx83j1ucJXOnOmk5tj8FJy4\nCB/l5rYO4MwUtsfBzXx8uVZWrwRcbaDunY4qri07hUWd9JpXqCorZR75FQJADycA\nYNx4hmn81n2xA+eFk41AXJrdet471bOWuS8hYI1AefWRt5tE2ps6rNpm1GotrBIo\n32le0QmbmWHWS+26gQJBALYrgSaKJEG4avc4fwCUF75CIZfiCUWYRSPndyQljXf0\n0XOhVNGDqFbQCtlggQ4Z+zxDoVM5LGZqw0XEIjd+/wc=\n-----END RSA PRIVATE KEY-----'
public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCuNd6JS7plbbj3XvsJBdBhOb5mkijXoA/VeGqiIPVjdWth25uI369z434y8VMYAcmQ9a44vdutn2Zwq9dagdfgadfgadfbcvxn+18+Hd4kWESCiycllJBsjwsNZK3PdnM7s2c6UmFuqesY3MDD/4QIDAQAB" # no comments
SWAPPLE_ROOT_API_URL = "https://api.swapple.org/api/v3"
def request_body_to_str_line(body, timestamp):
body = collections.OrderedDict(sorted(body.items()))
line = ""
for value, keys in body.items():
if keys:
try:
line += value.lower() + str(Decimal(str(keys)))
except Exception:
line += value.lower() + str(keys).lower()
line += "timestamp" + timestamp
return line
def request_sign(body, timestamp):
line = request_body_to_str_line(body, timestamp)
new_hash = SHA512.new(line.encode())
signer = PKCS115_SigScheme(RSA.importKey(private_key))
sign = signer.sign(new_hash)
return binascii.hexlify(sign).decode("utf-8")
def get_authorization_headers(body):
timestamp = str(int(time.time()))
signature = request_sign(body, timestamp)
return {
"public-key": public_key,
"signature": signature,
"timestamp": timestamp,
}
body = {"from": "CARDUAH", "fromAmount": "5000", "to": "USDT","toNetwork": "TRX"}
requests.post(
f"{SWAPPLE_ROOT_API_URL}/estimate-amount",
json=body,
headers=get_authorization_headers(body)
)
const crypto = require('crypto');
const fs = require('fs');
const privateKey = fs.readFileSync('private_key.pem');
const timestamp = '1678367651';
function requestBodyToStrLine(body, timestamp) {
const sortedBody = Object.entries(body).sort();
let line = '';
for (const [key, value] of sortedBody) {
if (value) {
line += key.toLowerCase() + value.toString().toLowerCase();
}
}
line += 'timestamp' + timestamp;
console.log(line);
return line;
}
function requestSign(body, timestamp) {
const line = Buffer.from(requestBodyToStrLine(body, timestamp));
// Створення об'єкту для генерації підпису
const signer = crypto.createSign('RSA-SHA512');
// Додавання даних для підпису
signer.update(line);
// Генерація підпису
const signature = signer.sign(privateKey, 'hex');
return signature
}
const body = {
from: 'CARDUAH',
to: 'USDT',
fromAmount: 20,
rateType: 'FLOATING',
fromNetwork: null,
toNetwork: 'TRC20'
};
console.log(requestSign(body, timestamp));
package main
import (
"bytes"
"crypto"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"sort"
"strconv"
"strings"
"time"
)
var privateKey *rsa.PrivateKey
var publicKey string
func initKeys(privateKeyPath, publicKeyPath string) {
// Read private key from file
privateKeyData, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
panic(err)
}
privateKeyBlock, _ := pem.Decode(privateKeyData)
if privateKeyBlock == nil {
panic("failed to parse private key")
}
privateKey, err = x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
if err != nil {
panic(err)
}
}
func generateTimestamp() string {
return strconv.FormatInt(time.Now().Unix(), 10)
}
func generateSignature(body map[string]interface{}, timestamp string) string {
// Create sorted string from the request body and timestamp
var keys []string
for k := range body {
keys = append(keys, k)
}
sort.Strings(keys)
var messageParts []string
for _, k := range keys {
v := body[k]
var valueString string
switch v := v.(type) {
case float64:
valueString = strconv.FormatFloat(v, 'f', -1, 64)
case string:
valueString = v
}
messageParts = append(messageParts, strings.ToLower(k)+valueString)
}
messageParts = append(messageParts, "timestamp"+timestamp)
message := strings.ToLower(strings.Join(messageParts, ""))
// Calculate SHA-512 hash
hash := sha512.New()
hash.Write([]byte(message))
hashed := hash.Sum(nil)
// Sign the hash with the private key
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA512, hashed)
if err != nil {
panic(err)
}
// Encode the signature to hexadecimal format
signatureHex := fmt.Sprintf("%x", signature)
return signatureHex
}
func main() {
// Paths to private and public key files
privateKeyPath := "private_key.pem"
publicKeyPath := "public_key.pem"
// Initialize keys
initKeys(privateKeyPath, publicKeyPath)
// Sample request body
body := map[string]interface{}{
"fromAmount": "100",
"from": "CARDUAH",
"to": "ETH",
}
// Generate timestamp and signature
timestamp := "1"
signature := generateSignature(body, timestamp)
// Create HTTP headers
headers := map[string]string{
"public-key": publicKey,
"signature": signature,
"timestamp": timestamp,
}
fmt.Println(headers)
// Send POST request
url := "https://api.swapple.org/api/v3/estimate-amount"
bodyJSON, err := json.Marshal(body)
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyJSON))
if err != nil {
panic(err)
}
for key, value := range headers {
req.Header.Set(key, value)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Handle abex response
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println("Status Code:", resp.StatusCode)
fmt.Println("Response Body:", string(responseBody))
}
* For a more detailed understanding of request signing, please read the information below:
To make a request, the partner needs two things - a public key and a private key.
Next, the partner needs to form a request to us according to our documentation, and this request needs to be signed with the private key.
The following is a method that will turn a request:
def request_body_to_str_line(body, timestamp):
body = collections.OrderedDict(sorted(body.items()))
line = ""
for value, keys in body.items():
if keys:
try:
line += value.lower() + str(Decimal(str(keys)))
except Exception:
line += value.lower() + str(keys).lower()
line += "timestamp" + timestamp
return line
Here, body and timestamp are transferred, then the previous function is called and the next four lines form the signature:
hash - we take it from the string we received
create a signer object - the one who has to sign based on our private key
then we sign the hash we got in the line above
and return it as a string - this decodes the signature into a string.
And that's the result of the signature that needs to be added to the request.
Next, we generate a request that has a timestamp (i.e. the current time), a signature that calls the previous function and we transfer the public key, the signature we generated, and the timestamp we received and based on which we generated the signature: