diff --git a/.env.example b/.env.example index 839cb70..8845fb8 100644 --- a/.env.example +++ b/.env.example @@ -77,4 +77,13 @@ BACKPACK_SANDBOX=false BACKPACK_SYMBOL=BTC_USD_PERP # Enable verbose adapter logging: set to "1" or "true" -BACKPACK_DEBUG=false \ No newline at end of file +BACKPACK_DEBUG=false + +# EdgeX exchange configuration +EDGEX_ACCOUNT_ID= +EDGEX_PRIVATE_KEY= +# EDGEX_POSITION_ID= # Defaults to EDGEX_ACCOUNT_ID when omitted +# EDGEX_BASE_URL=https://pro.edgex.exchange +# EDGEX_WS_PUBLIC_URL=wss://quote.edgex.exchange +# EDGEX_WS_PRIVATE_URL=wss://quote.edgex.exchange +# EDGEX_ORDER_TTL_MS=21600000 # Order expiration window (ms), default 6 hours diff --git a/README.md b/README.md index 93dfa99..128247f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,10 @@ 基于 Bun 的 Aster 永续合约量化终端,内置趋势跟随(SMA30)与做市策略,支持快速恢复、实时行情订阅与日志追踪。 * [Aster 30% 手续费优惠注册链接](https://www.asterdex.com/zh-CN/referral/4665f3) +* [Binance 手续费优惠注册链接](https://www.binance.com/join?ref=KNKCA9XC) * [GRVT 手续费优惠注册链接](https://grvt.io/exchange/sign-up?ref=sea) +* [Backpack 手续费优惠注册链接](https://backpack.exchange/join/41d60948-2a75-4d16-b7e9-523df74f2904) +* [edgex 手续费优惠注册链接](https://pro.edgex.exchange/referral/BULL) ## 文档索引 - [English README](README_en.md) diff --git a/bun.lock b/bun.lock index 2c7db02..61fb41f 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "name": "ritmex-bot", "dependencies": { "@grvt/client": "^1.6.4", + "@starkware-industries/starkware-crypto-utils": "^0.2.1", "axios": "^1.12.2", "ccxt": "^4.5.5", "dotenv": "^17.2.2", @@ -135,6 +136,10 @@ "@scure/bip39": ["@scure/bip39@1.3.0", "", { "dependencies": { "@noble/hashes": "~1.4.0", "@scure/base": "~1.1.6" } }, "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ=="], + "@starkware-industries/starkware-crypto-utils": ["@starkware-industries/starkware-crypto-utils@0.2.1", "", { "dependencies": { "assert": "^2.0.0", "bip39": "^3.0.4", "bn.js": "^4.12.0", "brorand": "^1.1.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", "elliptic": "^6.5.4", "enc-utils": "^3.0.0", "ethereumjs-wallet": "^1.0.2", "hash.js": "^1.1.7", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "js-sha3": "^0.8.0", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1", "stream-browserify": "^3.0.0" } }, "sha512-rA5O9b53zaoBOQwQxBd0cbumFbQoBm9NH/vfu+o0Cq3oouEbNPALneLlLjOmFEId2/WOJ5ecC64rFLI/PwuIPQ=="], + + "@types/bn.js": ["@types/bn.js@5.2.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q=="], + "@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="], "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], @@ -149,8 +154,12 @@ "@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="], + "@types/pbkdf2": ["@types/pbkdf2@3.1.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew=="], + "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], + "@types/secp256k1": ["@types/secp256k1@4.0.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-Rcvjl6vARGAKRO6jHeKMatGrvOMGrR/AR11N1x2LqintPCyDZ7NBhrh238Z2VZc7aM7KIwnFpFQ7fnfK4H/9Qw=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], @@ -165,26 +174,68 @@ "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + "aes-js": ["aes-js@3.1.2", "", {}, "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="], + "ansi-escapes": ["ansi-escapes@7.1.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g=="], "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "asn1.js": ["asn1.js@4.10.1", "", { "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw=="], + + "assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="], + "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bip39": ["bip39@3.1.0", "", { "dependencies": { "@noble/hashes": "^1.2.0" } }, "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A=="], + + "blakejs": ["blakejs@1.2.1", "", {}, "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ=="], + + "bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], + + "brorand": ["brorand@1.1.0", "", {}, "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="], + + "browserify-aes": ["browserify-aes@1.2.0", "", { "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", "evp_bytestokey": "^1.0.3", "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA=="], + + "browserify-cipher": ["browserify-cipher@1.0.1", "", { "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", "evp_bytestokey": "^1.0.0" } }, "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w=="], + + "browserify-des": ["browserify-des@1.0.2", "", { "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A=="], + + "browserify-rsa": ["browserify-rsa@4.1.1", "", { "dependencies": { "bn.js": "^5.2.1", "randombytes": "^2.1.0", "safe-buffer": "^5.2.1" } }, "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ=="], + + "browserify-sign": ["browserify-sign@4.2.5", "", { "dependencies": { "bn.js": "^5.2.2", "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "elliptic": "^6.6.1", "inherits": "^2.0.4", "parse-asn1": "^5.1.9", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" } }, "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw=="], + + "bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "bs58check": ["bs58check@2.1.2", "", { "dependencies": { "bs58": "^4.0.0", "create-hash": "^1.1.0", "safe-buffer": "^5.1.2" } }, "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "buffer-xor": ["buffer-xor@1.0.3", "", {}, "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="], + "bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "ccxt": ["ccxt@4.5.5", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-AyhwTFLkx4sO985ImIOfumEBox7AHD/iqk5tPGICObUSZG6wTXg0aRzU8Hjz974aCMG4msFwLk3A/iXPKAU4wA=="], "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], @@ -193,6 +244,8 @@ "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], + "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], "cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], @@ -205,20 +258,42 @@ "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + + "create-ecdh": ["create-ecdh@4.0.4", "", { "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A=="], + + "create-hash": ["create-hash@1.2.0", "", { "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", "md5.js": "^1.3.4", "ripemd160": "^2.0.1", "sha.js": "^2.4.0" } }, "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg=="], + + "create-hmac": ["create-hmac@1.1.7", "", { "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", "inherits": "^2.0.1", "ripemd160": "^2.0.0", "safe-buffer": "^5.0.1", "sha.js": "^2.4.8" } }, "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg=="], + + "crypto-browserify": ["crypto-browserify@3.12.1", "", { "dependencies": { "browserify-cipher": "^1.0.1", "browserify-sign": "^4.2.3", "create-ecdh": "^4.0.4", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "diffie-hellman": "^5.0.3", "hash-base": "~3.0.4", "inherits": "^2.0.4", "pbkdf2": "^3.1.2", "public-encrypt": "^4.0.3", "randombytes": "^2.1.0", "randomfill": "^1.0.4" } }, "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "des.js": ["des.js@1.1.0", "", { "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg=="], + + "diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="], + "dotenv": ["dotenv@17.2.2", "", {}, "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="], + "emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], + "enc-utils": ["enc-utils@3.0.0", "", { "dependencies": { "is-typedarray": "1.0.0", "typedarray-to-buffer": "3.1.5" } }, "sha512-e57t/Z2HzWOLwOp7DZcV0VMEY8t7ptWwsxyp6kM2b2zrk6JqIpXxzkruHAMiBsy5wg9jp/183GdiRXCvBtzsYg=="], + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -241,18 +316,28 @@ "ethereum-cryptography": ["ethereum-cryptography@2.2.1", "", { "dependencies": { "@noble/curves": "1.4.2", "@noble/hashes": "1.4.0", "@scure/bip32": "1.4.0", "@scure/bip39": "1.3.0" } }, "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg=="], + "ethereumjs-util": ["ethereumjs-util@7.1.5", "", { "dependencies": { "@types/bn.js": "^5.1.0", "bn.js": "^5.1.2", "create-hash": "^1.1.2", "ethereum-cryptography": "^0.1.3", "rlp": "^2.2.4" } }, "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg=="], + + "ethereumjs-wallet": ["ethereumjs-wallet@1.0.2", "", { "dependencies": { "aes-js": "^3.1.2", "bs58check": "^2.1.2", "ethereum-cryptography": "^0.1.3", "ethereumjs-util": "^7.1.2", "randombytes": "^2.1.0", "scrypt-js": "^3.0.1", "utf8": "^3.0.0", "uuid": "^8.3.2" } }, "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA=="], + + "evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="], + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -261,64 +346,148 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + "hash-base": ["hash-base@3.0.5", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" } }, "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg=="], + + "hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="], + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hmac-drbg": ["hmac-drbg@1.0.1", "", { "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "ink": ["ink@6.3.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.0", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.32.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-3wGwITGrzL6rkWsi2gEKzgwdafGn4ZYd3u4oRp+sOPvfoxEHlnoB5Vnk9Uy5dMRUhDOqF3hqr4rLQ4lEzBc2sQ=="], + "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + "is-in-ci": ["is-in-ci@2.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="], + "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="], + + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "js-sha3": ["js-sha3@0.8.0", "", {}, "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="], + "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "keccak": ["keccak@3.0.4", "", { "dependencies": { "node-addon-api": "^2.0.0", "node-gyp-build": "^4.2.0", "readable-stream": "^3.6.0" } }, "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "md5.js": ["md5.js@1.3.5", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg=="], + + "miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" }, "bin": { "miller-rabin": "bin/miller-rabin" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="], + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], + + "minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "node-addon-api": ["node-addon-api@2.0.2", "", {}, "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="], + + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + + "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "parse-asn1": ["parse-asn1@5.1.9", "", { "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", "pbkdf2": "^3.1.5", "safe-buffer": "^5.2.1" } }, "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg=="], + "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + "pbkdf2": ["pbkdf2@3.1.5", "", { "dependencies": { "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", "sha.js": "^2.4.12", "to-buffer": "^1.2.1" } }, "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="], + + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], + + "randomfill": ["randomfill@1.0.4", "", { "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" } }, "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw=="], + "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], "react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="], + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], + "ripemd160": ["ripemd160@2.0.3", "", { "dependencies": { "hash-base": "^3.1.2", "inherits": "^2.0.4" } }, "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA=="], + + "rlp": ["rlp@2.2.7", "", { "dependencies": { "bn.js": "^5.2.0" }, "bin": { "rlp": "bin/rlp" } }, "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ=="], + "rollup": ["rollup@4.52.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.0", "@rollup/rollup-android-arm64": "4.52.0", "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-darwin-x64": "4.52.0", "@rollup/rollup-freebsd-arm64": "4.52.0", "@rollup/rollup-freebsd-x64": "4.52.0", "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", "@rollup/rollup-linux-arm-musleabihf": "4.52.0", "@rollup/rollup-linux-arm64-gnu": "4.52.0", "@rollup/rollup-linux-arm64-musl": "4.52.0", "@rollup/rollup-linux-loong64-gnu": "4.52.0", "@rollup/rollup-linux-ppc64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-musl": "4.52.0", "@rollup/rollup-linux-s390x-gnu": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-linux-x64-musl": "4.52.0", "@rollup/rollup-openharmony-arm64": "4.52.0", "@rollup/rollup-win32-arm64-msvc": "4.52.0", "@rollup/rollup-win32-ia32-msvc": "4.52.0", "@rollup/rollup-win32-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g=="], + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "scrypt-js": ["scrypt-js@3.0.1", "", {}, "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA=="], + + "secp256k1": ["secp256k1@4.0.4", "", { "dependencies": { "elliptic": "^6.5.7", "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" } }, "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + + "sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="], + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -333,8 +502,12 @@ "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="], + "stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="], + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], @@ -351,18 +524,34 @@ "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + "to-buffer": ["to-buffer@1.2.2", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="], + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="], + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], "undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], + "utf8": ["utf8@3.0.0", "", {}, "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ=="], + + "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="], "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], @@ -373,8 +562,38 @@ "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], + "browserify-rsa/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], + + "browserify-sign/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], + + "browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + "ethereumjs-util/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], + + "ethereumjs-util/ethereum-cryptography": ["ethereum-cryptography@0.1.3", "", { "dependencies": { "@types/pbkdf2": "^3.0.0", "@types/secp256k1": "^4.0.1", "blakejs": "^1.1.0", "browserify-aes": "^1.2.0", "bs58check": "^2.1.2", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "hash.js": "^1.1.7", "keccak": "^3.0.0", "pbkdf2": "^3.0.17", "randombytes": "^2.1.0", "safe-buffer": "^5.1.2", "scrypt-js": "^3.0.0", "secp256k1": "^4.0.1", "setimmediate": "^1.0.5" } }, "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ=="], + + "ethereumjs-wallet/ethereum-cryptography": ["ethereum-cryptography@0.1.3", "", { "dependencies": { "@types/pbkdf2": "^3.0.0", "@types/secp256k1": "^4.0.1", "blakejs": "^1.1.0", "browserify-aes": "^1.2.0", "bs58check": "^2.1.2", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "hash.js": "^1.1.7", "keccak": "^3.0.0", "pbkdf2": "^3.0.17", "randombytes": "^2.1.0", "safe-buffer": "^5.1.2", "scrypt-js": "^3.0.0", "secp256k1": "^4.0.1", "setimmediate": "^1.0.5" } }, "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ=="], + + "ripemd160/hash-base": ["hash-base@3.1.2", "", { "dependencies": { "inherits": "^2.0.4", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.1" } }, "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg=="], + + "rlp/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], + + "secp256k1/node-addon-api": ["node-addon-api@5.1.0", "", {}, "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="], + + "to-buffer/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "browserify-sign/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "ripemd160/hash-base/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "ripemd160/hash-base/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "ripemd160/hash-base/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], } } diff --git a/docs/edgex/L2signature.md b/docs/edgex/L2signature.md new file mode 100644 index 0000000..ab93fea --- /dev/null +++ b/docs/edgex/L2signature.md @@ -0,0 +1,785 @@ +2 Signature +How To Sign Message +How To GET Your L2 Private Key +To sign messages on Layer 2, you need to obtain your L2 private key. This key is used to generate signatures that authorize various actions on the platform. + + +How To GET Your L2 Private Key +Warning: Keep your private key secure and never share it with anyone. Anyone with access to your private key can sign messages on your behalf. + +Signature Algorithm +The signature algorithm used is Ecdsa (Elliptic Curve Digital Signature Algorithm). This algorithm ensures that signatures are secure and verifiable. + +L2Signature for Operations (e.g., Order, Transfer, Withdraw): This will use Pedersen hash for signing. However, this hash computation will consume significantly more CPU resources. + + +

StarkWare Crypto Utils

+ + +

Signatures, keys and Pedersen hash on STARK friendly elliptic curve

+ + +

+ + + + + + + + + +

+ +## Installation + +```bash +// using npm +npm i @starkware-industries/starkware-crypto-utils + +// using yarn +yarn add @starkware-industries/starkware-crypto-utils +``` + +## How to use it + +```js +const starkwareCrypto = require('@starkware-industries/starkware-crypto-utils'); +``` + +## API + +```javascript + +{ + prime, + ec: starkEc, + constantPoints, + shiftPoint, + maxEcdsaVal, // Data. + pedersen, + getLimitOrderMsgHash, + getTransferMsgHash, + sign, + verify, + assertInRange, + getTransferMsgHashWithFee, + getLimitOrderMsgHashWithFee // Function. + + asset: { + getAssetType, + getAssetId // Function. + }, + + keyDerivation: { + StarkExEc: ec.n, // Data. + getPrivateKeyFromEthSignature, + privateToStarkKey, + getKeyPairFromPath, + getAccountPath, + grindKey // Function. + }, + + messageUtils: { + assertInRange // Function. + } +} +``` + +## Usage + +### Signing a StarkEx order + +```javascript +const starkwareCrypto = require('@starkware-libs/starkware-crypto-utils'); +const testData = require('test/config/signature_test_data.json'); + +const privateKey = testData.meta_data.party_a_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === + testData.settlement.party_a_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.settlement.party_a_order.public_key.substring(2)}` +); + +const {party_a_order: partyAOrder} = testData.settlement; +const msgHash = starkwareCrypto.getLimitOrderMsgHash( + partyAOrder.vault_id_sell, // - vault_sell (uint31) + partyAOrder.vault_id_buy, // - vault_buy (uint31) + partyAOrder.amount_sell, // - amount_sell (uint63 decimal str) + partyAOrder.amount_buy, // - amount_buy (uint63 decimal str) + partyAOrder.token_sell, // - token_sell (hex str with 0x prefix < prime) + partyAOrder.token_buy, // - token_buy (hex str with 0x prefix < prime) + partyAOrder.nonce, // - nonce (uint31) + partyAOrder.expiration_timestamp // - expiration_timestamp (uint22) +); + +assert( + msgHash === testData.meta_data.party_a_order.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.party_a_order.message_hash.substring(2) +); + +const msgSignature = starkwareCrypto.sign(keyPair, msgHash); +const {r, s} = msgSignature; + +assert(starkwareCrypto.verify(publicKey, msgHash, msgSignature)); +assert( + r.toString(16) === partyAOrder.signature.r.substring(2), + `Got: ${r.toString(16)}. Expected: ${partyAOrder.signature.r.substring(2)}` +); +assert( + s.toString(16) === partyAOrder.signature.s.substring(2), + `Got: ${s.toString(16)}. Expected: ${partyAOrder.signature.s.substring(2)}` +); + +// The following is the JSON representation of an order: +console.log('Order JSON representation: '); +console.log(partyAOrder); +console.log('\n'); +``` + +### StarkEx key serialization + +```javascript +const starkwareCrypto = require('@starkware-libs/starkware-crypto-utils'); + +const pubXStr = publicKey.pub.getX().toString('hex'); +const pubYStr = publicKey.pub.getY().toString('hex'); + +// Verify Deserialization. +const pubKeyDeserialized = starkwareCrypto.ec.keyFromPublic( + {x: pubXStr, y: pubYStr}, + 'hex' +); +assert(starkwareCrypto.verify(pubKeyDeserialized, msgHash, msgSignature)); +``` + +### Signing a StarkEx order with fee + +```javascript +const privateKey = testData.meta_data.party_a_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === + testData.settlement.party_a_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.settlement.party_a_order.public_key.substring(2)}` +); + +const {party_a_order: partyAOrder} = testData.settlement; +const feeInfo = testData.fee_info_user; +const msgHash = starkwareCrypto.getLimitOrderMsgHashWithFee( + partyAOrder.vault_id_sell, // - vault_sell (uint64) + partyAOrder.vault_id_buy, // - vault_buy (uint64) + partyAOrder.amount_sell, // - amount_sell (uint63 decimal str) + partyAOrder.amount_buy, // - amount_buy (uint63 decimal str) + partyAOrder.token_sell, // - token_sell (hex str with 0x prefix < prime) + partyAOrder.token_buy, // - token_buy (hex str with 0x prefix < prime) + partyAOrder.nonce, // - nonce (uint31) + partyAOrder.expiration_timestamp, // - expiration_timestamp (uint22) + feeInfo.token_id, // - token (hex str with 0x prefix < prime) + feeInfo.source_vault_id, // - fee_source_vault_id (uint31) + feeInfo.fee_limit // - amount (uint63 decimal str) +); + +assert( + msgHash === + testData.meta_data.party_a_order_with_fee.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.party_a_order_with_fee.message_hash.substring(2) +); + +// The following is the JSON representation of an order: +console.log('Order With Fee JSON representation: '); +// Fee info is added to the order, and will be also be seen in the JSON of Settlement. +partyAOrder.fee_info = feeInfo; // eslint-disable-line +console.log(partyAOrder); +console.log('\n'); +``` + +### StarkEx transfer + +```javascript +const starkwareCrypto = require('@starkware-libs/starkware-crypto-utils'); +const testData = require('test/config/signature_test_data.json'); + +const privateKey = testData.meta_data.transfer_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === testData.transfer_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.transfer_order.public_key.substring(2)}` +); + +const transfer = testData.transfer_order; +const msgHash = starkwareCrypto.getTransferMsgHash( + transfer.amount, // - amount (uint63 decimal str) + transfer.nonce, // - nonce (uint31) + transfer.sender_vault_id, // - sender_vault_id (uint31) + transfer.token, // - token (hex str with 0x prefix < prime) + transfer.target_vault_id, // - target_vault_id (uint31) + transfer.target_public_key, // - target_public_key (hex str with 0x prefix < prime) + transfer.expiration_timestamp // - expiration_timestamp (uint22) +); + +assert( + msgHash === testData.meta_data.transfer_order.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.transfer_order.message_hash.substring(2) +); + +// The following is the JSON representation of a transfer: +console.log('Transfer JSON representation: '); +console.log(transfer); +console.log('\n'); +``` + +### StarkEx conditional transfer + +```javascript +const starkwareCrypto = require('@starkware-libs/starkware-crypto-utils'); +const testData = require('test/config/signature_test_data.json'); + +const privateKey = + testData.meta_data.conditional_transfer_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === + testData.conditional_transfer_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.conditional_transfer_order.public_key.substring( + 2 + )}` +); + +const transfer = testData.conditional_transfer_order; +const msgHash = starkwareCrypto.getTransferMsgHash( + transfer.amount, // - amount (uint63 decimal str) + transfer.nonce, // - nonce (uint31) + transfer.sender_vault_id, // - sender_vault_id (uint31) + transfer.token, // - token (hex str with 0x prefix < prime) + transfer.target_vault_id, // - target_vault_id (uint31) + transfer.target_public_key, // - target_public_key (hex str with 0x prefix < prime) + transfer.expiration_timestamp, // - expiration_timestamp (uint22) + transfer.condition // - condition (hex str with 0x prefix < prime) +); + +assert( + msgHash === + testData.meta_data.conditional_transfer_order.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.conditional_transfer_order.message_hash.substring(2) +); + +// The following is the JSON representation of a transfer: +console.log('Conditional Transfer JSON representation: '); +console.log(transfer); +console.log('\n'); +``` + +### StarkEx transfer with fee + +```javascript +const privateKey = testData.meta_data.transfer_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === testData.transfer_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.transfer_order.public_key.substring(2)}` +); + +const transfer = testData.transfer_order; +const feeInfo = testData.fee_info_user; +const msgHash = starkwareCrypto.getTransferMsgHashWithFee( + transfer.amount, // - amount (uint63 decimal str) + transfer.nonce, // - nonce (uint31) + transfer.sender_vault_id, // - sender_vault_id (uint64) + transfer.token, // - token (hex str with 0x prefix < prime) + transfer.target_vault_id, // - target_vault_id (uint64) + transfer.target_public_key, // - target_public_key (hex str with 0x prefix < prime) + transfer.expiration_timestamp, // - expiration_timestamp (uint22) + feeInfo.token_id, // - token (hex str with 0x prefix < prime) + feeInfo.source_vault_id, // - fee_source_vault_id (uint64) + feeInfo.fee_limit // - amount (uint63 decimal str) +); + +assert( + msgHash === + testData.meta_data.transfer_order_with_fee.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.transfer_order.message_hash.substring(2) +); + +// The following is the JSON representation of a transfer: +console.log('Transfer With Fee JSON representation: '); +console.log(transfer); +console.log('\n'); +``` + +### StarkEx conditional Transfer with fee + +```javascript +const privateKey = + testData.meta_data.conditional_transfer_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === + testData.conditional_transfer_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.conditional_transfer_order.public_key.substring( + 2 + )}` +); + +const transfer = testData.conditional_transfer_order; +const feeInfo = testData.fee_info_user; +const msgHash = starkwareCrypto.getTransferMsgHashWithFee( + transfer.amount, // - amount (uint63 decimal str) + transfer.nonce, // - nonce (uint31) + transfer.sender_vault_id, // - sender_vault_id (uint64) + transfer.token, // - token (hex str with 0x prefix < prime) + transfer.target_vault_id, // - target_vault_id (uint64) + transfer.target_public_key, // - target_public_key (hex str with 0x prefix < prime) + transfer.expiration_timestamp, // - expiration_timestamp (uint22) + feeInfo.token_id, // - token (hex str with 0x prefix < prime) + feeInfo.source_vault_id, // - fee_source_vault_id (uint64) + feeInfo.fee_limit, // - amount (uint63 decimal str) + transfer.condition // - condition (hex str with 0x prefix < prime) +); + +assert( + msgHash === + testData.meta_data.conditional_transfer_order_with_fee.message_hash.substring( + 2 + ), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.conditional_transfer_order.message_hash.substring(2) +); + +// The following is the JSON representation of a transfer: +console.log('Conditional Transfer With Fee JSON representation: '); +console.log(transfer); +console.log('\n'); +``` + +### Adding a matching order to create a settlement + +```javascript +const starkwareCrypto = require('@starkware-libs/starkware-crypto-utils'); +const testData = require('test/config/signature_test_data.json'); + +const privateKey = testData.meta_data.party_b_order.private_key.substring(2); +const keyPair = starkwareCrypto.ec.keyFromPrivate(privateKey, 'hex'); +const publicKey = starkwareCrypto.ec.keyFromPublic( + keyPair.getPublic(true, 'hex'), + 'hex' +); +const publicKeyX = publicKey.pub.getX(); + +assert( + publicKeyX.toString(16) === + testData.settlement.party_b_order.public_key.substring(2), + `Got: ${publicKeyX.toString(16)}. + Expected: ${testData.settlement.party_b_order.public_key.substring(2)}` +); + +const {party_b_order: partyBOrder} = testData.settlement; +const msgHash = starkwareCrypto.getLimitOrderMsgHash( + partyBOrder.vault_id_sell, // - vault_sell (uint31) + partyBOrder.vault_id_buy, // - vault_buy (uint31) + partyBOrder.amount_sell, // - amount_sell (uint63 decimal str) + partyBOrder.amount_buy, // - amount_buy (uint63 decimal str) + partyBOrder.token_sell, // - token_sell (hex str with 0x prefix < prime) + partyBOrder.token_buy, // - token_buy (hex str with 0x prefix < prime) + partyBOrder.nonce, // - nonce (uint31) + partyBOrder.expiration_timestamp // - expiration_timestamp (uint22) +); + +assert( + msgHash === testData.meta_data.party_b_order.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.party_b_order.message_hash.substring(2) +); + +const msgSignature = starkwareCrypto.sign(keyPair, msgHash); +const {r, s} = msgSignature; + +assert(starkwareCrypto.verify(publicKey, msgHash, msgSignature)); +assert( + r.toString(16) === partyBOrder.signature.r.substring(2), + `Got: ${r.toString(16)}. Expected: ${partyBOrder.signature.r.substring(2)}` +); +assert( + s.toString(16) === partyBOrder.signature.s.substring(2), + `Got: ${s.toString(16)}. Expected: ${partyBOrder.signature.s.substring(2)}` +); + +// The following is the JSON representation of a settlement: +console.log('Settlement JSON representation: '); +console.log(testData.settlement); +``` + +## Valid transfer with sender_vault_id=2\*\*63+10 + +```javascript +const transfer = testData.transfer_order_2nd_valid_range; +const feeInfo = testData.fee_info_user; + +const msgHash = starkwareCrypto.getTransferMsgHashWithFee( + transfer.amount, // - amount (uint63 decimal str) + transfer.nonce, // - nonce (uint31) + transfer.sender_vault_id, // - sender_vault_id (uint64) + transfer.token, // - token (hex str with 0x prefix < prime) + transfer.target_vault_id, // - target_vault_id (uint64) + transfer.target_public_key, // - target_public_key (hex str with 0x prefix < prime) + transfer.expiration_timestamp, // - expiration_timestamp (uint22) + feeInfo.token_id, // - token (hex str with 0x prefix < prime) + feeInfo.source_vault_id, // - fee_source_vault_id (uint64) + feeInfo.fee_limit, // - amount (uint63 decimal str) + transfer.condition // - condition (hex str with 0x prefix < prime) +); + +assert( + msgHash === + testData.meta_data.transfer_order_2nd_valid_range.message_hash.substring(2), + `Got: ${msgHash}. Expected: ` + + testData.meta_data.transfer_order_2nd_valid_range.message_hash.substring(2) +); + +// The following is the JSON representation of a transfer with sender_vault_id in the second +// valid range: +console.log('Transfer JSON representation: '); +console.log(transfer); +console.log('\n'); +``` + +## License + +[Apache License 2.0](LICENSE.md) + +Java L2Signature Demo +Below is a Java implementation of the Ecdsa signature algorithm. This example demonstrates how to sign a message using a private key. + +Copy + + public static CreateOrderRequest signOrder( + CreateOrderRequest request, + Contract contract, + Coin quotelCoin, + PrivateKey privateKey) { + BigInteger msgHash = L2SignUtil.hashLimitOrder( + request.getSide() == OrderSide.BUY, + BigIntUtil.toBigInt(quotelCoin.getStarkExAssetId()), + BigIntUtil.toBigInt(contract.getStarkExSyntheticAssetId()), + BigIntUtil.toBigInt(quotelCoin.getStarkExAssetId()), + UnsignedLong.valueOf(new BigDecimal(request.getL2Value()) + .multiply(new BigDecimal(BigIntUtil.toBigInt(quotelCoin.getStarkExResolution()))) + .toBigIntegerExact()), + UnsignedLong.valueOf(new BigDecimal(request.getL2Size()) + .multiply(new BigDecimal(BigIntUtil.toBigInt(contract.getStarkExResolution()))) + .toBigIntegerExact()), + UnsignedLong.valueOf(new BigDecimal(request.getL2LimitFee()) + .multiply(new BigDecimal(BigIntUtil.toBigInt(quotelCoin.getStarkExResolution()))) + .toBigIntegerExact()), + UnsignedLong.fromLongBits(request.getAccountId()), + UnsignedInteger.valueOf(request.getL2Nonce()), + UnsignedInteger.valueOf(request.getL2ExpireTime() / (60 * 60 * 1000L))); + Signature signature = Ecdsa.sign(msgHash, privateKey); + return request.toBuilder() + .setL2Signature(L2Signature.newBuilder() + .setR(BigIntUtil.toHexStr(signature.r)) + .setS(BigIntUtil.toHexStr(signature.s)) + .build()) + .build(); + } + + public static BigInteger hashLimitOrder( + boolean isBuyingSynthetic, + BigInteger assetIdCollateral, + BigInteger assetIdSynthetic, + BigInteger assetIdFee, + UnsignedLong amountCollateral, + UnsignedLong amountSynthetic, + UnsignedLong maxAmountFee, + UnsignedLong positionId, + UnsignedInteger nonce, + UnsignedInteger expirationTimestamp) { + BigInteger assetIdSell; + BigInteger assetIdBuy; + UnsignedLong amountSell; + UnsignedLong amountBuy; + if (isBuyingSynthetic) { + assetIdSell = assetIdCollateral; + assetIdBuy = assetIdSynthetic; + amountSell = amountCollateral; + amountBuy = amountSynthetic; + } else { + assetIdSell = assetIdSynthetic; + assetIdBuy = assetIdCollateral; + amountSell = amountSynthetic; + amountBuy = amountCollateral; + } + BigInteger packedMessage0 = amountSell.bigIntegerValue(); + packedMessage0 = packedMessage0.shiftLeft(64).add(amountBuy.bigIntegerValue()); + packedMessage0 = packedMessage0.shiftLeft(64).add(maxAmountFee.bigIntegerValue()); + packedMessage0 = packedMessage0.shiftLeft(32).add(nonce.bigIntegerValue()); + + BigInteger packedMessage1 = BigInteger.valueOf(3); + packedMessage1 = packedMessage1.shiftLeft(64).add(positionId.bigIntegerValue()); + packedMessage1 = packedMessage1.shiftLeft(64).add(positionId.bigIntegerValue()); + packedMessage1 = packedMessage1.shiftLeft(64).add(positionId.bigIntegerValue()); + packedMessage1 = packedMessage1.shiftLeft(32).add(expirationTimestamp.bigIntegerValue()); + packedMessage1 = packedMessage1.shiftLeft(17); + + BigInteger msg = pedersenHash(assetIdSell, assetIdBuy); + msg = pedersenHash(msg, assetIdFee); + msg = pedersenHash(msg, packedMessage0); + msg = pedersenHash(msg, packedMessage1); + return msg; + } + + public static BigInteger pedersenHash(BigInteger... input) { + BigInteger[][] points = PEDERSEN_POINTS; + Point shiftPoint = new Point(points[0][0], points[0][1]); + for (int i = 0; i < input.length; i++) { + BigInteger x = input[i]; + for (int j = 0; j < 252; j++) { + int pos = 2 + i * 252 + j; + Point pt = new Point(points[pos][0], points[pos][1]); + if (x.and(BigInteger.ONE).intValue() != 0) { + shiftPoint = EcMath.add(shiftPoint, pt, Curve.secp256k1.A, Curve.secp256k1.P); + } + x = x.shiftRight(1); + } + } + return shiftPoint.x; + } + + public static Signature sign(BigInteger msgHash, PrivateKey privateKey) { + Curve curve = privateKey.curve; + BigInteger randNum = new BigInteger(curve.N.toByteArray().length * 8 - 1, new SecureRandom()).abs().add(BigInteger.ONE); + Point randomSignPoint = EcMath.multiply(curve.G, randNum, curve.N, curve.A, curve.P); + BigInteger r = randomSignPoint.x.mod(curve.N); + BigInteger s = ((msgHash.add(r.multiply(privateKey.secret))).multiply(EcMath.inv(randNum, curve.N))).mod(curve.N); + return Signature.create(r, s); + } +Signature Construction Guide +This section provides detailed instructions on constructing signatures for various actions on the platform. + +Withdrawal Signature +Used to authorize withdrawing assets from Layer 2 to an Ethereum address. + +Parameters +assetIdCollateral - Asset ID for the collateral token from meta_data.coinList.starkExAssetId + +positionId - User's account ID in Layer 2 + +ethAddress - Destination Ethereum address for withdrawal + +nonce - Unique transaction identifier to prevent replay attacks + +expirationTimestamp - Unix timestamp when signature expires + +amount - Amount to withdraw in base units + +Calculation +The following TypeScript function constructs the withdrawal message for signing: + +Copy +// Construct withdrawal message for signing +function getWithdrawalToAddressMsg({ + assetIdCollateral, + positionId, + ethAddress, + nonce, + expirationTimestamp, + amount +}) { + // Pack parameters into 256-bit words + const w1 = assetIdCollateral; + let w5 = BigInt(withdrawalToAddress); // Constant identifier + w5 = (w5 << 64) + BigInt(positionId); + w5 = (w5 << 32) + BigInt(nonce); + w5 = (w5 << 64) + BigInt(amount); + w5 = (w5 << 32) + BigInt(expirationTimestamp); + w5 = w5 << 49; + + // Calculate Pedersen hash + return pedersen([ + pedersen([w1, ethAddress]), + w5.toString(16) + ]); +} +Limit Order Signature +Used to authorize a limit order for perpetual trading. + +Parameters +assetIdSynthetic - Synthetic asset ID from meta_data.contractList.starkExSyntheticAssetId + +assetIdCollateral - Collateral asset ID from meta_data.coinList.starkExAssetId + +isBuyingSynthetic - true for buy orders, false for sell orders + +assetIdFee - Fee token asset ID from meta_data.coinList.starkExAssetId + +amountSynthetic - Amount of synthetic asset + +amountCollateral - Amount of collateral asset + +maxAmountFee - Maximum fee amount allowed + +nonce - Unique order identifier + +positionId - User's position ID + +expirationTimestamp - Unix timestamp when order expires + +Calculation +The following TypeScript function constructs the limit order message for signing: + +Copy +function getLimitOrderMsg({ + assetIdSynthetic, + assetIdCollateral, + isBuyingSynthetic, + assetIdFee, + amountSynthetic, + amountCollateral, + maxAmountFee, + nonce, + positionId, + expirationTimestamp +}) { + // Determine sell/buy assets based on order side + const [assetIdSell, assetIdBuy] = isBuyingSynthetic + ? [assetIdCollateral, assetIdSynthetic] + : [assetIdSynthetic, assetIdCollateral]; + const [amountSell, amountBuy] = isBuyingSynthetic + ? [amountCollateral, amountSynthetic] + : [amountSynthetic, amountCollateral]; + + // Pack order data into 256-bit words + const w1 = assetIdSell; + const w2 = assetIdBuy; + const w3 = assetIdFee; + + // Calculate message hash + let msg = pedersen([w1, w2]); + msg = pedersen([msg, w3]); + + let w4 = BigInt(amountSell); + w4 = (w4 << 64) + BigInt(amountBuy); + w4 = (w4 << 64) + BigInt(maxAmountFee); + w4 = (w4 << 32) + BigInt(nonce); + msg = pedersen([msg, w4.toString(16)]); + + let w5 = BigInt(limitOrderWithFees); // Constant identifier + w5 = (w5 << 64) + BigInt(positionId); + w5 = (w5 << 64) + BigInt(positionId); + w5 = (w5 << 64) + BigInt(positionId); + w5 = (w5 << 32) + BigInt(expirationTimestamp); + w5 = w5 << 17; + + return pedersen([msg, w5.toString(16)]); +} +Transfer Signature +Used to authorize transfers between Layer 2 accounts. + +Parameters +assetId - Asset ID being transferred + +receiverPublicKey - Recipient's public key + +senderPositionId - Sender's position ID + +receiverPositionId - Recipient's position ID + +srcFeePositionId - Fee source position ID + +nonce - Unique transfer identifier + +amount - Transfer amount + +expirationTimestamp - Unix timestamp when transfer expires + +assetIdFee - Fee token asset ID (optional, default '0') + +maxAmountFee - Maximum fee amount (optional, default '0') + +Calculation +The following TypeScript function constructs the transfer message for signing: + +Copy +function getTransferMsg({ + assetId, + receiverPublicKey, + senderPositionId, + receiverPositionId, + srcFeePositionId, + nonce, + amount, + expirationTimestamp, + assetIdFee = '0', + maxAmountFee = '0' +}) { + // Pack transfer data into 256-bit words + const w1 = assetId; + const w2 = assetIdFee; + const w3 = receiverPublicKey; + + let w4 = BigInt(senderPositionId); + w4 = (w4 << 64) + BigInt(receiverPositionId); + w4 = (w4 << 64) + BigInt(srcFeePositionId); + w4 = (w4 << 32) + BigInt(nonce); + + let w5 = BigInt(transfer); // Constant identifier + w5 = (w5 << 64) + BigInt(amount); + w5 = (w5 << 64) + BigInt(maxAmountFee); + w5 = (w5 << 32) + BigInt(expirationTimestamp); + w5 = w5 << 81; + + // Calculate message hash + let msg = pedersen([w1, w2]); + msg = pedersen([msg, w3]); + msg = pedersen([msg, w4.toString(16)]); + return pedersen([msg, w5.toString(16)]); +} +For more details on the signature construction, see the StarkEx documentation. \ No newline at end of file diff --git a/docs/edgex/auth.md b/docs/edgex/auth.md new file mode 100644 index 0000000..3e9b2eb --- /dev/null +++ b/docs/edgex/auth.md @@ -0,0 +1,172 @@ +Authentication +Authentication is crucial for ensuring that only authorized users can access private APIs. This document outlines the authentication mechanisms used for public and private APIs. + +Public API +Public APIs do not require authentication. These interfaces are accessible to anyone without the need for any credentials. + +Copy +No authentication is required for public interfaces. +Private API +Private APIs require authentication to ensure that only authorized users can access them. Authentication is achieved using custom headers that include a timestamp and a signature. + +Auth Header +The following headers must be included in the request to authenticate access to private APIs: + +Name +Location +Type +Required +Description +X-edgeX-Api-Timestamp + +header + +string + +must + +The timestamp when the request was made. This helps prevent replay attacks. + +X-edgeX-Api-Signature + +header + +string + +must + +The signature generated using the private key and request details. + +CURL Examble +Copy +curl --location --request GET 'https://pro.edgex.exchange/api/v1/private/account/getPositionTransactionPage?filterTypeList=SETTLE_FUNDING_FEE&size=10&accountId=544159487963955214' \ +--header 'X-edgeX-Api-Signature: 06d28020763542c0afc296dc8743797c6fda8ea9727745b57b671f70326dfed6077cd******************************aff3162e39d05d9df1c3ddf9648650382d6e62ff1076b14c0e6c687088d3917d8490e5412a080a6e9ea940c720ddd' \ +--header 'X-edgeX-Api-Timestamp: 1736313025024' +Signature Elements +The signature is generated using the following elements: + +Signature Element + +Description + +X-edgeX-Api-Timestamp + +The timestamp when the request was made. This is retrieved from the request header. + +Request Method (Uppercase) + +The HTTP method of the request, converted to uppercase (e.g., GET, POST). + +Request Path + +The URI path of the request (e.g., /api/v1/resource). + +Request Parameter/Body + +The query parameters or request body, sorted alphabetically. + +Request Parameter To Signature Content +The request parameters are concatenated into a single string that forms the signature content. This string includes the timestamp, HTTP method, request path, and sorted query parameters or request body, ensuring the integrity and authenticity of the request. + +For example, the following request parameters are concatenated into a single string: + +1735542383256GET/api/v1/private/account/getPositionTransactionPageaccountId=543429922991899150&filterTypeList=SETTLE_FUNDING_FEE&size=10 + +Generate Signature Java Example +Below is a Java implementation of the Ecdsa signature algorithm. This example demonstrates how to sign a message using a private key. + +Private API Auth Signature: This is used for authentication. We do not want the hash computation to consume excessive CPU resources. Therefore, this will use SHA3 to hash the request body string before signing. + +Copy +import java.math.BigInteger; +import org.web3j.abi.TypeEncoder; +import org.web3j.abi.datatypes.Utf8String; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.crypto.Hash; +import org.web3j.utils.Numeric; + +public class EcdsaSignatureDemo { + public static final BigInteger K_MODULUS = Numeric + .toBigInt("0x0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f"); + + public static void main(String[] args) { + String privateKeyHex = "0463ac809cc7d7c1baf*********************baff9fc6e3d8e5b160ea3fc"; + + // Ensure that the private key is a hexadecimal string without the "0x" prefix. + if (privateKeyHex.startsWith("0x")) { + privateKeyHex = privateKeyHex.substring(2); + } + + BigInteger mySecretKey = new BigInteger(privateKeyHex, 16); + PrivateKey privateKey = PrivateKey.create(mySecretKey); + + String message = "1735542383256GET/api/v1/private/account/getPositionTransactionPageaccountId=543429922991899150&filterTypeList=SETTLE_FUNDING_FEE&size=10"; + String msg = TypeEncoder.encodePacked(new Utf8String(message)); + + BigInteger msgHash = Numeric.toBigInt(Hash.sha3(Numeric.hexStringToByteArray(msg))); + + msgHash = msgHash.mod(K_MODULUS); + + Signature signature = Ecdsa.sign(msgHash, privateKey); + + String starkSignature = TypeEncoder.encodePacked(new Uint256(signature.r)) + + TypeEncoder.encodePacked(new Uint256(signature.s)) + + TypeEncoder.encodePacked(new Uint256(privateKey.publicKey().point.y)); + + System.out.println(starkSignature); + } + + public static Signature sign(BigInteger msgHash, PrivateKey privateKey) { + Curve curve = privateKey.curve; + BigInteger randNum = new BigInteger(curve.N.toByteArray().length * 8 - 1, new SecureRandom()).abs().add(BigInteger.ONE); + Point randomSignPoint = EcMath.multiply(curve.G, randNum, curve.N, curve.A, curve.P); + BigInteger r = randomSignPoint.x.mod(curve.N); + BigInteger s = ((msgHash.add(r.multiply(privateKey.secret))).multiply(EcMath.inv(randNum, curve.N))).mod(curve.N); + return Signature.create(r, s); + } +} +Request Body To Body String Code Example +The following Java code example demonstrates how to convert a JSON request body into a sorted string format suitable for signature generation: + +Copy +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public class RequestBodyToString { + private static final String EMPTY_STRING = ""; + + private static String getValue(JsonElement valueJson) { + if (valueJson.isJsonNull()) { + return EMPTY_STRING; + } else if (valueJson.isJsonPrimitive()) { + return valueJson.getAsString(); + } else if (valueJson.isJsonArray()) { + JsonArray valueArray = valueJson.getAsJsonArray(); + if (valueArray.isEmpty()) { + return EMPTY_STRING; + } + List values = new ArrayList<>(); + for (JsonElement itemValue : valueArray) { + values.add(getValue(itemValue)); + } + return String.join("&", values); + } else if (valueJson.isJsonObject()) { + TreeMap sortedDataMap = new TreeMap<>(); + JsonObject valueJsonObj = valueJson.getAsJsonObject(); + for (String key : valueJsonObj.keySet()) { + sortedDataMap.put(key, getValue(valueJsonObj.get(key))); + } + return sortedDataMap.keySet().stream() + .map(key -> key + "=" + sortedDataMap.get(key)) + .collect(Collectors.joining("&")); + } + return EMPTY_STRING; + } +} +Signature Algorithm +The signature algorithm used is Ecdsa (Elliptic Curve Digital Signature Algorithm). \ No newline at end of file diff --git a/docs/edgex/edgex-python-sdk-main/.github/workflows/publish-to-pypi.yml b/docs/edgex/edgex-python-sdk-main/.github/workflows/publish-to-pypi.yml new file mode 100644 index 0000000..255b6b0 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,45 @@ +name: Publish to PyPI + +on: + release: + types: [published] + workflow_dispatch: # Allow manual triggering + +jobs: + build-and-publish: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Check package + run: twine check dist/* + + - name: Publish to Test PyPI + if: github.event_name == 'workflow_dispatch' + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} + run: | + twine upload --repository testpypi dist/* + + - name: Publish to PyPI + if: github.event_name == 'release' + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + twine upload dist/* diff --git a/docs/edgex/edgex-python-sdk-main/.gitignore b/docs/edgex/edgex-python-sdk-main/.gitignore new file mode 100644 index 0000000..0042155 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo + +# Environment variables +.env + +# Python bytecode files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +dist/ +build/ +*.egg-info/ + +# Virtual environments +venv/ +env/ +ENV/ + +# OS specific files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/docs/edgex/edgex-python-sdk-main/README.md b/docs/edgex/edgex-python-sdk-main/README.md new file mode 100644 index 0000000..5bc3292 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/README.md @@ -0,0 +1,516 @@ +# EdgeX Python SDK + +A Python SDK for interacting with the EdgeX Exchange API. This SDK provides a comprehensive interface to the EdgeX API, allowing you to easily integrate EdgeX functionality into your Python applications. + +## Features + +- **Complete API Coverage**: Access all EdgeX API endpoints +- **WebSocket Support**: Real-time data streaming +- **Async/Await**: Modern Python async interface +- **Type Hints**: Comprehensive type annotations for better IDE support +- **Error Handling**: Proper error handling and validation +- **Pagination**: Support for paginated API endpoints +- **Authentication**: Automatic request signing + +## Installation + +### From PyPI + +```bash +pip install edgex-python-sdk +``` + +### From Source + +```bash +git clone https://github.com/edgex-Tech/edgex-python-sdk.git +cd edgex-python-sdk +pip install -e . +``` + +### Using Requirements Files + +For production use: +```bash +pip install -r requirements.txt +``` + +For development (includes testing and linting tools): +```bash +pip install -r requirements-dev.txt +``` + +### Virtual Environment (Recommended) + +It's recommended to use a virtual environment: + +```bash +# Create virtual environment +python3 -m venv venv + +# Activate virtual environment +source venv/bin/activate # On Windows: venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt + +# Or install in development mode +pip install -e . +``` + +## Quick Start + +```python +import asyncio +import os +from edgex_sdk import Client, OrderSide + +async def main(): + # Create a new client + client = Client( + base_url="https://pro.edgex.exchange", # Use https://testnet.edgex.exchange for testnet + account_id=12345, # Your account ID + stark_private_key="your-stark-private-key" # Your private key + ) + + # Get server time + server_time = await client.get_server_time() + print(f"Server Time: {server_time}") + + # Get exchange metadata + metadata = await client.get_metadata() + print(f"Available contracts: {len(metadata.get('data', {}).get('contractList', []))}") + + # Get account assets + assets = await client.get_account_asset() + print(f"Account Assets: {assets}") + + # Get account positions + positions = await client.get_account_positions() + print(f"Account Positions: {positions}") + + # Get 24-hour market data for BNB2USDT (contract ID: 10000004) + quote = await client.get_24_hour_quote("10000004") + print(f"BNB2USDT Price: {quote}") + + # Create a limit order (uncomment to place real order) + # order = await client.create_limit_order( + # contract_id="10000004", # BNB2USDT + # size="0.01", + # price="600.00", + # side=OrderSide.BUY + # ) + # print(f"Order created: {order}") + +# Run the async function +asyncio.run(main()) +``` + +## Architecture + +The SDK is organized into modules that correspond to the EdgeX API structure: + +``` +edgex_sdk/ +├── __init__.py +├── client.py # Main client +├── account/ # Account API +├── asset/ # Asset API +├── funding/ # Funding API +├── internal/ # Internal utilities +├── metadata/ # Metadata API +├── order/ # Order API +├── quote/ # Quote API +├── transfer/ # Transfer API +└── ws/ # WebSocket API +``` + +## Available APIs + +The SDK currently supports the following API modules: + +- **Account API**: Manage account positions, retrieve position transactions, and handle collateral transactions + - Get account positions + - Get position by contract ID + - Get position transaction history + - Get collateral transaction details + - Update leverage settings + +- **Asset API**: Handle asset management and withdrawals + - Get asset orders with pagination + - Get coin rates + - Manage withdrawals (normal, cross-chain, and fast) + - Get withdrawal records and sign information + - Check withdrawable amounts + +- **Funding API**: Manage funding operations and account balance + - Handle funding transactions + - Manage funding accounts + - Get funding transaction history + +- **Metadata API**: Access exchange system information + - Get server time + - Get exchange metadata (trading pairs, contracts, etc.) + +- **Order API**: Comprehensive order management + - Create and cancel orders + - Get active orders + - Get order fill transactions + - Calculate maximum order sizes + - Manage order history + +- **Quote API**: Access market data and pricing + - Get multi-contract K-line data + - Get order book depth + - Access real-time market quotes + - Get 24-hour ticker data + +- **Transfer API**: Handle asset transfers + - Create transfer out orders + - Get transfer records (in/out) + - Check available withdrawal amounts + - Manage transfer history + +- **WebSocket API**: Real-time data streaming + - Market data (tickers, K-lines, order book, trades) + - Account updates + - Order updates + - Position updates + +## WebSocket Support + +The SDK provides a WebSocket manager for handling real-time data: + +```python +import asyncio +from edgex_sdk import WebSocketManager + +async def main(): + # Create a WebSocket manager + ws_manager = WebSocketManager( + base_url="wss://quote.edgex.exchange", # Use wss://quote-testnet.edgex.exchange for testnet + account_id=12345, + stark_pri_key="your-stark-private-key" + ) + + # Define message handlers + def ticker_handler(message): + print(f"Ticker Update: {message}") + + def kline_handler(message): + print(f"K-line Update: {message}") + + # Connect to public WebSocket for market data + ws_manager.connect_public() + + # Subscribe to real-time updates for BNB2USDT (contract ID: 10000004) + ws_manager.subscribe_ticker("10000004", ticker_handler) + ws_manager.subscribe_kline("10000004", "1m", kline_handler) + + # Connect to private WebSocket for account updates + ws_manager.connect_private() + + # Wait for updates + await asyncio.sleep(30) + + # Disconnect all connections + ws_manager.disconnect_all() + +asyncio.run(main()) +``` + +## Signing Adapters + +The SDK provides a flexible signing mechanism through signing adapters. **StarkExSigningAdapter is used by default**, so you don't need to explicitly create one: + +```python +from edgex_sdk import Client + +# Create a client (uses StarkExSigningAdapter by default) +client = Client( + base_url="https://pro.edgex.exchange", # Use https://testnet.edgex.exchange for testnet + account_id=12345, + stark_private_key="your-stark-private-key" +) +``` + +If you need to use a custom signing adapter, you can still provide one: + +```python +from edgex_sdk import Client, StarkExSigningAdapter + +# Create a custom signing adapter (optional) +signing_adapter = StarkExSigningAdapter() + +# Create a client with a custom signing adapter +client = Client( + base_url="https://pro.edgex.exchange", # Use https://testnet.edgex.exchange for testnet + account_id=12345, + stark_private_key="your-stark-private-key", + signing_adapter=signing_adapter +) +``` + +The SDK includes the following signing adapters: + +- **StarkExSigningAdapter** (default): Full implementation using StarkWare cryptographic operations for production use + +You can also create your own signing adapter by implementing the `SigningAdapter` interface if you need custom cryptographic operations. + +## Error Handling + +The SDK provides proper error handling for API requests: + +```python +import asyncio +from edgex_sdk import Client, OrderSide + +async def main(): + client = Client( + base_url="https://pro.edgex.exchange", # Use https://testnet.edgex.exchange for testnet + account_id=12345, + stark_private_key="your-stark-private-key" + ) + + try: + # Create a limit order for BNB2USDT + order = await client.create_limit_order( + contract_id="10000004", # BNB2USDT + size="0.01", + price="600.00", + side=OrderSide.BUY + ) + print(f"Order created: {order}") + + # Cancel the order + from edgex_sdk import CancelOrderParams + cancel_params = CancelOrderParams( + order_id=order.get("data", {}).get("orderId") + ) + cancel_result = await client.cancel_order(cancel_params) + print(f"Order cancelled: {cancel_result}") + + except ValueError as e: + print(f"Failed to create/cancel order: {str(e)}") + except Exception as e: + print(f"Unexpected error: {str(e)}") + +asyncio.run(main()) +``` + +## Pagination + +Many API endpoints support pagination: + +```python +import asyncio +from edgex_sdk import Client, GetActiveOrderParams + +async def main(): + client = Client( + base_url="https://pro.edgex.exchange", # Use https://testnet.edgex.exchange for testnet + account_id=12345, + stark_private_key="your-stark-private-key" + ) + + # Create pagination parameters + params = GetActiveOrderParams( + size="10", + offset_data="" + ) + + # Get active orders + orders = await client.get_active_orders(params) + print(f"Active orders: {orders}") + + # Get next page if available + if orders.get("data", {}).get("hasNext"): + params.offset_data = orders.get("data", {}).get("offsetData") + next_page = await client.get_active_orders(params) + print(f"Next page: {next_page}") + +asyncio.run(main()) +``` + +## API Examples + +### Market Data + +```python +from edgex_sdk import Client, GetKLineParams, GetOrderBookDepthParams + +# Get 24-hour market quotes for BNB2USDT (contract ID: 10000004) +quote = await client.get_24_hour_quote("10000004") +print(f"Current price: {quote}") + +# Get K-line data for BTCUSDT (contract ID: 10000001) +kline_params = GetKLineParams( + contract_id="10000001", # BTCUSDT + interval="1m", + size="10" +) +klines = await client.quote.get_k_line(kline_params) +print(f"K-lines: {klines}") + +# Get order book depth for ETHUSDT (contract ID: 10000002) +depth_params = GetOrderBookDepthParams( + contract_id="10000002", # ETHUSDT + limit=10 +) +depth = await client.quote.get_order_book_depth(depth_params) +print(f"Order book: {depth}") +``` + +### Account Management + +```python +# Get account assets +assets = await client.get_account_asset() +print(f"Account assets: {assets}") + +# Get account positions +positions = await client.get_account_positions() +print(f"Positions: {positions}") + +# Get position transactions +from edgex_sdk import GetPositionTransactionPageParams +tx_params = GetPositionTransactionPageParams( + size="10", + offset_data="" +) +transactions = await client.account.get_position_transaction_page(tx_params) +print(f"Transactions: {transactions}") +``` + +### Order Management + +```python +from edgex_sdk import OrderSide, CreateOrderParams, CancelOrderParams + +# Create a limit order for BNBUSDT +order = await client.create_limit_order( + contract_id="10000004", # BNBUSDT + size="0.01", + price="600.00", + side=OrderSide.BUY +) +print(f"Order created: {order}") + +# Get maximum order size for BNBUSDT +max_size = await client.get_max_order_size("10000004", 600.00) +print(f"Max order size: {max_size}") + +# Cancel an order +cancel_params = CancelOrderParams( + order_id=order.get("data", {}).get("orderId") +) +cancel_result = await client.cancel_order(cancel_params) +print(f"Order cancelled: {cancel_result}") +``` + +### Contract IDs + +EdgeX uses numeric contract IDs instead of symbol-based identifiers. Here are some common contract mappings: + +| Contract ID | Symbol | Tick Size | +|-------------|---------------|-----------| +| 10000001 | BTCUSDT | 0.1 | +| 10000002 | ETHUSDT | 0.01 | +| 10000003 | SOLUSDT | 0.01 | + +To get the complete list of available contracts: + +```python +metadata = await client.get_metadata() +contracts = metadata.get("data", {}).get("contractList", []) +for contract in contracts: + print(f"ID: {contract['contractId']} - {contract['contractName']}") +``` + +For more detailed examples, please refer to the [examples](examples) directory. + +## Testing + +The SDK includes comprehensive test coverage with multiple test suites: + +### Unit Tests +```bash +# Run unit tests (no API credentials required) +python -m pytest tests/test_client.py tests/test_starkex_signing_adapter.py -v +``` + +### Public API Tests +```bash +# Run public endpoint tests (no authentication required) +python run_public_tests.py +``` + +### Mock Integration Tests +```bash +# Run mock tests (test structure without real API calls) +python run_mock_tests.py +``` + +### Full Integration Tests +```bash +# Run full integration tests (requires real API credentials) +python run_integration_tests.py +``` + +### All Tests +```bash +# Run all available tests +python run_tests.py +``` + +For more testing information, see [TESTING.md](TESTING.md). + +## Environment Variables + +For testing and development, you can set the following environment variables or create a `.env` file: + +```bash +# API Configuration +EDGEX_BASE_URL=https://pro.edgex.exchange # Use https://testnet.edgex.exchange for testnet +EDGEX_WS_URL=wss://quote.edgex.exchange # Use wss://quote-testnet.edgex.exchange for testnet + +# Account Credentials +EDGEX_ACCOUNT_ID=12345 +EDGEX_STARK_PRIVATE_KEY=your-stark-private-key + +# Signing Configuration +EDGEX_SIGNING_ADAPTER=starkex +``` + +Then load them in your code: + +```python +import os +from dotenv import load_dotenv +from edgex_sdk import Client + +# Load environment variables from .env file +load_dotenv() + +client = Client( + base_url=os.getenv("EDGEX_BASE_URL"), + account_id=int(os.getenv("EDGEX_ACCOUNT_ID")), + stark_private_key=os.getenv("EDGEX_STARK_PRIVATE_KEY") +) +``` + +## Documentation + +For detailed API documentation, please refer to the [EdgeX API documentation](https://docs.edgex.exchange). + +## Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin feature/my-new-feature`) +5. Create a new Pull Request + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/__init__.py new file mode 100644 index 0000000..86ab4fe --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/__init__.py @@ -0,0 +1,72 @@ +""" +EdgeX Python SDK - A Python SDK for interacting with the EdgeX Exchange API. +""" + +from .client import Client +from .internal.signing_adapter import SigningAdapter +from .internal.starkex_signing_adapter import StarkExSigningAdapter +from .order.types import ( + OrderType, + OrderSide, + TimeInForce, + CreateOrderParams, + CancelOrderParams, + GetActiveOrderParams, + OrderFillTransactionParams +) +from .account.client import ( + GetPositionTransactionPageParams, + GetCollateralTransactionPageParams, + GetPositionTermPageParams, + GetAccountAssetSnapshotPageParams +) +from .quote.client import ( + GetKLineParams, + GetOrderBookDepthParams, + GetMultiContractKLineParams +) +from .transfer.client import ( + GetTransferOutByIdParams, + GetTransferInByIdParams, + GetWithdrawAvailableAmountParams, + CreateTransferOutParams, + GetTransferOutPageParams, + GetTransferInPageParams +) +from .asset.client import ( + GetAssetOrdersParams, + CreateWithdrawalParams, + GetWithdrawalRecordsParams +) +from .ws.manager import Manager as WebSocketManager + +__version__ = "0.2.0" +__all__ = [ + "Client", + "OrderType", + "OrderSide", + "TimeInForce", + "CreateOrderParams", + "CancelOrderParams", + "GetActiveOrderParams", + "OrderFillTransactionParams", + "GetPositionTransactionPageParams", + "GetCollateralTransactionPageParams", + "GetPositionTermPageParams", + "GetAccountAssetSnapshotPageParams", + "GetKLineParams", + "GetOrderBookDepthParams", + "GetMultiContractKLineParams", + "GetTransferOutByIdParams", + "GetTransferInByIdParams", + "GetWithdrawAvailableAmountParams", + "CreateTransferOutParams", + "GetTransferOutPageParams", + "GetTransferInPageParams", + "GetAssetOrdersParams", + "CreateWithdrawalParams", + "GetWithdrawalRecordsParams", + "WebSocketManager", + "SigningAdapter", + "StarkExSigningAdapter" +] \ No newline at end of file diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/account/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/account/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/account/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/account/client.py new file mode 100644 index 0000000..e81d16d --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/account/client.py @@ -0,0 +1,437 @@ +from typing import Dict, Any, List, Optional + +from ..internal.async_client import AsyncClient + + +class GetPositionTransactionPageParams: + """Parameters for getting position transactions with pagination.""" + + def __init__( + self, + size: str = "", + offset_data: str = "", + filter_contract_id_list: List[str] = None, + filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0 + ): + self.size = size + self.offset_data = offset_data + self.filter_contract_id_list = filter_contract_id_list or [] + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class GetCollateralTransactionPageParams: + """Parameters for getting collateral transactions with pagination.""" + + def __init__( + self, + size: str = "", + offset_data: str = "", + filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0 + ): + self.size = size + self.offset_data = offset_data + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class GetPositionTermPageParams: + """Parameters for getting position terms with pagination.""" + + def __init__( + self, + size: str = "", + offset_data: str = "", + filter_contract_id_list: List[str] = None, + filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0 + ): + self.size = size + self.offset_data = offset_data + self.filter_contract_id_list = filter_contract_id_list or [] + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class GetAccountAssetSnapshotPageParams: + """Parameters for getting account asset snapshots with pagination.""" + + def __init__( + self, + size: str = "", + offset_data: str = "", + filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0 + ): + self.size = size + self.offset_data = offset_data + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class Client: + """Client for account-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the account client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def get_account_asset(self) -> Dict[str, Any]: + """ + Get the account asset information. + + Returns: + Dict[str, Any]: The account asset information + + Raises: + ValueError: If the request fails + """ + params = { + "accountId": str(self.async_client.get_account_id()) + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/account/getAccountAsset", + params=params + ) + + async def get_account_positions(self) -> Dict[str, Any]: + """ + Get the account positions. + + Note: This calls the same endpoint as get_account_asset, which returns both + collateral and position data. The position data is in the 'positionAssetList' field. + + Returns: + Dict[str, Any]: The account positions (same as account asset response) + + Raises: + ValueError: If the request fails + """ + # Use the same endpoint as get_account_asset (matching Go SDK behavior) + return await self.get_account_asset() + + async def get_position_transaction_page(self, params: GetPositionTransactionPageParams) -> Dict[str, Any]: + """ + Get the position transactions with pagination. + + Args: + params: Position transaction query parameters + + Returns: + Dict[str, Any]: The position transactions + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_contract_id_list: + query_params["filterContractIdList"] = ",".join(params.filter_contract_id_list) + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/account/getPositionTransactionPage", + params=query_params + ) + + async def get_collateral_transaction_page(self, params: GetCollateralTransactionPageParams) -> Dict[str, Any]: + """ + Get the collateral transactions with pagination. + + Args: + params: Collateral transaction query parameters + + Returns: + Dict[str, Any]: The collateral transactions + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/account/getCollateralTransactionPage", + params=query_params + ) + + async def get_position_term_page(self, params: GetPositionTermPageParams) -> Dict[str, Any]: + """ + Get the position terms with pagination. + + Args: + params: Position term query parameters + + Returns: + Dict[str, Any]: The position terms + + Raises: + ValueError: If the request fails + """ + url = f"{self.base_url}/api/v1/private/account/getPositionTermPage" + query_params = { + "accountId": str(self.internal_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_contract_id_list: + query_params["filterContractIdList"] = ",".join(params.filter_contract_id_list) + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + response = self.session.get(url, params=query_params) + + if response.status_code != 200: + raise ValueError(f"request failed with status code: {response.status_code}") + + resp_data = response.json() + + if resp_data.get("code") != ResponseCode.SUCCESS: + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + async def get_account_by_id(self) -> Dict[str, Any]: + """ + Get account information by ID. + + Returns: + Dict[str, Any]: The account information + + Raises: + ValueError: If the request fails + """ + params = { + "accountId": str(self.async_client.get_account_id()) + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/account/getAccountById", + params=params + ) + + async def get_account_deleverage_light(self) -> Dict[str, Any]: + """ + Get account deleverage light information. + + Returns: + Dict[str, Any]: The account deleverage light information + + Raises: + ValueError: If the request fails + """ + url = f"{self.base_url}/api/v1/private/account/getAccountDeleverageLight" + params = { + "accountId": str(self.internal_client.get_account_id()) + } + + response = self.session.get(url, params=params) + + if response.status_code != 200: + raise ValueError(f"request failed with status code: {response.status_code}") + + resp_data = response.json() + + if resp_data.get("code") != ResponseCode.SUCCESS: + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + async def get_account_asset_snapshot_page(self, params: GetAccountAssetSnapshotPageParams) -> Dict[str, Any]: + """ + Get account asset snapshots with pagination. + + Args: + params: Account asset snapshot query parameters + + Returns: + Dict[str, Any]: The account asset snapshots + + Raises: + ValueError: If the request fails + """ + url = f"{self.base_url}/api/v1/private/account/getAccountAssetSnapshotPage" + query_params = { + "accountId": str(self.internal_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + response = self.session.get(url, params=query_params) + + if response.status_code != 200: + raise ValueError(f"request failed with status code: {response.status_code}") + + resp_data = response.json() + + if resp_data.get("code") != ResponseCode.SUCCESS: + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + async def get_position_transaction_by_id(self, transaction_ids: List[str]) -> Dict[str, Any]: + """ + Get position transactions by IDs. + + Args: + transaction_ids: List of transaction IDs + + Returns: + Dict[str, Any]: The position transactions + + Raises: + ValueError: If the request fails + """ + url = f"{self.base_url}/api/v1/private/account/getPositionTransactionById" + query_params = { + "accountId": str(self.internal_client.get_account_id()), + "transactionIdList": ",".join(transaction_ids) + } + + response = self.session.get(url, params=query_params) + + if response.status_code != 200: + raise ValueError(f"request failed with status code: {response.status_code}") + + resp_data = response.json() + + if resp_data.get("code") != ResponseCode.SUCCESS: + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + async def get_collateral_transaction_by_id(self, transaction_ids: List[str]) -> Dict[str, Any]: + """ + Get collateral transactions by IDs. + + Args: + transaction_ids: List of transaction IDs + + Returns: + Dict[str, Any]: The collateral transactions + + Raises: + ValueError: If the request fails + """ + url = f"{self.base_url}/api/v1/private/account/getCollateralTransactionById" + query_params = { + "accountId": str(self.internal_client.get_account_id()), + "transactionIdList": ",".join(transaction_ids) + } + + response = self.session.get(url, params=query_params) + + if response.status_code != 200: + raise ValueError(f"request failed with status code: {response.status_code}") + + resp_data = response.json() + + if resp_data.get("code") != ResponseCode.SUCCESS: + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + async def update_leverage_setting(self, contract_id: str, leverage: str) -> None: + """ + Update the account leverage settings. + + Args: + contract_id: The contract ID + leverage: The leverage value + + Raises: + ValueError: If the request fails + """ + url = f"{self.base_url}/api/v1/private/account/updateLeverageSetting" + data = { + "accountId": str(self.internal_client.get_account_id()), + "contractId": contract_id, + "leverage": leverage + } + + response = self.session.post(url, json=data) + + if response.status_code != 200: + raise ValueError(f"request failed with status code: {response.status_code}") + + resp_data = response.json() + + if resp_data.get("code") != ResponseCode.SUCCESS: + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/asset/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/asset/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/asset/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/asset/client.py new file mode 100644 index 0000000..592ec10 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/asset/client.py @@ -0,0 +1,300 @@ +from typing import Dict, Any, List + +from ..internal.async_client import AsyncClient + + +class GetAssetOrdersParams: + """Parameters for getting asset orders.""" + + def __init__(self, size: str = "10", offset_data: str = "", filter_coin_id_list: List[str] = None, + filter_start_created_time_inclusive: int = 0, filter_end_created_time_exclusive: int = 0): + self.size = size + self.offset_data = offset_data + self.filter_coin_id_list = filter_coin_id_list or [] + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class CreateWithdrawalParams: + """Parameters for creating a withdrawal.""" + + def __init__(self, coin_id: str, amount: str, address: str, tag: str = ""): + self.coin_id = coin_id + self.amount = amount + self.address = address + self.tag = tag + + +class GetWithdrawalRecordsParams: + """Parameters for getting withdrawal records.""" + + def __init__(self, size: str = "10", offset_data: str = "", filter_coin_id_list: List[str] = None, + filter_status_list: List[str] = None, filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0): + self.size = size + self.offset_data = offset_data + self.filter_coin_id_list = filter_coin_id_list or [] + self.filter_status_list = filter_status_list or [] + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class Client: + """Client for asset-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the asset client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def get_account_asset(self) -> Dict[str, Any]: + """ + Get the account asset information. + Note: This method delegates to the account client since it's an account endpoint. + + Returns: + Dict[str, Any]: The account asset information + + Raises: + ValueError: If the request fails + """ + # This is actually an account endpoint, not an asset endpoint + # We should delegate to the account client + raise NotImplementedError("This method should be called from the account client: client.account.get_account_asset()") + + async def get_asset_orders( + self, + params: GetAssetOrdersParams + ) -> Dict[str, Any]: + """ + Get asset orders with pagination. + + Args: + params: Parameters for the request + + Returns: + Dict[str, Any]: The asset orders + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/assets/getAllOrdersPage", + params=query_params + ) + + async def get_coin_rates(self, chain_id: str = "1", coin: str = "0xdac17f958d2ee523a2206206994597c13d831ec7") -> Dict[str, Any]: + """ + Get coin rates. + + Args: + chain_id: Chain ID (default: "1" for Ethereum mainnet) + coin: Coin contract address (default: USDT) + + Returns: + Dict[str, Any]: The coin rates + + Raises: + ValueError: If the request fails + """ + params = { + "chainId": chain_id, + "coin": coin + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/assets/getCoinRate", + params=params + ) + + async def create_withdrawal( + self, + coin_id: str, + amount: str, + address: str, + network: str, + memo: str = "", + client_order_id: str = None + ) -> Dict[str, Any]: + """ + Create a withdrawal request. + + Args: + coin_id: The coin ID + amount: The withdrawal amount + address: The withdrawal address + network: The network + memo: Optional memo + client_order_id: Optional client order ID + + Returns: + Dict[str, Any]: The withdrawal result + + Raises: + ValueError: If the request fails + """ + data = { + "accountId": str(self.async_client.get_account_id()), + "coinId": coin_id, + "amount": amount, + "address": address, + "network": network + } + + if memo: + data["memo"] = memo + + if client_order_id: + data["clientOrderId"] = client_order_id + else: + data["clientOrderId"] = self.async_client.generate_uuid() + + return await self.async_client.make_authenticated_request( + method="POST", + path="/api/v1/private/assets/createNormalWithdraw", + data=data + ) + + async def get_withdrawal_records( + self, + size: str = "", + offset_data: str = "", + filter_coin_id_list: List[str] = None, + filter_status_list: List[str] = None, + filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0 + ) -> Dict[str, Any]: + """ + Get withdrawal records with pagination. + + Args: + size: Size of the page + offset_data: Offset data for pagination + filter_coin_id_list: Filter by coin IDs + filter_status_list: Filter by status + filter_start_created_time_inclusive: Filter start time (inclusive) + filter_end_created_time_exclusive: Filter end time (exclusive) + + Returns: + Dict[str, Any]: The withdrawal records + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if size: + query_params["size"] = size + if offset_data: + query_params["offsetData"] = offset_data + + # Add filter parameters + if filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(filter_coin_id_list) + if filter_status_list: + query_params["filterStatusList"] = ",".join(filter_status_list) + + # Add time filters + if filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(filter_start_created_time_inclusive) + if filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/assets/getNormalWithdrawById", + params=query_params + ) + + async def get_withdrawable_amount(self, address: str) -> Dict[str, Any]: + """ + Get the withdrawable amount for a coin. + + Args: + address: The coin contract address + + Returns: + Dict[str, Any]: The withdrawable amount information + + Raises: + ValueError: If the request fails + """ + query_params = { + "address": address + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/assets/getNormalWithdrawableAmount", + params=query_params + ) + + async def get_withdrawal_records(self, params: GetWithdrawalRecordsParams) -> Dict[str, Any]: + """ + Get withdrawal records with pagination. + + Args: + params: Parameters for the request + + Returns: + Dict[str, Any]: The withdrawal records + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + if params.filter_status_list: + query_params["filterStatusList"] = ",".join(params.filter_status_list) + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/assets/getNormalWithdrawById", + params=query_params + ) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/client.py new file mode 100644 index 0000000..55654b9 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/client.py @@ -0,0 +1,264 @@ +import json +import time +from typing import Dict, Any, Optional, List, Union +from decimal import Decimal + +from .internal.async_client import AsyncClient +from .internal.signing_adapter import SigningAdapter +from .internal.starkex_signing_adapter import StarkExSigningAdapter +from .account.client import Client as AccountClient +from .asset.client import Client as AssetClient +from .funding.client import Client as FundingClient +from .metadata.client import Client as MetadataClient +from .order.client import Client as OrderClient +from .quote.client import Client as QuoteClient +from .transfer.client import Client as TransferClient +from .order.types import CreateOrderParams, CancelOrderParams, GetActiveOrderParams, OrderFillTransactionParams + + +class Client: + """Main EdgeX SDK client.""" + + def __init__(self, base_url: str, account_id: int, stark_private_key: str, + signing_adapter: Optional[SigningAdapter] = None, timeout: float = 30.0): + """ + Initialize the EdgeX SDK client. + + Args: + base_url: Base URL for API endpoints + account_id: Account ID for authentication + stark_private_key: Stark private key for signing + signing_adapter: Optional signing adapter (defaults to StarkExSigningAdapter) + timeout: Request timeout in seconds + """ + # Use StarkExSigningAdapter as default if none provided + if signing_adapter is None: + signing_adapter = StarkExSigningAdapter() + + # Create async client + self.async_client = AsyncClient( + base_url=base_url, + account_id=account_id, + stark_pri_key=stark_private_key, + signing_adapter=signing_adapter, + timeout=timeout + ) + + # Initialize API clients + self.metadata = MetadataClient(self.async_client) + self.account = AccountClient(self.async_client) + self.order = OrderClient(self.async_client) + self.quote = QuoteClient(self.async_client) + self.funding = FundingClient(self.async_client) + self.transfer = TransferClient(self.async_client) + self.asset = AssetClient(self.async_client) + + async def __aenter__(self): + """Async context manager entry.""" + await self.async_client._ensure_session() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """Async context manager exit.""" + await self.close() + + async def close(self): + """Close the client and cleanup resources.""" + await self.async_client.close() + + @property + def internal_client(self): + """Backward compatibility property for accessing internal client.""" + return self.async_client + + async def get_metadata(self) -> Dict[str, Any]: + """Get the exchange metadata.""" + return await self.metadata.get_metadata() + + async def get_server_time(self) -> Dict[str, Any]: + """Get the current server time.""" + return await self.metadata.get_server_time() + + async def create_order(self, params: CreateOrderParams) -> Dict[str, Any]: + """ + Create a new order with the given parameters. + + Args: + params: Order parameters + + Returns: + Dict[str, Any]: The created order + """ + # Get metadata first + metadata = await self.get_metadata() + if not metadata: + raise ValueError("failed to get metadata") + + return await self.order.create_order(params, metadata.get("data", {})) + + async def get_max_order_size(self, contract_id: str, price: Decimal) -> Dict[str, Any]: + """ + Get the maximum order size for a given contract and price. + + Args: + contract_id: The contract ID + price: The price + + Returns: + Dict[str, Any]: The maximum order size information + """ + return await self.order.get_max_order_size(contract_id, float(price)) + + async def cancel_order(self, params: CancelOrderParams) -> Dict[str, Any]: + """ + Cancel a specific order. + + Args: + params: Cancel order parameters + + Returns: + Dict[str, Any]: The cancellation result + """ + return await self.order.cancel_order(params) + + async def get_active_orders(self, params: GetActiveOrderParams) -> Dict[str, Any]: + """ + Get active orders with pagination and filters. + + Args: + params: Active order query parameters + + Returns: + Dict[str, Any]: The active orders + """ + return await self.order.get_active_orders(params) + + async def get_order_fill_transactions(self, params: OrderFillTransactionParams) -> Dict[str, Any]: + """ + Get order fill transactions with pagination and filters. + + Args: + params: Order fill transaction query parameters + + Returns: + Dict[str, Any]: The order fill transactions + """ + return await self.order.get_order_fill_transactions(params) + + async def get_account_asset(self) -> Dict[str, Any]: + """Get the account asset information.""" + return await self.account.get_account_asset() + + async def get_account_positions(self) -> Dict[str, Any]: + """Get the account positions.""" + return await self.account.get_account_positions() + + async def create_limit_order( + self, + contract_id: str, + size: str, + price: str, + side: str, + client_order_id: Optional[str] = None + ) -> Dict[str, Any]: + """ + Create a new limit order with the given parameters. + + Args: + contract_id: The contract ID + size: The order size + price: The order price + side: The order side (BUY or SELL) + client_order_id: Optional client order ID + + Returns: + Dict[str, Any]: The created order + """ + from .order.types import OrderType + + params = CreateOrderParams( + contract_id=contract_id, + size=size, + price=price, + side=side, + type=OrderType.LIMIT, + client_order_id=client_order_id + ) + + return await self.create_order(params) + + async def create_market_order( + self, + contract_id: str, + size: str, + side: str, + client_order_id: Optional[str] = None + ) -> Dict[str, Any]: + """ + Create a new market order with the given parameters. + + Args: + contract_id: The contract ID + size: The order size + side: The order side (BUY or SELL) + client_order_id: Optional client order ID + + Returns: + Dict[str, Any]: The created order + """ + # Get metadata for contract info + metadata = await self.get_metadata() + if not metadata: + raise ValueError("failed to get metadata") + + # Find the contract + contract = None + contract_list = metadata.get("data", {}).get("contractList", []) + for c in contract_list: + if c.get("contractId") == contract_id: + contract = c + break + + if not contract: + raise ValueError(f"contract not found: {contract_id}") + + # Calculate price based on side + from .order.types import OrderSide, OrderType + + if side == OrderSide.BUY: + # For buy orders: oracle_price * 10, rounded to price precision + quote = await self.get_24_hour_quote(contract_id) + if not quote: + raise ValueError("failed to get 24-hour quotes") + + oracle_price = Decimal(quote.get("data", [])[0].get("oraclePrice", "0")) + multiplier = Decimal("10") + tick_size = Decimal(contract.get("tickSize", "0")) + precision = abs(tick_size.as_tuple().exponent) + price = str(round(oracle_price * multiplier, precision)) + else: + # For sell orders: use tick size + price = contract.get("tickSize", "0") + + params = CreateOrderParams( + contract_id=contract_id, + size=size, + price=price, + side=side, + type=OrderType.MARKET, + client_order_id=client_order_id + ) + + return await self.create_order(params) + + async def get_24_hour_quote(self, contract_id: str) -> Dict[str, Any]: + """ + Get the 24-hour quotes for a given contract. + + Args: + contract_id: The contract ID + + Returns: + Dict[str, Any]: The 24-hour quotes + """ + return await self.quote.get_24_hour_quote(contract_id) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/__init__.py new file mode 100644 index 0000000..06c6c35 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/__init__.py @@ -0,0 +1,13 @@ +""" +Cryptographic utilities for the EdgeX Python SDK. + +This module provides cryptographic functions including Pedersen hash +implementation compatible with StarkWare's specifications. +""" + +from .pedersen_hash import pedersen_hash, pedersen_hash_as_point + +__all__ = [ + 'pedersen_hash', + 'pedersen_hash_as_point', +] diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/constants.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/constants.py new file mode 100644 index 0000000..a1820b8 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/constants.py @@ -0,0 +1,2565 @@ +""" +Constants for StarkWare cryptographic operations. + +This module contains the constant points and parameters used in +Pedersen hash calculations, extracted from the StarkWare specification. + +Contains the complete set of 506 constant points for full compatibility +with the Go SDK and StarkWare reference implementation. +""" + +# StarkEx curve parameters +FIELD_PRIME = 0x800000000000011000000000000000000000000000000000000000000000001 +ALPHA = 1 +BETA = 0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89 +EC_ORDER = 0x800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f + +# Number of bits used in hash calculations +N_ELEMENT_BITS_HASH = 252 + +# Shift point (first constant point) - matches Go SDK cfg.ConstantPoints[0] +SHIFT_POINT = [ + 2089986280348253421170679821480865132823066470938446095505822317253594081284, + 1713931329540660377023406109199410414810705867260802078187082345529207694986 +] + +# Generator point for the Stark curve +EC_GEN = [ + 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca, + 0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f +] + +# Full constant points for Pedersen hash - extracted from Go SDK +# This provides the complete set of 506 constant points needed for full compatibility +CONSTANT_POINTS = [ + # Point 0 + [ + 2089986280348253421170679821480865132823066470938446095505822317253594081284, + 1713931329540660377023406109199410414810705867260802078187082345529207694986 + ], + # Point 1 + [ + 874739451078007766457464989774322083649278607533249481151382481072868806602, + 152666792071518830868575557812948353041420400780739481342941381225525861407 + ], + # Point 2 + [ + 996781205833008774514500082376783249102396023663454813447423147977397232763, + 1668503676786377725805489344771023921079126552019160156920634619255970485781 + ], + # Point 3 + [ + 100775230685312048816501234355008830851785728808228209380195522984287974518, + 3198314560325546891798262260233968848553481119985289977998522774043088964633 + ], + # Point 4 + [ + 1837189741429329983886833789246131275985545035599091291966623919967841244204, + 469920083884440505232139273974987899994000885911056071194573294589259802432 + ], + # Point 5 + [ + 1337726844298689299569036965005062374791732295462158862097564380968412485659, + 3094702644796621069343809899235459280874613277076424986270525032931210979878 + ], + # Point 6 + [ + 2997390320399291502365701712271136720873363591256030629621859546399086933620, + 2725742381037070528763700586156979930560374472266472382691451570287013862562 + ], + # Point 7 + [ + 3608386523905995279224196894194758246854376991737956048428718275550441491554, + 299638830690759628369563708877422667364443387620215168054000198378323554222 + ], + # Point 8 + [ + 1733017745745290190841058775834438078769759612359153596488000160651631909868, + 1973340172374381850851160588687352250788736199336041450103281811142396650489 + ], + # Point 9 + [ + 855657745844414012325398643860801166203065495756352613799675558543302817038, + 1379036914678019505188657918379814767819231204146554192918997656166330268474 + ], + # Point 10 + [ + 2860710426779608457334569506319606721823380279653117262373857444958848532006, + 1390846552016301495855136360351297463700036202880431397235275981413499580322 + ], + # Point 11 + [ + 2395624363109833935111082867579092089638282063493755655374369403894420657396, + 351237427147755677344067136337758392262982966921757802462075586166198965221 + ], + # Point 12 + [ + 1518817631841006315871038165514435660668372956425694825094659891110110998470, + 2435234811597428668734076452595083530950234466401981561306652881621340269965 + ], + # Point 13 + [ + 2173245854114081430013864960244839145346281378834121479101410821419573677603, + 2546798213917003006819050845641858786968858658397560158974242382076827691040 + ], + # Point 14 + [ + 2842565516483040219247288049386440051275065340592157409909273207335045943247, + 3243970369543480657564144388570283526584293743815525434693286186817417955980 + ], + # Point 15 + [ + 334001339911595275369567085510917903426590364565508070786916614629507192987, + 3111246400312591389547128607242178414610449977696648758380570718520342084022 + ], + # Point 16 + [ + 1524160182224703084171959692156493185929433834577885561910744542328224256855, + 1537801596806048756579645687819844574476915843680990392821821911338917834516 + ], + # Point 17 + [ + 1534228784878613855372285213913393282004680247144707529194564051083323737667, + 3521706376781514787959257460794337508069645724875214092054188903006114926236 + ], + # Point 18 + [ + 2578937995141029655393232141271255572790413762563128577126763729975116228193, + 17390356333795810120168422581001175036590566546824644641783194730252048211 + ], + # Point 19 + [ + 947940612979492942947148169286573131514814097313999984923945564630579515590, + 2308193393705297792974084886503909156992936885451139308263357445074155842124 + ], + # Point 20 + [ + 732404465937527082089939128149870791505934917542321234949662968808570781433, + 143709480454569956048931032102611838633822436488408778496842771196869318906 + ], + # Point 21 + [ + 241248627215637165874725355816367843299343644290443713521922700286140902436, + 3252553440660691138666231381716834106176440363202963142721270080741642531818 + ], + # Point 22 + [ + 3333115552336678637619322993761507811794447605372046548664704236825849321847, + 2074314011440265695926966409849756773065015399410882685131987099183343980472 + ], + # Point 23 + [ + 2828708362623152676836369441327395494506045083356924287447843608221054063061, + 107382801318187992328203770492115828936772008265759480771447426051158848300 + ], + # Point 24 + [ + 3093728769381682918281543022553646237541394965209383769732554106568421526166, + 3204173745255459543321323207111205642245664180117592291733272407863239345733 + ], + # Point 25 + [ + 2408410160653222627937499570601090771762354825212795227033567284727088044150, + 2304538566806563442614047090440785060491938762209719835685218901694719627776 + ], + # Point 26 + [ + 2758360715188072223623539313334284404194065029791792170224299872004682172868, + 1002646182229402950578888347706450598176482335256655665515308125378628073404 + ], + # Point 27 + [ + 2379910339855741683480364155463331175570260120162494489033888506779165916952, + 2649708833736663077287705299849077665696945338155198794587505679066706972556 + ], + # Point 28 + [ + 1176714920396664309204390318351093831295503091238549055894748705033779114462, + 715774545317274898026140714630411642171682270543528864055131586173491427672 + ], + # Point 29 + [ + 2785974441098456234843127330799770200846625265290625972155616950088804499059, + 307863489533861377687037248795744305150392367370243564208692826588510059533 + ], + # Point 30 + [ + 3127903794657845782054923624413460963746108626701051648823597412593664219443, + 2832400994360149010034695923237223654142501296305205824531678157494587069403 + ], + # Point 31 + [ + 1131830029003838132931634271160654858275272609594100638978880153740390535738, + 3607754722674459909791405256520586221653709952825470711876211792388292839610 + ], + # Point 32 + [ + 2759794674261780431984200708995704387783325908768350345798229435903528807938, + 1260417916396710926345525006943606967340884567049582926597956766543273788168 + ], + # Point 33 + [ + 2830057895043497782751868691208958763779500933641414034760294364554584648598, + 3148801330152002136119343944143478481505330324328753740340717562089558415415 + ], + # Point 34 + [ + 2506640265270419609137616465635205683276867684162736282412466285973014171890, + 517183264945713035190384665697926865674306942691511144684578407765174829225 + ], + # Point 35 + [ + 297135274309227547571122074141892368978567606416603548099453251059259457396, + 738308515934554491948011858951484620721581230541917718419817808748771885016 + ], + # Point 36 + [ + 3287710003144516108009450594509223314440628263909148329742349774812346409307, + 225177091586755328705836619671963709040245082813366263262829376695813391167 + ], + # Point 37 + [ + 1902667075164809149654789463437998238417922554649696913795190312096632954124, + 3609476325943007214468624874971854834826753291984136726633316509571578121273 + ], + # Point 38 + [ + 3452217073856686233854377494033704667278961088549888774623466171791636016755, + 2444544408047898094236889539040891081629098003972134793849574953018755818545 + ], + # Point 39 + [ + 2069167537346986671273731269107346759773110422380837126332493778223975152855, + 357269144817598369811221449775153952909196646906055962631273486250245080334 + ], + # Point 40 + [ + 1047243972090526803876529530618926456830431728514494419504365869386003201726, + 493385597033162791196594107722557650629404615534399757351633988473756315396 + ], + # Point 41 + [ + 965109286411904242713728581817485738428793211202426744037474223240067211186, + 2413690664561921424572393647761853376475215279589433231249258838213909974115 + ], + # Point 42 + [ + 1034828486658124322341241159997750207973283714015564270100039839265723642437, + 3320659525509256706388336697822491014651786050898707765488292630053240833630 + ], + # Point 43 + [ + 2924967592602073254569141793533465663326310402870978597207810641466195156731, + 2828397747600941786969312189630745459745605646639868710157419955197341205208 + ], + # Point 44 + [ + 1208329771806436797417016120088886878299172415075658275611114767604464163273, + 2810710693404583496584233908768327740199404051365142000873009254465681789118 + ], + # Point 45 + [ + 2187000042175732773516370724251479381096587891539947053664138571731476871402, + 746498189666538551392041580103908969319041062419800073057943031798594621183 + ], + # Point 46 + [ + 2000452964676707688903182602940959322690781577603915068119089450310903786954, + 866954387631286490641992457358203082174659959202834537990114265745821626191 + ], + # Point 47 + [ + 1296834309098498653963459206815713058813876510889656567958698555814791917906, + 900321515532234476515871433334993494171305935536543684417444011164731278279 + ], + # Point 48 + [ + 1595376832537627540806114085753076669172519984983967639366317789074759898235, + 1219266627855965397760533984052253611682860681989985974389624113620845749733 + ], + # Point 49 + [ + 1823240537897691300512000714094702014772232075125035605123065504505635249040, + 1906261986240090609038909222466712928329872469704972427112770233118685440655 + ], + # Point 50 + [ + 778303663772980866291056760213466667611301230393329301216572062540133184143, + 2984789228888160339109292850517099811453943454548552440328876677370962441196 + ], + # Point 51 + [ + 3543009119282959041814671650391719969699313481882590413207543576117841443934, + 1490213523215199378557197585333711645365263188010733339965078460230935145833 + ], + # Point 52 + [ + 1176236937487751405961855617527764992282992896230256112304717984169523263763, + 327501310716241925530534584357204203311238806558120970999031300336125027957 + ], + # Point 53 + [ + 2406374227501733859839714271591391036982988438954690468147627905682319529429, + 2498960794066678523664440543302013058525262611284856216226688049821146904878 + ], + # Point 54 + [ + 3452133497919418476271423809649290075304287340106989073706651714039300732642, + 1721794031770397703814538822528137647140579794352083932468384744314312603894 + ], + # Point 55 + [ + 1149850245936233973982137051189893468427998957468612707869248329754912343300, + 3458926667343838493950348154788263034977717528749131548957463567618227215963 + ], + # Point 56 + [ + 889697158819326131781010711389595245311511671705340221964679483759691059211, + 2019807322878676390755723975464224869137141739292295209065143377221547630036 + ], + # Point 57 + [ + 1115329342882491971826579323754611049286425400118842701871616630850493708107, + 279298782170669703031554266329450929495785208527313704344065678320374720785 + ], + # Point 58 + [ + 3365519876326833923487050935023315949049714901059255282163633136201965868269, + 1868260280532817409831058300719431634773697469228981331390874154636702517757 + ], + # Point 59 + [ + 2669519052792032403625707785046224942570603898100356640293648365762798392505, + 1740654081939116207510779753054238062700744649382792523869583125748991653229 + ], + # Point 60 + [ + 3121331648294614359396440970780137002689430823241383158647529355695088932901, + 873685066624425351999444458200994077868639460913455806504495956245857350007 + ], + # Point 61 + [ + 1816660828193076969492079690868149793376961857938776531133929200951688539889, + 1617163330180274180907112309026652344859788476549850480902966972316617122251 + ], + # Point 62 + [ + 928998762106806361096934313135623561544110643422813945289044484271228303836, + 464078854784700975668790446344005332658112186605529977536203111266829995975 + ], + # Point 63 + [ + 496484433760448456019075524089809339174015679144397089226817311073355526314, + 656343647912825200827812764729017634457244310809059156122299990679423100787 + ], + # Point 64 + [ + 1973676598953671410920434538501081964242302598763799105681937944844095305545, + 307489436917501023536717840176704363109773690206006513056594461780393179493 + ], + # Point 65 + [ + 518536787018692743767191710241721928752533989539898170372615978836875877432, + 2521626354257053998255710814293449754273786720271901759585107157901808273967 + ], + # Point 66 + [ + 690595468184470683559850269431813912957862648277508592008590933748732173747, + 2414429566032394919031053748274838227119236756361308391916397575422431579532 + ], + # Point 67 + [ + 2501095101472669025652293419986706422252484721018908950115277955062729551801, + 2787294359824056854441860940419185812333861607391901444904784313692077324784 + ], + # Point 68 + [ + 565118044580500186326118761527011487144705745596478022906701885062524158603, + 1799231527053210762358771329838631545632111862410291843983616507220396846052 + ], + # Point 69 + [ + 2037340835455495949556975891561839169602876578929015568690589435716642289270, + 2204387525434065888311157590483645040393870259860783204107223503007512510657 + ], + # Point 70 + [ + 2114937389277866993631127029230629118622631637448671765297876516930885448024, + 2772043872341063054220063825377798998299638741683281348998984204361894484463 + ], + # Point 71 + [ + 2097763236780897995115236415009286780722416534493059040644518024665003224715, + 2246556465712181592290422124919768779861225704408240007042932086214236197576 + ], + # Point 72 + [ + 3612119898822167069923931382556535386023574823466693257258182923730749602297, + 1269484610898538742657592460359658026176515519847024771198089933933954376158 + ], + # Point 73 + [ + 2881403268965266082547256964340568575654154178897275699709021163679539118655, + 1692819601432103623771042884269589390667189502599274435132350636867812310182 + ], + # Point 74 + [ + 750328371578183028452365126719312491229205795053566378454173411358094968605, + 953119186030327873981560224892058145045695472203493861081607133113834466378 + ], + # Point 75 + [ + 1656613311827787565035393466226110392419398195273172946657205368665450125099, + 2599745208280264384426758693760932474903320975443399225749144566172064401743 + ], + # Point 76 + [ + 2103692960985198293421966097241235578982164940176248129325331384077838061492, + 2570917734334329081039835855157625956461983506342767455507599344910133039387 + ], + # Point 77 + [ + 2956028884218562660581192985661469944972780357808337397013249554146206921192, + 408046758724714924414144046633476879802492779243354079741111917699605101737 + ], + # Point 78 + [ + 3295092543885183250245242131201657515391353210344574951962581116320755471573, + 1642807297265426717090166824916187234878363764999511552230372565887780637331 + ], + # Point 79 + [ + 3254747955211938731592559910914052760563649051803026767692046312174361324755, + 2597825541939769956115552787003343561035903289305000309435219987085455364052 + ], + # Point 80 + [ + 3061661576103456171574781131851984681727274357576590194413976242212020548895, + 1440946704104779823819051619396174008908467766221900978049401983321224347705 + ], + # Point 81 + [ + 838962129372725157269649943170983294581294061955271030939039912615696392969, + 2450855656904900531871678154573620082208900269187857841648756800522172293828 + ], + # Point 82 + [ + 623738578803750870859101112646088321855359403665383400994605481921028955581, + 1896987617779463068695767102228909909503881877404532152457554106592081120284 + ], + # Point 83 + [ + 3005336317328034100451063789097066979058288244531776915568829475740707033442, + 589659290793751995469380634546964377934517395448274738900772252627065284531 + ], + # Point 84 + [ + 12756189276497641079437863756137709474617047416617345367587373008165663758, + 1456142055978131072821045695636502275738045623685201320945241755292775664199 + ], + # Point 85 + [ + 1074674018821399880635425421583189259497571069746227839865670725436462244312, + 258858007368753107323242423300483485125794748975129950895267988132082308316 + ], + # Point 86 + [ + 681655034855073789847960596245322342285304909895796912572444021565696961022, + 2638585878119722820634387321875125243576892120356441191562743859796708349183 + ], + # Point 87 + [ + 484275861089119330366419813397313284916585101030604880621077715936286046362, + 2124340832207188633301820498272014794490866326891190193431359618065899307375 + ], + # Point 88 + [ + 405073150800337564971044639647327667948449004749665061152270060911349262451, + 1489771173602690638325396124433593050299556632624187628464627170731054511183 + ], + # Point 89 + [ + 2210274362697653963013276763896406028937726941229800687802476566182252928069, + 107281585620564596853732679859399446219714819235699352412476509704530753455 + ], + # Point 90 + [ + 1574843453768695165378209623495036727403155857878058624836922271817659867629, + 1555661183633642518402202300513733784743599394430116656383692711334768895734 + ], + # Point 91 + [ + 1123478630620218654285047429952065588412730975530361457071991777237712271756, + 2829567770920061439901039508116248775591132699709271161323340183772358690173 + ], + # Point 92 + [ + 1297650053639848975729187455845252204377426797153455899043246280172874099679, + 1368877225005672215217218872501867910626338344551878492763876596080463242259 + ], + # Point 93 + [ + 1370690034945541589206390781179695998551445194688862467131056389583582009970, + 1470539777067742221259042319802797839837448440044303811384116671245122402709 + ], + # Point 94 + [ + 3447187626984579154758815357460309989835728867823525757738362796350869385148, + 1816603979655684111669599224323721980301208636016613170580056711560881185458 + ], + # Point 95 + [ + 1813653163931885994109909806517422543930671118466754170167858925831571853970, + 3512341620174737184060311796746868634824078847254277831432645720920236439319 + ], + # Point 96 + [ + 2837307695083915103816975903131537789416753154015497312646069123780420491714, + 616619065833574133353826959462642204697394233876988991826103321230494864299 + ], + # Point 97 + [ + 594079125176886234216350989657852431152571525526195525372802362637040396383, + 2494644159641327263522333201651180948727022160625648361761185979144016156268 + ], + # Point 98 + [ + 2908396604150117877413275805593843594741970566352971841958566678954509739722, + 2270651079544400929734484036612745055500450526594372099549710701035274374884 + ], + # Point 99 + [ + 2678766929120334020289731872716504689707050034449508844312788171212235149009, + 335161156418784138664067328871308180390826302690183885244799298812600140579 + ], + # Point 100 + [ + 292866251242750359983852405638782648443335655395113648998608114038199112084, + 1929874131411396072089047462553509072816290408149385625319723159940078151305 + ], + # Point 101 + [ + 882230982554387611436849430391722624915254936568405968528170702860016504332, + 212032938028459898975134792582247420939126124794435382098720034240512634047 + ], + # Point 102 + [ + 128726401362994700814735301317860445928097536803417431852613367962589785439, + 3394124118935926464118252583967031473364357246179309645829616988941527947171 + ], + # Point 103 + [ + 677401161940040406778133733501716798305847724216576059351439033320244262845, + 2925999450717968641674678177839015248195288227966544952091044204990727381539 + ], + # Point 104 + [ + 2609307202957921769201454597723184920663265694086171832237331231605229071753, + 801185762689670517839772705471851009753066843170718046248101910206876036095 + ], + # Point 105 + [ + 3164184719276807654208802895852587431370308788690119771658913844464285523544, + 1905779810840212631464048113896329901522020622508861085333684930860131307278 + ], + # Point 106 + [ + 3611134164349757211212839193742321198628214417127680099335976437205997336534, + 2043780132560451089266915016427409559115509465273768103961201197432849551860 + ], + # Point 107 + [ + 674219880578998785256251799630500124321523321851449394348048261225376968813, + 3482911053866603432328977995361936638192865211341332440041477929097847986703 + ], + # Point 108 + [ + 3592898179794583311559546383565181628347625484079136119992096748458485223135, + 1465170880570843353446345695255388397672185811127330686304727767842553737080 + ], + # Point 109 + [ + 3479945591612641094508473900775458017345694655844890750906112888442669167437, + 3101936119331166017640859304880796239643688879875971179134677606262220157899 + ], + # Point 110 + [ + 1109415449398025759620102870831886622665877707210305893342279346428768457000, + 2860907454930708330476655483302818728978205359193672814876717458325223426655 + ], + # Point 111 + [ + 1357688207483205144399854674655145367528198873070486160807161516476844171794, + 2883341787687489538845947867713953817206085374003322509387259371280461173550 + ], + # Point 112 + [ + 2673153466150226365693822405439428986034577351717746967078667507408831331822, + 1711608722888314089357865502727953201489434753448475578858737527005192231458 + ], + # Point 113 + [ + 3399600989769963316575978481506459307955666332477901574428783938231920315833, + 3215591135790328619011904119580811201910745729655431204459208397816583809934 + ], + # Point 114 + [ + 1836471948074401070182493369353230908796463402812051721703561360825727986274, + 2080945706738209357213401773997996954868066166149050730738271792162977075010 + ], + # Point 115 + [ + 3078673344420931157936045314816499193943345153816528362806332351627801597029, + 1525425884468796152026606689861526369646610285280224319617687192867257228599 + ], + # Point 116 + [ + 2722661126018430265331502523339121563282558062510457654064467493725828237046, + 2961950709147303512642570304801930511214999580227551995337732709133755475031 + ], + # Point 117 + [ + 3274644392893639165667150492068461730297126947445956777112794513023991125188, + 2045331969496016255571307687320013551853048491733214352646441341466617604859 + ], + # Point 118 + [ + 112765822376634836986557327486952401905342511371567932656784212360129920193, + 2925784973032974919740053039088226660025084255241123579355659877189527261408 + ], + # Point 119 + [ + 1615139200617101600744409619536119151548135245881789111743749077087127202607, + 928952548463900597431429074097494026341638502402888819595959975139532597586 + ], + # Point 120 + [ + 89110835703618626006281975484745142479630710003689233432269611283359724860, + 215577133560624823939271084084157878843706221683942956847919314607432159969 + ], + # Point 121 + [ + 2365204845648678418296386461366871427710672197784591168077970521383279482418, + 1305956795685348559654727794862561316677393382417655218949059817923691390961 + ], + # Point 122 + [ + 3079404798552502156907780297645909071656015399012681371402409463283987518254, + 21056707131743755971547625076568417377953234492785298586731786122687871587 + ], + # Point 123 + [ + 1774615721844036510130640283396354163953067525827829514432813052928446640946, + 2335149358230730291686085189834918003837685389648150105141235796252419309479 + ], + # Point 124 + [ + 1735808336641971303276438949279976616427622212845904655791080520595464040270, + 1731419497051910986191800745158520013128509429166504524452212426369487243072 + ], + # Point 125 + [ + 225628732048332665517140883504098234968194122687532130198116739739190659914, + 2967548981364742052624681942465301778302189310173531702492567780724033172480 + ], + # Point 126 + [ + 799750724987433962024178782345988625264638811940430837962580781131993819643, + 2706844911451851414869588131946375233343117270234616506423955430971490523817 + ], + # Point 127 + [ + 1369724618979165159567545620227678034464433354639639473366228046556595024708, + 2336720505293481339154253277307615910867992206313138581663759107186258146704 + ], + # Point 128 + [ + 1930319710428527822612172469423758994346239585108798378474140896733181974186, + 3075715260783582884087648822116793974103741019719484227525273088977328650560 + ], + # Point 129 + [ + 3338564282673470966018775402725903223528182579868287010100527892104759349598, + 3420593956586273983490728381161913171223107153319939485895365605089451302873 + ], + # Point 130 + [ + 3555800392979470758767025620764223025989211483193054579480906006330657301837, + 2149899957029896266930433713982567969321434161670385808908337955713550175074 + ], + # Point 131 + [ + 3608545457332454869249792272060591549043786202385519868035962077469064609868, + 2809440208316315053241747840271233428261159310708153434099156262944856645508 + ], + # Point 132 + [ + 3322687396347671973732888070910250198991075315814763746941655084877070735058, + 832722278416792540687431048542953362405174329235971097088928697230453072453 + ], + # Point 133 + [ + 3256622670111196871124104758094097761744096984947383503745643093253708235118, + 978374695622574912744133217293321822318821008760942454268417456141195625584 + ], + # Point 134 + [ + 3410173130373258134073085888536026114751965010082957684030563699696697480724, + 1029778749451237919869173468746897632364581346265567722171293156697958069877 + ], + # Point 135 + [ + 3112433780585229726581053697334217985356658602483885980524816220344484038215, + 2532342168888613388980558686859546650509221859779222781594186523997684356788 + ], + # Point 136 + [ + 2105373184300453833566715269278756869308617438842998862447898598873739219726, + 877369785681960743494801702556244962845285506808792672979162666273085193389 + ], + # Point 137 + [ + 1393232346062500558436496118495653473949473378263920202909352539047530421805, + 2756127133762923853208501814856325859087297216739373809908612188013181865996 + ], + # Point 138 + [ + 2602735208672053198262611186569615211175565049926056251480233572878547833479, + 629329025417775185892796948300727047077553416972749774530835029538350502253 + ], + # Point 139 + [ + 1793674060552460299974233062088263069538015472516735538751555695772239138820, + 3267910293235826685471049883722469455862230494501330393412138559038075359794 + ], + # Point 140 + [ + 86863311610097694600007716574788775452468848027297474155593683107676444091, + 2437707467955141280580805831659061124536906723244797886865002980932492722630 + ], + # Point 141 + [ + 647853547298757687427688341299451732092383134491816678870980494283372695378, + 3617636854438826076469516140770697015113683007543372219324291693845766252558 + ], + # Point 142 + [ + 3334781490544017130581236435947958883590529475501049503331616858692678672100, + 1524104441388820711776621309003922630015731846383839927647934980171536936944 + ], + # Point 143 + [ + 1706920492017425179811478489658749041569247255320747028220786202351882167016, + 3136062931779844207701097197470225751864670973511824570767144629934893764829 + ], + # Point 144 + [ + 149961882640494348338043440935347934018050992098250101342463562074704074105, + 3512185327682078074777552971488882847736861539286017805546782388407073599152 + ], + # Point 145 + [ + 370860151698809780775659878430714854391039795566897134502189484720213964480, + 1238632390866429469777130064298502868565198081327737370254478336416980048965 + ], + # Point 146 + [ + 1282856472050108295483684218758047396436543346709509434400652555424465185837, + 3192157841056717224043634359360155098877589892941349993757962372268962797115 + ], + # Point 147 + [ + 3312829654231446830533616425871987858809512713259532177921343043407846981843, + 1806976906510042164961209222220236480094335196359899499152673663644589005295 + ], + # Point 148 + [ + 2826214476236947463134060188026701900955812519597178470627224598830931756638, + 1580208692732193723402036455390144043645806993218896764366725410799716087870 + ], + # Point 149 + [ + 379282466405240907198946408283110293887947304133389197401888303636683398267, + 2678326696284237155462064013317839501356719176588712513572353629324338176024 + ], + # Point 150 + [ + 523694191683203858486629087773330056719704186407500237062037916699142935895, + 2926557820959194747499923406515961933437184756076777735860240549085691053587 + ], + # Point 151 + [ + 124833202191020085496726895079225667003902078587119506095688146305802394652, + 3363531045637590226091127058344713074476460386293564329053575414302767346399 + ], + # Point 152 + [ + 2143651374532047414518845091756289685497664604612574318922876963772765730070, + 2673827034448835877302891179434422590334329367577351180190722501245625474345 + ], + # Point 153 + [ + 3221138751067425577747344145841964056907436939401545122534796814786108536883, + 235336175612617006468583569743134666841147774340073069013500151038053147462 + ], + # Point 154 + [ + 2555268812438549803835155012590418157639496654385486677760162898546595196513, + 1689750620936872465271905664884810894356966245587199342495604460756806777168 + ], + # Point 155 + [ + 625234954128220194452646182586699463591457452260757480259168774898052708673, + 213161676086201716193087398992571428821719377385651036018131821081133441051 + ], + # Point 156 + [ + 791204612818253360042670315855828265280744396446868065071639490833059389857, + 2903319472504514375570923883911935291656049846866663158923319947614657595965 + ], + # Point 157 + [ + 2454646497469354145786043440836113873410753626627793200029667058037294177092, + 2032912288570051024788875629005185779516597292453270591316074924761292028478 + ], + # Point 158 + [ + 2123337906329005118437052559658178011321855724007242277348144379266185921681, + 1894503378078529242131889602767919022671882244815906977803914704575270961364 + ], + # Point 159 + [ + 3602877859331983834496856242375420741994478431198393391854598373337944528390, + 1390248307806555980791681315101366407220742869690031661994109003230157894293 + ], + # Point 160 + [ + 639878381660983183381027433798087557736798886846049125326586828176785833470, + 3512419367912974372084649665826686824136068377404083433804922599142564888100 + ], + # Point 161 + [ + 1419134474578125103135604337269063811149389272963545586390539000069337598859, + 2235806112698740444007125277529836633381483234992038997663439878202987160165 + ], + # Point 162 + [ + 2778935883893248105666429190432349575416381537333107318387561120528861164061, + 1077278765722002922040368598422352116268480578772186030121792709039469085744 + ], + # Point 163 + [ + 428120812125473710465911539814876147732404851270862105382572167865288414733, + 311433631492674653227552598028197969484982737235311408621797869297871619494 + ], + # Point 164 + [ + 2952060790673513881810338872881675262252364545105524564508325071871367391468, + 3582199809541312286609332154241682748678754599041344486017973240874423950806 + ], + # Point 165 + [ + 1594461313084220822848406979098581830091616974334911374972620995310916825614, + 2807125397722833979284621487977999302186252215199452982876117209095859541679 + ], + # Point 166 + [ + 2043845413859332839989544551581949027414436310630598302726839258465898602603, + 1493460761394315567854883351837738680253927509880345067100858649883130457048 + ], + # Point 167 + [ + 910520306205610055407037560562219297599712042011958124271729124108713600122, + 1227332044591912072232756174484900317585931037443282067667157281800895006658 + ], + # Point 168 + [ + 1356322905904034407802797063629117404975010366322135572201704229186917875805, + 3062811278605169224008088325523606193685397687377873754697372903028059871958 + ], + # Point 169 + [ + 2651699680375521157731474794703697878196552894539932179312542308190248741615, + 1374999585439430684113300998459982464172009456194373596393318069525504982141 + ], + # Point 170 + [ + 2478956707907870604028063666882128463565422187569850243079631251653021026113, + 3484420957530723200820826205205389208856700316444106810018542093549855044781 + ], + # Point 171 + [ + 2745165789654622430617796031383732860380537410258722201878555322390065895409, + 1389786264162289064932319984048404041828181018706839586582409596900469763316 + ], + # Point 172 + [ + 792291068607630837524967871082697288161825884871154205347723150298096233265, + 1595885520235983259847467303703354388143203603602941324268342974672349807911 + ], + # Point 173 + [ + 1112766696158267198974840537128095637678660084973877593826070735478819877428, + 76854025577946938851193238773961903814332442336300641590764677722879845665 + ], + # Point 174 + [ + 3075088147214758449280318679771364592694228314680731955170139149431172226453, + 2631464825354322427832325664266556982858783834009349057603625173618876560686 + ], + # Point 175 + [ + 1946952991061874087036397224151139450490763292883750208451596293164980017180, + 1671486844817988227846350440864329091397847162425205354534975696550529649006 + ], + # Point 176 + [ + 447922002514491195056340638904274303849743543434622916451489369322819570022, + 481989665314939062190810517257104327416911189393567287995442516666694890745 + ], + # Point 177 + [ + 278768424991728679375985793004023299002069942702077846697446381167229872879, + 2117939832550233574127330090850877444733498561141023361942969078992294709841 + ], + # Point 178 + [ + 3216273780587845885344648641198104357960391646838456530374473185745790254775, + 3606237386675957259713749411947060068172141797816584221362457562749070134457 + ], + # Point 179 + [ + 888490252135075821787230358754699355629649708262317489940736076372880196421, + 2243899191640238208431440104126936619623401575061085948377697882800556601532 + ], + # Point 180 + [ + 1748762228708691375443278014066210232528543730943854135660867476022163374357, + 299810809101280072373711346937892828549469075660577914238588790523849116964 + ], + # Point 181 + [ + 21800463980443581727936921323539002523916110909487141095813330024439604509, + 3239991592162948599965263758537867210199504881270279333333530879723254864285 + ], + # Point 182 + [ + 1945649262091085266248324326387300230209483523911852369537103537534602188669, + 937547850790372496342721899405171497997439341142225383636485893081045476840 + ], + # Point 183 + [ + 95187198852190497553925369717104578448538072022660253488035756790745062479, + 3284691659350683572361562338651440251050266476442725062173141352883841793890 + ], + # Point 184 + [ + 396966715395293979183022035167233963473677021753099328784772352776439798275, + 1613322234499535439350563907086596802838800251144492804438492852751836971863 + ], + # Point 185 + [ + 1705843197146248589209974041619858441399461896194255757072890762703413482026, + 2709234803093296173390529287307769438552461179797518815269401286400797214943 + ], + # Point 186 + [ + 965896257227883986820160011629505229958129089095279852981573428580834882124, + 3222878455111086494470662906288505024631219103956585024274050126506278071618 + ], + # Point 187 + [ + 1711062768895650467512736187912681238407905324392544748841566987114959619940, + 2037166002293356114375846587992164902616900087753130705770148512236761109532 + ], + # Point 188 + [ + 306929171824313514409556697011428394218011837518848517597157431560368406919, + 2733907887575749650664983471979960087709811489243842872308868788028064963544 + ], + # Point 189 + [ + 1589132075979748817708848065945386431745143428233530475552120730272187989177, + 3479574566456289934918449243936076329515058364276769923171744108995406563734 + ], + # Point 190 + [ + 535088375724656896741944948092293887713409890408387277644439471611811707698, + 3084839991780046496389632054225148955101489706660124944353740712683200220129 + ], + # Point 191 + [ + 1108714469400502294909191174424037717245383586375174474928489701713698780203, + 344696096736515166863459054453204973598183407079213029580530012456358476055 + ], + # Point 192 + [ + 1896541196944951993777265586048099398652846183460110390523151621587553084390, + 1356944636529832014079353150870395726873584547194854259003641654476043751831 + ], + # Point 193 + [ + 2129740212445659709473295068445927295211143686808997136843204790705829600051, + 2784093412242244048858451658511978494505560368170364505963748965317959330652 + ], + # Point 194 + [ + 1164682312446525913981569213875150029132760939832308651498251601400403129032, + 860011516352836343688401715896426496333552023448545234232950198907527747215 + ], + # Point 195 + [ + 403381603306414312665420204496598046512017134030800187206579847445734886844, + 156710329648174386978898329138661964114318418114565494873287202679709752521 + ], + # Point 196 + [ + 3612709524322508414252878964415432233546157461869758422797096779080105910197, + 3298597797713539922720249912653326762917459398002362406667649739024691730641 + ], + # Point 197 + [ + 2788472028266807168790808585452088947945831098822807970786023663768554965883, + 780984533356516602828788864429847085423068302018929746266555371946539918101 + ], + # Point 198 + [ + 765015221711443098273066494715116075624315673921356068577773696969663768888, + 121864134634742875401471744901077698759842590065938894637311780483786803739 + ], + # Point 199 + [ + 3420458059929261250134488209708428351089317850483628650712157545902459535082, + 2753073224523413466263788479220930266258909876418753081552150110011967761473 + ], + # Point 200 + [ + 2435788601301668445647253094872163056944118800775256416712610409880768679053, + 600038247016283378097972299409314284965737590535869850167688589980357128171 + ], + # Point 201 + [ + 2791234127851217827076638673185921990434565258934782489657649399234080946948, + 3019532625505810270756207292319968517034464214472035802893765165728566407791 + ], + # Point 202 + [ + 2175512767988966931157181387193147869682253002700484615343260272758293076239, + 16356925783563920024655061104076219675967060212474163352137306664035714196 + ], + # Point 203 + [ + 2346289056582359849501003989438672266264448907419687867675159268542148069281, + 269429309748271385175467663457270641975914217681440684037010041835530573698 + ], + # Point 204 + [ + 481679002404926691230990214906065876613510532429501667509718578731645569407, + 1609976879746739928846288345159176310170130379856291476257183396230103110555 + ], + # Point 205 + [ + 2127782061956380596600761098774414382531840909564578638354721422890231607858, + 3522660228464874209807741153414518456209790498657511195587886434205303653276 + ], + # Point 206 + [ + 2990749966959304578563050621982841125682975201749885210174805380941485460083, + 295197603441351883186074553477194964532333406546643184464952640061744781463 + ], + # Point 207 + [ + 322299859114885008311771586431338392030172898629995798709098486233393241698, + 1023285551085384033485966778701460844235475326250602612915962425374866217237 + ], + # Point 208 + [ + 2679886206461898569320731641611496876582297592825799115233896892980598546123, + 3099565537546363902378775008459983997262715254411954165424270988571981464521 + ], + # Point 209 + [ + 853528159824941445672647399048964399831359191507493106790462832690982526176, + 1009640753029626140011297690792696405115462988587033910421277684043461588633 + ], + # Point 210 + [ + 1303674320335969124745880365084447847319437106063642511200542633107159295205, + 862053741658449155711084470286087651524720201941852653377417774320602998882 + ], + # Point 211 + [ + 371784976647836509603140476112794524387140033126839183825485397106022337443, + 1481126947243599849498136670060476522501192641446886664638431717153471858730 + ], + # Point 212 + [ + 256524228763970643322523560381608580385109852183329255389135850583181280611, + 406062519770964392170499110443173965524033961539587233636744514155485982986 + ], + # Point 213 + [ + 889909761640899390466461648537512393214660407373609451150870536404743826631, + 3579579432590016439918468051553122806010260839218529712227712227008716479453 + ], + # Point 214 + [ + 2181687776535382366933204874050926043644394510983340241902960182125931413775, + 125591469249575554746765789850870005842434420699124766244689384267560349454 + ], + # Point 215 + [ + 1698011648746714871368937594549801676987990963546029514109897028714017239145, + 2032238188811755521211420251330988300583305301151292725000783151183349336764 + ], + # Point 216 + [ + 207686199415566734810376310722982179068617367123996792470313948717972567639, + 1457942055660330932304294699096717861622662715626699977446009835743129689987 + ], + # Point 217 + [ + 732258411798183046441278957122759122697038828507788948928154704288224686840, + 2612299176220286719934749375400756907183274870137646747294920834312868644617 + ], + # Point 218 + [ + 1131610047798062548492567521656272602549897300746434874306725974344582031445, + 592276015836506844762700749171355258254203250155822561654862572344939966418 + ], + # Point 219 + [ + 1683203479448902722167828838268483614879733285176640956474769817134838920079, + 2990934852576563000604264577641206566403815930150479384314858100664762791309 + ], + # Point 220 + [ + 3379046363093840121427219819819164110884408212445336400953102193400631404907, + 3581767823598790218171818955173138326266037759069958949236204540729167542495 + ], + # Point 221 + [ + 267147147550523725868561003543093307186644052136871409944613118661005584614, + 1306549083362239169609006162611383775071361663461903546405449470601023271459 + ], + # Point 222 + [ + 296975912955049993838036596802334313092629826214334467025699377929310754375, + 1563707217733470474074970856533625421700727823760061087588764083345926207730 + ], + # Point 223 + [ + 409930698864338128894354352383270205190831536703476012076004429065058122460, + 810813209917474447569624498205572650260997215620628649252215243057189717098 + ], + # Point 224 + [ + 1244080118151222628554430592856097691345343883216811606005263269357031069918, + 3112961342564635769384393489844295061472524432493301559668801711930497029521 + ], + # Point 225 + [ + 3287905561524106163037657312076054800525947413789669708058413492038554328847, + 2950266460046239577741220775173597014955783375111361479081038783371923101261 + ], + # Point 226 + [ + 1834667074444753359896291977244884324984125181165619220523638440982002878973, + 1028938841439025536907121844571940895060940730436245288628559554616030756654 + ], + # Point 227 + [ + 2696579144808608214357559333220835033277874753649087677547926288655864862396, + 1205667596625326964488748126939548850792621845142605787353435428502428194722 + ], + # Point 228 + [ + 2681502303973249854884858344399536104286034851010362500805472440066210957701, + 2090624297500029615299510863468795665118802449870439334493020091696427901315 + ], + # Point 229 + [ + 1353314499186573664458206377062562808249080943135426913783009658343962961481, + 2765083962822990885404819840064727313062737767044627137615530427213920746301 + ], + # Point 230 + [ + 418493206714369123577433462079076010029158131200185602134250755432510923649, + 707998386543299716720343491204719022813222859618895609544390001004782644320 + ], + # Point 231 + [ + 994749436490026363483216765171719719813756635373513071896945407046466009916, + 3542023359278349542922434792313919575183783573969934645527389879728870323439 + ], + # Point 232 + [ + 1890008117079085184019219906457544800392678580280084811260498224586609070286, + 530233764155506684122011374141421351751148778529665626269052967779624308325 + ], + # Point 233 + [ + 1380484666773413147984807138259355829485624671596401959499503325097178446404, + 2703143567161792821055257608672826415980743659971254233408855036509811875962 + ], + # Point 234 + [ + 2143881856351418759124748482049358565778910428733836748398777699149555891034, + 2186504045350479968631256614333785165240148508469904761831572298072325147795 + ], + # Point 235 + [ + 2794147613012298673104841054632587797837325721650333247952170883643570500710, + 2613093490820907283519815366146280477230890436771830509953858178043843459008 + ], + # Point 236 + [ + 2625979094712716243539648580331695634041650060241725094563615224537054056479, + 145143322058807873791926380354826174760141531075716146087195666113341406100 + ], + # Point 237 + [ + 2912042742810958222559628370002186243459717464446177149380721928196350211880, + 1095311779741249761721276459609161253222688862762095992035499286908387724976 + ], + # Point 238 + [ + 1779688139950750136680613010527778524901137919784933323807835083178662865397, + 2443673470483539351187560062972650745287069240410986822268053157528350814229 + ], + # Point 239 + [ + 2131257578483960220347030216154712350782825143774667810440672905625819556803, + 868491622632592338574614600558827126185061061280839427086037749491272636942 + ], + # Point 240 + [ + 2219005280109778946483442325473674751018277532855518690831693876689847358912, + 268228073082292114390894552479318098546823436290210690954657472261887508233 + ], + # Point 241 + [ + 2467777871565605588821089206361725030593121231139954405293743820019316429337, + 2069991336503010889436721322920702620510647146193316568814112316083040831176 + ], + # Point 242 + [ + 3577501695097063979000880929536422727582550178446643147830010270693854635348, + 2035294884291881522017348441414523234930446764971044277218808172917534058938 + ], + # Point 243 + [ + 2916007638012715951040881085223392656327753091743451984926427831539302440694, + 101999113634064710169079402836107323839592545174054596535848670931124794072 + ], + # Point 244 + [ + 2824482828379328312126109430912691348039395134484392469690037535472985287979, + 170123149569960330410734287368175015043768931496132108203420592659991682297 + ], + # Point 245 + [ + 1722717317273017311710033441508078253294103515222910371014974460548425614659, + 2455794982320624552496521720819953146480298894982220570859668985011878438440 + ], + # Point 246 + [ + 3323332844428319232630841298737492488606773387534937061908049192786445054249, + 2775452603532873679877923100539353685218185462328621498382613902225794012998 + ], + # Point 247 + [ + 1608012153975591201510431671483571431120513181059080848126129285407754084748, + 575592304166370707508545110070168578909474113291472127326388774685581172878 + ], + # Point 248 + [ + 916809345825028426832118427347317839178426151786124666474024597549724310957, + 3069555112506328035675763181932227894553434381085323151827207060709357462248 + ], + # Point 249 + [ + 153560760075250381766471501226441998688409013753252062800770032940893580349, + 2329038724186047986576327612748573076822768420981449905843361105210433443270 + ], + # Point 250 + [ + 2251563274489750535117886426533222435294046428347329203627021249169616184184, + 1798716007562728905295480679789526322175868328062420237419143593021674992973 + ], + # Point 251 + [ + 1952032427782133959985228051054870623876234309599006856796795466237953231448, + 744413679445899225088843138289996934867918835619653321950444531036817070023 + ], + # Point 252 + [ + 3162883296795762041497700357918312017339509549159370543230518526626437112653, + 1148816493574468887215135074549621724232310091624566542087195747872792868322 + ], + # Point 253 + [ + 786950607934610388520508637842877731308927224203614303759337398330090790623, + 444134697710380413360573180457740543414390667171294106626775858829645857259 + ], + # Point 254 + [ + 2138414695194151160943305727036575959195309218611738193261179310511854807447, + 113410276730064486255102093846540133784865286929052426931474106396135072156 + ], + # Point 255 + [ + 955835104121335308947276399583596848959851821452355864667907221325383590383, + 1935683370537209806184243406809646867168041420482463925534831987536519170687 + ], + # Point 256 + [ + 1499611007946205543199170798196098786940176368383871138867689500928607330374, + 2662008410012406974490205380527854666032675717377005780162761284021647432331 + ], + # Point 257 + [ + 2335197826477933983066242217922654955176345813321200025173943441365679314150, + 86799141726517067858463816876244730494086420214168736349361077938664862865 + ], + # Point 258 + [ + 2426305297748003258516448191982872419574201776532744568583614742711471397997, + 611146900843862661160935598912412637915904741887617221933114742269852367033 + ], + # Point 259 + [ + 1819742747902080962279305391799448622733402573848283520339621821028260796959, + 1894241144684539746894260685967399165405614901992249187163143950195275701901 + ], + # Point 260 + [ + 1566900754533300858179807012858131246327928750222566819288909586499451206334, + 2323811598271524355017219639060977623543930469383254150460465078663134678714 + ], + # Point 261 + [ + 1338097767250711433700785616700918851742529208514529951106274243527927831096, + 3601290097662913510540208850467494388944371519879253105483940917906091319433 + ], + # Point 262 + [ + 1683076217460316609061101548674917172577600596732354862732552514637488814573, + 2586296790559369551539502725860235780473467857462493373020086667442440193461 + ], + # Point 263 + [ + 3470075416984304553645258515396988848402044843556043814143694549291509194436, + 1666299363887708992239894153878840011200424023864139235605837090808931067390 + ], + # Point 264 + [ + 129884650079435408471813606095297277059523238954518094473826756223429238429, + 913031838387203882010246435451325905809656720680281880669305438372695953289 + ], + # Point 265 + [ + 3445652480962486836667659474432628384578750077745289335336644514805586651762, + 1741653654983215120166402497338863916929176017304303902363120840611844416235 + ], + # Point 266 + [ + 1173074699967858342716736787366933102436976281324119440358211746283473098092, + 3075964035581410146694612776964161590283242001470153540218734715257792166291 + ], + # Point 267 + [ + 1948864707183825487572530934859230839519236819821721157111541446530257224135, + 798780108424505404488913172014962400148988458061505603829909122085722968572 + ], + # Point 268 + [ + 3165333849798411161138142302194551326502959074734249886012073955960267320358, + 2854077232678939262337800847761960471531706329482426572081757005793264065021 + ], + # Point 269 + [ + 584219684922011437746073096716853840728059302768805720402293458731947997988, + 2691605392627141392945611586058726186445966055416403501891746673585009447073 + ], + # Point 270 + [ + 264865088823397344079537851791331047852344700250947641609789815815437187715, + 856557814156118883640651140266045634237123586226962432863642966399740605873 + ], + # Point 271 + [ + 1212105529543873848411369707567547187480526619270372944387195134251016552437, + 2552579868998151139055870387891260255591250119487805856658511804923635744103 + ], + # Point 272 + [ + 2759929251220121311757096521396321905172783069804137943187241377410890848589, + 1013388642776766405334075600441854256934688539631486641630323046104523794325 + ], + # Point 273 + [ + 3435662243060467637677279408549422366191108299439077218684237876763341810879, + 1206100200764686560370450625743787352415337337047308087134628700539206972086 + ], + # Point 274 + [ + 1977901254287550894956728572957134897985203087357872251941076511047285452123, + 1346842642356348816775497637530312996348462152604627197529080983616576235485 + ], + # Point 275 + [ + 1581907638985290987975047412112008678916597048367684526184099730550194065415, + 2288246533671252346247687962179397967163408912494860510981460961024159618572 + ], + # Point 276 + [ + 3215377452673680185835021004830688268079390648010676635220128902519168416923, + 1674957009593589739638616939189211577318542927235942161494800623173476894918 + ], + # Point 277 + [ + 3396057617195027713159897957858092093258826926757827467732086865786377444102, + 2403568170323924513104032431512050355627094479417158474914772545814875796054 + ], + # Point 278 + [ + 325310856310207888841271598900176645709547071516271373679357299878055669525, + 2861677118910432905996905108378731982262901938718390134034663016688312343427 + ], + # Point 279 + [ + 1925427404573041560549638633971822727278056640694873177265388259055141273564, + 2149185931265263003264629477521934555446245963141224065368283592107464297049 + ], + # Point 280 + [ + 1057655004272797062633705695597766087036831552982842977913880603549380151466, + 2130130885627581719544847915036196574607634081790527579681856058518350163936 + ], + # Point 281 + [ + 1786242059890666469307365196518174814453782118901787482186686744245818012463, + 2172680482993759140271167348144713503721696709986572085951066664839388293484 + ], + # Point 282 + [ + 2592525455921742135918400168106149306210970009440868747852006912674191605906, + 2550305630907505809534995587257079662509793820536464892848197484572098781014 + ], + # Point 283 + [ + 111752568734708072011137681957171091740064310210630952635788677462490949489, + 1966538689680059826741582906363349945575114015419692615822155104740977191321 + ], + # Point 284 + [ + 2324264192758669920944928844116594735168711655659168025211571634463212989215, + 2453209181874565651016960427707879723586882840596638134526741863234420038217 + ], + # Point 285 + [ + 818792252748214652222203568101444114842946877275244081137505104552993989249, + 2471135473251293858404864644674805198948427457640609601515835463680395027066 + ], + # Point 286 + [ + 776167224210253175836285301928858826524230272765386107941590743597167238720, + 3044593275856778999572277625341930135050598088363464944610781758478552167560 + ], + # Point 287 + [ + 2767418361596469194389897577063980781767359707916121685125519618739970163355, + 1124183356341334924855702050560569148670124301541230516023323823985404910253 + ], + # Point 288 + [ + 3513158900447819618700445228863547036694626890925843610953740962149603482734, + 2038286698090839235054970774714871346106378670221757061557885786099548421055 + ], + # Point 289 + [ + 517681831514430710817158481088000571456636089484182444914961386383717794582, + 1459737506190708125961565377404508114648445797956698177580320945537417189767 + ], + # Point 290 + [ + 2764450063359771480330846034975350106048674448417446553801535244886068866892, + 2500955915434901122019038304617790171097079017113487693837548584032628559083 + ], + # Point 291 + [ + 3184601066099193053823632069416872853499121524829816523443698520118638972221, + 111399286855641690694757616558822271075634785548525857820633539322287438076 + ], + # Point 292 + [ + 1861764219607632476451968430680705159045765477029736548085454909942815526303, + 3542081125583459227861063385092240523576730790188055012978461087647652048846 + ], + # Point 293 + [ + 10925555246854055743289295413923076326390541878672074284471241830391529976, + 810379063403665716239985697021625834252465819699617633253170927366131373937 + ], + # Point 294 + [ + 1925469472110966024666451107813057200428670133173573112269655451746350245386, + 1187189363484828103108277567024110279560050728332792104397849042655830458502 + ], + # Point 295 + [ + 1257234032480175630185580604439948675879173824493927714083831311549585046766, + 482591378023150470327639933521764621888328430410377343971291074047537101431 + ], + # Point 296 + [ + 2334319993908953455700110484178707117214967972232088939662977870360647156970, + 2818056499399790981578195943345246458116384474394556164088380917912187593212 + ], + # Point 297 + [ + 1453145677148250112321822321443172335864030607730961070308740138762391252935, + 3617981792798631412887889951712686522258702128207981768326584469873932484754 + ], + # Point 298 + [ + 264643236147417159992044802699391039945632102423375233223220671619422214434, + 610904689309149986363589451581676922381464434446910426922817060056083567097 + ], + # Point 299 + [ + 2773555644105234303846021450759411618679442176305095821653566380427481880668, + 2878405667973852006171581845359598408350517329366546879570244783921286317021 + ], + # Point 300 + [ + 2380074631138752101437386328647668302433161481157298955599519485146473998414, + 2940162033501039253058405622329360778854888527422589401316007987697096499987 + ], + # Point 301 + [ + 59923324335814857005652966889729706976632643533439489239403033851211490829, + 3181196819190011787131540430610930324408745638588775047528706552833472475080 + ], + # Point 302 + [ + 1571134397970145634572604253962346713191790104259693653437624133884723196915, + 511309416166810662427969443722895543878733742083314900009440861143766415361 + ], + # Point 303 + [ + 21090530989878836908367190554625378531500191692603997969723554269923877778, + 2771500794785272828011587832172996564585508124740321638858260733779070073032 + ], + # Point 304 + [ + 621303564182234060808181503818404204870407855544326969616228975404966772646, + 1381758119149546152274589114806432955337739448745667222869629159555498152415 + ], + # Point 305 + [ + 1603367875109750844401808777605019545782079142993759227560886400939494569928, + 3404233473008527201696274593235213333767802823633271526617525914604458121747 + ], + # Point 306 + [ + 3064432839007969661251145008088305693584410819966794512614368597856219962482, + 783256745930834086288610539747908078655069214006352981421868494072213571354 + ], + # Point 307 + [ + 2573509260611834641929601495624663326040450806493971688424856342734838037808, + 489632329414366712473800098158402333281555035744201997032862455100685111530 + ], + # Point 308 + [ + 1373013110025567380060459077575726370083805340382038749245013725221282327457, + 2271658983436031571764113689216829854166985975189230813662757962593273370705 + ], + # Point 309 + [ + 243466876262712870310499646879826213360676861126171299065326838396697300684, + 891923608292171984769797750319491387015995834783589731015608767716778489152 + ], + # Point 310 + [ + 2035465927594189567864787276274121052811830838707542644166137070067107322500, + 2293243214098049976425980695256140756537564052456935637846524530699635529072 + ], + # Point 311 + [ + 1715507122267435124880907385699722499007841543359383748906967432836714601915, + 2065683622001523424317888964929493250974883654939917054448293842930286201120 + ], + # Point 312 + [ + 2906653642813167415863836383642871880050990532175816005542735996509246708445, + 2101103582375172516655999605681405804159432128657161521495199065707274548843 + ], + # Point 313 + [ + 1560478280364678701345899621740888632653158026800096320697331299727453323955, + 2502904681091262932533150924633878024793617322541538974642118205130709004124 + ], + # Point 314 + [ + 1088825136235742041556075285669369753699521218552343596173215811771470309575, + 607071558939135607706029375168245965172230512282049740495005044883485254652 + ], + # Point 315 + [ + 416480930348830698091647060484670913532953382502409400899099946640850762655, + 2303434329028515556374106529351629273202094072128674875449835104642893549223 + ], + # Point 316 + [ + 1059226326329269227798760274851746346121838934144866840722833084379215695718, + 455998255867283640545200132173274853754233138086189946906020858549998434504 + ], + # Point 317 + [ + 1024559718945017260657672873923464530437289768440141950557818820996778411842, + 525500033723589597309036453730301911712422803406229661029628208639589866511 + ], + # Point 318 + [ + 2056028306188984024699016201885443563164872865972667060670091877117616276834, + 1419412493706512914027360568510428136300835377049697326470468114560769950308 + ], + # Point 319 + [ + 2521038744059190227863027780309672155134248925803510182471085885871826328010, + 2055664921579551707407017847075753140537141245184993747243302270696485943484 + ], + # Point 320 + [ + 1408309654797092197568389828883985051397496347829594535681640019995307652445, + 295921513421790144469734685069951228414338437813405505294106756210992032229 + ], + # Point 321 + [ + 1987857595984681386153299845716030466354725651174836127615155461210837869564, + 2293923060372667346124288305276256569940238390418855305479173407955750665287 + ], + # Point 322 + [ + 1074973828105956295697455820943040985586724095278707830076756602555601108444, + 3326765437245957223406988059616777659852354773867298474672160569379624598430 + ], + # Point 323 + [ + 1401862061794147305806657568952279890802547970381675217693442582127912005310, + 910058274349373118447954573574441457656632333180366570053619401133150262813 + ], + # Point 324 + [ + 2040966475187373851921668684777109791068677257197750053913982531497273133975, + 1167958332666304570814202030094477634345363022473150543742806601986529818461 + ], + # Point 325 + [ + 2881780782119017791457583552000797082603415839437706575588805401515980505801, + 2436105237174666086889653702042028393653105027534224748338553775980791489395 + ], + # Point 326 + [ + 1138781660141971412207137170011812332951155719283266572057616562955205995829, + 2657768812194147024915418195050409322486415583242017023943729410483676474626 + ], + # Point 327 + [ + 88581112279530612334542943009705636136002388463681106693549082166685838725, + 3496134215297708121790719225608456452574651558469594407547800088961881026765 + ], + # Point 328 + [ + 866718519953191029204822844477480603275933535155329865978768093814160529674, + 2022882774094084071548678749116103222489874860976240925473298660635212244802 + ], + # Point 329 + [ + 1103918207045639319456460136379598726437052653753960546349780312287131158223, + 1656811459960430943611479537236804458740132078453755963357596576700576629867 + ], + # Point 330 + [ + 2975764785119134105842544511285384981490365867343400021422469912399500877645, + 2687980107817591862475555961986751017099827078208385723645417765552791682099 + ], + # Point 331 + [ + 2054447365318177209738241671190147649701574355450360479017715224292009927393, + 1227206940403531159772964107232373497220530020193348767638035040017379885984 + ], + # Point 332 + [ + 402275007431361887464920597581616397711484740464854159502779178788056390885, + 1547405786479003871142635515616266099578579586990668594451288428801472362903 + ], + # Point 333 + [ + 2669324117696876754084336282080783960314916540857113798726442554388023656184, + 3074286759765067223595900394644532377637168990646921851127828180794850850636 + ], + # Point 334 + [ + 611932396022950536967338459506782836623876614969030243077175645873783092390, + 2986433254570022449958119232739190557250941610456904564848292348906028792067 + ], + # Point 335 + [ + 1110995377995699045828083953623434494450657345505392977032896710538879544144, + 3071470793346350302357608763590610007478960895381257765814531991563057030742 + ], + # Point 336 + [ + 2191168535074842056167853224940102773478321048031820308130136731344529117475, + 492617323352024208418412531798559543092874332708570994021422823607454559418 + ], + # Point 337 + [ + 1818759115593575585308068448791508192789866090941451793170759417890828490762, + 1305353903124550364838683686338031038285043137449345518795766390174337151903 + ], + # Point 338 + [ + 2631013586413722723014078756848572110540232554858227410413816548047325168051, + 2105417412111265728359541491742019675861543959418319743611117424131871073867 + ], + # Point 339 + [ + 494722738667200879981866755759016615873180216451714847099458992922362500227, + 1539404447578224883291548288433633968332223396361092124453992010526489574088 + ], + # Point 340 + [ + 1894316435470914382555165707375744683572341270069865836107052559173764639667, + 401287370121559774698743821124544382770349508247396498174321596110198582372 + ], + # Point 341 + [ + 1339247525522496394127023115420380868431447741930632224270989291600289579235, + 2330930519705316142879600410372647788413471043363313385713997026124191817517 + ], + # Point 342 + [ + 2320587088454453474461645476206103420492539243459981382841070259500342411471, + 707303545980793210971675132996162637283200017126206538680628878370110521153 + ], + # Point 343 + [ + 2377659897024221375470644518744478788982138263672700674443630590978849683809, + 3419989952269036538781036113174387172326270380530270982346793780460729200034 + ], + # Point 344 + [ + 2687585515362755421896341880578567868284030156693703993329134509207013359293, + 550674138339341111775701426337380778844351792663711912206724163064378058172 + ], + # Point 345 + [ + 619785501193479957534515714310017569776352132000912157061487205381157886132, + 1961366910419378396302827972401933597848792446727660497590498798601343853897 + ], + # Point 346 + [ + 2960597928226130798805429646261618976240340007412692237798738046399566677924, + 3508695180344311906202946672427154710274134142520774461776498819979015518079 + ], + # Point 347 + [ + 2926849849449158589418504157622781316761588577631946629896933307400237548255, + 3579904677936943713540587614497052174139389714845927197988064028772422409729 + ], + # Point 348 + [ + 2528266877173878010055911786705258188545546534139599446181068037003246641972, + 2090593124885859667054468890302718556366617309852711480847196828672489780384 + ], + # Point 349 + [ + 2444375104485440204606531945078810145038148666393834678853843328800784491413, + 2715725794432857257355582195062001572910526811270194737205485553709223325149 + ], + # Point 350 + [ + 22830464908174189828089916657865484119153417213263588787115884234394820551, + 2144656372197073668726998695697011727390449826738494763726208492934675687351 + ], + # Point 351 + [ + 2160911736065509245304608365930348966695110267270046309451656634373627649798, + 1163682116033125825657519424156769471044453229066709940373641637423931934054 + ], + # Point 352 + [ + 3337375727076925923347860860990507186997043901018925752183704995009780982617, + 789849303430659746633537234664825369690245855962498730835768457501816445262 + ], + # Point 353 + [ + 2716386435720211080341482697862373973972358535390181213762877306777737012402, + 1694833211852186945995412154030088403536183890500755705683031155572812198349 + ], + # Point 354 + [ + 2068303401887779893147375033717840031634024130559056428854775490478246923099, + 1302558735633136858997628501160702103360976241643583615145118788605800501823 + ], + # Point 355 + [ + 2528669114399089536204487719847492540160633221112006566613245017377953815975, + 3355482321728480378876070603715444548564989647597270339584107783929482403385 + ], + # Point 356 + [ + 1261599618177618962793920186597687295419265824130061936332619302494768478919, + 2783617516834130711477034905360493329011706287809097673216506817763076782638 + ], + # Point 357 + [ + 2296010137652334615076906848600702206420819604705598933314103330038064507904, + 593137988508667983800537181778974626700843711375991890922024569808180330689 + ], + # Point 358 + [ + 3550068663997426488921976653382588625011435366616319009448120503817895378028, + 333410037907206817306890788780367628702748092247180262748072669817226553712 + ], + # Point 359 + [ + 1578244233650262393441190533726865755228496176869169339972883019206417143875, + 2916675240291132751272523072177820169167325640678555998767122677068069504203 + ], + # Point 360 + [ + 2578708307946151727285356080366654771256622596667386016883098096024764936468, + 3547149991717605187248264974055140267141465574101933313814496584854066409070 + ], + # Point 361 + [ + 1067242594064079501648168388172061910333397472745655832282853323030820063843, + 108409220808480307369448490727067533487994293376444700684037781053298403259 + ], + # Point 362 + [ + 1438879968873284678993516485008080871992105419269026450500135928583384168688, + 333856453999414929392413408589358932084193824353874579187651515313020359769 + ], + # Point 363 + [ + 2085247683945044480852971266898030909967072967111091473808832254928660354607, + 2485835628881711191910958556160457454778996828420985267633930125611726115768 + ], + # Point 364 + [ + 477210647966136763919276861800763093178095337893692383194424592690231940061, + 249607430987277999234526517540164575126654109544108544480054797746736570800 + ], + # Point 365 + [ + 3439303457066047146897968415062046665605853275226669283725228224973410585910, + 576124419653395914968187737099874519814103127209134795696921745172483058236 + ], + # Point 366 + [ + 3028687819239678122848199946542573863395992135514552484651611678970005763129, + 2961643823025691121513520662201781259179479037567198808346912150041025760642 + ], + # Point 367 + [ + 3217248534103588798939334232631337032410025788470581973981396764231465681047, + 2382487873308676654385098962584563887276844064402347832716704744043722784812 + ], + # Point 368 + [ + 70223145378785170243702578121178644986245368450401326703486231450287863627, + 566197969536520322901287947074318556204558945230303949390810423446802406323 + ], + # Point 369 + [ + 3028144136397350722039435806065376945342224221363467830775872476071042099596, + 167098690867154485139002287996988541908068352838119188548912894969546326088 + ], + # Point 370 + [ + 1424858530411014835161893358951895950005600021438253129735780882682252689593, + 2305727793424806539572292293130051301594968780927626275319782923070998204084 + ], + # Point 371 + [ + 2137513716516327603808109201538743935930903413061745351331773762548511760158, + 1642530299556920094344308735995008094003081457462443723760327041776469019076 + ], + # Point 372 + [ + 2170444911584390774802338541806841054793712556211735475250435550223474552592, + 925789261675057834371037875523035088704965828465840628994713637520457333016 + ], + # Point 373 + [ + 239299595833165228919005931278081722748571104921165506568868902567605074538, + 485001543376876278371961680415603784960370504830892419674368813289405424875 + ], + # Point 374 + [ + 3080380062043844759018736411849858262358110468668995639984779021951968636078, + 2402265611218391177266008066876605247210502592712781467199097469773462678225 + ], + # Point 375 + [ + 726523175548388386261502324171774291430220813407320118077006064537560392330, + 786330320941681238670628195289980168691286367143496676979339036480301999376 + ], + # Point 376 + [ + 527181411328890981971052946489613295601720332326053123212209359114463976205, + 2153641849366054831187204654204168306183349514724183599886856681068048360647 + ], + # Point 377 + [ + 548403912567710249138395852931702442214070304260935779684833978128636533986, + 1820119432165084967589110130985988930607430366654346658826186881340393135345 + ], + # Point 378 + [ + 1055707400443116102787479640936967686027177253722371245083270825233659393085, + 1529404944427212407459549740446357844894249706911152782301314430602619804108 + ], + # Point 379 + [ + 3604699563774018613013478662139866299897364136688436784910471089709564771121, + 1877420639173665439817870412707771026257151154196414247016132896387470215862 + ], + # Point 380 + [ + 1334227623025169197188407571635794051283637310231760303667085410581915700021, + 1949877078010470778680860969498171171122335730952599024624803418631307495103 + ], + # Point 381 + [ + 3308762084022080981421381628996649710517264452719086435874478919169368804715, + 2402101985509711398701847117755018203425777675996940000965112623957330145868 + ], + # Point 382 + [ + 1665881871958289138802712383541734953129067055326286239727041865486531283570, + 3616247644667366899175075134655397038478794588048970408476914992988017251161 + ], + # Point 383 + [ + 2112164335316897143532032199025725867588905166012009239598709182355280065515, + 2825402900866689367007140167090188180425426910788355395313762834688260248117 + ], + # Point 384 + [ + 634951525865404702868066023611980901349684561467873427882470721637495099919, + 2178647893306834053326446190201725650048611097288227403773599186740980976285 + ], + # Point 385 + [ + 1341178436815612054719172387245100541421209962514061177862693819245929984135, + 1326643103813011222249037873591741605099080649257821474786188634904101413693 + ], + # Point 386 + [ + 1928710057077228449920040314271488497108943704097705502064531376997670270931, + 2266181469128837281287656381761672050739367882500404480638892977272852366881 + ], + # Point 387 + [ + 969218608841843132346322324841729971529593954176078767277133720598489668357, + 606900183168454040777559974758602149603502324650244544303079399500690042393 + ], + # Point 388 + [ + 2013350832011494097843192023013584600183073420263235866273101164869312611708, + 90712660236563155479754789398598402903732977477056735001011271430022436372 + ], + # Point 389 + [ + 3396976507268771940572242446745957441443927281190120339106378150765794683243, + 2412741837818252081470894623640222254809923360460175760983381510548136273698 + ], + # Point 390 + [ + 3606409001635563285156761927226165887070229114178147857036216526058311335249, + 1092396833411428890667907446500502751825053988217814009934347833612745642070 + ], + # Point 391 + [ + 1544923824473254019116239003129558626125362616742425500116394033599505254529, + 1465349443371708061238467795142261954743713344644799596216285990321977000373 + ], + # Point 392 + [ + 113491804090467857612125750322844575213787440679203395630526874638303077885, + 2028057101479207102461777655354995420997496563459354054546268475793811643845 + ], + # Point 393 + [ + 1652495545845371778467883968680026022222422089600601509520876233591221298667, + 1257512680478734992352959448953843834930888651298716349936596227713642044718 + ], + # Point 394 + [ + 1735132339493184243340725542423753987254833174769560101474361526571946583351, + 27330540439335548628321946448059137005887875330678118281933391074303528644 + ], + # Point 395 + [ + 999589601024651596708840623565818878499651672244526787051482333844246243941, + 1556707544224102520772713942483754928174646286011090264336584778980967757013 + ], + # Point 396 + [ + 1681312560856804229526683984753830811943700510863269888887897515341932766251, + 2065757756192665992865056781757244443742379869237378116242523960469865631998 + ], + # Point 397 + [ + 2058951870735992982026605733702307208101813514180467857318656495202383185176, + 3268996299695136959341188422640393244547564085474313632107392993963324140014 + ], + # Point 398 + [ + 2056960676848885737798371134589584163780497279931334671919866051948581811168, + 1741359147160041371302807510240561900995865771279421888187249838945794238723 + ], + # Point 399 + [ + 1167002070266749840825005896490956016236165528302729567545471999112944906391, + 724716405138963198014997547695908810889048549156514080027596147160291370305 + ], + # Point 400 + [ + 717433242268160049163784137690479364638414455354853503549047874912074602102, + 1548748206365927633434472413810887260565959748266730133832127499425371516599 + ], + # Point 401 + [ + 1113770395880110291540688660582111108167550152891676896388447618267044770699, + 225614903467949057887693353344008462684737622787852941160491287904124662539 + ], + # Point 402 + [ + 1485058593675291436770632813229171213081600725926085342541850536046844108938, + 1793621954142096162709529585852653649155672217087744074142012644930653464937 + ], + # Point 403 + [ + 3476670695984120275482827092638881388770060638684248848714588194147006700081, + 1886524232365250879956678425943479251575532912879489640410209691903853633501 + ], + # Point 404 + [ + 78942502761918568770028495237440346726648682399909104284710887733399086106, + 2352329060006211734866796061368085624251562468004096360671974444953215839619 + ], + # Point 405 + [ + 813411306209308549231686755344288448950826770557611647490856063401538266082, + 388260357229176651922921451505223699462456291090208287248038895703757789197 + ], + # Point 406 + [ + 2400377653395741560762585517571389386824872672242294297839787377212513042206, + 625963574857112775116550276725479329998557438550085741821320481021022314633 + ], + # Point 407 + [ + 2328311306030212189043981540771838515808167713931548944984202663139515716155, + 2492267419179473373149177460543961766435995439950143787911026640253198630634 + ], + # Point 408 + [ + 2387278462118275663019682655223568758817746496473668610784380666316276847549, + 2366392912056517351075178537570019722508034279024404671859228940369425653110 + ], + # Point 409 + [ + 2114043901163989266960550512790503188352140805965143750858598214845925650606, + 1263535257267454609784459072919175411484521178579707297022413088083025242903 + ], + # Point 410 + [ + 59742145227906406483850036502479681434689473008676866553687016399244912392, + 2542708853923609004067985135730679808561712875491472740521746862583772210812 + ], + # Point 411 + [ + 636010637932045133320800908704730609314370804715286131693312911610589568614, + 961184011917051478754418596971670071602153673068923576248721877819369343802 + ], + # Point 412 + [ + 1385321288061225556756217710115128107797163633073592537851457891527164069955, + 884133458066727093516473175829774361048238638693699012568518768170315343692 + ], + # Point 413 + [ + 758367055389438625364170431009355773885665422613428814074753204465381568635, + 1297727568042288931569138891121976026502156814623013130863114577102812764955 + ], + # Point 414 + [ + 347250288556045883174524792293804631653970133899259158293492482097831569584, + 1961316292966031207016509998947408765986081794211886403901120755246344327975 + ], + # Point 415 + [ + 1618690143706255801807773941554549190817935595982010405820190876061657720006, + 869166062594354289846899813362309895350435099932527145815122092397979607037 + ], + # Point 416 + [ + 1838634315648320098377933813041132543577219555069642930639820023291216325415, + 1529983605494771676066515349178506396294249260663468133203781588859819007226 + ], + # Point 417 + [ + 1605761809110472849967286703249646269449262088256390507097425667304759998260, + 2778705695237273113564800057362189513234607857754067598956643514325456052945 + ], + # Point 418 + [ + 3411908887615707635377666445857046055873541270252857286357865762696431003144, + 731871578063282896385513070338640294885089868946599519285295305156675019160 + ], + # Point 419 + [ + 3087826179342245618253320870694339144383060104280990174148199503867526625383, + 3491370412646223145431588455284304413010853471815192082926914611232193577659 + ], + # Point 420 + [ + 1436482751270429389743178806038120026461471807922309148229447715029903414786, + 1666868445318653269944338633661851246179662160526363040276638175667354338167 + ], + # Point 421 + [ + 2083331818739824497741363579518842947887353398223338451239015909684569222581, + 803732308089347618300580814078570019393110433387113056533917045601819866173 + ], + # Point 422 + [ + 1333165738767294811324643473740459024513360282013848003638568328453569807861, + 3324872774354633383424936425649606578793830970593411555017483797025902771350 + ], + # Point 423 + [ + 1701353517399938600269579188937389421636718828509341624534004474699861544304, + 982045252370486712463601150815912589800926352240648829942255288306435594694 + ], + # Point 424 + [ + 154403491180633257693040438747387470378033364863415878175486169393433469355, + 3204938488959824048546277939996603027140231747699901842459271581175521669368 + ], + # Point 425 + [ + 723459534533890816489433777735842913898349668429456824296910758117461179034, + 242799587564906089500242560200906533162881507520621156475321851862194708981 + ], + # Point 426 + [ + 2753553332546729968386491500304817162737325726065761497175967890302687642812, + 1607942131855693595003166153590488091960419143993462584570217492975562129656 + ], + # Point 427 + [ + 1318380449900940858865061333131606747009833602974109493687016869645480407519, + 824831465625011385685225132275899244334146133266851046834317903702436443339 + ], + # Point 428 + [ + 3406605858133553361333265720938744225870879664771897953784469453809670221140, + 951771439755520751413184037659273661906475899204291367776225253586492726939 + ], + # Point 429 + [ + 322881185844437667428941222631269048437916337535492466690110884912942662893, + 2408973683372380251710642884281850709418801321112976394586138388159314573007 + ], + # Point 430 + [ + 405651299272338889177305609117326577035390334272323319632791541534976007116, + 2403189350929802616202215631814070933678092854541380504903809068323291560762 + ], + # Point 431 + [ + 224362689432001608162440684107608338550077679080690583663897253625637368804, + 1089475930411433192334001657654788489125032803526758217361824123802939165263 + ], + # Point 432 + [ + 3427005033874860450481529297496326414099998098069008018777339291027217607772, + 1838523767315535107424920135038015356690704952046462913411177863052889918094 + ], + # Point 433 + [ + 971265369167429416740822747897568727646706124584943344118217646401474769333, + 1841017974237665428880957941884047733042022771551566493876831131552515616862 + ], + # Point 434 + [ + 593870383382776876660279010014844606756390978498220975404532129561707141345, + 1686799834245885653972636115594234495471835994524713453126028613189622517928 + ], + # Point 435 + [ + 158526420068961916859457596090914564342752702816639997380569526445580395317, + 829117473118004491391038359292216575457137041347974965494549565557957856696 + ], + # Point 436 + [ + 1145779849055685339419364615770769333393499278335670275163049680913140509683, + 2788919154400397863209918313477978879516215345298341199550275309881538159925 + ], + # Point 437 + [ + 3116362794833038183976492329109547313095843900691440633169686540582464014059, + 3493161563205582068593545591027301392887053692389997071622408416554079460959 + ], + # Point 438 + [ + 1484626752109997960148427439402147978447920625565926552692192513400801032840, + 106197164952655019784492783946283241349642545386756801239606388396528838557 + ], + # Point 439 + [ + 1905886704700624859591161108842694006068401527337545253689518506514368311453, + 2930319791340393503878111037261975689788636509301758506631633748789105811524 + ], + # Point 440 + [ + 2520975745491353699235764487116637116999082965127347369976279028452140192996, + 1121629571053704096076618751099836481664435087676492791791464786738401934277 + ], + # Point 441 + [ + 243197297174950489047429059272755382647269925832357483684904121385442353161, + 2417007034799146494310932386003928951978739444041601103686030150489540099099 + ], + # Point 442 + [ + 808704503940529633042533527196170354739879165235854099335954018048090166094, + 3250069164566476474875427019235306310423414676070245668865855537758860899226 + ], + # Point 443 + [ + 154229390082782874040656704404635500870072484057583506571360633170672994819, + 2702580522127958238752043600053843204107536082922189692357192526470020183763 + ], + # Point 444 + [ + 2296876062395150663198655775945704456626070946553449162794630784785525562017, + 2954047352783324167659477602480890942097095078541501369002104009282407983605 + ], + # Point 445 + [ + 2369843936932443670822535910120393374869045918271657278773939763527759009977, + 1504523220772650882417075434754212591932774780940943124208852030753505106366 + ], + # Point 446 + [ + 939818528450006271533633387575035651098636084551955010694000346778607054148, + 3288036962429189904257974342922419507417520404552917887258043999949243251337 + ], + # Point 447 + [ + 3261482392020733556192001297773623607067547108643955544388526649674482826812, + 3591646574338256506691865028073929692592246225874807859597509821099836701353 + ], + # Point 448 + [ + 1787025500314509707209621873912066561653490978360118110526034359358411964417, + 3421109838181987883898563834025345980326698037134918112926105203592921164968 + ], + # Point 449 + [ + 2311067462891595099991833582046690970468764702035366824733716384542858538356, + 470591103372156288650571624843786296549423881481598013229606695835762897581 + ], + # Point 450 + [ + 342559482224571965025189023541087083168706464251790471061097523992515769052, + 2368202754453331817392255123704696636258753331639084720511390832405023708522 + ], + # Point 451 + [ + 2572444430230869831563822697183525106374829659556462575156759807443152314062, + 1629527166738200666038465328035159152469458766421654684492365855846947518815 + ], + # Point 452 + [ + 1737389803319738463977618408470915902963778725702701713925657991380950954649, + 607082767712049126327196794168950822631607164722155751481706035333917604213 + ], + # Point 453 + [ + 2871556558000552242893836092551691122481664492915647218631883507493547887264, + 500384672074954803514661117946887054859762315512424468765642494189718439748 + ], + # Point 454 + [ + 3461109898461294404913712148331642005392308171104928758316008253448940997221, + 1749611977268851605988230236285965166361721258988901681470546570063157256891 + ], + # Point 455 + [ + 78615564253467413638171585726207276986620474184921384429093571227531830518, + 1981471018460691817560093727855711117339515783816308036046814473313844294189 + ], + # Point 456 + [ + 1474473102515930508594064469182212180992913337870765723235685741540431041359, + 1469840572053747949720853289796099707562318378077626046833147818685776895044 + ], + # Point 457 + [ + 2774277348282328170085742674659971495571500392161601753293416467112865068097, + 3137849475565620656681313987560185334413793189698703604034985791122356336275 + ], + # Point 458 + [ + 672166412479844650342747861081472852496472428762577522054587528815692740558, + 1663866879616164274903483340205017481052054060286752355521506181082184021495 + ], + # Point 459 + [ + 3180945697595716737370693680466484191429900897946060952610697010699886591271, + 370261256417300103212360232751326303498788243730491970857436044670395786744 + ], + # Point 460 + [ + 1201682462650513594149029305106730482154241597912555054934133306913124052927, + 2702519627582077982048055809065014719130205668704469328908061241215382284753 + ], + # Point 461 + [ + 2573098232031718801548737595348908190100964683270928802802545310591952021697, + 421880381247280215923854338509978900130125935079274502459764130391391089799 + ], + # Point 462 + [ + 3080354619128885268149548971617703089581818473398812557354479708280835955809, + 256390985131798172675848658065159234227830929883285189158702397702435740036 + ], + # Point 463 + [ + 3305006433068254197629607860194494003641107593674801290918966761186348868243, + 3339026583930460592461023889277320373514761589020084302062859793412730118034 + ], + # Point 464 + [ + 1923241940978623629561560707497295652725232345651095835780507541645042579369, + 1034987404476549078390013964157937064176009291357135198452850234267991254512 + ], + # Point 465 + [ + 1242878056067701165715334664693705019638823755244701687487484225344981621708, + 2778020811088591142544537894197696376781458923989081525073574174237563505877 + ], + # Point 466 + [ + 103159350300766901891674139940778235823347527941771255048984655353665811131, + 2998408586900586528158477448701326671048806329090729531148457644075161991659 + ], + # Point 467 + [ + 71218614431436475107665191107448218056096758969742620338129750710339397239, + 2301542035924343190515686028267024414404475677263930511758245534549493426119 + ], + # Point 468 + [ + 1192223060475270158246514565467403478627193592812213292121865811070708026418, + 3208674842884877565377729626392486507374130037365033772947426862394273679084 + ], + # Point 469 + [ + 2701994874964587709938210915198043688526355413043491003913483399075336026535, + 2886333482965974313702782452985415254520267819631653595494065556318924996302 + ], + # Point 470 + [ + 3306009714468475346677294651301864623371911173285781201209240934162798592783, + 605466198613255313875059832114128899690381126611676641778478133738267702577 + ], + # Point 471 + [ + 1450098270166428194278914193816868101013739489893607935042066987079292018245, + 60254834310937851613452000117427371272748499709961477924319944539270878930 + ], + # Point 472 + [ + 3472634693037182832142729498155218303319113785483118130849719300023909673520, + 1641508292869191604146734378916767617013327049910565729753281855691648138118 + ], + # Point 473 + [ + 1894817968472977732589643163725633598408826498334595898528746877517402007256, + 193046947184625502016096786396456839948351998258967537298275688147378157385 + ], + # Point 474 + [ + 798518742331453195093601874576575285888369661705098199469223675278431898599, + 107377575629504498627957848614396996685294283100119805971247351789316564355 + ], + # Point 475 + [ + 72958015465538907944252844948497831886498059248823205873862483987609118945, + 382019162223291772579736282379325284805025953290359275955015292364404489133 + ], + # Point 476 + [ + 3314147563388677609583640640385513143694481753397403848688825124581323140921, + 3133015892701145825892878874198767217439365812782859844688129544070997319541 + ], + # Point 477 + [ + 2444095071507078967491725470181706633826193926858202958437137361403432629390, + 3611326330343873710939978620686319096878621369582121415988028359537867630792 + ], + # Point 478 + [ + 2107740367200683219354058003519333570583941271011338026730935614636775950367, + 953104025671531038979165726896993106119997272237926795749220750824852831326 + ], + # Point 479 + [ + 3234656093348684616411954956071945712517868853923240328793520762092030352104, + 593797219275153872348854593022027262920747808515266478952508298984248786194 + ], + # Point 480 + [ + 1602095369325252473599793473477096021341779224145909730627015015569257565602, + 3272207934512491788245765497830881358513949839139669547034252308088069134971 + ], + # Point 481 + [ + 3149982146511124605253586861983261220008132001764223357617600582721741955729, + 2095033709871635786785037791315376486221829475125386227641672208905379085173 + ], + # Point 482 + [ + 3001928273494873166012582714776651165587585607934603548408913923445707516447, + 1090266449665582127890982346068170500195143341692394417827957481874850148101 + ], + # Point 483 + [ + 1200188694803418163698740652082064619102559508865389996854547617715365179733, + 2701249247866363010207334776256734312390753341409567681725083482804664533572 + ], + # Point 484 + [ + 2181535855195652401374211290838795366238299923929423873085735410894622521047, + 2376412857416063533601832417302250911062085318826783757605801470091451365057 + ], + # Point 485 + [ + 482216227882593950837484293882738923170227555670484160841935409522897080275, + 3568625956076637648484022304136932386854238480511347282187445648692365982275 + ], + # Point 486 + [ + 2597799669063340267995599892195854202917996255210920411104497198234262573206, + 3599688873496047961188179975715306136394737017927100659789000949688648352738 + ], + # Point 487 + [ + 1074803073878259665128704812644508804681887514658526880458531694790041080733, + 2797007314237358398853761880558407828488654212157591536243300688627270327611 + ], + # Point 488 + [ + 468516314257643341712703808920275922572701541039900608201362821198817231181, + 1374527914917866113032496762177078616084751647116110264806150108724834406388 + ], + # Point 489 + [ + 1208295021405021769918747031548141820393186404250802478605705170489054454157, + 1808650369660257850053512198148641736021245568921649705336796845945032764079 + ], + # Point 490 + [ + 1565766387634045687737221757815822397332528241094852872444435795933964809539, + 3012257951756551767601579252037773433674622118381097513581891561867035592007 + ], + # Point 491 + [ + 296854483963576847362550837105567519963678928320906132062353506063381160038, + 2997621846549637009998526882695309289938245125374301351844169021609166074993 + ], + # Point 492 + [ + 967192065183772403576792198824105189328662921952353748318585728895552782317, + 343259257621685032456419166732348373845783852374756662051672936363788935338 + ], + # Point 493 + [ + 456199991075423266290193634515181612721738873753897142916892886319728411618, + 3009804764464949268795409945347301690744692042792259709767667423717478448229 + ], + # Point 494 + [ + 242210936510534966601707881924518098930470912600217515366037129037143259602, + 2698396953115175496998177800235267214917400761768300598355994305287141321271 + ], + # Point 495 + [ + 943070145114606420402378547694273832277173459266557776382587828687889744387, + 463274201648439487604179878678549669204059390508994779482933722127905403494 + ], + # Point 496 + [ + 958740504301613688526614902955971923282735882762427544770668882582144756278, + 1648715733425449195104866742590789175535499260400395501324113794510596751843 + ], + # Point 497 + [ + 2738467504442851843676882768027062309179245908086784030957627999641833602734, + 403058158250173068725971433188527745485878446592356816888847490041658726521 + ], + # Point 498 + [ + 851584175891221844656684173543344216725376446573878021753176795750916050165, + 2989949954698892115645230865442980084822288702406021287339635187958555345139 + ], + # Point 499 + [ + 2648599427528401936572004779373684054684563511138695864967500218937169746782, + 545540526832939914359571382575431287745621956136188859527645452849687661804 + ], + # Point 500 + [ + 2781909713387830233242081629088160756893816968845366829353355409163126437112, + 3474066345655056798683792894555452083305620276965763551620554269294421970656 + ], + # Point 501 + [ + 62007937137219837991330947293062511223089197148302827535260096716770644685, + 3537197938414984401398955238628360208832764753222288521326061893140886724271 + ], + # Point 502 + [ + 2379962749567351885752724891227938183011949129833673362440656643086021394946, + 776496453633298175483985398648758586525933812536653089401905292063708816422 + ], + # Point 503 + [ + 553697491755753712548822408932664734674730150084063981046477343718694621804, + 2797798649021537247229237999331435556632872779265479409612091247299955463913 + ], + # Point 504 + [ + 2026114267613810970244390071397350467776533880677809710454617259260017487512, + 3330593270696197494966018967263043594632970418364498628573044882141635806155 + ], + # Point 505 + [ + 1254733481274108825174693797237617285863727098996450904398879255272288617861, + 2644890941682394074696857415419096381561354281743803087373802494123523779468 + ] +] diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/pedersen_hash.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/pedersen_hash.py new file mode 100644 index 0000000..dc19c7e --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/crypto/pedersen_hash.py @@ -0,0 +1,209 @@ +""" +Pedersen hash implementation for StarkWare cryptography. + +This module provides a full implementation of the Pedersen hash function +as specified by StarkWare, compatible with the reference implementation. +""" + +from typing import List, Tuple, Union + +# Handle both relative and absolute imports +try: + from .constants import ( + FIELD_PRIME, ALPHA, BETA, N_ELEMENT_BITS_HASH, + SHIFT_POINT, CONSTANT_POINTS + ) +except ImportError: + from constants import ( + FIELD_PRIME, ALPHA, BETA, N_ELEMENT_BITS_HASH, + SHIFT_POINT, CONSTANT_POINTS + ) + + +def _div_mod(n: int, m: int, p: int) -> int: + """ + Calculate (n / m) mod p. + + Args: + n: The numerator + m: The denominator + p: The modulus + + Returns: + int: The result of the division modulo p + """ + return (n * pow(m, -1, p)) % p + + +def _ec_add(p1: Tuple[int, int], p2: Tuple[int, int]) -> Tuple[int, int]: + """ + Add two points on the elliptic curve. + + Args: + p1: The first point as (x, y) coordinates + p2: The second point as (x, y) coordinates + + Returns: + Tuple[int, int]: The resulting point as (x, y) coordinates + """ + if p1[0] == p2[0]: + if (p1[1] + p2[1]) % FIELD_PRIME == 0: + # The points are negatives of each other, return the point at infinity + # We represent the point at infinity as None, but this should never happen + # in our use case, so we raise an exception instead + raise ValueError("Points are negatives of each other") + + # The points are the same, so we're doubling + return _ec_double(p1) + + # Calculate the slope + slope = _div_mod(p2[1] - p1[1], p2[0] - p1[0], FIELD_PRIME) + + # Calculate the new point + x3 = (slope * slope - p1[0] - p2[0]) % FIELD_PRIME + y3 = (slope * (p1[0] - x3) - p1[1]) % FIELD_PRIME + + return (x3, y3) + + +def _ec_double(p: Tuple[int, int]) -> Tuple[int, int]: + """ + Double a point on the elliptic curve. + + Args: + p: The point to double as (x, y) coordinates + + Returns: + Tuple[int, int]: The resulting point as (x, y) coordinates + """ + # Calculate the slope + slope = _div_mod(3 * p[0] * p[0] + ALPHA, 2 * p[1], FIELD_PRIME) + + # Calculate the new point + x3 = (slope * slope - 2 * p[0]) % FIELD_PRIME + y3 = (slope * (p[0] - x3) - p[1]) % FIELD_PRIME + + return (x3, y3) + + +def _ec_mult(m: int, p: Tuple[int, int]) -> Tuple[int, int]: + """ + Multiply a point on the elliptic curve by a scalar. + + Args: + m: The scalar + p: The point as (x, y) coordinates + + Returns: + Tuple[int, int]: The resulting point as (x, y) coordinates + """ + if m == 0: + raise ValueError("Cannot multiply by 0") + + if m == 1: + return p + + if m % 2 == 0: + return _ec_mult(m // 2, _ec_double(p)) + else: + return _ec_add(p, _ec_mult(m - 1, p)) + + +def pedersen_hash_as_point(*elements: int) -> Tuple[int, int]: + """ + Calculate the Pedersen hash of a list of integers and return the full EC point. + + This is the full implementation following StarkWare's specification: + For each element, iterate through its 252 bits and add corresponding + constant points based on the bit values. + + Args: + *elements: Variable number of integers to hash + + Returns: + Tuple[int, int]: The resulting EC point as (x, y) coordinates + + Raises: + ValueError: If any element is out of range or if there are insufficient constant points + """ + # Start with the shift point + point = tuple(SHIFT_POINT) + + for i, element in enumerate(elements): + # Validate element is in valid range + if not (0 <= element < FIELD_PRIME): + raise ValueError(f"Element {element} is out of range [0, {FIELD_PRIME})") + + # Calculate the starting index for this element's constant points + start_idx = 2 + i * N_ELEMENT_BITS_HASH + + # Check if we have enough constant points + if start_idx + N_ELEMENT_BITS_HASH > len(CONSTANT_POINTS): + raise ValueError(f"Insufficient constant points for element {i}. Need {start_idx + N_ELEMENT_BITS_HASH}, have {len(CONSTANT_POINTS)}") + + # Full implementation using all 252 bits + for j in range(N_ELEMENT_BITS_HASH): + pt = tuple(CONSTANT_POINTS[start_idx + j]) + + # Check for unhashable input (same x coordinate) + if point[0] == pt[0]: + raise ValueError('Unhashable input: point collision detected') + + if element & 1: + point = _ec_add(point, pt) + element >>= 1 + + # Ensure all bits have been processed + if element != 0: + raise ValueError(f"Element too large: remaining bits {element}") + + return point + + +def pedersen_hash(*elements: int) -> int: + """ + Calculate the Pedersen hash of a list of integers. + + This function returns only the x-coordinate of the resulting EC point, + which is the standard Pedersen hash value. + + Args: + *elements: Variable number of integers to hash + + Returns: + int: The Pedersen hash as an integer (x-coordinate of the EC point) + + Raises: + ValueError: If any element is out of range + """ + point = pedersen_hash_as_point(*elements) + return point[0] + + +def pedersen_hash_bytes(*elements: Union[int, bytes]) -> bytes: + """ + Calculate the Pedersen hash and return as bytes. + + Args: + *elements: Variable number of integers or bytes to hash + + Returns: + bytes: The hash result as 32 bytes (big-endian) + + Raises: + ValueError: If any element is invalid + """ + # Convert bytes to integers if needed + int_elements = [] + for element in elements: + if isinstance(element, bytes): + if len(element) > 32: + raise ValueError(f"Bytes element too long: {len(element)} > 32") + int_elements.append(int.from_bytes(element, byteorder='big')) + elif isinstance(element, int): + int_elements.append(element) + else: + raise ValueError(f"Invalid element type: {type(element)}") + + hash_result = pedersen_hash(*int_elements) + return hash_result.to_bytes(32, byteorder='big') diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/funding/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/funding/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/funding/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/funding/client.py new file mode 100644 index 0000000..1898149 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/funding/client.py @@ -0,0 +1,114 @@ +from typing import Dict, Any, List + +from ..internal.async_client import AsyncClient + + +class Client: + """Client for funding-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the funding client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def get_funding_transactions( + self, + size: str = "", + offset_data: str = "", + filter_coin_id_list: List[str] = None, + filter_type_list: List[str] = None, + filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0 + ) -> Dict[str, Any]: + """ + Get funding transactions with pagination. + + Args: + size: Size of the page + offset_data: Offset data for pagination + filter_coin_id_list: Filter by coin IDs + filter_type_list: Filter by transaction types + filter_start_created_time_inclusive: Filter start time (inclusive) + filter_end_created_time_exclusive: Filter end time (exclusive) + + Returns: + Dict[str, Any]: The funding transactions + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if size: + query_params["size"] = size + if offset_data: + query_params["offsetData"] = offset_data + + # Add filter parameters + if filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(filter_coin_id_list) + if filter_type_list: + query_params["filterTypeList"] = ",".join(filter_type_list) + + # Add time filters + if filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(filter_start_created_time_inclusive) + if filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/public/funding/getFundingRatePage", + params=query_params + ) + + async def get_funding_account(self) -> Dict[str, Any]: + """ + Get funding account information. + + Returns: + Dict[str, Any]: The funding account information + + Raises: + ValueError: If the request fails + """ + params = { + "accountId": str(self.async_client.get_account_id()) + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/account/getAccountAsset", + params=params + ) + + async def get_funding_transaction_by_id(self, transaction_ids: List[str]) -> Dict[str, Any]: + """ + Get funding transactions by IDs. + + Args: + transaction_ids: List of transaction IDs + + Returns: + Dict[str, Any]: The funding transactions + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()), + "transactionIdList": ",".join(transaction_ids) + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/public/funding/getLatestFundingRate", + params=query_params + ) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/async_client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/async_client.py new file mode 100644 index 0000000..41b51ef --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/async_client.py @@ -0,0 +1,466 @@ +import asyncio +import binascii +import hashlib +import time +import uuid +from typing import Dict, Any, Optional, Tuple, List, Union +import json + +import aiohttp +from Crypto.Hash import keccak + +from .signing_adapter import SigningAdapter + +# Import field prime for modular arithmetic +try: + from ..crypto.constants import FIELD_PRIME +except ImportError: + # Fallback if crypto module is not available + FIELD_PRIME = 0x800000000000011000000000000000000000000000000000000000000000001 + +# Constants +LIMIT_ORDER_WITH_FEE_TYPE = 3 + + +class L2Signature: + """Represents a signature for L2 operations.""" + + def __init__(self, r: str, s: str, v: str = ""): + self.r = r + self.s = s + self.v = v + + +class AsyncClient: + """Async base client with common functionality.""" + + def __init__(self, base_url: str, account_id: int, stark_pri_key: str, + signing_adapter: Optional[SigningAdapter] = None, + timeout: float = 30.0, connector_limit: int = 100): + """ + Initialize the async internal client. + + Args: + base_url: Base URL for API endpoints + account_id: Account ID for authentication + stark_pri_key: Stark private key for signing + signing_adapter: Optional signing adapter to use for cryptographic operations + timeout: Request timeout in seconds + connector_limit: Maximum number of connections in the pool + """ + self.base_url = base_url + self.account_id = account_id + self.stark_pri_key = stark_pri_key + + # Use the provided signing adapter (required) + if signing_adapter is None: + raise ValueError("signing_adapter is required") + self.signing_adapter = signing_adapter + + # Store configuration for later session creation + self._session = None + self._timeout = timeout + self._connector_limit = connector_limit + self._closed = False + + async def __aenter__(self): + """Async context manager entry.""" + await self._ensure_session() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """Async context manager exit.""" + await self.close() + + async def _ensure_session(self): + """Ensure the aiohttp session is created.""" + if self._session is None or self._session.closed: + # Create connector and session when needed (inside event loop) + timeout_config = aiohttp.ClientTimeout(total=self._timeout) + connector = aiohttp.TCPConnector( + limit=self._connector_limit, + limit_per_host=30, + keepalive_timeout=30, + enable_cleanup_closed=True + ) + + self._session = aiohttp.ClientSession( + timeout=timeout_config, + connector=connector, + headers={ + "Content-Type": "application/json", + "Accept": "application/json" + } + ) + + async def close(self): + """Close the HTTP session and cleanup resources.""" + if self._session and not self._session.closed: + await self._session.close() + self._closed = True + + @property + def session(self) -> aiohttp.ClientSession: + """Get the HTTP session, ensuring it's created.""" + if self._session is None or self._session.closed: + raise RuntimeError("Session not initialized. Use 'async with client:' or call '_ensure_session()'") + return self._session + + def get_account_id(self) -> int: + """Get the account ID.""" + return self.account_id + + def get_stark_pri_key(self) -> str: + """Get the stark private key.""" + return self.stark_pri_key + + def sign(self, message_hash: bytes) -> L2Signature: + """ + Sign a message hash using the client's Stark private key. + + Args: + message_hash: The hash of the message to sign + + Returns: + L2Signature: The signature components + + Raises: + ValueError: If the stark private key is not set or invalid + """ + private_key = self.get_stark_pri_key() + if not private_key: + raise ValueError("stark private key not set") + + # Sign the message using the signing adapter + try: + r, s = self.signing_adapter.sign(message_hash, private_key) + return L2Signature(r=r, s=s, v="") + except Exception as e: + raise ValueError(f"failed to sign message: {str(e)}") + + def generate_uuid(self) -> str: + """Generate a UUID for client order IDs.""" + return str(uuid.uuid4()) + + def calc_nonce(self, client_order_id: str) -> int: + """ + Calculate a nonce from a client order ID. + + Args: + client_order_id: The client order ID + + Returns: + int: The calculated nonce + """ + # Use SHA256 like the Go SDK (not Keccak256) + h = hashlib.sha256() + h.update(client_order_id.encode()) + hash_hex = h.hexdigest() + return int(hash_hex[:8], 16) + + async def make_authenticated_request( + self, + method: str, + path: str, + data: Optional[Dict[str, Any]] = None, + params: Optional[Dict[str, Any]] = None + ) -> Dict[str, Any]: + """ + Make an authenticated HTTP request. + + Args: + method: HTTP method (GET, POST, etc.) + path: API path (e.g., '/api/v1/private/order/createOrder') + data: JSON data for POST requests + params: Query parameters for GET requests + + Returns: + Dict[str, Any]: Response JSON data + + Raises: + ValueError: If the request fails + """ + await self._ensure_session() + + # Generate timestamp + timestamp = int(time.time() * 1000) + + # Build full URL + url = f"{self.base_url}{path}" + + # Generate signature content + sign_content = self._build_signature_content(timestamp, method, path, data, params) + + # Sign the content + keccak_hash = keccak.new(digest_bits=256) + keccak_hash.update(sign_content.encode()) + content_hash = keccak_hash.digest() + + sig = self.sign(content_hash) + + # Prepare headers + headers = { + "X-edgeX-Api-Timestamp": str(timestamp), + "X-edgeX-Api-Signature": f"{sig.r}{sig.s}" + } + + # Make the request + try: + async with self.session.request( + method=method, + url=url, + json=data, + params=params, + headers=headers + ) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except (aiohttp.ContentTypeError, json.JSONDecodeError): + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + # Check response code + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except aiohttp.ClientError as e: + raise ValueError(f"HTTP request failed: {str(e)}") + + def _build_signature_content( + self, + timestamp: int, + method: str, + path: str, + data: Optional[Dict[str, Any]], + params: Optional[Dict[str, Any]] + ) -> str: + """Build the content string for signature generation.""" + if data: + # Convert body to sorted string format + body_str = self.get_value(data) + sign_content = f"{timestamp}{method}{path}{body_str}" + else: + # For requests without body, use query parameters if present + if params: + # Sort query parameters as strings (matching Go SDK exactly) + param_pairs = [] + for key, value in sorted(params.items()): + param_pairs.append(f"{key}={value}") + query_string = "&".join(param_pairs) + sign_content = f"{timestamp}{method}{path}{query_string}" + else: + sign_content = f"{timestamp}{method}{path}" + + return sign_content + + def get_value(self, data: Union[Dict[str, Any], List[Any], str, int, float, None]) -> str: + """ + Convert a value to a string representation for signing. + This function recursively processes dictionaries, lists, and primitive types. + + Args: + data: The value to convert + + Returns: + str: The string representation + """ + if data is None: + return "" + + if isinstance(data, str): + return data + + if isinstance(data, bool): + # Convert boolean to lowercase string to match Go SDK + return str(data).lower() + + if isinstance(data, (int, float)): + return str(data) + + if isinstance(data, list): + if len(data) == 0: + return "" + values = [self.get_value(item) for item in data] + return "&".join(values) + + if isinstance(data, dict): + # Convert all values to strings and sort by keys + sorted_map = {} + for key, val in data.items(): + sorted_map[key] = self.get_value(val) + + # Get sorted keys + keys = sorted(sorted_map.keys()) + + # Build key=value pairs + pairs = [f"{key}={sorted_map[key]}" for key in keys] + return "&".join(pairs) + + # Handle other types by converting to string + return str(data) + + def calc_limit_order_hash( + self, + synthetic_asset_id: str, + collateral_asset_id: str, + fee_asset_id: str, + is_buy: bool, + amount_synthetic: int, + amount_collateral: int, + amount_fee: int, + nonce: int, + account_id: int, + expire_time: int + ) -> bytes: + """ + Calculate the hash for a limit order using StarkEx protocol. + + Args: + synthetic_asset_id: The synthetic asset ID (hex string) + collateral_asset_id: The collateral asset ID (hex string) + fee_asset_id: The fee asset ID (hex string) + is_buy: Whether the order is a buy order + amount_synthetic: The synthetic amount + amount_collateral: The collateral amount + amount_fee: The fee amount + nonce: The nonce + account_id: The account ID (position ID) + expire_time: The expiration time + + Returns: + bytes: The calculated hash + """ + # Remove 0x prefix if present + if synthetic_asset_id.startswith('0x'): + synthetic_asset_id = synthetic_asset_id[2:] + if collateral_asset_id.startswith('0x'): + collateral_asset_id = collateral_asset_id[2:] + if fee_asset_id.startswith('0x'): + fee_asset_id = fee_asset_id[2:] + + # Convert hex strings to integers and ensure they're within the field + asset_id_synthetic = int(synthetic_asset_id, 16) % FIELD_PRIME + asset_id_collateral = int(collateral_asset_id, 16) % FIELD_PRIME + asset_id_fee = int(fee_asset_id, 16) % FIELD_PRIME + + # Determine buy/sell assets based on order direction + if is_buy: + asset_id_sell = asset_id_collateral + asset_id_buy = asset_id_synthetic + amount_sell = amount_collateral + amount_buy = amount_synthetic + else: + asset_id_sell = asset_id_synthetic + asset_id_buy = asset_id_collateral + amount_sell = amount_synthetic + amount_buy = amount_collateral + + # Use the signing adapter to calculate the Pedersen hash + # First hash: hash(asset_id_sell, asset_id_buy) + msg = self.signing_adapter.pedersen_hash([asset_id_sell, asset_id_buy]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Second hash: hash(msg, asset_id_fee) + msg = self.signing_adapter.pedersen_hash([msg_int, asset_id_fee]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 0 + # packed_message0 = amount_sell * 2^64 + amount_buy * 2^64 + max_amount_fee * 2^32 + nonce + packed_message0 = amount_sell + packed_message0 = (packed_message0 << 64) + amount_buy + packed_message0 = (packed_message0 << 64) + amount_fee + packed_message0 = (packed_message0 << 32) + nonce + packed_message0 = packed_message0 % FIELD_PRIME # Ensure within field + + # Third hash: hash(msg, packed_message0) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_message0]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 1 + # packed_message1 = LIMIT_ORDER_WITH_FEES * 2^64 + position_id * 2^64 + position_id * 2^64 + position_id * 2^32 + expiration_timestamp * 2^17 + packed_message1 = LIMIT_ORDER_WITH_FEE_TYPE + packed_message1 = (packed_message1 << 64) + account_id + packed_message1 = (packed_message1 << 64) + account_id + packed_message1 = (packed_message1 << 64) + account_id + packed_message1 = (packed_message1 << 32) + expire_time + packed_message1 = packed_message1 << 17 # Padding + packed_message1 = packed_message1 % FIELD_PRIME # Ensure within field + + # Final hash: hash(msg, packed_message1) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_message1]) + + return msg + + def calc_transfer_hash( + self, + asset_id: int, + asset_id_fee: int, + receiver_public_key: int, + sender_position_id: int, + receiver_position_id: int, + fee_position_id: int, + nonce: int, + amount: int, + max_amount_fee: int, + expiration_timestamp: int + ) -> bytes: + """ + Calculate the hash for a transfer using StarkEx protocol. + + Args: + asset_id: The asset ID + asset_id_fee: The fee asset ID + receiver_public_key: The receiver's public key + sender_position_id: The sender's position ID + receiver_position_id: The receiver's position ID + fee_position_id: The fee position ID + nonce: The nonce + amount: The transfer amount + max_amount_fee: The maximum fee amount + expiration_timestamp: The expiration timestamp + + Returns: + bytes: The calculated hash + """ + # First hash: hash(asset_id, asset_id_fee) + msg = self.signing_adapter.pedersen_hash([asset_id, asset_id_fee]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Second hash: hash(msg, receiver_public_key) + msg = self.signing_adapter.pedersen_hash([msg_int, receiver_public_key]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 0 + # packed_msg0 = sender_position_id * 2^64 + receiver_position_id * 2^64 + fee_position_id * 2^32 + nonce + packed_msg0 = sender_position_id + packed_msg0 = (packed_msg0 << 64) + receiver_position_id + packed_msg0 = (packed_msg0 << 64) + fee_position_id + packed_msg0 = (packed_msg0 << 32) + nonce + packed_msg0 = packed_msg0 % FIELD_PRIME # Ensure within field + + # Third hash: hash(msg, packed_msg0) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_msg0]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 1 + # packed_msg1 = 4 * 2^64 + amount * 2^64 + max_amount_fee * 2^32 + expiration_timestamp * 2^81 + packed_msg1 = 4 # Transfer type + packed_msg1 = (packed_msg1 << 64) + amount + packed_msg1 = (packed_msg1 << 64) + max_amount_fee + packed_msg1 = (packed_msg1 << 32) + expiration_timestamp + packed_msg1 = packed_msg1 << 81 # Padding + packed_msg1 = packed_msg1 % FIELD_PRIME # Ensure within field + + # Final hash: hash(msg, packed_msg1) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_msg1]) + + return msg diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/client.py new file mode 100644 index 0000000..730a2db --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/client.py @@ -0,0 +1,312 @@ +import binascii +import hashlib +import time +import uuid +from typing import Dict, Any, Optional, Tuple, List, Union + +import requests +from Crypto.Hash import keccak + +from .signing_adapter import SigningAdapter + +# Import field prime for modular arithmetic +try: + from ..crypto.constants import FIELD_PRIME +except ImportError: + # Fallback if crypto module is not available + FIELD_PRIME = 0x800000000000011000000000000000000000000000000000000000000000001 + +# Constants +LIMIT_ORDER_WITH_FEE_TYPE = 3 + + +class L2Signature: + """Represents a signature for L2 operations.""" + + def __init__(self, r: str, s: str, v: str = ""): + self.r = r + self.s = s + self.v = v + + +class Client: + """Base client with common functionality.""" + + def __init__(self, base_url: str, account_id: int, stark_pri_key: str, signing_adapter: Optional[SigningAdapter] = None): + """ + Initialize the internal client. + + Args: + base_url: Base URL for API endpoints + account_id: Account ID for authentication + stark_pri_key: Stark private key for signing + signing_adapter: Optional signing adapter to use for cryptographic operations + """ + self.http_client = requests.Session() + self.http_client.headers.update({ + "Content-Type": "application/json", + "Accept": "application/json" + }) + self.base_url = base_url + self.account_id = account_id + self.stark_pri_key = stark_pri_key + + # Use the provided signing adapter (required) + if signing_adapter is None: + raise ValueError("signing_adapter is required") + self.signing_adapter = signing_adapter + + def get_account_id(self) -> int: + """Get the account ID.""" + return self.account_id + + def get_stark_pri_key(self) -> str: + """Get the stark private key.""" + return self.stark_pri_key + + def sign(self, message_hash: bytes) -> L2Signature: + """ + Sign a message hash using the client's Stark private key. + + Args: + message_hash: The hash of the message to sign + + Returns: + L2Signature: The signature components + + Raises: + ValueError: If the stark private key is not set or invalid + """ + private_key = self.get_stark_pri_key() + if not private_key: + raise ValueError("stark private key not set") + + # Sign the message using the signing adapter + try: + r, s = self.signing_adapter.sign(message_hash, private_key) + return L2Signature(r=r, s=s, v="") + except Exception as e: + raise ValueError(f"failed to sign message: {str(e)}") + + def generate_uuid(self) -> str: + """Generate a UUID for client order IDs.""" + return str(uuid.uuid4()) + + def calc_nonce(self, client_order_id: str) -> int: + """ + Calculate a nonce from a client order ID. + + Args: + client_order_id: The client order ID + + Returns: + int: The calculated nonce + """ + # Use SHA256 like the Go SDK (not Keccak256) + h = hashlib.sha256() + h.update(client_order_id.encode()) + hash_hex = h.hexdigest() + return int(hash_hex[:8], 16) + + def calc_limit_order_hash( + self, + synthetic_asset_id: str, + collateral_asset_id: str, + fee_asset_id: str, + is_buy: bool, + amount_synthetic: int, + amount_collateral: int, + amount_fee: int, + nonce: int, + account_id: int, + expire_time: int + ) -> bytes: + """ + Calculate the hash for a limit order using StarkEx protocol. + + Args: + synthetic_asset_id: The synthetic asset ID (hex string) + collateral_asset_id: The collateral asset ID (hex string) + fee_asset_id: The fee asset ID (hex string) + is_buy: Whether the order is a buy order + amount_synthetic: The synthetic amount + amount_collateral: The collateral amount + amount_fee: The fee amount + nonce: The nonce + account_id: The account ID (position ID) + expire_time: The expiration time + + Returns: + bytes: The calculated hash + """ + # Remove 0x prefix if present + if synthetic_asset_id.startswith('0x'): + synthetic_asset_id = synthetic_asset_id[2:] + if collateral_asset_id.startswith('0x'): + collateral_asset_id = collateral_asset_id[2:] + if fee_asset_id.startswith('0x'): + fee_asset_id = fee_asset_id[2:] + + # Convert hex strings to integers and ensure they're within the field + asset_id_synthetic = int(synthetic_asset_id, 16) % FIELD_PRIME + asset_id_collateral = int(collateral_asset_id, 16) % FIELD_PRIME + asset_id_fee = int(fee_asset_id, 16) % FIELD_PRIME + + # Determine buy/sell assets based on order direction + if is_buy: + asset_id_sell = asset_id_collateral + asset_id_buy = asset_id_synthetic + amount_sell = amount_collateral + amount_buy = amount_synthetic + else: + asset_id_sell = asset_id_synthetic + asset_id_buy = asset_id_collateral + amount_sell = amount_synthetic + amount_buy = amount_collateral + + # Use the signing adapter to calculate the Pedersen hash + # First hash: hash(asset_id_sell, asset_id_buy) + msg = self.signing_adapter.pedersen_hash([asset_id_sell, asset_id_buy]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Second hash: hash(msg, asset_id_fee) + msg = self.signing_adapter.pedersen_hash([msg_int, asset_id_fee]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 0 + # packed_message0 = amount_sell * 2^64 + amount_buy * 2^64 + max_amount_fee * 2^32 + nonce + packed_message0 = amount_sell + packed_message0 = (packed_message0 << 64) + amount_buy + packed_message0 = (packed_message0 << 64) + amount_fee + packed_message0 = (packed_message0 << 32) + nonce + packed_message0 = packed_message0 % FIELD_PRIME # Ensure within field + + # Third hash: hash(msg, packed_message0) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_message0]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 1 + # packed_message1 = LIMIT_ORDER_WITH_FEES * 2^64 + position_id * 2^64 + position_id * 2^64 + position_id * 2^32 + expiration_timestamp * 2^17 + packed_message1 = LIMIT_ORDER_WITH_FEE_TYPE + packed_message1 = (packed_message1 << 64) + account_id + packed_message1 = (packed_message1 << 64) + account_id + packed_message1 = (packed_message1 << 64) + account_id + packed_message1 = (packed_message1 << 32) + expire_time + packed_message1 = packed_message1 << 17 # Padding + packed_message1 = packed_message1 % FIELD_PRIME # Ensure within field + + # Final hash: hash(msg, packed_message1) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_message1]) + + return msg + + def calc_transfer_hash( + self, + asset_id: int, + asset_id_fee: int, + receiver_public_key: int, + sender_position_id: int, + receiver_position_id: int, + fee_position_id: int, + nonce: int, + amount: int, + max_amount_fee: int, + expiration_timestamp: int + ) -> bytes: + """ + Calculate the hash for a transfer using StarkEx protocol. + + Args: + asset_id: The asset ID + asset_id_fee: The fee asset ID + receiver_public_key: The receiver's public key + sender_position_id: The sender's position ID + receiver_position_id: The receiver's position ID + fee_position_id: The fee position ID + nonce: The nonce + amount: The transfer amount + max_amount_fee: The maximum fee amount + expiration_timestamp: The expiration timestamp + + Returns: + bytes: The calculated hash + """ + # First hash: hash(asset_id, asset_id_fee) + msg = self.signing_adapter.pedersen_hash([asset_id, asset_id_fee]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Second hash: hash(msg, receiver_public_key) + msg = self.signing_adapter.pedersen_hash([msg_int, receiver_public_key]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 0 + # packed_msg0 = sender_position_id * 2^64 + receiver_position_id * 2^64 + fee_position_id * 2^32 + nonce + packed_msg0 = sender_position_id + packed_msg0 = (packed_msg0 << 64) + receiver_position_id + packed_msg0 = (packed_msg0 << 64) + fee_position_id + packed_msg0 = (packed_msg0 << 32) + nonce + packed_msg0 = packed_msg0 % FIELD_PRIME # Ensure within field + + # Third hash: hash(msg, packed_msg0) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_msg0]) + msg_int = int.from_bytes(msg, byteorder='big') + + # Pack message 1 + # packed_msg1 = 4 * 2^64 + amount * 2^64 + max_amount_fee * 2^32 + expiration_timestamp * 2^81 + packed_msg1 = 4 # Transfer type + packed_msg1 = (packed_msg1 << 64) + amount + packed_msg1 = (packed_msg1 << 64) + max_amount_fee + packed_msg1 = (packed_msg1 << 32) + expiration_timestamp + packed_msg1 = packed_msg1 << 81 # Padding + packed_msg1 = packed_msg1 % FIELD_PRIME # Ensure within field + + # Final hash: hash(msg, packed_msg1) + msg = self.signing_adapter.pedersen_hash([msg_int, packed_msg1]) + + return msg + + def get_value(self, data: Union[Dict[str, Any], List[Any], str, int, float, None]) -> str: + """ + Convert a value to a string representation for signing. + This function recursively processes dictionaries, lists, and primitive types. + + Args: + data: The value to convert + + Returns: + str: The string representation + """ + if data is None: + return "" + + if isinstance(data, str): + return data + + if isinstance(data, bool): + # Convert boolean to lowercase string to match Go SDK + return str(data).lower() + + if isinstance(data, (int, float)): + return str(data) + + if isinstance(data, list): + if len(data) == 0: + return "" + values = [self.get_value(item) for item in data] + return "&".join(values) + + if isinstance(data, dict): + # Convert all values to strings and sort by keys + sorted_map = {} + for key, val in data.items(): + sorted_map[key] = self.get_value(val) + + # Get sorted keys + keys = sorted(sorted_map.keys()) + + # Build key=value pairs + pairs = [f"{key}={sorted_map[key]}" for key in keys] + return "&".join(pairs) + + # Handle other types by converting to string + return str(data) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/signing_adapter.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/signing_adapter.py new file mode 100644 index 0000000..aa9e12c --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/signing_adapter.py @@ -0,0 +1,77 @@ +""" +Signing adapter interface for the EdgeX Python SDK. + +This module defines the interface for signing adapters that can be used with the SDK. +Different implementations can be provided for different environments (development, testing, production). +""" + +from abc import ABC, abstractmethod +from typing import Tuple, List + + +class SigningAdapter(ABC): + """Interface for signing adapters.""" + + @abstractmethod + def sign(self, message_hash: bytes, private_key: str) -> Tuple[str, str]: + """ + Sign a message hash using a private key. + + Args: + message_hash: The hash of the message to sign + private_key: The private key as a hex string + + Returns: + Tuple[str, str]: The signature as (r, s) hex strings + + Raises: + ValueError: If the private key is invalid or the signing fails + """ + pass + + @abstractmethod + def get_public_key(self, private_key: str) -> str: + """ + Get the public key from a private key. + + Args: + private_key: The private key as a hex string + + Returns: + str: The public key as a hex string + + Raises: + ValueError: If the private key is invalid + """ + pass + + @abstractmethod + def verify(self, message_hash: bytes, signature: Tuple[str, str], public_key: str) -> bool: + """ + Verify a signature using a public key. + + Args: + message_hash: The hash of the message + signature: The signature as (r, s) hex strings + public_key: The public key as a hex string + + Returns: + bool: Whether the signature is valid + """ + pass + + @abstractmethod + def pedersen_hash(self, elements: List[int]) -> bytes: + """ + Calculate the Pedersen hash of a list of integers. + + Args: + elements: List of integers to hash + + Returns: + bytes: The hash result + + Raises: + ValueError: If the calculation fails + """ + pass diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/starkex_signing_adapter.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/starkex_signing_adapter.py new file mode 100644 index 0000000..abfbed9 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/internal/starkex_signing_adapter.py @@ -0,0 +1,496 @@ +""" +StarkEx signing adapter for the EdgeX Python SDK. + +This module provides an implementation of the signing adapter interface +that uses the StarkWare cryptographic primitives for signing operations. +""" + +import binascii +import math +import secrets +from typing import List, Tuple + +from .signing_adapter import SigningAdapter +from ..crypto.pedersen_hash import pedersen_hash_bytes + + +# StarkEx curve parameters +FIELD_PRIME = 0x800000000000011000000000000000000000000000000000000000000000001 +ALPHA = 1 +BETA = 0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89 +EC_ORDER = 0x800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f +N_ELEMENT_BITS_ECDSA = math.floor(math.log(FIELD_PRIME, 2)) +assert N_ELEMENT_BITS_ECDSA == 251 + +# Generator point for the Stark curve +EC_GEN = ( + 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca, + 0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f +) + + +class StarkExSigningAdapter(SigningAdapter): + """StarkEx implementation of the signing adapter interface.""" + + def sign(self, message_hash: bytes, private_key: str) -> Tuple[str, str]: + """ + Sign a message hash using a private key. + + Args: + message_hash: The hash of the message to sign + private_key: The private key as a hex string + + Returns: + Tuple[str, str]: The signature as (r, s) hex strings + + Raises: + ValueError: If the private key is invalid or the signing fails + """ + try: + # Validate private key format + binascii.unhexlify(private_key) + except binascii.Error: + raise ValueError("Invalid private key hex string") + + # Convert message hash to integer + msg_hash_int = int.from_bytes(message_hash, byteorder='big') + + # Ensure the message hash is in the valid range + # Use the same modulus as the Golang SDK (EC_ORDER, which is starkcurve.N) + msg_hash_int = msg_hash_int % EC_ORDER + + # Convert private key to integer + priv_key_int = int(private_key, 16) + + # Ensure the private key is in the valid range + # For testing purposes, we'll just take the modulus + priv_key_int = priv_key_int % EC_ORDER + if priv_key_int == 0: + priv_key_int = 1 + + # Sign the message + r, s = self._sign(msg_hash_int, priv_key_int) + + # Convert r and s to hex strings + r_hex = format(r, '064x') + s_hex = format(s, '064x') + + return r_hex, s_hex + + def get_public_key(self, private_key: str) -> str: + """ + Get the public key from a private key. + + Args: + private_key: The private key as a hex string + + Returns: + str: The public key as a hex string + + Raises: + ValueError: If the private key is invalid + """ + try: + # Validate private key format + binascii.unhexlify(private_key) + except binascii.Error: + raise ValueError("Invalid private key hex string") + + # Convert private key to integer + priv_key_int = int(private_key, 16) + + # Ensure the private key is in the valid range + # For testing purposes, we'll just take the modulus + priv_key_int = priv_key_int % EC_ORDER + if priv_key_int == 0: + priv_key_int = 1 + + # Get the public key + public_key = self._private_to_stark_key(priv_key_int) + + # Convert public key to hex string + public_key_hex = format(public_key, '064x') + + return public_key_hex + + def verify(self, message_hash: bytes, signature: Tuple[str, str], public_key: str) -> bool: + """ + Verify a signature using a public key. + + Args: + message_hash: The hash of the message + signature: The signature as (r, s) hex strings + public_key: The public key as a hex string + + Returns: + bool: Whether the signature is valid + """ + try: + # Convert message hash to integer + msg_hash_int = int.from_bytes(message_hash, byteorder='big') + + # Ensure the message hash is in the valid range + # Use the same modulus as the sign method (EC_ORDER) + msg_hash_int = msg_hash_int % EC_ORDER + + # Convert signature components to integers + r_int = int(signature[0], 16) + s_int = int(signature[1], 16) + + # Ensure r and s are in the valid range + if not (1 <= r_int < 2**N_ELEMENT_BITS_ECDSA and 1 <= s_int < EC_ORDER): + return False + + # Convert public key to integer + pub_key_int = int(public_key, 16) + + # Verify the signature + return self._verify(msg_hash_int, r_int, s_int, pub_key_int) + except Exception: + return False + + def pedersen_hash(self, elements: List[int]) -> bytes: + """ + Calculate the Pedersen hash of a list of integers. + + This method now uses the full Pedersen hash implementation + that follows StarkWare's specification. + + Args: + elements: List of integers to hash + + Returns: + bytes: The hash result + + Raises: + ValueError: If the calculation fails + """ + try: + # Use the full Pedersen hash implementation + return pedersen_hash_bytes(*elements) + except Exception as e: + raise ValueError(f"Failed to calculate Pedersen hash: {str(e)}") + + def _sign(self, msg_hash: int, priv_key: int) -> Tuple[int, int]: + """ + Sign a message hash using a private key. + + Args: + msg_hash: The hash of the message to sign as an integer + priv_key: The private key as an integer + + Returns: + Tuple[int, int]: The signature as (r, s) integers + """ + # Choose a valid k. In our version of ECDSA not every k value is valid, + # and there is a negligible probability a drawn k cannot be used for signing. + # This is why we have this loop. + while True: + # Use random nonce generation like the Go SDK + k = self._generate_random_k() + + # Cannot fail because 0 < k < EC_ORDER and EC_ORDER is prime. + x = self._ec_mult(k, EC_GEN)[0] + + # DIFF: in classic ECDSA, we take int(x) % n. + r = int(x) + if not (1 <= r < 2**N_ELEMENT_BITS_ECDSA): + # Bad value. This fails with negligible probability. + continue + + if (msg_hash + r * priv_key) % EC_ORDER == 0: + # Bad value. This fails with negligible probability. + continue + + w = self._div_mod(k, msg_hash + r * priv_key, EC_ORDER) + if not (1 <= w < 2**N_ELEMENT_BITS_ECDSA): + # Bad value. This fails with negligible probability. + continue + + s = self._inv_mod_curve_size(w) + return r, s + + def _verify(self, msg_hash: int, r: int, s: int, public_key: int) -> bool: + """ + Verify a signature using a public key. + + Args: + msg_hash: The hash of the message as an integer + r: The r component of the signature as an integer + s: The s component of the signature as an integer + public_key: The public key as an integer + + Returns: + bool: Whether the signature is valid + """ + # Compute w = s^-1 (mod EC_ORDER). + if not (1 <= s < EC_ORDER): + return False + + w = self._inv_mod_curve_size(s) + + # Preassumptions: + # DIFF: in classic ECDSA, we assert 1 <= r, w <= EC_ORDER-1. + # Since r, w < 2**N_ELEMENT_BITS_ECDSA < EC_ORDER, we only need to verify r, w != 0. + if not (1 <= r < 2**N_ELEMENT_BITS_ECDSA and 1 <= w < 2**N_ELEMENT_BITS_ECDSA): + return False + + if not (0 <= msg_hash < 2**N_ELEMENT_BITS_ECDSA): + return False + + # Only the x coordinate of the point is given, check the two possibilities for the y + # coordinate. + try: + y = self._get_y_coordinate(public_key) + except ValueError: + return False + + # Verify it is on the curve. + if (y**2 - (public_key**3 + ALPHA * public_key + BETA)) % FIELD_PRIME != 0: + return False + + # Try both possible y coordinates. + for y_candidate in [y, (-y) % FIELD_PRIME]: + public_key_point = (public_key, y_candidate) + + # Signature validation. + try: + # Calculate u1 = msg_hash * w mod n + u1 = (msg_hash * w) % EC_ORDER + + # Calculate u2 = r * w mod n + u2 = (r * w) % EC_ORDER + + # Calculate u1*G + u2*Q + point1 = self._ec_mult(u1, EC_GEN) + point2 = self._ec_mult(u2, public_key_point) + point = self._ec_add(point1, point2) + + # The signature is valid if the x-coordinate of the resulting point equals r + if point[0] == r: + return True + except Exception: + continue + + return False + + def _generate_random_k(self) -> int: + """ + Generate a cryptographically secure random k value. + + Returns: + int: The generated k value in range [1, EC_ORDER) + """ + # Generate a cryptographically secure random number in the range [1, EC_ORDER) + # This matches the Go implementation's approach of using random nonces + return secrets.randbelow(EC_ORDER - 1) + 1 + + def _private_to_stark_key(self, priv_key: int) -> int: + """ + Convert a private key to a Stark public key. + + Args: + priv_key: The private key as an integer + + Returns: + int: The public key as an integer + """ + return self._private_key_to_ec_point_on_stark_curve(priv_key)[0] + + def _private_key_to_ec_point_on_stark_curve(self, priv_key: int) -> Tuple[int, int]: + """ + Convert a private key to an EC point on the Stark curve. + + Args: + priv_key: The private key as an integer + + Returns: + Tuple[int, int]: The EC point as (x, y) coordinates + """ + # Ensure the private key is in the valid range + # For testing purposes, we'll just take the modulus + priv_key = priv_key % EC_ORDER + if priv_key == 0: + priv_key = 1 + + return self._ec_mult(priv_key, EC_GEN) + + def _inv_mod_curve_size(self, x: int) -> int: + """ + Calculate the modular inverse of x modulo the curve order. + + Args: + x: The value to invert + + Returns: + int: The modular inverse + """ + return self._div_mod(1, x, EC_ORDER) + + def _div_mod(self, n: int, m: int, p: int) -> int: + """ + Calculate (n / m) mod p. + + Args: + n: The numerator + m: The denominator + p: The modulus + + Returns: + int: The result of the division modulo p + """ + return (n * pow(m, -1, p)) % p + + def _is_quad_residue(self, n: int, p: int) -> bool: + """ + Check if n is a quadratic residue modulo p. + + Args: + n: The number to check + p: The modulus + + Returns: + bool: True if n is a quadratic residue modulo p, False otherwise + """ + return pow(n, (p - 1) // 2, p) == 1 + + def _sqrt_mod(self, n: int, p: int) -> int: + """ + Calculate the square root of n modulo p. + + Args: + n: The number to take the square root of + p: The modulus + + Returns: + int: The square root of n modulo p + """ + # Handle the case where p = 3 mod 4 + if p % 4 == 3: + return pow(n, (p + 1) // 4, p) + + # Handle the general case using the Tonelli-Shanks algorithm + q = p - 1 + s = 0 + while q % 2 == 0: + q //= 2 + s += 1 + + # Find a non-residue + z = 2 + while self._is_quad_residue(z, p): + z += 1 + + m = s + c = pow(z, q, p) + t = pow(n, q, p) + r = pow(n, (q + 1) // 2, p) + + while t != 1: + # Find the least i, 0 < i < m, such that t^(2^i) = 1 + i = 0 + t_sq = t + while t_sq != 1 and i < m - 1: + t_sq = (t_sq * t_sq) % p + i += 1 + + # Calculate b = c^(2^(m-i-1)) + b = pow(c, 2**(m - i - 1), p) + + m = i + c = (b * b) % p + t = (t * b * b) % p + r = (r * b) % p + + return r + + def _get_y_coordinate(self, x: int) -> int: + """ + Given the x coordinate of a point, returns a possible y coordinate such that + together the point (x,y) is on the curve. + + Args: + x: The x coordinate + + Returns: + int: A possible y coordinate + + Raises: + ValueError: If x is not a valid x coordinate on the curve + """ + y_squared = (x * x * x + ALPHA * x + BETA) % FIELD_PRIME + if not self._is_quad_residue(y_squared, FIELD_PRIME): + raise ValueError("Given x coordinate does not represent any point on the elliptic curve.") + + return self._sqrt_mod(y_squared, FIELD_PRIME) + + def _ec_add(self, p1: Tuple[int, int], p2: Tuple[int, int]) -> Tuple[int, int]: + """ + Add two points on the elliptic curve. + + Args: + p1: The first point as (x, y) coordinates + p2: The second point as (x, y) coordinates + + Returns: + Tuple[int, int]: The resulting point as (x, y) coordinates + """ + if p1[0] == p2[0]: + if (p1[1] + p2[1]) % FIELD_PRIME == 0: + # The points are negatives of each other, return the point at infinity + # We represent the point at infinity as None, but this should never happen + # in our use case, so we raise an exception instead + raise ValueError("Points are negatives of each other") + + # The points are the same, so we're doubling + return self._ec_double(p1) + + # Calculate the slope + slope = self._div_mod(p2[1] - p1[1], p2[0] - p1[0], FIELD_PRIME) + + # Calculate the new point + x3 = (slope * slope - p1[0] - p2[0]) % FIELD_PRIME + y3 = (slope * (p1[0] - x3) - p1[1]) % FIELD_PRIME + + return (x3, y3) + + def _ec_double(self, p: Tuple[int, int]) -> Tuple[int, int]: + """ + Double a point on the elliptic curve. + + Args: + p: The point to double as (x, y) coordinates + + Returns: + Tuple[int, int]: The resulting point as (x, y) coordinates + """ + # Calculate the slope + slope = self._div_mod(3 * p[0] * p[0] + ALPHA, 2 * p[1], FIELD_PRIME) + + # Calculate the new point + x3 = (slope * slope - 2 * p[0]) % FIELD_PRIME + y3 = (slope * (p[0] - x3) - p[1]) % FIELD_PRIME + + return (x3, y3) + + def _ec_mult(self, m: int, p: Tuple[int, int]) -> Tuple[int, int]: + """ + Multiply a point on the elliptic curve by a scalar. + + Args: + m: The scalar + p: The point as (x, y) coordinates + + Returns: + Tuple[int, int]: The resulting point as (x, y) coordinates + """ + if m == 0: + raise ValueError("Cannot multiply by 0") + + if m == 1: + return p + + if m % 2 == 0: + return self._ec_mult(m // 2, self._ec_double(p)) + else: + return self._ec_add(p, self._ec_mult(m - 1, p)) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/metadata/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/metadata/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/metadata/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/metadata/client.py new file mode 100644 index 0000000..2dcc9d4 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/metadata/client.py @@ -0,0 +1,96 @@ +from typing import Dict, Any + +from ..internal.async_client import AsyncClient + + +class Client: + """Client for metadata-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the metadata client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def get_metadata(self) -> Dict[str, Any]: + """ + Get the exchange metadata. + + Returns: + Dict[str, Any]: The exchange metadata + + Raises: + ValueError: If the request fails + """ + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/meta/getMetaData" + + try: + async with self.async_client.session.get(url) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") + + async def get_server_time(self) -> Dict[str, Any]: + """ + Get the current server time. + + Returns: + Dict[str, Any]: The server time information + + Raises: + ValueError: If the request fails + """ + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/meta/getServerTime" + + try: + async with self.async_client.session.get(url) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/client.py new file mode 100644 index 0000000..b6ccfc3 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/client.py @@ -0,0 +1,343 @@ +import math +import time +from decimal import Decimal +from typing import Dict, Any, Optional, List + +from ..internal.async_client import AsyncClient +from .types import ( + CreateOrderParams, + CancelOrderParams, + GetActiveOrderParams, + OrderFillTransactionParams, + TimeInForce, + OrderType +) + + +class Client: + """Client for order-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the order client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def create_order(self, params: CreateOrderParams, metadata: Dict[str, Any]) -> Dict[str, Any]: + """ + Create a new order with the given parameters. + + Args: + params: Order parameters + metadata: Exchange metadata + + Returns: + Dict[str, Any]: The created order + + Raises: + ValueError: If required parameters are missing or invalid + """ + # Set default TimeInForce based on order type if not specified + if not params.time_in_force: + if params.type == OrderType.MARKET: + params.time_in_force = TimeInForce.IMMEDIATE_OR_CANCEL + elif params.type == OrderType.LIMIT: + params.time_in_force = TimeInForce.GOOD_TIL_CANCEL + + # Find the contract from metadata + contract = None + contract_list = metadata.get("contractList", []) + for c in contract_list: + if c.get("contractId") == params.contract_id: + contract = c + break + + if not contract: + raise ValueError(f"contract not found: {params.contract_id}") + + # Get collateral coin from metadata + global_data = metadata.get("global", {}) + collateral_coin = global_data.get("starkExCollateralCoin", {}) + + # Parse decimal values + try: + size = Decimal(params.size) + price = Decimal(params.price) + except (ValueError, TypeError): + raise ValueError("failed to parse size or price") + + # Convert hex resolution to decimal + hex_resolution = contract.get("starkExResolution", "0x0") + # Remove "0x" prefix if present + hex_resolution = hex_resolution.replace("0x", "") + # Parse hex string to int + try: + resolution_int = int(hex_resolution, 16) + resolution = Decimal(resolution_int) + except (ValueError, TypeError): + raise ValueError("failed to parse hex resolution") + + client_order_id = params.client_order_id or self.async_client.generate_uuid() + + # Calculate values + value_dm = price * size + amount_synthetic = int(size * resolution) + amount_collateral = int(value_dm * Decimal("1000000")) # Shift 6 decimal places + + # Calculate fee based on order type (maker/taker) + try: + fee_rate = Decimal(contract.get("defaultTakerFeeRate", "0")) + except (ValueError, TypeError): + raise ValueError("failed to parse fee rate") + + # Calculate fee amount in decimal with ceiling to integer + amount_fee_dm = Decimal(str(math.ceil(float(value_dm * fee_rate)))) + amount_fee_str = str(amount_fee_dm) + + # Convert to the required integer format for the protocol + amount_fee = int(amount_fee_dm * Decimal("1000000")) # Shift 6 decimal places + + nonce = self.async_client.calc_nonce(client_order_id) + l2_expire_time = int(time.time() * 1000) + (14 * 24 * 60 * 60 * 1000) # 14 days + + # Calculate signature using asset IDs from metadata + expire_time_unix = l2_expire_time // (60 * 60 * 1000) + + sig_hash = self.async_client.calc_limit_order_hash( + contract.get("starkExSyntheticAssetId", ""), + collateral_coin.get("starkExAssetId", ""), + collateral_coin.get("starkExAssetId", ""), + params.side.value == "BUY", + amount_synthetic, + amount_collateral, + amount_fee, + nonce, + self.async_client.get_account_id(), + expire_time_unix + ) + + # Sign the order + sig = self.async_client.sign(sig_hash) + + # Convert signature to string (include v component like Go SDK, even though it's empty) + sig_str = f"{sig.r}{sig.s}{sig.v if hasattr(sig, 'v') and sig.v else ''}" + + + + # Create order request + account_id = str(self.async_client.get_account_id()) + nonce_str = str(nonce) + l2_expire_time_str = str(l2_expire_time) + expire_time_str = str(l2_expire_time - 864000000) # 10 days earlier + value_str = str(value_dm) + + price_str = params.price if params.type == OrderType.LIMIT else "0" + + # Prepare request data + request_data = { + "accountId": account_id, + "contractId": params.contract_id, + "price": price_str, + "size": params.size, + "type": params.type.value, # Use .value to get the string value + "timeInForce": params.time_in_force.value, # Use .value to get the string value + "side": params.side.value, # Use .value to get the string value + "l2Signature": sig_str, + "l2Nonce": nonce_str, + "l2ExpireTime": l2_expire_time_str, + "l2Value": value_str, + "l2Size": params.size, + "l2LimitFee": amount_fee_str, + "clientOrderId": client_order_id, + "expireTime": expire_time_str, + "reduceOnly": params.reduce_only + } + + # Execute request using async client + return await self.async_client.make_authenticated_request( + method="POST", + path="/api/v1/private/order/createOrder", + data=request_data + ) + + async def cancel_order(self, params: CancelOrderParams) -> Dict[str, Any]: + """ + Cancel a specific order. + + Args: + params: Cancel order parameters + + Returns: + Dict[str, Any]: The cancellation result + + Raises: + ValueError: If required parameters are missing or invalid + """ + account_id = str(self.async_client.get_account_id()) + + if params.order_id: + path = "/api/v1/private/order/cancelOrderById" + request_data = { + "accountId": account_id, + "orderIdList": [params.order_id] + } + elif params.client_id: + path = "/api/v1/private/order/cancelOrderByClientOrderId" + request_data = { + "accountId": account_id, + "clientOrderIdList": [params.client_id] + } + elif params.contract_id: + path = "/api/v1/private/order/cancelAllOrder" + request_data = { + "accountId": account_id, + "filterContractIdList": [params.contract_id] + } + else: + raise ValueError("must provide either order_id, client_id, or contract_id") + + # Execute request using async client + return await self.async_client.make_authenticated_request( + method="POST", + path=path, + data=request_data + ) + + async def get_active_orders(self, params: GetActiveOrderParams) -> Dict[str, Any]: + """ + Get active orders with pagination and filters. + + Args: + params: Active order query parameters + + Returns: + Dict[str, Any]: The active orders + + Raises: + ValueError: If the request fails + """ + # Build query parameters + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + if params.filter_contract_id_list: + query_params["filterContractIdList"] = ",".join(params.filter_contract_id_list) + if params.filter_type_list: + query_params["filterTypeList"] = ",".join(params.filter_type_list) + if params.filter_status_list: + query_params["filterStatusList"] = ",".join(params.filter_status_list) + + # Add boolean filters + if params.filter_is_liquidate is not None: + query_params["filterIsLiquidateList"] = str(params.filter_is_liquidate).lower() + if params.filter_is_deleverage is not None: + query_params["filterIsDeleverageList"] = str(params.filter_is_deleverage).lower() + if params.filter_is_position_tpsl is not None: + query_params["filterIsPositionTpslList"] = str(params.filter_is_position_tpsl).lower() + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + # Execute request using async client + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/order/getActiveOrderPage", + params=query_params + ) + + async def get_order_fill_transactions(self, params: OrderFillTransactionParams) -> Dict[str, Any]: + """ + Get order fill transactions with pagination and filters. + + Args: + params: Order fill transaction query parameters + + Returns: + Dict[str, Any]: The order fill transactions + + Raises: + ValueError: If the request fails + """ + # Build query parameters + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + if params.filter_contract_id_list: + query_params["filterContractIdList"] = ",".join(params.filter_contract_id_list) + if params.filter_order_id_list: + query_params["filterOrderIdList"] = ",".join(params.filter_order_id_list) + + # Add boolean filters + if params.filter_is_liquidate is not None: + query_params["filterIsLiquidateList"] = str(params.filter_is_liquidate).lower() + if params.filter_is_deleverage is not None: + query_params["filterIsDeleverageList"] = str(params.filter_is_deleverage).lower() + if params.filter_is_position_tpsl is not None: + query_params["filterIsPositionTpslList"] = str(params.filter_is_position_tpsl).lower() + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + # Execute request using async client + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/order/getHistoryOrderFillTransactionPage", + params=query_params + ) + + async def get_max_order_size(self, contract_id: str, price: float) -> Dict[str, Any]: + """ + Get the maximum order size for a given contract and price. + + Args: + contract_id: The contract ID + price: The price + + Returns: + Dict[str, Any]: The maximum order size information + + Raises: + ValueError: If the request fails + """ + # Build request body (API expects POST with JSON body) + data = { + "accountId": str(self.async_client.get_account_id()), + "contractId": contract_id, + "price": str(price) + } + + # Execute request using async client + return await self.async_client.make_authenticated_request( + method="POST", + path="/api/v1/private/order/getMaxCreateOrderSize", + data=data + ) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/types.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/types.py new file mode 100644 index 0000000..a34d356 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/order/types.py @@ -0,0 +1,165 @@ +from dataclasses import dataclass +from enum import Enum +from typing import List, Optional, Dict, Any + + +class TimeInForce(str, Enum): + """Time in force options for orders.""" + UNKNOWN_TIME_IN_FORCE = "UNKNOWN_TIME_IN_FORCE" + GOOD_TIL_CANCEL = "GOOD_TIL_CANCEL" + FILL_OR_KILL = "FILL_OR_KILL" + IMMEDIATE_OR_CANCEL = "IMMEDIATE_OR_CANCEL" + POST_ONLY = "POST_ONLY" + + +class OrderSide(str, Enum): + """Order side options.""" + BUY = "BUY" + SELL = "SELL" + + +class ResponseCode(str, Enum): + """API response codes.""" + SUCCESS = "SUCCESS" + + +class OrderType(str, Enum): + """Order type options.""" + UNKNOWN = "UNKNOWN_ORDER_TYPE" + LIMIT = "LIMIT" + MARKET = "MARKET" + STOP_LIMIT = "STOP_LIMIT" + STOP_MARKET = "STOP_MARKET" + TAKE_PROFIT_LIMIT = "TAKE_PROFIT_LIMIT" + TAKE_PROFIT_MARKET = "TAKE_PROFIT_MARKET" + + +@dataclass +class OrderFilterParams: + """Common filter types used across different order APIs.""" + filter_coin_id_list: List[str] = None # Filter by coin IDs, empty means all coins + filter_contract_id_list: List[str] = None # Filter by contract IDs, empty means all contracts + filter_type_list: List[str] = None # Filter by order types + filter_status_list: List[str] = None # Filter by order statuses + filter_is_liquidate: Optional[bool] = None # Filter by liquidation status + filter_is_deleverage: Optional[bool] = None # Filter by deleverage status + filter_is_position_tpsl: Optional[bool] = None # Filter by position take-profit/stop-loss status + + def __post_init__(self): + """Initialize empty lists.""" + if self.filter_coin_id_list is None: + self.filter_coin_id_list = [] + if self.filter_contract_id_list is None: + self.filter_contract_id_list = [] + if self.filter_type_list is None: + self.filter_type_list = [] + if self.filter_status_list is None: + self.filter_status_list = [] + + +@dataclass +class PaginationParams: + """Common pagination parameters.""" + size: str = "" # Size of the page, must be greater than 0 and less than or equal to 100/200 + offset_data: str = "" # Offset data for pagination. Empty string gets the first page + + +@dataclass +class OrderFillTransactionParams(PaginationParams, OrderFilterParams): + """Parameters for getting order fill transactions.""" + filter_order_id_list: List[str] = None # Filter by order IDs, empty means all orders + filter_start_created_time_inclusive: int = 0 # Filter start time (inclusive), 0 means from earliest + filter_end_created_time_exclusive: int = 0 # Filter end time (exclusive), 0 means until latest + + def __post_init__(self): + """Initialize empty lists.""" + super().__post_init__() + if self.filter_order_id_list is None: + self.filter_order_id_list = [] + + +@dataclass +class GetActiveOrderParams(PaginationParams, OrderFilterParams): + """Parameters for getting active orders.""" + filter_start_created_time_inclusive: int = 0 # Filter start time (inclusive), 0 means from earliest + filter_end_created_time_exclusive: int = 0 # Filter end time (exclusive), 0 means until latest + + +@dataclass +class GetHistoryOrderParams(PaginationParams, OrderFilterParams): + """Parameters for getting historical orders.""" + filter_start_created_time_inclusive: int = 0 # Filter start time (inclusive), 0 means from earliest + filter_end_created_time_exclusive: int = 0 # Filter end time (exclusive), 0 means until latest + + +@dataclass +class CreateOrderParams: + """Parameters for creating an order.""" + contract_id: str + price: str + size: str + type: OrderType + side: str + client_order_id: Optional[str] = None + l2_expire_time: Optional[int] = None + time_in_force: Optional[str] = None + reduce_only: bool = False + + +@dataclass +class CancelOrderParams: + """Parameters for canceling orders.""" + order_id: str = "" # Order ID to cancel + client_id: str = "" # Client order ID to cancel + contract_id: str = "" # Contract ID for canceling all orders + + +class OrderResponse: + """Response from creating an order.""" + code: str + data: Dict[str, Any] + error_param: Optional[Dict[str, Any]] + request_time: str + response_time: str + trace_id: str + + def __init__(self, response_data: Dict[str, Any]): + """Initialize from response data.""" + self.code = response_data.get("code", "") + self.data = response_data.get("data", {}) + self.error_param = response_data.get("errorParam") + self.request_time = response_data.get("requestTime", "") + self.response_time = response_data.get("responseTime", "") + self.trace_id = response_data.get("traceId", "") + + +class MaxOrderSizeResponse(OrderResponse): + """Response from getting max order size.""" + pass + + +class OrderListResponse(OrderResponse): + """Response from getting a list of orders.""" + pass + + +class OrderPageResponse(OrderResponse): + """Response from getting paginated orders.""" + pass + + +class OrderFillTransactionResponse(OrderResponse): + """Response from getting order fill transactions.""" + pass + + +@dataclass +class OrderFillFilterParams(OrderFilterParams): + """Parameters for filtering order fill transactions.""" + filter_order_id_list: List[str] = None # Filter by order IDs, empty means all orders + + def __post_init__(self): + """Initialize empty lists.""" + super().__post_init__() + if self.filter_order_id_list is None: + self.filter_order_id_list = [] diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/quote/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/quote/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/quote/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/quote/client.py new file mode 100644 index 0000000..079d893 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/quote/client.py @@ -0,0 +1,312 @@ +from typing import Dict, Any, List + +from ..internal.async_client import AsyncClient + + +class GetKLineParams: + """Parameters for getting K-line data.""" + + def __init__( + self, + contract_id: str, + interval: str, + size: str = "", + offset_data: str = "", + filter_start_time_inclusive: int = 0, + filter_end_time_exclusive: int = 0 + ): + self.contract_id = contract_id + self.interval = interval + self.size = size + self.offset_data = offset_data + self.filter_start_time_inclusive = filter_start_time_inclusive + self.filter_end_time_exclusive = filter_end_time_exclusive + + +class GetOrderBookDepthParams: + """Parameters for getting order book depth.""" + + def __init__( + self, + contract_id: str, + limit: int = 50 + ): + self.contract_id = contract_id + self.limit = limit + + +class GetMultiContractKLineParams: + """Parameters for getting K-line data for multiple contracts.""" + + def __init__( + self, + contract_id_list: List[str], + interval: str, + limit: int = 1 + ): + self.contract_id_list = contract_id_list + self.interval = interval + self.limit = limit + + +class Client: + """Client for quote-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the quote client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def get_quote_summary(self, contract_id: str) -> Dict[str, Any]: + """ + Get the quote summary for a given contract. + + Args: + contract_id: The contract ID + + Returns: + Dict[str, Any]: The quote summary + + Raises: + ValueError: If the request fails + """ + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/quote/getTicketSummary" + params = { + "contractId": contract_id + } + + try: + async with self.async_client.session.get(url, params=params) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") + + async def get_24_hour_quote(self, contract_id: str) -> Dict[str, Any]: + """ + Get the 24-hour quotes for a given contract. + + Args: + contract_id: The contract ID + + Returns: + Dict[str, Any]: The 24-hour quotes + + Raises: + ValueError: If the request fails + """ + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/quote/getTicker" + params = { + "contractId": contract_id + } + + try: + async with self.async_client.session.get(url, params=params) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") + + async def get_k_line(self, params: GetKLineParams) -> Dict[str, Any]: + """ + Get the K-line data for a contract. + + Args: + params: K-line query parameters + + Returns: + Dict[str, Any]: The K-line data + + Raises: + ValueError: If the request fails + """ + url = f"{self.async_client.base_url}/api/v1/public/quote/getKline" + query_params = { + "contractId": params.contract_id, + "interval": params.interval + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add time filters + if params.filter_start_time_inclusive > 0: + query_params["filterStartTimeInclusive"] = str(params.filter_start_time_inclusive) + if params.filter_end_time_exclusive > 0: + query_params["filterEndTimeExclusive"] = str(params.filter_end_time_exclusive) + + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/quote/getKline" + + try: + async with self.async_client.session.get(url, params=query_params) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") + + async def get_order_book_depth(self, params: GetOrderBookDepthParams) -> Dict[str, Any]: + """ + Get the order book depth for a contract. + + Args: + params: Order book depth query parameters + + Returns: + Dict[str, Any]: The order book depth + + Raises: + ValueError: If the request fails + """ + url = f"{self.async_client.base_url}/api/v1/public/quote/getDepth" + query_params = { + "contractId": params.contract_id, + "level": str(params.limit) # The API expects 'level', not 'limit' + } + + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/quote/getDepth" + + try: + async with self.async_client.session.get(url, params=query_params) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") + + async def get_multi_contract_k_line(self, params: GetMultiContractKLineParams) -> Dict[str, Any]: + """ + Get the K-line data for multiple contracts. + + Args: + params: Multi-contract K-line query parameters + + Returns: + Dict[str, Any]: The K-line data for multiple contracts + + Raises: + ValueError: If the request fails + """ + # Public endpoint - use simple GET request + await self.async_client._ensure_session() + + url = f"{self.async_client.base_url}/api/v1/public/quote/getMultiContractKline" + query_params = { + "contractIdList": ",".join(params.contract_id_list), + "interval": params.interval, + "limit": str(params.limit) + } + + try: + async with self.async_client.session.get(url, params=query_params) as response: + if response.status != 200: + try: + error_detail = await response.json() + raise ValueError(f"request failed with status code: {response.status}, response: {error_detail}") + except: + text = await response.text() + raise ValueError(f"request failed with status code: {response.status}, response: {text}") + + resp_data = await response.json() + + if resp_data.get("code") != "SUCCESS": + error_param = resp_data.get("errorParam") + if error_param: + raise ValueError(f"request failed with error params: {error_param}") + raise ValueError(f"request failed with code: {resp_data.get('code')}") + + return resp_data + + except Exception as e: + if isinstance(e, ValueError): + raise + raise ValueError(f"request failed: {str(e)}") diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/transfer/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/transfer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/transfer/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/transfer/client.py new file mode 100644 index 0000000..ee2aa5a --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/transfer/client.py @@ -0,0 +1,288 @@ +from typing import Dict, Any, List + +from ..internal.async_client import AsyncClient + + +class GetTransferOutByIdParams: + """Parameters for getting transfer out records by ID.""" + + def __init__(self, transfer_id_list: List[str]): + self.transfer_id_list = transfer_id_list + + +class GetTransferInByIdParams: + """Parameters for getting transfer in records by ID.""" + + def __init__(self, transfer_id_list: List[str]): + self.transfer_id_list = transfer_id_list + + +class GetWithdrawAvailableAmountParams: + """Parameters for getting available withdrawal amount.""" + + def __init__(self, coin_id: str): + self.coin_id = coin_id + + +class CreateTransferOutParams: + """Parameters for creating a transfer out order.""" + + def __init__( + self, + coin_id: str, + amount: str, + address: str, + network: str, + memo: str = "", + client_order_id: str = None + ): + self.coin_id = coin_id + self.amount = amount + self.address = address + self.network = network + self.memo = memo + self.client_order_id = client_order_id + + +class GetTransferOutPageParams: + """Parameters for getting transfer out page.""" + + def __init__(self, size: str = "10", offset_data: str = "", filter_coin_id_list: List[str] = None, + filter_status_list: List[str] = None, filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0): + self.size = size + self.offset_data = offset_data + self.filter_coin_id_list = filter_coin_id_list or [] + self.filter_status_list = filter_status_list or [] + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class GetTransferInPageParams: + """Parameters for getting transfer in page.""" + + def __init__(self, size: str = "10", offset_data: str = "", filter_coin_id_list: List[str] = None, + filter_status_list: List[str] = None, filter_start_created_time_inclusive: int = 0, + filter_end_created_time_exclusive: int = 0): + self.size = size + self.offset_data = offset_data + self.filter_coin_id_list = filter_coin_id_list or [] + self.filter_status_list = filter_status_list or [] + self.filter_start_created_time_inclusive = filter_start_created_time_inclusive + self.filter_end_created_time_exclusive = filter_end_created_time_exclusive + + +class Client: + """Client for transfer-related API endpoints.""" + + def __init__(self, async_client: AsyncClient): + """ + Initialize the transfer client. + + Args: + async_client: The async client for common functionality + """ + self.async_client = async_client + + async def get_transfer_out_by_id(self, params: GetTransferOutByIdParams) -> Dict[str, Any]: + """ + Get transfer out records by ID. + + Args: + params: Transfer out query parameters + + Returns: + Dict[str, Any]: The transfer out records + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()), + "transferIdList": ",".join(params.transfer_id_list) + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/transfer/getTransferOutById", + params=query_params + ) + + async def get_transfer_in_by_id(self, params: GetTransferInByIdParams) -> Dict[str, Any]: + """ + Get transfer in records by ID. + + Args: + params: Transfer in query parameters + + Returns: + Dict[str, Any]: The transfer in records + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()), + "transferIdList": ",".join(params.transfer_id_list) + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/transfer/getTransferInById", + params=query_params + ) + + async def get_withdraw_available_amount(self, params: GetWithdrawAvailableAmountParams) -> Dict[str, Any]: + """ + Get the available withdrawal amount. + + Args: + params: Withdrawal available amount query parameters + + Returns: + Dict[str, Any]: The available withdrawal amount + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()), + "coinId": params.coin_id + } + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/transfer/getTransferOutAvailableAmount", + params=query_params + ) + + async def create_transfer_out(self, params: CreateTransferOutParams, metadata: Dict[str, Any] = None) -> Dict[str, Any]: + """ + Create a new transfer out order. + + Args: + params: Transfer out parameters + metadata: Exchange metadata (optional, not used in current implementation) + + Returns: + Dict[str, Any]: The created transfer out order + + Raises: + ValueError: If the request fails + """ + client_order_id = params.client_order_id or self.async_client.generate_uuid() + + data = { + "accountId": str(self.async_client.get_account_id()), + "coinId": params.coin_id, + "amount": params.amount, + "address": params.address, + "network": params.network, + "clientOrderId": client_order_id + } + + if params.memo: + data["memo"] = params.memo + + # TODO: Implement signature calculation for transfer out + # This would require: + # 1. Asset ID from metadata based on coin_id + # 2. Receiver public key from address + # 3. Position IDs for sender, receiver, and fee + # 4. Proper expiration time calculation + # 5. Call to calc_transfer_hash and sign the result + # For now, the API call is made without signature (may fail on actual server) + + return await self.async_client.make_authenticated_request( + method="POST", + path="/api/v1/private/transfer/createTransferOut", + data=data + ) + + async def get_transfer_out_page( + self, + params: GetTransferOutPageParams + ) -> Dict[str, Any]: + """ + Get transfer out records with pagination. + + Args: + params: Parameters for the request + + Returns: + Dict[str, Any]: The transfer out records + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + if params.filter_status_list: + query_params["filterStatusList"] = ",".join(params.filter_status_list) + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/transfer/getActiveTransferOut", + params=query_params + ) + + async def get_transfer_in_page( + self, + params: GetTransferInPageParams + ) -> Dict[str, Any]: + """ + Get transfer in records with pagination. + + Args: + params: Parameters for the request + + Returns: + Dict[str, Any]: The transfer in records + + Raises: + ValueError: If the request fails + """ + query_params = { + "accountId": str(self.async_client.get_account_id()) + } + + # Add pagination parameters + if params.size: + query_params["size"] = params.size + if params.offset_data: + query_params["offsetData"] = params.offset_data + + # Add filter parameters + if params.filter_coin_id_list: + query_params["filterCoinIdList"] = ",".join(params.filter_coin_id_list) + if params.filter_status_list: + query_params["filterStatusList"] = ",".join(params.filter_status_list) + + # Add time filters + if params.filter_start_created_time_inclusive > 0: + query_params["filterStartCreatedTimeInclusive"] = str(params.filter_start_created_time_inclusive) + if params.filter_end_created_time_exclusive > 0: + query_params["filterEndCreatedTimeExclusive"] = str(params.filter_end_created_time_exclusive) + + return await self.async_client.make_authenticated_request( + method="GET", + path="/api/v1/private/transfer/getActiveTransferIn", + params=query_params + ) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/__init__.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/client.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/client.py new file mode 100644 index 0000000..0571bce --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/client.py @@ -0,0 +1,302 @@ +import asyncio +import binascii +import json +import logging +import threading +import time +from typing import Dict, Any, List, Optional, Callable, Union + +import websocket +from Crypto.Hash import keccak + +from ..internal.signing_adapter import SigningAdapter + +from ..internal.client import Client as InternalClient + + +class Client: + """WebSocket client for real-time data.""" + + def __init__(self, url: str, is_private: bool, account_id: int, stark_pri_key: str, signing_adapter: Optional[SigningAdapter] = None): + """ + Initialize the WebSocket client. + + Args: + url: WebSocket URL + is_private: Whether this is a private WebSocket connection + account_id: Account ID for authentication + stark_pri_key: Stark private key for signing + """ + self.url = url + self.is_private = is_private + self.account_id = account_id + self.stark_pri_key = stark_pri_key + + # Use the provided signing adapter (required) + if signing_adapter is None: + raise ValueError("signing_adapter is required") + self.signing_adapter = signing_adapter + + self.conn = None + self.handlers = {} + self.done = threading.Event() + self.ping_thread = None + self.subscriptions = set() + self.on_connect_hooks = [] + self.on_message_hooks = [] + self.on_disconnect_hooks = [] + + self.logger = logging.getLogger(__name__) + + def connect(self): + """ + Establish a WebSocket connection. + + Raises: + ValueError: If the connection fails + """ + headers = {} + url = self.url + + # Add timestamp parameter for both public and private connections + timestamp = int(time.time() * 1000) + + if self.is_private: + # Add timestamp header + headers["X-edgeX-Api-Timestamp"] = str(timestamp) + + # Generate signature content (no ? separator, matching Go SDK) + path = f"/api/v1/private/wsaccountId={self.account_id}" + sign_content = f"{timestamp}GET{path}" + + # Hash the content + keccak_hash = keccak.new(digest_bits=256) + keccak_hash.update(sign_content.encode()) + message_hash = keccak_hash.digest() + + # Sign the message using the signing adapter + try: + r, s = self.signing_adapter.sign(message_hash, self.stark_pri_key) + except Exception as e: + raise ValueError(f"failed to sign message: {str(e)}") + + # Set signature header + headers["X-edgeX-Api-Signature"] = f"{r}{s}" + else: + # For public connections, add timestamp as URL parameter + separator = "&" if "?" in url else "?" + url = f"{url}{separator}timestamp={timestamp}" + + # Create WebSocket connection + try: + self.conn = websocket.create_connection(url, header=headers) + except Exception as e: + raise ValueError(f"failed to connect to WebSocket: {str(e)}") + + # Start ping thread + self.done.clear() + self.ping_thread = threading.Thread(target=self._ping_loop) + self.ping_thread.daemon = True + self.ping_thread.start() + + # Start message handling thread + self.message_thread = threading.Thread(target=self._handle_messages) + self.message_thread.daemon = True + self.message_thread.start() + + # Call connect hooks + for hook in self.on_connect_hooks: + hook() + + def close(self): + """Close the WebSocket connection.""" + self.done.set() + + if self.conn: + self.conn.close() + self.conn = None + + def _ping_loop(self): + """Send periodic ping messages.""" + while not self.done.is_set(): + if self.conn: + ping_msg = { + "type": "ping", + "time": str(int(time.time() * 1000)) + } + + try: + self.conn.send(json.dumps(ping_msg)) + except Exception as e: + self.logger.error(f"Failed to send ping: {str(e)}") + break + + # Wait for 30 seconds or until done + self.done.wait(30) + + def _handle_messages(self): + """Process incoming WebSocket messages.""" + while not self.done.is_set(): + if not self.conn: + break + + try: + message = self.conn.recv() + + # Call message hooks + for hook in self.on_message_hooks: + hook(message) + + # Parse message + try: + msg = json.loads(message) + except json.JSONDecodeError: + continue + + # Handle ping messages + if msg.get("type") == "ping": + self._handle_pong(msg.get("time", "")) + continue + + # Handle quote events + if msg.get("type") == "quote-event": + channel = msg.get("channel", "") + channel_type = channel.split(".")[0] if "." in channel else channel + + if channel_type in self.handlers: + self.handlers[channel_type](message) + continue + + # Call registered handlers for other message types + msg_type = msg.get("type", "") + if msg_type in self.handlers: + self.handlers[msg_type](message) + + except Exception as e: + self.logger.error(f"Error handling message: {str(e)}") + + # Call disconnect hooks + for hook in self.on_disconnect_hooks: + hook(e) + + break + + def _handle_pong(self, timestamp: str): + """ + Send pong response to server ping. + + Args: + timestamp: The timestamp from the ping message + """ + pong_msg = { + "type": "pong", + "time": timestamp + } + + try: + self.conn.send(json.dumps(pong_msg)) + except Exception as e: + self.logger.error(f"Failed to send pong: {str(e)}") + + def subscribe(self, topic: str, params: Dict[str, Any] = None) -> bool: + """ + Subscribe to a topic (for public WebSocket). + + Args: + topic: The topic to subscribe to + params: Optional parameters for the subscription + + Returns: + bool: Whether the subscription was successful + + Raises: + ValueError: If the subscription fails + """ + if self.is_private: + raise ValueError("cannot subscribe on private WebSocket connection") + + if not self.conn: + raise ValueError("WebSocket connection is not established") + + sub_msg = { + "type": "subscribe", + "channel": topic + } + + if params: + sub_msg.update(params) + + try: + self.conn.send(json.dumps(sub_msg)) + self.subscriptions.add(topic) + return True + except Exception as e: + raise ValueError(f"failed to subscribe: {str(e)}") + + def unsubscribe(self, topic: str) -> bool: + """ + Unsubscribe from a topic (for public WebSocket). + + Args: + topic: The topic to unsubscribe from + + Returns: + bool: Whether the unsubscription was successful + + Raises: + ValueError: If the unsubscription fails + """ + if self.is_private: + raise ValueError("cannot unsubscribe on private WebSocket connection") + + if not self.conn: + raise ValueError("WebSocket connection is not established") + + unsub_msg = { + "type": "unsubscribe", + "channel": topic + } + + try: + self.conn.send(json.dumps(unsub_msg)) + self.subscriptions.discard(topic) + return True + except Exception as e: + raise ValueError(f"failed to unsubscribe: {str(e)}") + + def on_message(self, msg_type: str, handler: Callable[[str], None]): + """ + Register a handler for a specific message type. + + Args: + msg_type: The message type to handle + handler: The handler function + """ + self.handlers[msg_type] = handler + + def on_message_hook(self, hook: Callable[[str], None]): + """ + Register a hook that will be called for all messages. + + Args: + hook: The hook function + """ + self.on_message_hooks.append(hook) + + def on_connect(self, hook: Callable[[], None]): + """ + Register a hook that will be called when connection is established. + + Args: + hook: The hook function + """ + self.on_connect_hooks.append(hook) + + def on_disconnect(self, hook: Callable[[Exception], None]): + """ + Register a hook that will be called when connection is closed. + + Args: + hook: The hook function + """ + self.on_disconnect_hooks.append(hook) diff --git a/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/manager.py b/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/manager.py new file mode 100644 index 0000000..121cb33 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/edgex_sdk/ws/manager.py @@ -0,0 +1,231 @@ +import logging +from typing import Dict, Any, List, Optional, Callable + +from ..internal.signing_adapter import SigningAdapter +from ..internal.starkex_signing_adapter import StarkExSigningAdapter +from .client import Client + + +class Manager: + """Manager for WebSocket connections.""" + + def __init__(self, base_url: str, account_id: int, stark_pri_key: str, signing_adapter: Optional[SigningAdapter] = None): + """ + Initialize the WebSocket manager. + + Args: + base_url: Base WebSocket URL + account_id: Account ID for authentication + stark_pri_key: Stark private key for signing + signing_adapter: Optional signing adapter (defaults to StarkExSigningAdapter) + """ + self.base_url = base_url + self.account_id = account_id + self.stark_pri_key = stark_pri_key + + # Use StarkExSigningAdapter as default if none provided + if signing_adapter is None: + signing_adapter = StarkExSigningAdapter() + self.signing_adapter = signing_adapter + + self.public_client = None + self.private_client = None + + self.logger = logging.getLogger(__name__) + + def get_public_client(self) -> Client: + """ + Get the public WebSocket client. + + Returns: + Client: The public WebSocket client + """ + if not self.public_client: + self.public_client = Client( + url=f"{self.base_url}/api/v1/public/ws", + is_private=False, + account_id=self.account_id, + stark_pri_key=self.stark_pri_key, + signing_adapter=self.signing_adapter + ) + + return self.public_client + + def get_private_client(self) -> Client: + """ + Get the private WebSocket client. + + Returns: + Client: The private WebSocket client + """ + if not self.private_client: + self.private_client = Client( + url=f"{self.base_url}/api/v1/private/ws?accountId={self.account_id}", + is_private=True, + account_id=self.account_id, + stark_pri_key=self.stark_pri_key, + signing_adapter=self.signing_adapter + ) + + return self.private_client + + def connect_public(self): + """ + Connect to the public WebSocket. + + Raises: + ValueError: If the connection fails + """ + client = self.get_public_client() + client.connect() + + def connect_private(self): + """ + Connect to the private WebSocket. + + Raises: + ValueError: If the connection fails + """ + client = self.get_private_client() + client.connect() + + def disconnect_public(self): + """Disconnect from the public WebSocket.""" + if self.public_client: + self.public_client.close() + + def disconnect_private(self): + """Disconnect from the private WebSocket.""" + if self.private_client: + self.private_client.close() + + def disconnect_all(self): + """Disconnect from all WebSockets.""" + self.disconnect_public() + self.disconnect_private() + + def subscribe_ticker(self, contract_id: str, handler: Callable[[str], None]): + """ + Subscribe to ticker updates for a contract. + + Args: + contract_id: The contract ID + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_public_client() + + # Register handler + client.on_message("ticker", handler) + + # Subscribe to ticker channel + channel = f"ticker.{contract_id}" + client.subscribe(channel) + + def subscribe_kline(self, contract_id: str, interval: str, handler: Callable[[str], None]): + """ + Subscribe to K-line updates for a contract. + + Args: + contract_id: The contract ID + interval: The K-line interval + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_public_client() + + # Register handler + client.on_message("kline", handler) + + # Subscribe to kline channel + channel = f"kline.{contract_id}.{interval}" + client.subscribe(channel) + + def subscribe_depth(self, contract_id: str, handler: Callable[[str], None]): + """ + Subscribe to depth updates for a contract. + + Args: + contract_id: The contract ID + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_public_client() + + # Register handler + client.on_message("depth", handler) + + # Subscribe to depth channel + channel = f"depth.{contract_id}" + client.subscribe(channel) + + def subscribe_trade(self, contract_id: str, handler: Callable[[str], None]): + """ + Subscribe to trade updates for a contract. + + Args: + contract_id: The contract ID + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_public_client() + + # Register handler + client.on_message("trade", handler) + + # Subscribe to trade channel + channel = f"trade.{contract_id}" + client.subscribe(channel) + + def subscribe_account_update(self, handler: Callable[[str], None]): + """ + Subscribe to account updates. + + Args: + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_private_client() + + # Register handler + client.on_message("account", handler) + + def subscribe_order_update(self, handler: Callable[[str], None]): + """ + Subscribe to order updates. + + Args: + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_private_client() + + # Register handler + client.on_message("order", handler) + + def subscribe_position_update(self, handler: Callable[[str], None]): + """ + Subscribe to position updates. + + Args: + handler: The handler function + + Raises: + ValueError: If the subscription fails + """ + client = self.get_private_client() + + # Register handler + client.on_message("position", handler) diff --git a/docs/edgex/edgex-python-sdk-main/examples/README.md b/docs/edgex/edgex-python-sdk-main/examples/README.md new file mode 100644 index 0000000..48eff77 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/examples/README.md @@ -0,0 +1,120 @@ +# EdgeX Python SDK Examples + +This directory contains examples demonstrating how to use the EdgeX Python SDK. + +## Prerequisites + +Before running the examples, make sure you have installed the EdgeX Python SDK: + +```bash +pip install edgex-python-sdk +``` + +Or, if you're working with the source code: + +```bash +cd edgex-python-sdk +pip install -e . +``` + +## Environment Variables + +The examples use the following environment variables: + +- `EDGEX_BASE_URL`: Base URL for HTTP API endpoints (e.g., "https://pro.edgex.exchange" for production, "https://testnet.edgex.exchange" for testnet) +- `EDGEX_WS_URL`: Base URL for WebSocket endpoints (e.g., "wss://quote.edgex.exchange" for production, "wss://quote-testnet.edgex.exchange" for testnet) +- `EDGEX_ACCOUNT_ID`: Your account ID +- `EDGEX_STARK_PRIVATE_KEY`: Your stark private key + +You can set these variables in your environment or create a `.env` file in the examples directory: + +``` +EDGEX_BASE_URL=https://pro.edgex.exchange # Use https://testnet.edgex.exchange for testnet +EDGEX_WS_URL=wss://quote.edgex.exchange # Use wss://quote-testnet.edgex.exchange for testnet +EDGEX_ACCOUNT_ID=12345 +EDGEX_STARK_PRIVATE_KEY=your-stark-private-key +``` + +## Examples + +### Basic Usage + +The `basic_usage.py` example demonstrates the basic functionality of the SDK: + +- Creating a client +- Getting server time and metadata +- Getting account assets and positions +- Getting market data (K-lines, order book depth) +- Creating orders (commented out to avoid actual order creation) +- Using WebSockets for real-time data + +To run the example: + +```bash +python basic_usage.py +``` + +### Advanced Usage + +The `advanced_usage.py` example demonstrates more advanced features of the SDK: + +- Order management (creating and canceling orders) +- WebSocket integration with proper handlers +- Error handling +- Pagination +- Using a trader class to encapsulate functionality + +To run the example: + +```bash +python advanced_usage.py +``` + +## Contract IDs + +EdgeX uses numeric contract IDs instead of symbol-based identifiers. Here are some common contract mappings: + +| Contract ID | Symbol | Tick Size | +|-------------|---------------|-----------| +| 10000001 | BTCUSDT | 0.1 | +| 10000002 | ETHUSDT | 0.01 | +| 10000003 | SOLUSDT | 0.01 | +| 10000004 | BNBUSDT | 0.01 | + +To get the complete list of available contracts: + +```python +metadata = await client.get_metadata() +contracts = metadata.get("data", {}).get("contractList", []) +for contract in contracts: + print(f"ID: {contract['contractId']} - {contract['contractName']}") +``` + +## Notes + +- The examples include order creation code that is commented out to avoid creating actual orders. Uncomment this code if you want to create real orders. +- The WebSocket examples will run for a short time and then disconnect. Adjust the sleep time if you want to receive more updates. +- The examples use asyncio for asynchronous operations. Make sure you're using Python 3.7 or later. +- All examples use numeric contract IDs (e.g., "10000001" for BTCUSDT) as required by the EdgeX API. +- For order book depth queries, valid limit values are 15 or 200. + +## Customization + +Feel free to modify the examples to suit your needs. Some ideas: + +- Implement a trading strategy +- Add more error handling +- Implement a command-line interface +- Create a web interface using a framework like Flask or FastAPI +- Add logging to a file +- Add more sophisticated order management + +## Troubleshooting + +If you encounter issues: + +1. Check that your environment variables are set correctly +2. Verify that you have the latest version of the SDK +3. Check the EdgeX API documentation for any changes +4. Look for error messages in the console output +5. Try with a smaller subset of functionality to isolate the issue diff --git a/docs/edgex/edgex-python-sdk-main/examples/advanced_usage.py b/docs/edgex/edgex-python-sdk-main/examples/advanced_usage.py new file mode 100644 index 0000000..985b19c --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/examples/advanced_usage.py @@ -0,0 +1,657 @@ +""" +Advanced usage example for the EdgeX Python SDK. + +This example demonstrates more advanced features of the SDK, including: +- Order management +- WebSocket integration +- Error handling +- Pagination +""" + +import asyncio +import os +import logging +from decimal import Decimal +from typing import Dict, Any, List + +from edgex_sdk import ( + Client, + OrderSide, + OrderType, + TimeInForce, + CreateOrderParams, + CancelOrderParams, + GetActiveOrderParams, + OrderFillTransactionParams, + GetKLineParams, + GetOrderBookDepthParams, + WebSocketManager +) + + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +class EdgeXTrader: + """Example trader using the EdgeX Python SDK.""" + + def __init__(self, base_url: str, ws_url: str, account_id: int, stark_private_key: str): + """ + Initialize the trader. + + Args: + base_url: Base URL for API endpoints + ws_url: Base URL for WebSocket endpoints + account_id: Account ID for authentication + stark_private_key: Stark private key for signing + """ + self.client = Client( + base_url=base_url, + account_id=account_id, + stark_private_key=stark_private_key + ) + + self.ws_manager = WebSocketManager( + base_url=ws_url, + account_id=account_id, + stark_pri_key=stark_private_key + ) + + self.metadata = None + self.contracts = {} + self.market_data = {} + self.active_orders = {} + self.positions = {} + self.assets = {} + + async def initialize(self): + """Initialize the trader by fetching metadata and account information.""" + logger.info("Initializing trader...") + + try: + # Get metadata + self.metadata = await self.client.get_metadata() + logger.info("Metadata retrieved") + + # Extract contracts + contract_list = self.metadata.get("data", {}).get("contractList", []) + for contract in contract_list: + contract_id = contract.get("contractId") + if contract_id: + self.contracts[contract_id] = contract + + logger.info(f"Found {len(self.contracts)} contracts") + + # Get account assets + assets_response = await self.client.get_account_asset() + self.assets = assets_response.get("data", {}) + logger.info("Account assets retrieved") + + # Get account positions + positions_response = await self.client.get_account_positions() + positions_data = positions_response.get("data", {}) + position_list = positions_data.get("positionList", []) + for position in position_list: + contract_id = position.get("contractId") + if contract_id: + self.positions[contract_id] = position + + logger.info(f"Found {len(self.positions)} positions") + + # Get active orders + await self.update_active_orders() + + # Initialize WebSocket + await self.initialize_websocket() + + logger.info("Trader initialized successfully") + return True + + except Exception as e: + logger.error(f"Failed to initialize trader: {str(e)}") + return False + + async def update_active_orders(self): + """Update the list of active orders.""" + try: + params = GetActiveOrderParams() + active_orders_response = await self.client.get_active_orders(params) + + order_list = active_orders_response.get("data", {}).get("list", []) + self.active_orders = {} + + for order in order_list: + order_id = order.get("orderId") + if order_id: + self.active_orders[order_id] = order + + logger.info(f"Found {len(self.active_orders)} active orders") + return True + + except Exception as e: + logger.error(f"Failed to update active orders: {str(e)}") + return False + + async def initialize_websocket(self): + """Initialize WebSocket connections and subscriptions.""" + try: + # Connect to public WebSocket + self.ws_manager.connect_public() + logger.info("Connected to public WebSocket") + + # Connect to private WebSocket + self.ws_manager.connect_private() + logger.info("Connected to private WebSocket") + + # Subscribe to account updates + self.ws_manager.subscribe_account_update(self.handle_account_update) + logger.info("Subscribed to account updates") + + # Subscribe to order updates + self.ws_manager.subscribe_order_update(self.handle_order_update) + logger.info("Subscribed to order updates") + + # Subscribe to position updates + self.ws_manager.subscribe_position_update(self.handle_position_update) + logger.info("Subscribed to position updates") + + # Subscribe to market data for BTCUSDT (contract ID: 10000001) + self.ws_manager.subscribe_ticker("10000001", self.handle_ticker_update) + self.ws_manager.subscribe_kline("10000001", "1m", self.handle_kline_update) + self.ws_manager.subscribe_depth("10000001", self.handle_depth_update) + logger.info("Subscribed to market data for BTCUSDT (10000001)") + + return True + + except Exception as e: + logger.error(f"Failed to initialize WebSocket: {str(e)}") + return False + + def handle_account_update(self, message: str): + """ + Handle account update messages from WebSocket. + + Args: + message: The WebSocket message + """ + try: + import json + data = json.loads(message) + logger.info(f"Account update: {data}") + + # Update assets + account_data = data.get("content", {}).get("data", {}) + if account_data: + self.assets = account_data + + except Exception as e: + logger.error(f"Failed to handle account update: {str(e)}") + + def handle_order_update(self, message: str): + """ + Handle order update messages from WebSocket. + + Args: + message: The WebSocket message + """ + try: + import json + data = json.loads(message) + logger.info(f"Order update: {data}") + + # Update active orders + asyncio.create_task(self.update_active_orders()) + + except Exception as e: + logger.error(f"Failed to handle order update: {str(e)}") + + def handle_position_update(self, message: str): + """ + Handle position update messages from WebSocket. + + Args: + message: The WebSocket message + """ + try: + import json + data = json.loads(message) + logger.info(f"Position update: {data}") + + # Update positions + position_data = data.get("content", {}).get("data", {}) + contract_id = position_data.get("contractId") + + if contract_id: + self.positions[contract_id] = position_data + + except Exception as e: + logger.error(f"Failed to handle position update: {str(e)}") + + def handle_ticker_update(self, message: str): + """ + Handle ticker update messages from WebSocket. + + Args: + message: The WebSocket message + """ + try: + import json + data = json.loads(message) + + # Extract ticker data + content = data.get("content", {}) + ticker_data_list = content.get("data", []) + + # Handle both single ticker and list of tickers + if isinstance(ticker_data_list, list) and ticker_data_list: + ticker_data = ticker_data_list[0] # Take the first ticker + else: + ticker_data = ticker_data_list + + contract_id = ticker_data.get("contractId") if isinstance(ticker_data, dict) else None + + if contract_id: + if "ticker" not in self.market_data: + self.market_data["ticker"] = {} + + self.market_data["ticker"][contract_id] = ticker_data + logger.info(f"Ticker update for {contract_id}: {ticker_data.get('lastPrice')}") + + except Exception as e: + logger.error(f"Failed to handle ticker update: {str(e)}") + + def handle_kline_update(self, message: str): + """ + Handle K-line update messages from WebSocket. + + Args: + message: The WebSocket message + """ + try: + import json + data = json.loads(message) + + # Extract K-line data + kline_data = data.get("content", {}).get("data", {}) + contract_id = kline_data.get("contractId") + interval = kline_data.get("interval") + + if contract_id and interval: + if "kline" not in self.market_data: + self.market_data["kline"] = {} + + if contract_id not in self.market_data["kline"]: + self.market_data["kline"][contract_id] = {} + + self.market_data["kline"][contract_id][interval] = kline_data + logger.info(f"K-line update for {contract_id} {interval}: {kline_data.get('close')}") + + except Exception as e: + logger.error(f"Failed to handle K-line update: {str(e)}") + + def handle_depth_update(self, message: str): + """ + Handle depth update messages from WebSocket. + + Args: + message: The WebSocket message + """ + try: + import json + data = json.loads(message) + + # Extract depth data + depth_data = data.get("content", {}).get("data", {}) + contract_id = depth_data.get("contractId") + + if contract_id: + if "depth" not in self.market_data: + self.market_data["depth"] = {} + + self.market_data["depth"][contract_id] = depth_data + logger.info(f"Depth update for {contract_id}") + + except Exception as e: + logger.error(f"Failed to handle depth update: {str(e)}") + + async def create_limit_order( + self, + contract_id: str, + size: str, + price: str, + side: str, + time_in_force: str = TimeInForce.GOOD_TIL_CANCEL, + reduce_only: bool = False + ) -> Dict[str, Any]: + """ + Create a limit order. + + Args: + contract_id: The contract ID + size: The order size + price: The order price + side: The order side (BUY or SELL) + time_in_force: The time in force + reduce_only: Whether the order is reduce-only + + Returns: + Dict[str, Any]: The created order + + Raises: + ValueError: If the order creation fails + """ + try: + # Create order parameters + params = CreateOrderParams( + contract_id=contract_id, + size=size, + price=price, + type=OrderType.LIMIT, + side=side, + time_in_force=time_in_force, + reduce_only=reduce_only + ) + + # Create the order + result = await self.client.create_order(params) + + # Check for success + if result.get("code") != "SUCCESS": + error_param = result.get("errorParam") + if error_param: + raise ValueError(f"Failed to create order: {error_param}") + raise ValueError(f"Failed to create order: {result.get('code')}") + + # Update active orders + await self.update_active_orders() + + logger.info(f"Created limit order: {result.get('data', {}).get('orderId')}") + return result + + except Exception as e: + logger.error(f"Failed to create limit order: {str(e)}") + raise + + async def cancel_order(self, order_id: str) -> Dict[str, Any]: + """ + Cancel an order. + + Args: + order_id: The order ID + + Returns: + Dict[str, Any]: The cancellation result + + Raises: + ValueError: If the order cancellation fails + """ + try: + # Create cancel order parameters + params = CancelOrderParams(order_id=order_id) + + # Cancel the order + result = await self.client.cancel_order(params) + + # Check for success + if result.get("code") != "SUCCESS": + error_param = result.get("errorParam") + if error_param: + raise ValueError(f"Failed to cancel order: {error_param}") + raise ValueError(f"Failed to cancel order: {result.get('code')}") + + # Update active orders + await self.update_active_orders() + + logger.info(f"Cancelled order: {order_id}") + return result + + except Exception as e: + logger.error(f"Failed to cancel order: {str(e)}") + raise + + async def cancel_all_orders(self, contract_id: str = None) -> Dict[str, Any]: + """ + Cancel all orders for a contract. + + Args: + contract_id: The contract ID (optional) + + Returns: + Dict[str, Any]: The cancellation result + + Raises: + ValueError: If the order cancellation fails + """ + try: + # Create cancel order parameters + params = CancelOrderParams(contract_id=contract_id or "") + + # Cancel the orders + result = await self.client.cancel_order(params) + + # Check for success + if result.get("code") != "SUCCESS": + error_param = result.get("errorParam") + if error_param: + raise ValueError(f"Failed to cancel orders: {error_param}") + raise ValueError(f"Failed to cancel orders: {result.get('code')}") + + # Update active orders + await self.update_active_orders() + + logger.info(f"Cancelled all orders for contract: {contract_id or 'all'}") + return result + + except Exception as e: + logger.error(f"Failed to cancel all orders: {str(e)}") + raise + + async def get_order_fill_transactions( + self, + contract_id: str = None, + size: str = "10", + offset_data: str = "" + ) -> Dict[str, Any]: + """ + Get order fill transactions. + + Args: + contract_id: The contract ID (optional) + size: The page size + offset_data: The offset data for pagination + + Returns: + Dict[str, Any]: The order fill transactions + + Raises: + ValueError: If the request fails + """ + try: + # Create parameters + params = OrderFillTransactionParams( + size=size, + offset_data=offset_data + ) + + if contract_id: + params.filter_contract_id_list = [contract_id] + + # Get order fill transactions + result = await self.client.get_order_fill_transactions(params) + + # Check for success + if result.get("code") != "SUCCESS": + error_param = result.get("errorParam") + if error_param: + raise ValueError(f"Failed to get order fill transactions: {error_param}") + raise ValueError(f"Failed to get order fill transactions: {result.get('code')}") + + logger.info(f"Got order fill transactions: {len(result.get('data', {}).get('list', []))}") + return result + + except Exception as e: + logger.error(f"Failed to get order fill transactions: {str(e)}") + raise + + async def get_k_line( + self, + contract_id: str, + interval: str, + size: str = "100", + offset_data: str = "" + ) -> Dict[str, Any]: + """ + Get K-line data. + + Args: + contract_id: The contract ID + interval: The K-line interval + size: The page size + offset_data: The offset data for pagination + + Returns: + Dict[str, Any]: The K-line data + + Raises: + ValueError: If the request fails + """ + try: + # Create parameters + params = GetKLineParams( + contract_id=contract_id, + interval=interval, + size=size, + offset_data=offset_data + ) + + # Get K-line data + result = await self.client.quote.get_k_line(params) + + # Check for success + if result.get("code") != "SUCCESS": + error_param = result.get("errorParam") + if error_param: + raise ValueError(f"Failed to get K-line data: {error_param}") + raise ValueError(f"Failed to get K-line data: {result.get('code')}") + + logger.info(f"Got K-line data: {len(result.get('data', {}).get('list', []))}") + return result + + except Exception as e: + logger.error(f"Failed to get K-line data: {str(e)}") + raise + + async def get_order_book_depth( + self, + contract_id: str, + limit: int = 15 + ) -> Dict[str, Any]: + """ + Get order book depth. + + Args: + contract_id: The contract ID + limit: The depth limit (valid values are 15 or 200) + + Returns: + Dict[str, Any]: The order book depth + + Raises: + ValueError: If the request fails + """ + try: + # Create parameters + params = GetOrderBookDepthParams( + contract_id=contract_id, + limit=limit + ) + + # Get order book depth + result = await self.client.quote.get_order_book_depth(params) + + # Check for success + if result.get("code") != "SUCCESS": + error_param = result.get("errorParam") + if error_param: + raise ValueError(f"Failed to get order book depth: {error_param}") + raise ValueError(f"Failed to get order book depth: {result.get('code')}") + + logger.info(f"Got order book depth for {contract_id}") + return result + + except Exception as e: + logger.error(f"Failed to get order book depth: {str(e)}") + raise + + async def close(self): + """Close all connections.""" + try: + # Disconnect WebSocket + self.ws_manager.disconnect_all() + logger.info("Disconnected from WebSocket") + + return True + + except Exception as e: + logger.error(f"Failed to close connections: {str(e)}") + return False + + +async def main(): + """Main function.""" + # Load configuration from environment variables + base_url = os.getenv("EDGEX_BASE_URL", "https://testnet.edgex.exchange") + ws_url = os.getenv("EDGEX_WS_URL", "wss://quote-testnet.edgex.exchange") + account_id = int(os.getenv("EDGEX_ACCOUNT_ID", "12345")) + stark_private_key = os.getenv("EDGEX_STARK_PRIVATE_KEY", "your-stark-private-key") + + # Create trader + trader = EdgeXTrader( + base_url=base_url, + ws_url=ws_url, + account_id=account_id, + stark_private_key=stark_private_key + ) + + # Initialize trader + if not await trader.initialize(): + logger.error("Failed to initialize trader") + return + + try: + # Get K-line data for BTCUSDT (contract ID: 10000001) + klines = await trader.get_k_line("10000001", "1m") + logger.info(f"Retrieved K-line data: {len(klines.get('data', {}).get('list', []))} entries") + + # Get order book depth for BTCUSDT (contract ID: 10000001) + await trader.get_order_book_depth("10000001") + logger.info(f"Retrieved order book depth") + + # Create a limit order (commented out to avoid actual order creation) + # order = await trader.create_limit_order( + # contract_id="10000001", # BTCUSDT + # size="0.001", + # price="30000", + # side=OrderSide.BUY + # ) + # + # # Cancel the order + # if order and order.get("data", {}).get("orderId"): + # await trader.cancel_order(order.get("data", {}).get("orderId")) + + # Wait for some WebSocket updates + logger.info("Waiting for WebSocket updates...") + await asyncio.sleep(60) + + finally: + # Close connections + await trader.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/edgex/edgex-python-sdk-main/examples/basic_usage.py b/docs/edgex/edgex-python-sdk-main/examples/basic_usage.py new file mode 100644 index 0000000..32f15a7 --- /dev/null +++ b/docs/edgex/edgex-python-sdk-main/examples/basic_usage.py @@ -0,0 +1,114 @@ +""" +Basic usage example for the EdgeX Python SDK. + +This example demonstrates the basic functionality of the SDK: +- Creating a client +- Getting server time and metadata +- Getting account assets and positions +- Getting market data (K-lines, order book depth) +- Creating orders (commented out to avoid actual order creation) +- Using WebSockets for real-time data +""" + +import asyncio +import os + +from edgex_sdk import ( + Client, + OrderSide, + GetKLineParams, + GetOrderBookDepthParams, + WebSocketManager +) + + +async def main(): + # Load configuration from environment variables + base_url = os.getenv("EDGEX_BASE_URL", "https://testnet.edgex.exchange") + account_id = int(os.getenv("EDGEX_ACCOUNT_ID", "12345")) + stark_private_key = os.getenv("EDGEX_STARK_PRIVATE_KEY", "your-stark-private-key") + + # Create a new client + client = Client( + base_url=base_url, + account_id=account_id, + stark_private_key=stark_private_key + ) + + # Get server time + server_time = await client.get_server_time() + print(f"Server Time: {server_time}") + + # Get exchange metadata + metadata = await client.get_metadata() + print(f"Available contracts: {len(metadata.get('data', {}).get('contractList', []))}") + + # Get account assets + assets = await client.get_account_asset() + print(f"Account Assets: {assets}") + + # Get account positions + positions = await client.get_account_positions() + print(f"Account Positions: {positions}") + + # Get 24-hour market data for BNBUSDT (contract ID: 10000004) + quote = await client.get_24_hour_quote("10000004") + print(f"BNBUSDT Price: {quote}") + + # Get K-line data for BTCUSDT (contract ID: 10000001) + kline_params = GetKLineParams( + contract_id="10000001", # BTCUSDT + interval="1m", + size="10" + ) + klines = await client.quote.get_k_line(kline_params) + print(f"K-lines: {klines}") + + # Get order book depth for ETHUSDT (contract ID: 10000002) + depth_params = GetOrderBookDepthParams( + contract_id="10000002", # ETHUSDT + limit=15 # Valid values are 15 or 200 + ) + depth = await client.quote.get_order_book_depth(depth_params) + print(f"Order Book Depth: {depth}") + + # Create a limit order (commented out to avoid actual order creation) + # order = await client.create_limit_order( + # contract_id="10000004", # BNBUSDT + # size="0.01", + # price="600.00", + # side=OrderSide.BUY + # ) + # print(f"Order created: {order}") + + # WebSocket example + ws_url = os.getenv("EDGEX_WS_URL", "wss://quote-testnet.edgex.exchange") + ws_manager = WebSocketManager( + base_url=ws_url, + account_id=account_id, + stark_pri_key=stark_private_key + ) + + # Define message handlers + def ticker_handler(message): + print(f"Ticker Update: {message}") + + def kline_handler(message): + print(f"K-line Update: {message}") + + # Connect to public WebSocket for market data + ws_manager.connect_public() + + # Subscribe to real-time updates for BNBUSDT (contract ID: 10000004) + ws_manager.subscribe_ticker("10000004", ticker_handler) + ws_manager.subscribe_kline("10000004", "1m", kline_handler) + + # Wait for updates + await asyncio.sleep(30) + + # Disconnect all connections + ws_manager.disconnect_all() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/edgex/endpoint.md b/docs/edgex/endpoint.md new file mode 100644 index 0000000..0e7e1fb --- /dev/null +++ b/docs/edgex/endpoint.md @@ -0,0 +1,7 @@ +API Endpoint Domain +HTTP Endpoint +Copy +https://pro.edgex.exchange +WebSocket Endpoint +Copy +wss://quote.edgex.exchange \ No newline at end of file diff --git a/docs/edgex/private-api.md b/docs/edgex/private-api.md new file mode 100644 index 0000000..25114ec --- /dev/null +++ b/docs/edgex/private-api.md @@ -0,0 +1,2657 @@ +Account API | edgeX Docs

Account API

AccountPrivateApi

GET Get Position Transaction Page

GET /api/v1/private/account/getPositionTransactionPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to retrieve. Must be greater than 0 and less than or equal to 100

offsetData

query

string

No

Pagination offset. If empty or not provided, the first page is retrieved

filterCoinIdList

query

string

No

Filter position transaction records by specified coin IDs. If not provided, all collateral transaction records are retrieved

filterContractIdList

query

string

No

Filter position transaction records by specified contract IDs. If not provided, all position transaction records are retrieved

filterTypeList

query

string

No

Filter position transaction records by specified types. If not provided, all position transaction records are retrieved

filterStartCreatedTimeInclusive

query

string

No

Filter position transaction records created after or at the specified start time (inclusive). If not provided or 0, retrieves records from the earliest time

filterEndCreatedTimeExclusive

query

string

No

Filter position transaction records created before the specified end time (exclusive). If not provided or 0, retrieves records up to the latest time

filterCloseOnly

query

string

No

Whether to return only position transactions that include closing positions. true: only return records with closing; false: return all records

filterOpenOnly

query

string

No

Whether to return only position transactions that include opening positions. true: only return records with opening; false: return all records

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "564809510904923406",
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "type": "SELL_POSITION",
+                "deltaOpenSize": "-0.001",
+                "deltaOpenValue": "-96.813200",
+                "deltaOpenFee": "0.048406",
+                "deltaFundingFee": "0.000000",
+                "beforeOpenSize": "0.001",
+                "beforeOpenValue": "96.813200",
+                "beforeOpenFee": "-0.048406",
+                "beforeFundingFee": "0",
+                "fillCloseSize": "-0.001",
+                "fillCloseValue": "-96.857100",
+                "fillCloseFee": "-0.048428",
+                "fillOpenSize": "0.000",
+                "fillOpenValue": "0.000000",
+                "fillOpenFee": "0.000000",
+                "fillPrice": "96857.1",
+                "liquidateFee": "0",
+                "realizePnl": "-0.004528",
+                "isLiquidate": false,
+                "isDeleverage": false,
+                "fundingTime": "0",
+                "fundingRate": "",
+                "fundingIndexPrice": "",
+                "fundingOraclePrice": "",
+                "fundingPositionSize": "",
+                "orderId": "564809510842007822",
+                "orderFillTransactionId": "564809510875562254",
+                "collateralTransactionId": "564809510904922382",
+                "forceTradeId": "0",
+                "extraType": "",
+                "extraDataJson": "",
+                "censorStatus": "CENSOR_SUCCESS",
+                "censorTxId": "892720",
+                "censorTime": "1734661081049",
+                "censorFailCode": "",
+                "censorFailReason": "",
+                "l2TxId": "1084271",
+                "l2RejectTime": "0",
+                "l2RejectCode": "",
+                "l2RejectReason": "",
+                "l2ApprovedTime": "0",
+                "createdTime": "1734661081049",
+                "updatedTime": "1734661081053"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734661416266",
+    "responseTime": "1734661416277",
+    "traceId": "a87a52a4e189045b7b7b9948ea7b5c54"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Position Transactions By Account ID and Transaction ID

GET /api/v1/private/account/getPositionTransactionById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

positionTransactionIdList

query

string

No

Position Transaction IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "564809510904923406",
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "type": "SELL_POSITION",
+                "deltaOpenSize": "-0.001",
+                "deltaOpenValue": "-96.813200",
+                "deltaOpenFee": "0.048406",
+                "deltaFundingFee": "0.000000",
+                "beforeOpenSize": "0.001",
+                "beforeOpenValue": "96.813200",
+                "beforeOpenFee": "-0.048406",
+                "beforeFundingFee": "0",
+                "fillCloseSize": "-0.001",
+                "fillCloseValue": "-96.857100",
+                "fillCloseFee": "-0.048428",
+                "fillOpenSize": "0.000",
+                "fillOpenValue": "0.000000",
+                "fillOpenFee": "0.000000",
+                "fillPrice": "96857.1",
+                "liquidateFee": "0",
+                "realizePnl": "-0.004528",
+                "isLiquidate": false,
+                "isDeleverage": false,
+                "fundingTime": "0",
+                "fundingRate": "",
+                "fundingIndexPrice": "",
+                "fundingOraclePrice": "",
+                "fundingPositionSize": "",
+                "orderId": "564809510842007822",
+                "orderFillTransactionId": "564809510875562254",
+                "collateralTransactionId": "564809510904922382",
+                "forceTradeId": "0",
+                "extraType": "",
+                "extraDataJson": "",
+                "censorStatus": "CENSOR_SUCCESS",
+                "censorTxId": "892720",
+                "censorTime": "1734661081049",
+                "censorFailCode": "",
+                "censorFailReason": "",
+                "l2TxId": "1084271",
+                "l2RejectTime": "0",
+                "l2RejectCode": "",
+                "l2RejectReason": "",
+                "l2ApprovedTime": "0",
+                "createdTime": "1734661081049",
+                "updatedTime": "1734661081053"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734661416266",
+    "responseTime": "1734661416277",
+    "traceId": "a87a52a4e189045b7b7b9948ea7b5c54"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Position Term Page by Account ID

GET /api/v1/private/account/getPositionTermPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to retrieve. Must be greater than 0 and less than or equal to 100

offsetData

query

string

No

Pagination offset. If empty or not provided, the first page is retrieved

filterCoinIdList

query

string

No

Filter position term records by specified coin IDs. If not provided, all position term records are retrieved

filterContractIdList

query

string

No

Filter position term records by specified contract IDs. If not provided, all position term records are retrieved

filterIsLongPosition

query

string

No

Filter position term records by position direction. If not provided, all position term records are retrieved

filterStartCreatedTimeInclusive

query

string

No

Filter position term records created after or at the specified start time (inclusive). If not provided or 0, retrieves records from the earliest time

filterEndCreatedTimeExclusive

query

string

No

Filter position term records created before the specified end time (exclusive). If not provided or 0, retrieves records up to the latest time

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "termCount": 2,
+                "cumOpenSize": "0.001",
+                "cumOpenValue": "96.813000",
+                "cumOpenFee": "-0.048406",
+                "cumCloseSize": "0",
+                "cumCloseValue": "0",
+                "cumCloseFee": "0",
+                "cumFundingFee": "0",
+                "cumLiquidateFee": "0",
+                "createdTime": "1734661093450",
+                "updatedTime": "1734661093450",
+                "currentLeverage": "50"
+            },
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "termCount": 1,
+                "cumOpenSize": "0.001",
+                "cumOpenValue": "96.813200",
+                "cumOpenFee": "-0.048406",
+                "cumCloseSize": "-0.001",
+                "cumCloseValue": "-96.857100",
+                "cumCloseFee": "-0.048428",
+                "cumFundingFee": "0",
+                "cumLiquidateFee": "0",
+                "createdTime": "1734661018663",
+                "updatedTime": "1734661081053",
+                "currentLeverage": "50"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734661416272",
+    "responseTime": "1734661416281",
+    "traceId": "ad4515e50fa7a57610736753d8f987aa"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Position By Account ID and Contract ID

GET /api/v1/private/account/getPositionByContractId

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

contractIdList

query

string

No

Specified contract IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "contractId": "10000001",
+            "openSize": "0.001",
+            "openValue": "97.444500",
+            "openFee": "-0.017540",
+            "fundingFee": "0.000000",
+            "longTermCount": 3,
+            "longTermStat": {
+                "cumOpenSize": "0.001",
+                "cumOpenValue": "97.444500",
+                "cumOpenFee": "-0.017540",
+                "cumCloseSize": "0",
+                "cumCloseValue": "0",
+                "cumCloseFee": "0",
+                "cumFundingFee": "0",
+                "cumLiquidateFee": "0"
+            },
+            "longTermCreatedTime": "1734662617992",
+            "longTermUpdatedTime": "1734662617992",
+            "shortTermCount": 0,
+            "shortTermStat": {
+                "cumOpenSize": "0",
+                "cumOpenValue": "0",
+                "cumOpenFee": "0",
+                "cumCloseSize": "0",
+                "cumCloseValue": "0",
+                "cumCloseFee": "0",
+                "cumFundingFee": "0",
+                "cumLiquidateFee": "0"
+            },
+            "shortTermCreatedTime": "0",
+            "shortTermUpdatedTime": "0",
+            "longTotalStat": {
+                "cumOpenSize": "0.004",
+                "cumOpenValue": "388.464500",
+                "cumOpenFee": "-0.131882",
+                "cumCloseSize": "-0.003",
+                "cumCloseValue": "-291.736700",
+                "cumCloseFee": "-0.083506",
+                "cumFundingFee": "0",
+                "cumLiquidateFee": "0"
+            },
+            "shortTotalStat": {
+                "cumOpenSize": "0",
+                "cumOpenValue": "0",
+                "cumOpenFee": "0",
+                "cumCloseSize": "0",
+                "cumCloseValue": "0",
+                "cumCloseFee": "0",
+                "cumFundingFee": "0",
+                "cumLiquidateFee": "0"
+            },
+            "createdTime": "1734661018663",
+            "updatedTime": "1734662617992"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734664849770",
+    "responseTime": "1734664849790",
+    "traceId": "17a421d1b23652c5b3836239274b0352"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

Inline

Response Data Structure

GET Get Collateral Transaction Page by Account ID

GET /api/v1/private/account/getCollateralTransactionPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to retrieve. Must be greater than 0 and less than or equal to 100

offsetData

query

string

No

Pagination offset. If empty or not provided, the first page is retrieved

filterCoinIdList

query

string

No

Filter collateral transaction records by specified coin IDs. If not provided, all collateral transaction records are retrieved

filterTypeList

query

string

No

Filter collateral transaction records by specified transaction types. If not provided, all collateral transaction records are retrieved

filterStartCreatedTimeInclusive

query

string

No

Filter collateral transaction records created after or at the specified start time (inclusive). If not provided or 0, retrieves records from the earliest time

filterEndCreatedTimeExclusive

query

string

No

Filter collateral transaction records created before the specified end time (exclusive). If not provided or 0, retrieves records up to the latest time

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "564815957260763406",
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "orderId": "564815695875932430",
+                "orderSide": "BUY",
+                "fillSize": "0.001",
+                "fillValue": "97.4445",
+                "fillFee": "0.017540",
+                "fillPrice": "97444.5",
+                "liquidateFee": "0",
+                "realizePnl": "-0.017540",
+                "direction": "MAKER",
+                "isPositionTpsl": false,
+                "isLiquidate": false,
+                "isDeleverage": false,
+                "isWithoutMatch": false,
+                "matchSequenceId": "35196430",
+                "matchIndex": 0,
+                "matchTime": "1734662617982",
+                "matchAccountId": "555790606509539863",
+                "matchOrderId": "564815957235597591",
+                "matchFillId": "05d14491-db7d-478a-9d9f-2dc55c3ff3ca",
+                "positionTransactionId": "564815957294318862",
+                "collateralTransactionId": "564815957294317838",
+                "extraType": "",
+                "extraDataJson": "",
+                "censorStatus": "CENSOR_SUCCESS",
+                "censorTxId": "893031",
+                "censorTime": "1734662617988",
+                "censorFailCode": "",
+                "censorFailReason": "",
+                "l2TxId": "1084582",
+                "l2RejectTime": "0",
+                "l2RejectCode": "",
+                "l2RejectReason": "",
+                "l2ApprovedTime": "0",
+                "createdTime": "1734662617984",
+                "updatedTime": "1734662617992"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734662681040",
+    "responseTime": "1734662681051",
+    "traceId": "770fcce6222c2d88b65b4ecb36e84c43"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

Inline

Response Data Structure

GET Get Collateral Transactions By Account ID and Transaction ID

GET /api/v1/private/account/getCollateralTransactionById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

collateralTransactionIdList

query

string

No

Collateral Transaction IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "563516408265179918",
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "type": "DEPOSIT",
+            "deltaAmount": "10.000000",
+            "deltaLegacyAmount": "10.000000",
+            "beforeAmount": "6.000000",
+            "beforeLegacyAmount": "6.000000",
+            "fillCloseSize": "",
+            "fillCloseValue": "",
+            "fillCloseFee": "",
+            "fillOpenSize": "",
+            "fillOpenValue": "",
+            "fillOpenFee": "",
+            "fillPrice": "",
+            "liquidateFee": "",
+            "realizePnl": "",
+            "isLiquidate": false,
+            "isDeleverage": false,
+            "fundingTime": "0",
+            "fundingRate": "",
+            "fundingIndexPrice": "",
+            "fundingOraclePrice": "",
+            "fundingPositionSize": "",
+            "depositId": "563516408235819790",
+            "withdrawId": "0",
+            "transferInId": "0",
+            "transferOutId": "0",
+            "transferReason": "UNKNOWN_TRANSFER_REASON",
+            "orderId": "0",
+            "orderFillTransactionId": "0",
+            "orderAccountId": "0",
+            "positionContractId": "0",
+            "positionTransactionId": "0",
+            "forceWithdrawId": "0",
+            "forceTradeId": "0",
+            "extraType": "",
+            "extraDataJson": "",
+            "censorStatus": "L2_APPROVED",
+            "censorTxId": "830852",
+            "censorTime": "1734352781355",
+            "censorFailCode": "",
+            "censorFailReason": "",
+            "l2TxId": "1022403",
+            "l2RejectTime": "0",
+            "l2RejectCode": "",
+            "l2RejectReason": "",
+            "l2ApprovedTime": "1734353551654",
+            "createdTime": "1734352781355",
+            "updatedTime": "1734353551715"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734664486740",
+    "responseTime": "1734664486761",
+    "traceId": "b3086f53c2d4503f6a4790b80f0e534b"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

Inline

Response Data Structure

GET Get Collateral By Account ID and Coin ID

GET /api/v1/private/account/getCollateralByCoinId

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

coinIdList

query

string

No

Filter collateral information by specified coin IDs. If not provided, all collateral information is retrieved

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "amount": "-81.943188",
+            "legacyAmount": "15.501312",
+            "cumDepositAmount": "70.000000",
+            "cumWithdrawAmount": "0",
+            "cumTransferInAmount": "0",
+            "cumTransferOutAmount": "-55.000000",
+            "cumPositionBuyAmount": "-388.4645",
+            "cumPositionSellAmount": "291.7367",
+            "cumFillFeeAmount": "-0.215388",
+            "cumFundingFeeAmount": "0",
+            "cumFillFeeIncomeAmount": "0",
+            "createdTime": "1730204434094",
+            "updatedTime": "1734663352066"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734664569244",
+    "responseTime": "1734664569260",
+    "traceId": "4b7ff82fb92aa3b10d9fc0367a069270"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

Inline

Response Data Structure

GET Get Account Page by User ID

GET /api/v1/private/account/getAccountPage

Request Parameters

Name
Location
Type
Required
Description

size

query

string

No

Number of items to retrieve. Must be greater than 0 and less than or equal to 100

offsetData

query

string

No

Pagination offset. If empty or not provided, the first page is retrieved

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "543429922991899150",
+                "userId": "543429922866069763",
+                "ethAddress": "0x1fB51aa234287C3CA1F957eA9AD0E148Bb814b7A",
+                "l2Key": "0x5580341e2c99823a0a35356b8ac84e372dd38fd1f4b50f607b931ec8038c211",
+                "l2KeyYCoordinate": "0x6ea3dd81a7fc864893c8c6f674e4a4510c369f939bdc0259a0980dfde882c2d",
+                "clientAccountId": "main",
+                "isSystemAccount": false,
+                "defaultTradeSetting": {
+                    "isSetFeeRate": true,
+                    "takerFeeRate": "0.000500",
+                    "makerFeeRate": "0.000180",
+                    "isSetFeeDiscount": false,
+                    "takerFeeDiscount": "0",
+                    "makerFeeDiscount": "0",
+                    "isSetMaxLeverage": false,
+                    "maxLeverage": "0"
+                },
+                "contractIdToTradeSetting": {
+                    "10000001": {
+                        "isSetFeeRate": false,
+                        "takerFeeRate": "",
+                        "makerFeeRate": "",
+                        "isSetFeeDiscount": false,
+                        "takerFeeDiscount": "",
+                        "makerFeeDiscount": "",
+                        "isSetMaxLeverage": true,
+                        "maxLeverage": "50"
+                    }
+                },
+                "maxLeverageLimit": "0",
+                "createOrderPerMinuteLimit": 0,
+                "createOrderDelayMillis": 0,
+                "extraType": "",
+                "extraDataJson": "",
+                "status": "NORMAL",
+                "isLiquidating": false,
+                "createdTime": "1730204434094",
+                "updatedTime": "1733993378059"
+            }
+        ],
+        "nextPageOffsetData": "551109015904453258"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734661416005",
+    "responseTime": "1734661416008",
+    "traceId": "dc6a8442169c8cdb831ceb15c812b7fc"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Account Deleverage Light

GET /api/v1/private/account/getAccountDeleverageLight

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "positionContractIdToLightNumberMap": {
+            "10000001": 3
+        }
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734661307929",
+    "responseTime": "1734661307935",
+    "traceId": "202ee8ba15ab633b68a35a8bc9756952"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Account By Account ID

GET /api/v1/private/account/getAccountById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "id": "543429922991899150",
+        "userId": "543429922866069763",
+        "ethAddress": "0x1fB51aa234287C3CA1F957eA9AD0E148Bb814b7A",
+        "l2Key": "0x5580341e2c99823a0a35356b8ac84e372dd38fd1f4b50f607b931ec8038c211",
+        "l2KeyYCoordinate": "0x6ea3dd81a7fc864893c8c6f674e4a4510c369f939bdc0259a0980dfde882c2d",
+        "clientAccountId": "main",
+        "isSystemAccount": false,
+        "defaultTradeSetting": {
+            "isSetFeeRate": true,
+            "takerFeeRate": "0.000500",
+            "makerFeeRate": "0.000180",
+            "isSetFeeDiscount": false,
+            "takerFeeDiscount": "0",
+            "makerFeeDiscount": "0",
+            "isSetMaxLeverage": false,
+            "maxLeverage": "0"
+        },
+        "contractIdToTradeSetting": {
+            "10000001": {
+                "isSetFeeRate": false,
+                "takerFeeRate": "",
+                "makerFeeRate": "",
+                "isSetFeeDiscount": false,
+                "takerFeeDiscount": "",
+                "makerFeeDiscount": "",
+                "isSetMaxLeverage": true,
+                "maxLeverage": "50"
+            }
+        },
+        "maxLeverageLimit": "0",
+        "createOrderPerMinuteLimit": 0,
+        "createOrderDelayMillis": 0,
+        "extraType": "",
+        "extraDataJson": "",
+        "status": "NORMAL",
+        "isLiquidating": false,
+        "createdTime": "1730204434094",
+        "updatedTime": "1733993378059"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734664605752",
+    "responseTime": "1734664605760",
+    "traceId": "c7be70afbf00d7f879d2809e0f042dfe"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Account Asset

GET /api/v1/private/account/getAccountAsset

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "account": {
+            "id": "543429922991899150",
+            "userId": "543429922866069763",
+            "ethAddress": "0x1fB51aa234287C3CA1F957eA9AD0E148Bb814b7A",
+            "l2Key": "0x5580341e2c99823a0a35356b8ac84e372dd38fd1f4b50f607b931ec8038c211",
+            "l2KeyYCoordinate": "0x6ea3dd81a7fc864893c8c6f674e4a4510c369f939bdc0259a0980dfde882c2d",
+            "clientAccountId": "main",
+            "isSystemAccount": false,
+            "defaultTradeSetting": {
+                "isSetFeeRate": true,
+                "takerFeeRate": "0.000500",
+                "makerFeeRate": "0.000180",
+                "isSetFeeDiscount": false,
+                "takerFeeDiscount": "0",
+                "makerFeeDiscount": "0",
+                "isSetMaxLeverage": false,
+                "maxLeverage": "0"
+            },
+            "contractIdToTradeSetting": {
+                "10000001": {
+                    "isSetFeeRate": false,
+                    "takerFeeRate": "",
+                    "makerFeeRate": "",
+                    "isSetFeeDiscount": false,
+                    "takerFeeDiscount": "",
+                    "makerFeeDiscount": "",
+                    "isSetMaxLeverage": true,
+                    "maxLeverage": "50"
+                }
+            },
+            "maxLeverageLimit": "0",
+            "createOrderPerMinuteLimit": 0,
+            "createOrderDelayMillis": 0,
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "NORMAL",
+            "isLiquidating": false,
+            "createdTime": "1730204434094",
+            "updatedTime": "1733993378059"
+        },
+        "collateralList": [
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "amount": "-81.943188",
+                "legacyAmount": "15.501312",
+                "cumDepositAmount": "70.000000",
+                "cumWithdrawAmount": "0",
+                "cumTransferInAmount": "0",
+                "cumTransferOutAmount": "-55.000000",
+                "cumPositionBuyAmount": "-388.4645",
+                "cumPositionSellAmount": "291.7367",
+                "cumFillFeeAmount": "-0.215388",
+                "cumFundingFeeAmount": "0",
+                "cumFillFeeIncomeAmount": "0",
+                "createdTime": "1730204434094",
+                "updatedTime": "1734663352066"
+            }
+        ],
+        "positionList": [
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "openSize": "0.001",
+                "openValue": "97.444500",
+                "openFee": "-0.017540",
+                "fundingFee": "0.000000",
+                "longTermCount": 3,
+                "longTermStat": {
+                    "cumOpenSize": "0.001",
+                    "cumOpenValue": "97.444500",
+                    "cumOpenFee": "-0.017540",
+                    "cumCloseSize": "0",
+                    "cumCloseValue": "0",
+                    "cumCloseFee": "0",
+                    "cumFundingFee": "0",
+                    "cumLiquidateFee": "0"
+                },
+                "longTermCreatedTime": "1734662617992",
+                "longTermUpdatedTime": "1734662617992",
+                "shortTermCount": 0,
+                "shortTermStat": {
+                    "cumOpenSize": "0",
+                    "cumOpenValue": "0",
+                    "cumOpenFee": "0",
+                    "cumCloseSize": "0",
+                    "cumCloseValue": "0",
+                    "cumCloseFee": "0",
+                    "cumFundingFee": "0",
+                    "cumLiquidateFee": "0"
+                },
+                "shortTermCreatedTime": "0",
+                "shortTermUpdatedTime": "0",
+                "longTotalStat": {
+                    "cumOpenSize": "0.004",
+                    "cumOpenValue": "388.464500",
+                    "cumOpenFee": "-0.131882",
+                    "cumCloseSize": "-0.003",
+                    "cumCloseValue": "-291.736700",
+                    "cumCloseFee": "-0.083506",
+                    "cumFundingFee": "0",
+                    "cumLiquidateFee": "0"
+                },
+                "shortTotalStat": {
+                    "cumOpenSize": "0",
+                    "cumOpenValue": "0",
+                    "cumOpenFee": "0",
+                    "cumCloseSize": "0",
+                    "cumCloseValue": "0",
+                    "cumCloseFee": "0",
+                    "cumFundingFee": "0",
+                    "cumLiquidateFee": "0"
+                },
+                "createdTime": "1734661018663",
+                "updatedTime": "1734662617992"
+            }
+        ],
+        "version": "1021",
+        "positionAssetList": [
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "positionValue": "97.734426609240472316741943359375",
+                "maxLeverage": "50",
+                "initialMarginRequirement": "1.954688532184809446334838867187500000",
+                "starkExRiskRate": "0.00500000012107193470001220703125",
+                "starkExRiskValue": "0.48867214487909847796080764492643311314168386161327362060546875",
+                "avgEntryPrice": "97444.5",
+                "liquidatePrice": "82354.9",
+                "bankruptPrice": "81943.1",
+                "worstClosePrice": "81984.2",
+                "unrealizePnl": "0.289926609240472316741943359375",
+                "termRealizePnl": "0.000000",
+                "totalRealizePnl": "0.716700"
+            }
+        ],
+        "collateralAssetModelList": [
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "totalEquity": "15.791238609240472316741943359375",
+                "totalPositionValueAbs": "97.734426609240472316741943359375",
+                "initialMarginRequirement": "1.954688532184809446334838867187500000",
+                "starkExRiskValue": "0.48867214487909847796080764492643311314168386161327362060546875",
+                "pendingWithdrawAmount": "0",
+                "pendingTransferOutAmount": "0",
+                "orderFrozenAmount": "0",
+                "availableAmount": "13.836550"
+            }
+        ],
+        "oraclePriceList": []
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734664627939",
+    "responseTime": "1734664627957",
+    "traceId": "4a3a5cd027ea6c255c8c944567b634f1"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Account Asset Snapshot Page by Account ID

GET /api/v1/private/account/getAccountAssetSnapshotPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to retrieve. Must be greater than 0 and less than or equal to 1000

offsetData

query

string

No

Pagination offset. If empty or not provided, the first page is retrieved

coinId

query

string

Yes

Filter by the specified coin ID.

filterTimeTag

query

string

No

Specifies time tag. If not provided or 0, returns snapshots by the hour. 1 returns snapshots by the day

filterStartTimeInclusive

query

string

No

Filter snapshots created after or at the specified start time (inclusive). If not provided or 0, retrieves records from the earliest time

filterEndTimeExclusive

query

string

No

Filter snapshots created before the specified end time (exclusive). If not provided or 0, retrieves records up to the latest time

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "timeTag": 1,
+                "snapshotTime": "1734652800000",
+                "totalEquity": "16.000000",
+                "termRealizePnl": "0",
+                "unrealizePnl": "0",
+                "totalRealizePnl": "0"
+            },
+            {
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "timeTag": 1,
+                "snapshotTime": "1734566400000",
+                "totalEquity": "16.000000",
+                "termRealizePnl": "0",
+                "unrealizePnl": "0",
+                "totalRealizePnl": "0"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734663257066",
+    "responseTime": "1734663257075",
+    "traceId": "f52222cd41b6ff8bcd059a57ecd986a1"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

default response

Data Models

accountassetsnapshot

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Generic Paginated Response

errorParam

object

false

none

Error Parameters

Error message parameter information

» additionalProperties

string

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

schemapagedataaccountassetsnapshot

Name
Type
Required
Constraints
Description
Notes

dataList

false

none

Data List

nextPageOffsetData

string

false

none

Next Page Offset

Offset for retrieving the next page. If no next page data, empty string.

schemaaccountassetsnapshot

Name
Type
Required
Constraints
Description
Notes

userId

string(int64)

false

none

User ID

ID of the owning user

accountId

string(int64)

false

none

Account ID

ID of the owning account

coinId

string(int64)

false

none

Collateral Coin ID

ID of the associated collateral coin

timeTag

integer(int32)

false

none

Time Tag

Time tag. 1 represents the snapshot time is for the whole day.

snapshotTime

string(int64)

false

none

Snapshot Time

Snapshot time, hourly timestamp at the top of the hour.

totalEquity

string

false

none

Total Collateral Value

Current total value of the collateral

termRealizePnl

string

false

none

Term Realized PnL

Realized PnL for the term

unrealizePnl

string

false

none

Unrealized PnL

Unrealized PnL

totalRealizePnl

string

false

none

Total Realized PnL

Total realized PnL of the position

schemaresultgetaccountasset

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Get Account Asset Response

Response structure for fetching account asset data.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

schemagetaccountasset

Name
Type
Required
Constraints
Description
Notes

account

false

none

Account Information

Account information data.

collateralList

false

none

Collateral Information List

List of collateral information data.

positionList

false

none

Perpetual Contract Position List

List of perpetual contract position information.

version

string(int64)

false

none

Account Version

Account version number, incremented with each update.

positionAssetList

false

none

Position Asset Information List

List of position asset information.

collateralAssetModelList

false

none

Account-Level Asset Information List

List of account-level asset information.

oraclePriceList

false

none

Oracle Price List

List of all oracle prices used to calculate assets (only those used).

schemaindexprice

Name
Type
Required
Constraints
Description
Notes

contractId

string(int64)

false

none

Contract ID

Contract ID

priceType

string

false

none

Price Type

priceValue

string

false

none

Price Value

Price value

createdTime

string(int64)

false

none

Creation Time

Time of creation

oraclePriceSignature

false

none

Oracle Price Signature Information

Oracle price signature information, only exists when price_type=ORACLE_PRICE.

Enumerated Values

Property
Value

priceType

UNKNOWN_PRICE_TYPE

priceType

ORACLE_PRICE

priceType

INDEX_PRICE

priceType

LAST_PRICE

priceType

ASK1_PRICE

priceType

BID1_PRICE

priceType

OPEN_INTEREST

priceType

UNRECOGNIZED

schemaoraclepricesignature

Name
Type
Required
Constraints
Description
Notes

contractId

string(int64)

false

none

Contract ID

Contract ID

signer

string

false

none

Signer ID

Signer identifier

price

string

false

none

Signed Price

The price signed (price after stark ex precision processing)

externalAssetId

string

false

none

Concatenated Asset and Oracle Names

Concatenation of the asset name and the oracle name (both in hex encoding).

signature

false

none

L2 Signature Information

L2 signature information

timestamp

string(int64)

false

none

Signature Creation Time

The time the signature was created.

schemal2signature

Name
Type
Required
Constraints
Description
Notes

r

string

false

none

R Value

Bigint for hex string

s

string

false

none

S Value

Bigint for hex string

v

string

false

none

V Value

Bigint for hex string

schemacollateralasset

Name
Type
Required
Constraints
Description
Notes

userId

string(int64)

false

none

User ID

ID of the owning user.

accountId

string(int64)

false

none

Account ID

ID of the owning account.

coinId

string(int64)

false

none

Collateral Coin ID

ID of the associated collateral coin.

totalEquity

string

false

none

Total Collateral Value

Current total value of the collateral.

totalPositionValueAbs

string

false

none

Sum of Absolute Position Values

Sum of the absolute position values for the current collateral

initialMarginRequirement

string

false

none

Initial Margin Requirement

The initial margin requirement for the current collateral.

starkExRiskValue

string

false

none

Total StarkEx Risk Value

The total starkEx risk amount for the current collateral.

pendingWithdrawAmount

string

false

none

Pending Withdrawal Amount

The amount of collateral pending withdrawal.

pendingTransferOutAmount

string

false

none

Pending Transfer Out Amount

The amount of collateral pending transfer out.

orderFrozenAmount

string

false

none

Order Frozen Amount

The amount of collateral frozen by orders.

availableAmount

string

false

none

Available Amount

The amount of collateral available for use.

schemapositionasset

Name
Type
Required
Constraints
Description
Notes

userId

string(int64)

false

none

User ID

ID of the owning user.

accountId

string(int64)

false

none

Account ID

ID of the owning account.

coinId

string(int64)

false

none

Collateral Coin ID

ID of the associated collateral coin.

contractId

string(int64)

false

none

Contract ID

ID of the associated contract.

positionValue

string

false

none

Position Value

Position value, positive for long positions, negative for short positions.

maxLeverage

string

false

none

Maximum Leverage

The maximum leverage for current contract position.

initialMarginRequirement

string

false

none

Initial Margin Requirement

Initial margin requirement for the position.

starkExRiskRate

string

false

none

StarkEx Risk Rate

StarkEx risk rate calculated based on risk tiers. Similar to maintenance margin rate with different precision.

starkExRiskValue

string

false

none

StarkEx Risk Value

StarkEx risk amount, similar to maintenance margin, with different precision.

avgEntryPrice

string

false

none

Average Entry Price

Average entry price.

liquidatePrice

string

false

none

Liquidation Price

Liquidation price (force liquidation price). If oracle price reaches this price, liquidation is triggered.

bankruptPrice

string

false

none

Bankruptcy Price

Bankruptcy price. If the oracle price reaches this level, account total value is less than 0.

worstClosePrice

string

false

none

Worst Close Price

The worst closing price. The closing transaction price can not be worse than this price.

unrealizePnl

string

false

none

Unrealized PnL

Unrealized profit and loss for the position.

termRealizePnl

string

false

none

Term Realized PnL

Realized PnL for the term.

totalRealizePnl

string

false

none

Total Realized PnL

Total realized PnL of the position.

schemaposition

Name
Type
Required
Constraints
Description
Notes

userId

string(int64)

false

none

User ID

ID of the owning user.

accountId

string(int64)

false

none

Account ID

ID of the owning account.

coinId

string(int64)

false

none

Collateral Coin ID

ID of the associated collateral coin.

contractId

string(int64)

false

none

Contract ID

ID of the associated contract.

openSize

string

false

none

Current Open Size

Current open size (positive for long, negative for short).

openValue

string

false

none

Current Open Value

Current open value (increases upon opening, proportionally decreases upon closing).

openFee

string

false

none

Current Open Fee

Current allocated open fee (increases upon opening, proportionally decreases upon closing).

fundingFee

string

false

none

Current Funding Fee

Current allocated funding fee (increases upon settlement, proportionally decreases upon closing).

longTermCount

integer(int32)

false

none

Long Position Term Count

Long position term count. Starts from 1, increases by one upon complete closure of a position

longTermStat

false

none

Long Position Cumulative Statistics

Cumulative statistics for the position.

longTermCreatedTime

string

false

none

Long Position Term Creation Time

Creation time for the long position term.

longTermUpdatedTime

string

false

none

Long Position Term Update Time

Update time for the long position term.

shortTermCount

integer(int32)

false

none

Short Position Term Count

Short position term count. Starts from 1, increases by one upon complete closure of a position

shortTermStat

false

none

Short Position Cumulative Statistics

Cumulative statistics for the position.

shortTermCreatedTime

string

false

none

Short Position Term Creation Time

Creation time for the short position term.

shortTermUpdatedTime

string

false

none

Short Position Term Update Time

Update time for the short position term.

longTotalStat

false

none

Long Cumulative Statistics

Cumulative statistics for the position.

shortTotalStat

false

none

Short Cumulative Statistics

Cumulative statistics for the position.

createdTime

string(int64)

false

none

Creation Time

Creation time.

updatedTime

string(int64)

false

none

Update Time

Update time.

schemapositionstat

Name
Type
Required
Constraints
Description
Notes

cumOpenSize

string

false

none

Cumulative Open Size

Cumulative open size.

cumOpenValue

string

false

none

Cumulative Open Value

Cumulative open value.

cumOpenFee

string

false

none

Cumulative Open Fee

Cumulative open fees.

cumCloseSize

string

false

none

Cumulative Close Size

Cumulative close size.

cumCloseValue

string

false

none

Cumulative Close Value

Cumulative close value.

cumCloseFee

string

false

none

Cumulative Close Fee

Cumulative close fees.

cumFundingFee

string

false

none

Cumulative Funding Fee

Cumulative funding fees settled.

cumLiquidateFee

string

false

none

Cumulative Liquidate Fee

Cumulative liquidation fees.

schemacollateral

Name
Type
Required
Constraints
Description
Notes

userId

string(int64)

false

none

User ID

ID of the owning user.

accountId

string(int64)

false

none

Account ID

ID of the owning account.

coinId

string(int64)

false

none

Coin ID

Collateral coin ID.

amount

string(decimal)

false

none

Collateral Amount

Collateral amount, actually of decimal type.

legacyAmount

string(decimal)

false

none

Legacy Amount

Legacy balance field, for display purposes only, not for calculations.

cumDepositAmount

string(decimal)

false

none

Cumulative Deposit Amount

Cumulative deposit amount.

cumWithdrawAmount

string(decimal)

false

none

Cumulative Withdrawal Amount

Cumulative withdrawal amount.

cumTransferInAmount

string(decimal)

false

none

Cumulative Transfer In Amount

Cumulative transfer in amount.

cumTransferOutAmount

string(decimal)

false

none

Cumulative Transfer Out Amount

Cumulative transfer out amount.

cumPositionBuyAmount

string(decimal)

false

none

Cumulative Position Buy Amount

Cumulative collateral amount deducted from position buy.

cumPositionSellAmount

string(decimal)

false

none

Cumulative Position Sell Amount

Cumulative collateral amount added from position sell.

cumFillFeeAmount

string(decimal)

false

none

Cumulative Fill Fee Amount

Cumulative transaction fee amount.

cumFundingFeeAmount

string(decimal)

false

none

Cumulative Funding Fee Amount

Cumulative funding fee amount.

cumFillFeeIncomeAmount

string(decimal)

false

none

Cumulative Order Fill Fee Income Amount

Cumulative amount from order fill fee income.

createdTime

string(int64)

false

none

Creation Time

Creation time.

updatedTime

string(int64)

false

none

Update Time

Update time.

schemaaccount

Name
Type
Required
Constraints
Description
Notes

id

string(int64)

false

none

Account ID

Account ID, must be greater than 0.

userId

string(int64)

false

none

User ID

ID of the owning user.

ethAddress

string

false

none

Wallet ETH Address

Wallet ETH address.

l2Key

string

false

none

L2 Account Key

Account key on L2. Stark key in starkEx. Bigint for hex string

l2KeyYCoordinate

string

false

none

L2 Key Y Coordinate

Used only for verifying l2Signature. Not returned to end users. Bigint for hex string.

clientAccountId

string

false

none

Client Account ID

Client account ID for idempotency check.

isSystemAccount

boolean

false

none

System Account

Whether it is a system account (system accounts are not subject to contract risk settings, use separate MQ for trade messages).

defaultTradeSetting

false

none

Default Trade Setting

Trade settings. Trade setting calculation priority: Account contract trade settings -> Account default trade settings -> Contract configuration trade settings. Note: Only one of is_set_fee_rate and is_set_fee_discount can be true.

contractIdToTradeSetting

object

false

none

Contract-Level Account Trade Settings

Account contract-level trade settings.

» additionalProperties

false

none

Contract-Level Account Trade Settings

Trade settings. Trade setting calculation priority: Account contract trade settings -> Account default trade settings -> Contract configuration trade settings. Note: Only one of is_set_fee_rate and is_set_fee_discount can be true.

maxLeverageLimit

string

false

none

Maximum Leverage Limit

User-set maximum leverage limit. If 0, uses the leverage limit of the corresponding trading contract.

createOrderPerMinuteLimit

integer(int32)

false

none

Order Creation Limit per Minute

Order frequency limit per minute. If 0, default limit is used; if < 0, no limit is applied.

createOrderDelayMillis

integer(int32)

false

none

Order Creation Delay Milliseconds

Order delay milliseconds, must be greater than or equal to 0.

extraType

string

false

none

Extra Type

Extra type for upper-layer use.

extraDataJson

string

false

none

Extra Data

Extra data in JSON format, default is an empty string.

status

string

false

none

Account Status

Account status.

isLiquidating

boolean

false

none

Is Liquidating

Whether is being liquidated.

createdTime

string(int64)

false

none

Creation Time

Creation time.

updatedTime

string(int64)

false

none

Update Time

Update time.

Enumerated Values

Property
Value

status

UNKNOWN_ACCOUNT_STATUS

status

CENSORING

status

NORMAL

status

DISABLED

status

INVALID

status

UNRECOGNIZED

schematradesetting

Name
Type
Required
Constraints
Description
Notes

isSetFeeRate

boolean

false

none

Whether Fee Rate is Set

Whether to set a specific fee rate value.

takerFeeRate

string(decimal)

false

none

Taker Fee Rate

Taker fee rate, range [0, 1), valid only when is_set_fee_rate=true.

makerFeeRate

string(decimal)

false

none

Maker Fee Rate

Maker fee rate, range [0, 1), valid only when is_set_fee_rate=true.

isSetFeeDiscount

boolean

false

none

Whether Fee Discount is Set

Whether to set a fee discount.

takerFeeDiscount

string(decimal)

false

none

Taker Fee Discount

Taker fee discount, range [0, 1), valid only when is_set_fee_discount=true.

makerFeeDiscount

string(decimal)

false

none

Maker Fee Discount

Maker fee discount, range [0, 1), valid only when is_set_fee_discount=true.

isSetMaxLeverage

boolean

false

none

Whether Maximum Leverage is Set

Whether to set maximum trading leverage.

maxLeverage

string(decimal)

false

none

Maximum Leverage

Maximum trading leverage.

schemaresultaccount

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Account Information

Account information data.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

getaccountdeleveragelight

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Get Account Deleverage Light Response

Response structure for fetching deleverage light information.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

Response structure for fetching deleverage light information.

Name
Type
Required
Constraints
Description
Notes

positionContractIdToLightNumberMap

object

false

none

Map from Position Contract ID to Light Number

Maps position contract ID to light number. light_number ranges from 1-5, which represent 1-5 lights.

account

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Generic Paginated Response

Generic paginated response.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

Generic Paginated Response

Name
Type
Required
Constraints
Description
Notes

dataList

false

none

Data List

List of account data.

nextPageOffsetData

string

false

none

Next Page Offset

Offset for retrieving the next page. If no next page data, empty string.

collateral

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Response Data

Correct response data.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

collateraltransaction

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Response Data

Correct response data.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

Collateral transaction details

Name
Type
Required
Constraints
Description
Notes

id

string(int64)

false

none

Unique Identifier

Unique identifier.

userId

string(int64)

false

none

User ID

ID of the owning user.

accountId

string(int64)

false

none

Account ID

ID of the owning account.

coinId

string(int64)

false

none

Coin ID

Collateral coin ID.

type

string

false

none

Detail Type

Detail type.

deltaAmount

string(decimal)

false

none

Collateral Change Amount

Amount of the collateral change.

deltaLegacyAmount

string(decimal)

false

none

Legacy Balance Change Amount

Change amount of the legacy balance field.

beforeAmount

string(decimal)

false

none

Collateral Amount Before Change

Collateral amount before the change.

beforeLegacyAmount

string(decimal)

false

none

Legacy Balance Amount Before Change

Legacy balance before the change.

fillCloseSize

string(decimal)

false

none

Transaction Close Size

Transaction close size (positive for buy, negative for sell).

fillCloseValue

string

false

none

Transaction Close Value

Transaction close value (positive for buy, negative for sell).

fillCloseFee

string

false

none

Transaction Close Fee

Transaction close fee (typically zero or negative).

fillOpenSize

string(decimal)

false

none

Transaction Open Size

Transaction open size (positive for buy, negative for sell).

fillOpenValue

string

false

none

Transaction Open Value

Transaction open value (positive for buy, negative for sell).

fillOpenFee

string

false

none

Transaction Open Fee

Transaction open fee (typically zero or negative).

fillPrice

string(decimal)

false

none

Transaction Price

Transaction price (not precise, for display).

liquidateFee

string(decimal)

false

none

Liquidation Fee

Liquidation fee (if close transaction is a liquidation, typically zero or negative).

realizePnl

string(decimal)

false

none

Realized Profit and Loss

Realized profit and loss from a close (if a close transaction. Not precise, for display).

isLiquidate

boolean

false

none

Is Liquidation

Whether the transaction is a liquidation.

isDeleverage

boolean

false

none

Is Auto-Deleveraging

Whether the transaction is from auto-deleveraging.

fundingTime

string(int64)

false

none

Funding Settlement Time

Funding settlement time.

fundingRate

string(decimal)

false

none

Funding Rate

Funding rate.

fundingIndexPrice

string(decimal)

false

none

Funding Index Price

Index price related to funding rate.

fundingOraclePrice

string(decimal)

false

none

Funding Oracle Price

Oracle price related to funding rate.

fundingPositionSize

string(decimal)

false

none

Position Size During Funding Settlement

Position size during funding settlement (positive for long, negative for short).

depositId

string(int64)

false

none

Deposit Order ID

Associated deposit order ID when type=DEPOSIT.

withdrawId

string(int64)

false

none

Withdrawal Order ID

Associated withdrawal order ID when type=WITHDRAW.

transferInId

string(int64)

false

none

Transfer In Order ID

Associated transfer-in order ID when type=TRANSFER_IN.

transferOutId

string(int64)

false

none

Transfer Out Order ID

Associated transfer-out order ID when type=TRANSFER_OUT.

transferReason

string

false

none

Transfer Reason

Transfer reason when type=TRANSFER_IN/TRANSFER_OUT.

orderId

string(int64)

false

none

Order ID

Associated order ID when type=POSITION_BUY/POSITION_SELL/FILL_FEE_INCOME.

orderFillTransactionId

string(int64)

false

none

Order Fill Transaction ID

Associated order fill transaction ID when type=POSITION_BUY/POSITION_SELL/FILL_FEE_INCOME.

orderAccountId

string(int64)

false

none

Order Account ID

Associated order account ID when type=FILL_FEE_INCOME.

positionContractId

string(int64)

false

none

Position Contract ID

Associated position contract ID when type=POSITION_BUY/POSITION_SELL/POSITION_FUNDING/FILL_FEE_INCOME.

positionTransactionId

string(int64)

false

none

Position Transaction ID

Associated position transaction ID when type=POSITION_BUY/POSITION_SELL/POSITION_FUNDING.

forceWithdrawId

string

false

none

Force Withdrawal Order ID

Associated force withdrawal order ID when type=WITHDRAW.

forceTradeId

string

false

none

Force Trade ID

Associated force trade order ID when type=POSITION_BUY/POSITION_SELL.

extraType

string

false

none

Extra Type

Extra type for upper-layer business use.

extraDataJson

string

false

none

Extra Data

Extra data in JSON format, default is empty string.

censorStatus

string

false

none

Current Censoring Status

Current censoring status.

censorTxId

string(int64)

false

none

Censoring Processing Sequence Number

Censoring processing sequence number, exists when censor_status=CENSOR_SUCCESS/CENSOR_FAILURE/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

censorTime

string(int64)

false

none

Censoring Processing Time

Censoring processing time, exists when censor_status=CENSOR_SUCCESS/CENSOR_FAILURE/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

censorFailCode

string

false

none

Censoring Failure Code

Censoring failure code, exists when censor_status=CENSOR_FAILURE.

censorFailReason

string

false

none

Censoring Failure Reason

Censoring failure reason, exists when censor_status=CENSOR_FAILURE.

l2TxId

string(int64)

false

none

L2 Push Transaction ID

L2 push transaction ID, exists when censor_status=CENSOR_SUCCESS/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

l2RejectTime

string(int64)

false

none

L2 Rejection Time

L2 rejection time, exists when censor_status=L2_REJECT/L2_REJECT_APPROVED.

l2RejectCode

string

false

none

L2 Rejection Error Code

L2 rejection error code, exists when censor_status=L2_REJECT/L2_REJECT_APPROVED.

l2RejectReason

string

false

none

L2 Rejection Reason

L2 rejection reason, exists when censor_status=L2_REJECT/L2_REJECT_APPROVED.

l2ApprovedTime

string(int64)

false

none

L2 Batch Verification Time

L2 batch verification time, exists when censor_status=L2_APPROVED/L2_REJECT_APPROVED.

createdTime

string(int64)

false

none

Creation Time

Creation time.

updatedTime

string(int64)

false

none

Update Time

Update time.

Enumerated Values

Property
Value

type

UNKNOWN_COLLATERAL_TRANSACTION_TYPE

type

DEPOSIT

type

WITHDRAW

type

TRANSFER_IN

type

TRANSFER_OUT

type

POSITION_BUY

type

POSITION_SELL

type

POSITION_FUNDING

type

FILL_FEE_INCOME

type

BUG_FIX_COLLATERAL_TRANSACTION_TYPE

type

UNRECOGNIZED

transferReason

UNKNOWN_TRANSFER_REASON

transferReason

USER_TRANSFER

transferReason

FAST_WITHDRAW

transferReason

CROSS_DEPOSIT

transferReason

CROSS_WITHDRAW

transferReason

UNRECOGNIZED

censorStatus

UNKNOWN_TRANSACTION_STATUS

censorStatus

INIT

censorStatus

CENSOR_SUCCESS

censorStatus

CENSOR_FAILURE

censorStatus

L2_APPROVED

censorStatus

L2_REJECT

censorStatus

L2_REJECT_APPROVED

censorStatus

UNRECOGNIZED

collateraltransaction

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Generic Paginated Response

Generic paginated response.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

schemapagedatacollateraltransaction

Name
Type
Required
Constraints
Description
Notes

dataList

false

none

Data List

List of collateral transaction data.

nextPageOffsetData

string

false

none

Next Page Offset

Offset for retrieving the next page. If no next page data, empty string.

position

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Response Data

Correct response data.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

positionterm

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Generic Paginated Response

Generic paginated response.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

schemapagedatapositionterm

Name
Type
Required
Constraints
Description
Notes

dataList

false

none

Data List

List of position term data.

nextPageOffsetData

string

false

none

Next Page Offset

Offset for retrieving the next page. If no next page data, empty string.

schemapositionterm

Name
Type
Required
Constraints
Description
Notes

userId

string

false

none

User ID

ID of the owning user.

accountId

string

false

none

Account ID

ID of the owning account.

coinId

string

false

none

Collateral Coin ID

ID of the associated collateral coin.

contractId

string

false

none

Contract ID

ID of the associated contract.

termCount

integer(int32)

false

none

Term Count

Term count. Starts from 1, increases by one each time a position is fully closed and then re-opened.

cumOpenSize

string

false

none

Cumulative Open Size

Cumulative open size.

cumOpenValue

string

false

none

Cumulative Open Value

Cumulative open value.

cumOpenFee

string

false

none

Cumulative Open Fee

Cumulative open fees.

cumCloseSize

string

false

none

Cumulative Close Size

Cumulative close size.

cumCloseValue

string

false

none

Cumulative Close Value

Cumulative close value.

cumCloseFee

string

false

none

Cumulative Close Fee

Cumulative close fees.

cumFundingFee

string

false

none

Cumulative Funding Fee

Cumulative funding fees that have been settled.

cumLiquidateFee

string

false

none

Cumulative Liquidation Fee

Cumulative liquidation fees.

createdTime

string(int64)

false

none

Creation Time

Creation time.

updatedTime

string(int64)

false

none

Update Time

Update time.

currentLeverage

string

false

none

Leverage at Close

Leverage multiple at the time of close position.

positiontransaction

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Response Data

Correct response data.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

schemapositiontransaction

Name
Type
Required
Constraints
Description
Notes

id

string(int64)

false

none

Unique Identifier

Unique identifier.

userId

string(int64)

false

none

User ID

ID of the owning user.

accountId

string(int64)

false

none

Account ID

ID of the owning account.

coinId

string(int64)

false

none

Collateral Coin ID

ID of the associated collateral coin.

contractId

string(int64)

false

none

Contract ID

ID of the associated contract.

type

string

false

none

Detail Type

Detail type.

deltaOpenSize

string

false

none

Change in Open Size

Change in holding size.

deltaOpenValue

string

false

none

Change in Open Value

Change in open value.

deltaOpenFee

string

false

none

Change in Open Fee

Change in open fee.

deltaFundingFee

string

false

none

Change in Funding Fee

Change in funding fee.

beforeOpenSize

string

false

none

Open Size Before Change

Holding size before the change.

beforeOpenValue

string

false

none

Open Value Before Change

Open value before the change.

beforeOpenFee

string

false

none

Open Fee Before Change

Open fee before the change.

beforeFundingFee

string

false

none

Funding Fee Before Change

Funding fee before the change.

fillCloseSize

string

false

none

Transaction Close Size

Transaction close size (positive for buy, negative for sell).

fillCloseValue

string

false

none

Transaction Close Value

Transaction close value (positive for buy, negative for sell).

fillCloseFee

string

false

none

Transaction Close Fee

Transaction close fee (typically zero or negative).

fillOpenSize

string

false

none

Transaction Open Size

Transaction open size (positive for buy, negative for sell).

fillOpenValue

string

false

none

Transaction Open Value

Transaction open value (positive for buy, negative for sell).

fillOpenFee

string

false

none

Transaction Open Fee

Transaction open fee (typically zero or negative).

fillPrice

string

false

none

Transaction Price

Transaction price (not precise, for display).

liquidateFee

string

false

none

Liquidation Fee

Liquidation fee (if close transaction is a liquidation, typically zero or negative).

realizePnl

string

false

none

Realized Profit and Loss

Realized profit and loss from a close (if a close transaction. Not precise, for display).

isLiquidate

boolean

false

none

Is Liquidation

Whether the transaction is a liquidation.

isDeleverage

boolean

false

none

Is Auto-Deleveraging

Whether the transaction is from auto-deleveraging.

fundingTime

string(int64)

false

none

Funding Settlement Time

Funding settlement time.

fundingRate

string

false

none

Funding Rate

Funding rate.

fundingIndexPrice

string

false

none

Funding Index Price

Index price related to funding rate.

fundingOraclePrice

string

false

none

Funding Oracle Price

Oracle price related to funding rate.

fundingPositionSize

string

false

none

Position Size During Funding Settlement

Position size during funding settlement (positive for long, negative for short).

orderId

string(int64)

false

none

Order ID

Associated order ID.

orderFillTransactionId

string(int64)

false

none

Order Fill Transaction ID

Associated order fill transaction ID.

collateralTransactionId

string(int64)

false

none

Collateral Transaction ID

Associated collateral transaction detail ID.

forceTradeId

string

false

none

Force Trade ID

Associated force trade order ID.

extraType

string

false

none

Extra Type

Extra type for upper-layer business use.

extraDataJson

string

false

none

Extra Data

Extra data in JSON format, default is empty string.

censorStatus

string

false

none

Current Censoring Status

Current censoring status.

censorTxId

string(int64)

false

none

Censoring Processing Sequence Number

Censoring processing sequence number, exists when censor_status=CENSOR_SUCCESS/CENSOR_FAILURE/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

censorTime

string(int64)

false

none

Censoring Processing Time

Censoring processing time, exists when censor_status=CENSOR_SUCCESS/CENSOR_FAILURE/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

censorFailCode

string

false

none

Censoring Failure Code

Censoring failure code, exists when censor_status=CENSOR_FAILURE.

censorFailReason

string

false

none

Censoring Failure Reason

Censoring failure reason, exists when censor_status=CENSOR_FAILURE.

l2TxId

string(int64)

false

none

L2 Push Transaction ID

L2 push transaction ID, exists when censor_status=CENSOR_SUCCESS/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

l2RejectTime

string(int64)

false

none

L2 Rejection Time

L2 rejection time, exists when censor_status=L2_REJECT/L2_REJECT_APPROVED.

l2RejectCode

string

false

none

L2 Rejection Error Code

L2 rejection error code, exists when censor_status=L2_REJECT/L2_REJECT_APPROVED.

l2RejectReason

string

false

none

L2 Rejection Reason

L2 rejection reason, exists when censor_status=L2_REJECT/L2_REJECT_APPROVED.

l2ApprovedTime

string(int64)

false

none

L2 Batch Verification Time

L2 batch verification time, exists when censor_status=L2_APPROVED/L2_REJECT_APPROVED.

createdTime

string(int64)

false

none

Creation Time

Creation time.

updatedTime

string(int64)

false

none

Update Time

Update time.

Enumerated Values

Property
Value

type

UNKNOWN_POSITION_TRANSACTION_TYPE

type

BUY_POSITION

type

SELL_POSITION

type

SETTLE_FUNDING_FEE

type

BUG_FIX_POSITION_TRANSACTION_TYPE

type

UNRECOGNIZED

censorStatus

UNKNOWN_TRANSACTION_STATUS

censorStatus

INIT

censorStatus

CENSOR_SUCCESS

censorStatus

CENSOR_FAILURE

censorStatus

L2_APPROVED

censorStatus

L2_REJECT

censorStatus

L2_REJECT_APPROVED

censorStatus

UNRECOGNIZED

positiontransactionpage

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status Code

Returns "SUCCESS" on success; otherwise, it indicates failure.

data

false

none

Generic Paginated Response

Generic paginated response.

errorParam

object

false

none

Error Parameters

Error message parameter information

requestTime

string(timestamp)

false

none

Server Request Time

Time at which the server received the request

responseTime

string(timestamp)

false

none

Server Response Time

Time at which the server sent the response

traceId

string

false

none

Trace ID

Invocation trace ID

schemapagedatapositiontransaction

Name
Type
Required
Constraints
Description
Notes

dataList

false

none

Data List

List of position transaction data.

nextPageOffsetData

string

false

none

Next Page Offset

Offset for retrieving the next page. If no next page data, empty string.

Last updated

+ + Order API | edgeX Docs

Order API

OrderPrivateApi

POST Get Maximum Order Creation Size

POST /api/v1/private/order/getMaxCreateOrderSize

Body Request Parameters

{
+  "accountId": "551109015904453258",
+  "contractId": "10000001",
+  "price": "97,463.4"
+}

Request Parameters

Name
Location
Type
Required
Description

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "maxBuySize": "0.004",
+        "maxSellSize": "0.009",
+        "ask1Price": "97532.1",
+        "bid1Price": "97496.1"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734665285662",
+    "responseTime": "1734665285679",
+    "traceId": "38fc6d0f57f3e64c8c6239fae7d35f84"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

POST Create Order

POST /api/v1/private/order/createOrder

Body Request Parameters

{
+    "price": "97393.8",
+    "size": "0.001",
+    "type": "LIMIT",
+    "timeInForce": "GOOD_TIL_CANCEL",
+    "reduceOnly": false,
+    "isPositionTpsl": false,
+    "isSetOpenTp": false,
+    "isSetOpenSl": false,
+    "accountId": "543429922991899150",
+    "contractId": "10000001",
+    "side": "BUY",
+    "triggerPrice": "",
+    "triggerPriceType": "LAST_PRICE",
+    "clientOrderId": "21163368294502694",
+    "expireTime": "1736476772359",
+    "l2Nonce": "808219",
+    "l2Value": "97.3938",
+    "l2Size": "0.001",
+    "l2LimitFee": "0.048697",
+    "l2ExpireTime": "1737254372359",
+    "l2Signature": "0537b3051bb9ffb98bb842ff6cadf69807a8dbf74f94d8c6106cf4f59b1fd2dc01aa2d93420408d60298831ddb82f7b482574ecb0fc285294dfeec732379ec40",
+    "extraType": "",
+    "extraDataJson": "",
+    "symbol": "BTCUSDT",
+    "showEqualValInput": true,
+    "maxSellQTY": 0.009,
+    "maxBuyQTY": 0.007
+}

Request Parameters

Name
Location
Type
Required
Description

body

body

No

none

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "orderId": "564814927928230158"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734662372560",
+    "responseTime": "1734662372575",
+    "traceId": "364c0020d1fe90bbfcca3cf3a9d54759"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Response Data Structure

POST Cancel Order by Order ID

POST /api/v1/private/order/cancelOrderById

Body Request Parameters

{
+    "accountId": "551109015904453258",
+    "orderIdList": [
+        "564827797948727434"
+    ]
+}

Request Parameters

Name
Location
Type
Required
Description

body

body

No

none

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "cancelResultMap": {
+            "564827797948727434": "SUCCESS"
+        }
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734665453034",
+    "responseTime": "1734665453043",
+    "traceId": "5597699fd044ed965bfed23fb3728ba5"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

POST Cancel All Orders under Account

POST /api/v1/private/order/cancelAllOrder

Body Request Parameters

{
+    "accountId": "551109015904453258"
+}

Request Parameters

Name
Location
Type
Required
Description

body

body

No

none

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "cancelResultMap": {
+            "564828209955209354": "SUCCESS"
+        }
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734665771719",
+    "responseTime": "1734665771743",
+    "traceId": "c0a1da9b75ad55d64ce0e98f86279c34"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Inline

Response Data Structure

GET Get Orders by Account ID and Order IDs (Batch)

GET /api/v1/private/order/getOrderById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

orderIdList

query

string

No

Order IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564829588270612618",
+            "userId": "543429922866069763",
+            "accountId": "551109015904453258",
+            "coinId": "1000",
+            "contractId": "10000001",
+            "side": "BUY",
+            "price": "96260.7",
+            "size": "0.001",
+            "clientOrderId": "9311381563209122",
+            "type": "LIMIT",
+            "timeInForce": "GOOD_TIL_CANCEL",
+            "reduceOnly": false,
+            "triggerPrice": "0",
+            "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+            "expireTime": "1736480267612",
+            "sourceKey": "",
+            "isPositionTpsl": false,
+            "isLiquidate": false,
+            "isDeleverage": false,
+            "openTpslParentOrderId": "0",
+            "isSetOpenTp": false,
+            "openTp": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isSetOpenSl": false,
+            "openSl": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isWithoutMatch": false,
+            "withoutMatchFillSize": "0",
+            "withoutMatchFillValue": "0",
+            "withoutMatchPeerAccountId": "0",
+            "withoutMatchPeerOrderId": "0",
+            "maxLeverage": "50",
+            "takerFeeRate": "0.000500",
+            "makerFeeRate": "0.000180",
+            "liquidateFeeRate": "0.01",
+            "marketLimitPrice": "0",
+            "marketLimitValue": "0",
+            "l2Nonce": "3353661024",
+            "l2Value": "96.260700",
+            "l2Size": "0.001",
+            "l2LimitFee": "0.048131",
+            "l2ExpireTime": "1737257867612",
+            "l2Signature": {
+                "r": "0x072f299b86c199e161508ed554889d50476bc96d5a0c320bcd5b7e579b692c06",
+                "s": "0x05b24b988f84bca951b3bb4e69b11e0aa1ce08cca38fea0f3afd94b5e7cd09f8",
+                "v": ""
+            },
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "OPEN",
+            "matchSequenceId": "35229771",
+            "triggerTime": "0",
+            "triggerPriceTime": "0",
+            "triggerPriceValue": "0",
+            "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
+            "cumFillSize": "0",
+            "cumFillValue": "0",
+            "cumFillFee": "0",
+            "maxFillPrice": "0",
+            "minFillPrice": "0",
+            "cumLiquidateFee": "0",
+            "cumRealizePnl": "0",
+            "cumMatchSize": "0",
+            "cumMatchValue": "0",
+            "cumMatchFee": "0",
+            "cumFailSize": "0",
+            "cumFailValue": "0",
+            "cumFailFee": "0",
+            "cumApprovedSize": "0",
+            "cumApprovedValue": "0",
+            "cumApprovedFee": "0",
+            "createdTime": "1734665867870",
+            "updatedTime": "1734665867876"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734665901264",
+    "responseTime": "1734665901281",
+    "traceId": "65ca2097b4d9cfb487bd9cef53097040"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Inline

Response Data Structure

GET Get Orders by Client Order IDs (Batch)

GET /api/v1/private/order/getOrderByClientOrderId

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

clientOrderIdList

query

string

No

Client-defined order IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564829588270612618",
+            "userId": "543429922866069763",
+            "accountId": "551109015904453258",
+            "coinId": "1000",
+            "contractId": "10000001",
+            "side": "BUY",
+            "price": "96260.7",
+            "size": "0.001",
+            "clientOrderId": "9311381563209122",
+            "type": "LIMIT",
+            "timeInForce": "GOOD_TIL_CANCEL",
+            "reduceOnly": false,
+            "triggerPrice": "0",
+            "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+            "expireTime": "1736480267612",
+            "sourceKey": "",
+            "isPositionTpsl": false,
+            "isLiquidate": false,
+            "isDeleverage": false,
+            "openTpslParentOrderId": "0",
+            "isSetOpenTp": false,
+            "openTp": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isSetOpenSl": false,
+            "openSl": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isWithoutMatch": false,
+            "withoutMatchFillSize": "0",
+            "withoutMatchFillValue": "0",
+            "withoutMatchPeerAccountId": "0",
+            "withoutMatchPeerOrderId": "0",
+            "maxLeverage": "50",
+            "takerFeeRate": "0.000500",
+            "makerFeeRate": "0.000180",
+            "liquidateFeeRate": "0.01",
+            "marketLimitPrice": "0",
+            "marketLimitValue": "0",
+            "l2Nonce": "3353661024",
+            "l2Value": "96.260700",
+            "l2Size": "0.001",
+            "l2LimitFee": "0.048131",
+            "l2ExpireTime": "1737257867612",
+            "l2Signature": {
+                "r": "0x072f299b86c199e161508ed554889d50476bc96d5a0c320bcd5b7e579b692c06",
+                "s": "0x05b24b988f84bca951b3bb4e69b11e0aa1ce08cca38fea0f3afd94b5e7cd09f8",
+                "v": ""
+            },
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "OPEN",
+            "matchSequenceId": "35229771",
+            "triggerTime": "0",
+            "triggerPriceTime": "0",
+            "triggerPriceValue": "0",
+            "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
+            "cumFillSize": "0",
+            "cumFillValue": "0",
+            "cumFillFee": "0",
+            "maxFillPrice": "0",
+            "minFillPrice": "0",
+            "cumLiquidateFee": "0",
+            "cumRealizePnl": "0",
+            "cumMatchSize": "0",
+            "cumMatchValue": "0",
+            "cumMatchFee": "0",
+            "cumFailSize": "0",
+            "cumFailValue": "0",
+            "cumFailFee": "0",
+            "cumApprovedSize": "0",
+            "cumApprovedValue": "0",
+            "cumApprovedFee": "0",
+            "createdTime": "1734665867870",
+            "updatedTime": "1734665867876"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734665947238",
+    "responseTime": "1734665947256",
+    "traceId": "64913ab9c62058bc2d9edaeafc3da271"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Inline

GET Get Historical Orders (Paginated)

GET /api/v1/private/order/getHistoryOrderPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to fetch. Must be greater than 0 and less than or equal to 100.

offsetData

query

string

No

Pagination offset. If not provided or empty, retrieves the first page.

filterCoinIdList

query

string

No

Filters by collateral coin IDs. If empty, fetches orders for all collateral coin IDs.

filterContractIdList

query

string

No

Filters by contract IDs. If empty, fetches orders for all contracts.

filterTypeList

query

string

No

Filters by order types. If empty, fetches orders of all types.

filterStatusList

query

string

No

Filters by order status. If empty, fetches orders of all statuses.

filterIsLiquidateList

query

string

No

Filters by liquidate orders. If empty, fetches all orders

filterIsDeleverageList

query

string

No

Filters by deleverage orders. If empty, fetches all orders

filterIsPositionTpslList

query

string

No

Filters by position TP/SL orders. If empty, fetches all orders

filterStartCreatedTimeInclusive

query

string

No

Filters orders created after or on this time (inclusive). If empty or 0, retrieves from the earliest.

filterEndCreatedTimeExclusive

query

string

No

Filters orders created before this time (exclusive). If empty or 0, retrieves to the latest.

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "564815695875932430",
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "side": "BUY",
+                "price": "97444.5",
+                "size": "0.001",
+                "clientOrderId": "553364074986685",
+                "type": "LIMIT",
+                "timeInForce": "GOOD_TIL_CANCEL",
+                "reduceOnly": false,
+                "triggerPrice": "0",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "1736476955478",
+                "sourceKey": "",
+                "isPositionTpsl": false,
+                "isLiquidate": false,
+                "isDeleverage": false,
+                "openTpslParentOrderId": "0",
+                "isSetOpenTp": false,
+                "openTp": {
+                    "side": "UNKNOWN_ORDER_SIDE",
+                    "price": "",
+                    "size": "",
+                    "clientOrderId": "",
+                    "triggerPrice": "",
+                    "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                    "expireTime": "0",
+                    "l2Nonce": "0",
+                    "l2Value": "",
+                    "l2Size": "",
+                    "l2LimitFee": "",
+                    "l2ExpireTime": "0",
+                    "l2Signature": {
+                        "r": "",
+                        "s": "",
+                        "v": ""
+                    }
+                },
+                "isSetOpenSl": false,
+                "openSl": {
+                    "side": "UNKNOWN_ORDER_SIDE",
+                    "price": "",
+                    "size": "",
+                    "clientOrderId": "",
+                    "triggerPrice": "",
+                    "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                    "expireTime": "0",
+                    "l2Nonce": "0",
+                    "l2Value": "",
+                    "l2Size": "",
+                    "l2LimitFee": "",
+                    "l2ExpireTime": "0",
+                    "l2Signature": {
+                        "r": "",
+                        "s": "",
+                        "v": ""
+                    }
+                },
+                "isWithoutMatch": false,
+                "withoutMatchFillSize": "0",
+                "withoutMatchFillValue": "0",
+                "withoutMatchPeerAccountId": "0",
+                "withoutMatchPeerOrderId": "0",
+                "maxLeverage": "50",
+                "takerFeeRate": "0.000500",
+                "makerFeeRate": "0.000180",
+                "liquidateFeeRate": "0.01",
+                "marketLimitPrice": "0",
+                "marketLimitValue": "0",
+                "l2Nonce": "2054491946",
+                "l2Value": "97.444500",
+                "l2Size": "0.001",
+                "l2LimitFee": "0.048723",
+                "l2ExpireTime": "1737254555478",
+                "l2Signature": {
+                    "r": "0x009af59c2963f1650449904fde059a83fed5beb4acdd67ffa22c551c4be977de",
+                    "s": "0x04940a395f2d1b39c0f2b969e47fec478fbfea9ce227bfedd07d407175dcac3e",
+                    "v": ""
+                },
+                "extraType": "",
+                "extraDataJson": "",
+                "status": "FILLED",
+                "matchSequenceId": "35196430",
+                "triggerTime": "0",
+                "triggerPriceTime": "0",
+                "triggerPriceValue": "0",
+                "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
+                "cumFillSize": "0.001",
+                "cumFillValue": "97.4445",
+                "cumFillFee": "0.017540",
+                "maxFillPrice": "97444.5",
+                "minFillPrice": "97444.5",
+                "cumLiquidateFee": "0",
+                "cumRealizePnl": "-0.017540",
+                "cumMatchSize": "0.001",
+                "cumMatchValue": "97.4445",
+                "cumMatchFee": "0.017540",
+                "cumFailSize": "0",
+                "cumFailValue": "0",
+                "cumFailFee": "0",
+                "cumApprovedSize": "0",
+                "cumApprovedValue": "0",
+                "cumApprovedFee": "0",
+                "createdTime": "1734662555665",
+                "updatedTime": "1734662617992"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734662697584",
+    "responseTime": "1734662697601",
+    "traceId": "1cd03694d7da308cb13603f34b0836e6"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

GET Get Historical Order Fill Transactions (Paginated)

GET /api/v1/private/order/getHistoryOrderFillTransactionPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to fetch. Must be greater than 0 and less than or equal to 100.

offsetData

query

string

No

Pagination offset. If not provided or empty, retrieves the first page.

filterCoinIdList

query

string

No

Filters by collateral coin IDs. If empty, fetches order fill transactions for all collateral coin IDs.

filterContractIdList

query

string

No

Filters by contract IDs. If empty, fetches order fill transactions for all contracts.

filterOrderIdList

query

string

No

Filters by order IDs. If empty, fetches order fill transactions for all orders.

filterIsLiquidateList

query

string

No

Filters by liquidate orders. If empty, fetches all orders

filterIsDeleverageList

query

string

No

Filters by deleverage orders. If empty, fetches all orders

filterIsPositionTpslList

query

string

No

Filters by position TP/SL orders. If empty, fetches all orders

filterStartCreatedTimeInclusive

query

string

No

Filters order fill transactions created after or on this time (inclusive). If empty or 0, retrieves from the earliest.

filterEndCreatedTimeExclusive

query

string

No

Filters order fill transactions created before this time (exclusive). If empty or 0, retrieves to the latest.

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "564815957260763406",
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "orderId": "564815695875932430",
+                "orderSide": "BUY",
+                "fillSize": "0.001",
+                "fillValue": "97.4445",
+                "fillFee": "0.017540",
+                "fillPrice": "97444.5",
+                "liquidateFee": "0",
+                "realizePnl": "-0.017540",
+                "direction": "MAKER",
+                "isPositionTpsl": false,
+                "isLiquidate": false,
+                "isDeleverage": false,
+                "isWithoutMatch": false,
+                "matchSequenceId": "35196430",
+                "matchIndex": 0,
+                "matchTime": "1734662617982",
+                "matchAccountId": "555790606509539863",
+                "matchOrderId": "564815957235597591",
+                "matchFillId": "05d14491-db7d-478a-9d9f-2dc55c3ff3ca",
+                "positionTransactionId": "564815957294318862",
+                "collateralTransactionId": "564815957294317838",
+                "extraType": "",
+                "extraDataJson": "",
+                "censorStatus": "CENSOR_SUCCESS",
+                "censorTxId": "893031",
+                "censorTime": "1734662617988",
+                "censorFailCode": "",
+                "censorFailReason": "",
+                "l2TxId": "1084582",
+                "l2RejectTime": "0",
+                "l2RejectCode": "",
+                "l2RejectReason": "",
+                "l2ApprovedTime": "0",
+                "createdTime": "1734662617984",
+                "updatedTime": "1734662617992"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734662681040",
+    "responseTime": "1734662681051",
+    "traceId": "770fcce6222c2d88b65b4ecb36e84c43"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

GET Get Historical Order Fill Transactions by ID (Batch)

GET /api/v1/private/order/getHistoryOrderFillTransactionById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

orderFillTransactionIdList

query

string

No

Order fill transaction IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564815957260763406",
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "contractId": "10000001",
+            "orderId": "564815695875932430",
+            "orderSide": "BUY",
+            "fillSize": "0.001",
+            "fillValue": "97.4445",
+            "fillFee": "0.017540",
+            "fillPrice": "97444.5",
+            "liquidateFee": "0",
+            "realizePnl": "-0.017540",
+            "direction": "MAKER",
+            "isPositionTpsl": false,
+            "isLiquidate": false,
+            "isDeleverage": false,
+            "isWithoutMatch": false,
+            "matchSequenceId": "35196430",
+            "matchIndex": 0,
+            "matchTime": "1734662617982",
+            "matchAccountId": "555790606509539863",
+            "matchOrderId": "564815957235597591",
+            "matchFillId": "05d14491-db7d-478a-9d9f-2dc55c3ff3ca",
+            "positionTransactionId": "564815957294318862",
+            "collateralTransactionId": "564815957294317838",
+            "extraType": "",
+            "extraDataJson": "",
+            "censorStatus": "CENSOR_SUCCESS",
+            "censorTxId": "893031",
+            "censorTime": "1734662617988",
+            "censorFailCode": "",
+            "censorFailReason": "",
+            "l2TxId": "1084582",
+            "l2RejectTime": "0",
+            "l2RejectCode": "",
+            "l2RejectReason": "",
+            "l2ApprovedTime": "0",
+            "createdTime": "1734662617984",
+            "updatedTime": "1734662617992"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734666041607",
+    "responseTime": "1734666041619",
+    "traceId": "629acb9f32074715a1ea9befd171c452"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

GET Get Historical Orders by ID (Batch)

GET /api/v1/private/order/getHistoryOrderById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

orderIdList

query

string

No

Order IDs

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564815695875932430",
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "contractId": "10000001",
+            "side": "BUY",
+            "price": "97444.5",
+            "size": "0.001",
+            "clientOrderId": "553364074986685",
+            "type": "LIMIT",
+            "timeInForce": "GOOD_TIL_CANCEL",
+            "reduceOnly": false,
+            "triggerPrice": "0",
+            "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+            "expireTime": "1736476955478",
+            "sourceKey": "",
+            "isPositionTpsl": false,
+            "isLiquidate": false,
+            "isDeleverage": false,
+            "openTpslParentOrderId": "0",
+            "isSetOpenTp": false,
+            "openTp": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isSetOpenSl": false,
+            "openSl": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isWithoutMatch": false,
+            "withoutMatchFillSize": "0",
+            "withoutMatchFillValue": "0",
+            "withoutMatchPeerAccountId": "0",
+            "withoutMatchPeerOrderId": "0",
+            "maxLeverage": "50",
+            "takerFeeRate": "0.000500",
+            "makerFeeRate": "0.000180",
+            "liquidateFeeRate": "0.01",
+            "marketLimitPrice": "0",
+            "marketLimitValue": "0",
+            "l2Nonce": "2054491946",
+            "l2Value": "97.444500",
+            "l2Size": "0.001",
+            "l2LimitFee": "0.048723",
+            "l2ExpireTime": "1737254555478",
+            "l2Signature": {
+                "r": "0x009af59c2963f1650449904fde059a83fed5beb4acdd67ffa22c551c4be977de",
+                "s": "0x04940a395f2d1b39c0f2b969e47fec478fbfea9ce227bfedd07d407175dcac3e",
+                "v": ""
+            },
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "FILLED",
+            "matchSequenceId": "35196430",
+            "triggerTime": "0",
+            "triggerPriceTime": "0",
+            "triggerPriceValue": "0",
+            "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
+            "cumFillSize": "0.001",
+            "cumFillValue": "97.4445",
+            "cumFillFee": "0.017540",
+            "maxFillPrice": "97444.5",
+            "minFillPrice": "97444.5",
+            "cumLiquidateFee": "0",
+            "cumRealizePnl": "-0.017540",
+            "cumMatchSize": "0.001",
+            "cumMatchValue": "97.4445",
+            "cumMatchFee": "0.017540",
+            "cumFailSize": "0",
+            "cumFailValue": "0",
+            "cumFailFee": "0",
+            "cumApprovedSize": "0",
+            "cumApprovedValue": "0",
+            "cumApprovedFee": "0",
+            "createdTime": "1734662555665",
+            "updatedTime": "1734662617992"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734666077826",
+    "responseTime": "1734666077846",
+    "traceId": "5773d3492c5913ba4f8c93071f3e426e"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Response Data Structure

GET Get Historical Orders by Client Order IDs (Batch)

GET /api/v1/private/order/getHistoryOrderByClientOrderId

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

clientOrderIdList

query

string

No

Order client order id

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564815695875932430",
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "contractId": "10000001",
+            "side": "BUY",
+            "price": "97444.5",
+            "size": "0.001",
+            "clientOrderId": "553364074986685",
+            "type": "LIMIT",
+            "timeInForce": "GOOD_TIL_CANCEL",
+            "reduceOnly": false,
+            "triggerPrice": "0",
+            "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+            "expireTime": "1736476955478",
+            "sourceKey": "",
+            "isPositionTpsl": false,
+            "isLiquidate": false,
+            "isDeleverage": false,
+            "openTpslParentOrderId": "0",
+            "isSetOpenTp": false,
+            "openTp": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isSetOpenSl": false,
+            "openSl": {
+                "side": "UNKNOWN_ORDER_SIDE",
+                "price": "",
+                "size": "",
+                "clientOrderId": "",
+                "triggerPrice": "",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "0",
+                "l2Nonce": "0",
+                "l2Value": "",
+                "l2Size": "",
+                "l2LimitFee": "",
+                "l2ExpireTime": "0",
+                "l2Signature": {
+                    "r": "",
+                    "s": "",
+                    "v": ""
+                }
+            },
+            "isWithoutMatch": false,
+            "withoutMatchFillSize": "0",
+            "withoutMatchFillValue": "0",
+            "withoutMatchPeerAccountId": "0",
+            "withoutMatchPeerOrderId": "0",
+            "maxLeverage": "50",
+            "takerFeeRate": "0.000500",
+            "makerFeeRate": "0.000180",
+            "liquidateFeeRate": "0.01",
+            "marketLimitPrice": "0",
+            "marketLimitValue": "0",
+            "l2Nonce": "2054491946",
+            "l2Value": "97.444500",
+            "l2Size": "0.001",
+            "l2LimitFee": "0.048723",
+            "l2ExpireTime": "1737254555478",
+            "l2Signature": {
+                "r": "0x009af59c2963f1650449904fde059a83fed5beb4acdd67ffa22c551c4be977de",
+                "s": "0x04940a395f2d1b39c0f2b969e47fec478fbfea9ce227bfedd07d407175dcac3e",
+                "v": ""
+            },
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "FILLED",
+            "matchSequenceId": "35196430",
+            "triggerTime": "0",
+            "triggerPriceTime": "0",
+            "triggerPriceValue": "0",
+            "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
+            "cumFillSize": "0.001",
+            "cumFillValue": "97.4445",
+            "cumFillFee": "0.017540",
+            "maxFillPrice": "97444.5",
+            "minFillPrice": "97444.5",
+            "cumLiquidateFee": "0",
+            "cumRealizePnl": "-0.017540",
+            "cumMatchSize": "0.001",
+            "cumMatchValue": "97.4445",
+            "cumMatchFee": "0.017540",
+            "cumFailSize": "0",
+            "cumFailValue": "0",
+            "cumFailFee": "0",
+            "cumApprovedSize": "0",
+            "cumApprovedValue": "0",
+            "cumApprovedFee": "0",
+            "createdTime": "1734662555665",
+            "updatedTime": "1734662617992"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734666143318",
+    "responseTime": "1734666143331",
+    "traceId": "e19f71177e8cf0c7f34d45d85064c07c"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Response Data Structure

GET Get Active Orders (Paginated)

GET /api/v1/private/order/getActiveOrderPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

size

query

string

No

Number of items to fetch. Must be greater than 0 and less than or equal to 200.

offsetData

query

string

No

Pagination offset. If not provided or empty, retrieves the first page.

filterCoinIdList

query

string

No

Filters by collateral coin IDs. If empty, fetches active orders for all collateral coin IDs.

filterContractIdList

query

string

No

Filters by contract IDs. If empty, fetches active orders for all contracts.

filterTypeList

query

string

No

Filters by order types. If empty, fetches orders of all types.

filterStatusList

query

string

No

Filters by order status. If empty, fetches orders of all statuses.

filterIsLiquidateList

query

string

No

Filters by liquidate orders. If empty, fetches all orders

filterIsDeleverageList

query

string

No

Filters by deleverage orders. If empty, fetches all orders

filterIsPositionTpslList

query

string

No

Filters by position TP/SL orders. If empty, fetches all orders

filterStartCreatedTimeInclusive

query

string

No

Filters orders created after or on this time (inclusive). If empty or 0, retrieves from the earliest.

filterEndCreatedTimeExclusive

query

string

No

Filters orders created before this time (exclusive). If empty or 0, retrieves to the latest.

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "id": "564815695875932430",
+                "userId": "543429922866069763",
+                "accountId": "543429922991899150",
+                "coinId": "1000",
+                "contractId": "10000001",
+                "side": "BUY",
+                "price": "97444.5",
+                "size": "0.001",
+                "clientOrderId": "553364074986685",
+                "type": "LIMIT",
+                "timeInForce": "GOOD_TIL_CANCEL",
+                "reduceOnly": false,
+                "triggerPrice": "0",
+                "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                "expireTime": "1736476955478",
+                "sourceKey": "",
+                "isPositionTpsl": false,
+                "isLiquidate": false,
+                "isDeleverage": false,
+                "openTpslParentOrderId": "0",
+                "isSetOpenTp": false,
+                "openTp": {
+                    "side": "UNKNOWN_ORDER_SIDE",
+                    "price": "",
+                    "size": "",
+                    "clientOrderId": "",
+                    "triggerPrice": "",
+                    "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                    "expireTime": "0",
+                    "l2Nonce": "0",
+                    "l2Value": "",
+                    "l2Size": "",
+                    "l2LimitFee": "",
+                    "l2ExpireTime": "0",
+                    "l2Signature": {
+                        "r": "",
+                        "s": "",
+                        "v": ""
+                    }
+                },
+                "isSetOpenSl": false,
+                "openSl": {
+                    "side": "UNKNOWN_ORDER_SIDE",
+                    "price": "",
+                    "size": "",
+                    "clientOrderId": "",
+                    "triggerPrice": "",
+                    "triggerPriceType": "UNKNOWN_PRICE_TYPE",
+                    "expireTime": "0",
+                    "l2Nonce": "0",
+                    "l2Value": "",
+                    "l2Size": "",
+                    "l2LimitFee": "",
+                    "l2ExpireTime": "0",
+                    "l2Signature": {
+                        "r": "",
+                        "s": "",
+                        "v": ""
+                    }
+                },
+                "isWithoutMatch": false,
+                "withoutMatchFillSize": "0",
+                "withoutMatchFillValue": "0",
+                "withoutMatchPeerAccountId": "0",
+                "withoutMatchPeerOrderId": "0",
+                "maxLeverage": "50",
+                "takerFeeRate": "0.000500",
+                "makerFeeRate": "0.000180",
+                "liquidateFeeRate": "0.01",
+                "marketLimitPrice": "0",
+                "marketLimitValue": "0",
+                "l2Nonce": "2054491946",
+                "l2Value": "97.444500",
+                "l2Size": "0.001",
+                "l2LimitFee": "0.048723",
+                "l2ExpireTime": "1737254555478",
+                "l2Signature": {
+                    "r": "0x009af59c2963f1650449904fde059a83fed5beb4acdd67ffa22c551c4be977de",
+                    "s": "0x04940a395f2d1b39c0f2b969e47fec478fbfea9ce227bfedd07d407175dcac3e",
+                    "v": ""
+                },
+                "extraType": "",
+                "extraDataJson": "",
+                "status": "OPEN",
+                "matchSequenceId": "35195888",
+                "triggerTime": "0",
+                "triggerPriceTime": "0",
+                "triggerPriceValue": "0",
+                "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
+                "cumFillSize": "0",
+                "cumFillValue": "0",
+                "cumFillFee": "0",
+                "maxFillPrice": "0",
+                "minFillPrice": "0",
+                "cumLiquidateFee": "0",
+                "cumRealizePnl": "0",
+                "cumMatchSize": "0",
+                "cumMatchValue": "0",
+                "cumMatchFee": "0",
+                "cumFailSize": "0",
+                "cumFailValue": "0",
+                "cumFailFee": "0",
+                "cumApprovedSize": "0",
+                "cumApprovedValue": "0",
+                "cumApprovedFee": "0",
+                "createdTime": "1734662555665",
+                "updatedTime": "1734662555672"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734662566830",
+    "responseTime": "1734662566836",
+    "traceId": "4a97b2e8da4933980f399581dd4a1264"
+}

Response

Status Code
Status Code Description
Description
Schema

200

default response

Data Models

schemaresultlistorderfilltransaction

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Successful response data.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemaorderfilltransaction

Name
Type
Required
Constraints
Description

id

string(int64)

false

none

Unique identifier.

userId

string(int64)

false

none

User ID.

accountId

string(int64)

false

none

Account ID.

coinId

string(int64)

false

none

Collateral coin ID.

contractId

string(int64)

false

none

Contract ID.

orderId

string(int64)

false

none

Order ID.

orderSide

string

false

none

Buy/Sell direction.

fillSize

string

false

none

Actual filled quantity.

fillValue

string

false

none

Actual filled value.

fillFee

string

false

none

Actual filled fee.

fillPrice

string

false

none

Fill price (not precise, for display purposes only).

liquidateFee

string

false

none

Liquidation fee if it's a liquidation (forced liquidation) transaction.

realizePnl

string

false

none

Realized profit and loss (only available if the fill includes closing a position).

direction

string

false

none

Actual fill direction.

isPositionTpsl

boolean

false

none

Whether this is a position take-profit/stop-loss order.

isLiquidate

boolean

false

none

Whether this is a liquidation (forced liquidation) fill.

isDeleverage

boolean

false

none

Whether this is an auto-deleverage fill.

isWithoutMatch

boolean

false

none

Whether this order was filled directly without matching.

matchSequenceId

string(int64)

false

none

Sequence ID after submitting to the matching engine.

matchIndex

integer(int32)

false

none

Index for multiple fills after submitting to the matching engine.

matchTime

string(int64)

false

none

Time after submitting to the matching engine.

matchAccountId

string(int64)

false

none

Counterparty account ID.

matchOrderId

string(int64)

false

none

Counterparty order ID.

matchFillId

string

false

none

Fill ID returned by the matching engine.

positionTransactionId

string(int64)

false

none

Associated position transaction ID.

collateralTransactionId

string(int64)

false

none

Associated collateral transaction ID.

extraType

string

false

none

Additional type for upper-layer business use.

extraDataJson

string

false

none

Additional data in JSON format. Defaults to an empty string.

censorStatus

string

false

none

Current censorship status.

censorTxId

string(int64)

false

none

Censorship processing sequence ID. Exists when censor_status is CENSOR_SUCCESS/CENSOR_FAILURE/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

censorTime

string(int64)

false

none

Censorship processing time. Exists when censor_status is CENSOR_SUCCESS/CENSOR_FAILURE/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

censorFailCode

string

false

none

Censorship failure error code. Exists when censor_status is CENSOR_FAILURE.

censorFailReason

string

false

none

Censorship failure reason. Exists when censor_status is CENSOR_FAILURE.

l2TxId

string(int64)

false

none

L2 push transaction ID. Exists when censor_status is CENSOR_SUCCESS/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED.

l2RejectTime

string(int64)

false

none

L2 rejection time. Exists when censor_status is L2_REJECT/L2_REJECT_APPROVED.

l2RejectCode

string

false

none

L2 rejection error code. Exists when censor_status is L2_REJECT/L2_REJECT_APPROVED.

l2RejectReason

string

false

none

L2 rejection reason. Exists when censor_status is L2_REJECT/L2_REJECT_APPROVED.

l2ApprovedTime

string(int64)

false

none

L2 batch verification time. Exists when status is L2_APPROVED/L2_REJECT_APPROVED.

createdTime

string(int64)

false

none

Creation time.

updatedTime

string(int64)

false

none

Update time.

Enum Values

Property
Value

orderSide

UNKNOWN_ORDER_SIDE

orderSide

BUY

orderSide

SELL

orderSide

UNRECOGNIZED

direction

UNKNOWN_LIQUIDITY_DIRECTION

direction

MAKER

direction

TAKER

direction

UNRECOGNIZED

censorStatus

UNKNOWN_TRANSACTION_STATUS

censorStatus

INIT

censorStatus

CENSOR_SUCCESS

censorStatus

CENSOR_FAILURE

censorStatus

L2_APPROVED

censorStatus

L2_REJECT

censorStatus

L2_REJECT_APPROVED

censorStatus

UNRECOGNIZED

schemaresultpagedataorderfilltransaction

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Generic paginated response.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemapagedataorderfilltransaction

Name
Type
Required
Constraints
Description

dataList

false

none

Data list.

nextPageOffsetData

string

false

none

Offset for retrieving the next page. Empty string if no more data available.

schemaresultpagedataorder

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Generic paginated response.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemapagedataorder

Name
Type
Required
Constraints
Description

dataList

false

none

Data list.

nextPageOffsetData

string

false

none

Offset for retrieving the next page. Empty string if no more data available.

schemaorder

Name
Type
Required
Constraints
Description

id

string(int64)

false

none

Order ID. Value greater than 0.

userId

string(int64)

false

none

User ID.

accountId

string(int64)

false

none

Account ID.

coinId

string(int64)

false

none

Collateral coin ID.

contractId

string(int64)

false

none

Contract ID.

side

string

false

none

Buy/Sell direction.

price

string

false

none

Order price (worst acceptable price), actual type is decimal.

size

string

false

none

Order quantity, actual type is decimal.

clientOrderId

string

false

none

Client-defined ID for idempotency checks.

type

string

false

none

Order type.

timeInForce

string

false

none

Order execution policy. Relevant when type is LIMIT/STOP_LIMIT/TAKE_PROFIT_LIMIT.

reduceOnly

boolean

false

none

Whether this is a reduce-only order.

triggerPrice

string

false

none

Trigger price. Relevant when type is STOP_LIMIT/STOP_MARKET/TAKE_PROFIT_LIMIT/TAKE_PROFIT_MARKET. If 0, the field is empty. Actual type is decimal.

triggerPriceType

string

false

none

Price type: Last price, Mark price, etc. Relevant when type is STOP_LIMIT/STOP_MARKET/TAKE_PROFIT_LIMIT/TAKE_PROFIT_MARKET.

expireTime

string(int64)

false

none

Expiration time.

sourceKey

string

false

none

Source key, UUID.

isPositionTpsl

boolean

false

none

Whether this is a position take-profit/stop-loss order.

isLiquidate

boolean

false

none

Whether this is a liquidation (forced liquidation) order.

isDeleverage

boolean

false

none

Whether this is an auto-deleverage order.

openTpslParentOrderId

string(int64)

false

none

Order ID of the opening order for a take-profit or stop-loss order.

isSetOpenTp

boolean

false

none

Whether take-profit is set for opening order.

openTp

false

none

Take-profit/stop-loss parameters for opening order.

isSetOpenSl

boolean

false

none

Whether stop-loss is set for opening order.

openSl

false

none

Take-profit/stop-loss parameters for opening order.

isWithoutMatch

boolean

false

none

Whether this order is directly filled without matching.

withoutMatchFillSize

string

false

none

Off-exchange fill quantity (valid only when is_without_match is true).

withoutMatchFillValue

string

false

none

Off-exchange fill value (valid only when is_without_match is true).

withoutMatchPeerAccountId

string(int64)

false

none

Off-exchange counterparty account ID (valid only when is_without_match is true).

withoutMatchPeerOrderId

string(int64)

false

none

Off-exchange counterparty order ID (valid only when is_without_match is true).

maxLeverage

string

false

none

Leverage used when placing the order. Actual type is decimal.

takerFeeRate

string

false

none

Taker fee rate when placing the order. Actual type is decimal.

makerFeeRate

string

false

none

Maker fee rate when placing the order. Actual type is decimal.

liquidateFeeRate

string

false

none

Liquidation fee rate when placing the order. Actual type is decimal.

marketLimitPrice

string

false

none

Limit price for submitting market orders to the matching engine (only exists for market orders, 0 otherwise). Actual type is decimal.

marketLimitValue

string

false

none

Limit value for submitting market orders to the matching engine (only exists for market orders, 0 otherwise). Actual type is decimal.

l2Nonce

string(int64)

false

none

L2 signature nonce. Takes the first 32 bits of sha256(client_order_id).

l2Value

string

false

none

L2 signature order value (the actual filled price must be equal to or better than l2_value / l2_size). May differ from price x size. Actual type is decimal.

l2Size

string

false

none

L2 signature order quantity. May differ from the size field. Actual type is decimal.

l2LimitFee

string

false

none

Maximum acceptable fee for L2 signature.

l2ExpireTime

string(int64)

false

none

L2 signature expiration time in milliseconds. The hour value should be used when generating/verifying the signature, i.e. l2_expire_time / 3600000. Note that this value must be greater or equal to expire_time + 8 * 24 * 60 * 60 * 1000 (8 days).

l2Signature

false

none

L2 signature information.

extraType

string

false

none

Additional type for upper-layer business use.

extraDataJson

string

false

none

Additional data in JSON format. Defaults to an empty string.

status

string

false

none

Order status.

matchSequenceId

string(int64)

false

none

Sequence ID handled by the matching engine.

triggerTime

string(int64)

false

none

Conditional order trigger time.

triggerPriceTime

string(int64)

false

none

Conditional order trigger price time.

triggerPriceValue

string

false

none

Conditional order trigger price value.

cancelReason

string

false

none

Order cancellation reason.

cumFillSize

string(decimal)

false

none

Cumulative filled quantity after censorship. Actual type is decimal.

cumFillValue

string(decimal)

false

none

Cumulative filled value after censorship. Actual type is decimal.

cumFillFee

string(decimal)

false

none

Cumulative filled fee after censorship. Actual type is decimal.

maxFillPrice

string(decimal)

false

none

Maximum filled price for the current order after censorship. Actual type is decimal.

minFillPrice

string(decimal)

false

none

Minimum filled price for the current order after censorship. Actual type is decimal.

cumLiquidateFee

string(decimal)

false

none

Cumulative liquidation fee after censorship. Actual type is decimal.

cumRealizePnl

string(decimal)

false

none

Cumulative realized PnL after censorship. Actual type is decimal.

cumMatchSize

string(decimal)

false

none

Cumulative matched quantity. Actual type is decimal.

cumMatchValue

string(decimal)

false

none

Cumulative matched value. Actual type is decimal.

cumMatchFee

string(decimal)

false

none

Cumulative matched fee. Actual type is decimal.

cumFailSize

string

false

none

Cumulative failed/L2 rejected quantity. Actual type is decimal.

cumFailValue

string

false

none

Cumulative failed/L2 rejected value. Actual type is decimal.

cumFailFee

string

false

none

Cumulative failed/L2 rejected fee. Actual type is decimal.

cumApprovedSize

string

false

none

Cumulative quantity approved by L2.

cumApprovedValue

string

false

none

Cumulative value approved by L2.

cumApprovedFee

string

false

none

Cumulative fee approved by L2.

createdTime

string(int64)

false

none

Creation time.

updatedTime

string(int64)

false

none

Update time.

Enum Values

Property
Value

side

UNKNOWN_ORDER_SIDE

side

BUY

side

SELL

side

UNRECOGNIZED

type

UNKNOWN_ORDER_TYPE

type

LIMIT

type

MARKET

type

STOP_LIMIT

type

STOP_MARKET

type

TAKE_PROFIT_LIMIT

type

TAKE_PROFIT_MARKET

type

UNRECOGNIZED

timeInForce

UNKNOWN_TIME_IN_FORCE

timeInForce

GOOD_TIL_CANCEL

timeInForce

FILL_OR_KILL

timeInForce

IMMEDIATE_OR_CANCEL

timeInForce

POST_ONLY

timeInForce

UNRECOGNIZED

triggerPriceType

UNKNOWN_PRICE_TYPE

triggerPriceType

ORACLE_PRICE

triggerPriceType

INDEX_PRICE

triggerPriceType

LAST_PRICE

triggerPriceType

ASK1_PRICE

triggerPriceType

BID1_PRICE

triggerPriceType

OPEN_INTEREST

triggerPriceType

UNRECOGNIZED

status

UNKNOWN_ORDER_STATUS

status

PENDING

status

OPEN

status

FILLED

status

CANCELING

status

CANCELED

status

UNTRIGGERED

status

UNRECOGNIZED

cancelReason

UNKNOWN_ORDER_CANCEL_REASON

cancelReason

USER_CANCELED

cancelReason

EXPIRE_CANCELED

cancelReason

COULD_NOT_FILL

cancelReason

REDUCE_ONLY_CANCELED

cancelReason

LIQUIDATE_CANCELED

cancelReason

MARGIN_NOT_ENOUGH

cancelReason

SYSTEM_LIMIT_EVICTED

cancelReason

ADMIN_CANCELED

cancelReason

INTERNAL_FAILED

cancelReason

UNRECOGNIZED

schemal2signature

Name
Type
Required
Constraints
Description

r

string

false

none

Big integer as a hex string.

s

string

false

none

Big integer as a hex string.

v

string

false

none

Big integer as a hex string.

schemaopentpsl

Name
Type
Required
Constraints
Description

side

string

false

none

Buy/sell direction. This field is required.

price

string

false

none

Order price (worst acceptable price), actual type is decimal. Required, enter 0 for market orders.

size

string

false

none

Order quantity, actual type is decimal. Required.

clientOrderId

string

false

none

Client-defined ID for signature and idempotency checks. This field is required.

triggerPrice

string

false

none

Trigger price. This field is required.

triggerPriceType

string

false

none

Price type: Last price, Mark price, etc. This field is required.

expireTime

string(int64)

false

none

Expiration time.

l2Nonce

string(int64)

false

none

L2 signature nonce. Takes the first 32 bits of sha256(client_order_id).

l2Value

string

false

none

L2 signature order value (the actual filled price must be equal to or better than l2_value / l2_price). May differ from price x size. Actual type is decimal.

l2Size

string

false

none

L2 signature order quantity. May differ from the size field. Actual type is decimal.

l2LimitFee

string

false

none

Maximum acceptable fee for L2 signature.

l2ExpireTime

string(int64)

false

none

L2 signature expiration time in unix hour. Must be at least 10 hours after expire_time.

l2Signature

false

none

L2 signature information.

Enum Values

Property
Value

side

UNKNOWN_ORDER_SIDE

side

BUY

side

SELL

side

UNRECOGNIZED

triggerPriceType

UNKNOWN_PRICE_TYPE

triggerPriceType

ORACLE_PRICE

triggerPriceType

INDEX_PRICE

triggerPriceType

LAST_PRICE

triggerPriceType

ASK1_PRICE

triggerPriceType

BID1_PRICE

triggerPriceType

OPEN_INTEREST

triggerPriceType

UNRECOGNIZED

schemaresultlistorder

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Successful response data.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemacancelallorderparam

Name
Type
Required
Constraints
Description

accountId

string(int64)

false

none

Account ID.

filterCoinIdList

[string]

false

none

Filter to cancel active orders for specific collateral coin IDs. If empty, cancels all.

filterContractIdList

[string]

false

none

Filter to cancel active orders for specific contract IDs. If empty, cancels all.

filterOrderTypeList

[string]

false

none

Filter to cancel orders of specific types. If empty, cancels all types.

filterOrderStatusList

[string]

false

none

Filter to cancel orders of specific statuses. If empty, cancels all statuses.

filterIsPositionTpsl

[boolean]

false

none

Filter to cancel only corresponding position take-profit/stop-loss orders. If empty, cancels all contract orders.

<### schemacancelorderbyclientorderid

Name
Type
Required
Constraints
Description

cancelResultMap

object

false

none

None

Enum Values

Property
Value

additionalProperties

UNKNOWN_ORDER_CANCEL_RESULT

additionalProperties

SUCCESS

additionalProperties

SUCCESS_ORDER_CANCELING

additionalProperties

SUCCESS_ORDER_CANCELED

additionalProperties

FAILED_ORDER_NOT_FOUND

additionalProperties

FAILED_ORDER_FILLED

additionalProperties

FAILED_ORDER_UNKNOWN_STATUS

additionalProperties

UNRECOGNIZED

schemaresultcancelorder

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Response for canceling orders.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemacancelorder

Name
Type
Required
Constraints
Description

cancelResultMap

object

false

none

None

Enum Values

Property
Value

additionalProperties

UNKNOWN_ORDER_CANCEL_RESULT

additionalProperties

SUCCESS

additionalProperties

SUCCESS_ORDER_CANCELING

additionalProperties

SUCCESS_ORDER_CANCELED

additionalProperties

FAILED_ORDER_NOT_FOUND

additionalProperties

FAILED_ORDER_FILLED

additionalProperties

FAILED_ORDER_UNKNOWN_STATUS

additionalProperties

UNRECOGNIZED

schemacancelorderbyidparam

Name
Type
Required
Constraints
Description

accountId

string(int64)

false

none

Account ID.

orderIdList

[string]

true

none

Order ID.

schemaresultcreateorder

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Response for creating orders.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemacreateorder

Name
Type
Required
Constraints
Description

orderId

string(int64)

false

none

Order ID.

schemacreateorderparam

Name
Type
Required
Constraints
Description

accountId

string(int64)

false

none

Account ID.

contractId

string(int64)

false

none

Contract ID.

side

string

false

none

Buy/sell direction. This field is required.

size

string

false

none

Order quantity. Actual type is decimal. This field is required.

price

string

false

none

Order price (worst acceptable price). Actual type is decimal. This field is required, enter 0 for market orders.

clientOrderId

string

false

none

Client-defined ID for idempotency checks. This field is required.

type

string

false

none

Order type. This field is required.

timeInForce

string

false

none

Order execution policy. Relevant when type is LIMIT/STOP_LIMIT/TAKE_PROFIT_LIMIT. This field is required, and should be IMMEDIATE_OR_CANCEL for market orders.

reduceOnly

boolean

false

none

Whether this is a reduce-only order. This field is required.

triggerPrice

string

false

none

Trigger price. Relevant when type is STOP_LIMIT/STOP_MARKET/TAKE_PROFIT_LIMIT/TAKE_PROFIT_MARKET. If 0, the field is empty. Actual type is decimal. Required for conditional orders.

triggerPriceType

string

false

none

Price type: Last price, Mark price, etc. Relevant when the order is conditional. Required for conditional orders.

expireTime

string(int64)

false

none

Expiration time.

sourceKey

string

false

none

Source key, UUID.

isPositionTpsl

boolean

false

none

Whether this is a position take-profit/stop-loss order. This field is required, defaults to false.

openTpslParentOrderId

string(int64)

false

none

Order ID of the opening order for a take-profit or stop-loss order.

isSetOpenTp

boolean

false

none

Whether to set take-profit for the opening order. This field is required.

openTp

false

none

Take-profit/stop-loss parameters for the opening order.

openSl

false

none

Take-profit/stop-loss parameters for the opening order.

l2Nonce

string(int64)

false

none

L2 signature nonce. Takes the first 32 bits of sha256(client_order_id).

l2Value

string

false

none

L2 signature order value (the actual filled price must be equal to or better than l2_value / l2_price). May differ from price x size. Actual type is decimal.

l2Size

string

false

none

L2 signature order quantity. May differ from the size field. Actual type is decimal.

l2LimitFee

string

false

none

Maximum acceptable fee for L2 signature.

l2ExpireTime

string(int64)

false

none

L2 signature expiration time in milliseconds. The hour value should be used when generating/verifying the signature, i.e. l2_expire_time / 3600000. Note that this value must be greater or equal to expire_time + 8 * 24 * 60 * 60 * 1000 (8 days).

l2Signature

string

false

none

L2 signature.

extraType

string

false

none

Additional type for upper-layer business use.

extraDataJson

string

false

none

Additional data in JSON format. Defaults to an empty string.

Enum Values

Property
Value

side

UNKNOWN_ORDER_SIDE

side

BUY

side

SELL

side

UNRECOGNIZED

type

UNKNOWN_ORDER_TYPE

type

LIMIT

type

MARKET

type

STOP_LIMIT

type

STOP_MARKET

type

TAKE_PROFIT_LIMIT

type

TAKE_PROFIT_MARKET

type

UNRECOGNIZED

timeInForce

UNKNOWN_TIME_IN_FORCE

timeInForce

GOOD_TIL_CANCEL

timeInForce

FILL_OR_KILL

timeInForce

IMMEDIATE_OR_CANCEL

timeInForce

POST_ONLY

timeInForce

UNRECOGNIZED

triggerPriceType

UNKNOWN_PRICE_TYPE

triggerPriceType

ORACLE_PRICE

triggerPriceType

INDEX_PRICE

triggerPriceType

LAST_PRICE

triggerPriceType

ASK1_PRICE

triggerPriceType

BID1_PRICE

triggerPriceType

OPEN_INTEREST

triggerPriceType

UNRECOGNIZED

schemaopentpslparam

Name
Type
Required
Constraints
Description

side

string

false

none

Buy/sell direction. This field is required.

price

string

false

none

Order price (worst acceptable price). Actual type is decimal. This field is required, enter 0 for market orders.

size

string

false

none

Order quantity. Actual type is decimal. This field is required.

clientOrderId

string

false

none

Client-defined ID for signature and idempotency checks. This field is required.

triggerPrice

string

false

none

Trigger price. This field is required.

triggerPriceType

string

false

none

Price type: Last price, Mark price, etc. This field is required.

expireTime

string(int64)

false

none

Expiration time.

l2Nonce

string(int64)

false

none

L2 signature nonce. Takes the first 32 bits of sha256(client_order_id).

l2Value

string

false

none

L2 signature order value (the actual filled price must be equal to or better than l2_value / l2_price). May differ from price x size. Actual type is decimal.

l2Size

string

false

none

L2 signature order quantity. May differ from the size field. Actual type is decimal.

l2LimitFee

string

false

none

Maximum acceptable fee for L2 signature.

l2ExpireTime

string

false

none

L2 signature expiration time in unix hour. Must be at least 10 hours after expire_time.

l2Signature

string

false

none

L2 signature.

Enum Values

Property
Value

side

UNKNOWN_ORDER_SIDE

side

BUY

side

SELL

side

UNRECOGNIZED

triggerPriceType

UNKNOWN_PRICE_TYPE

triggerPriceType

ORACLE_PRICE

triggerPriceType

INDEX_PRICE

triggerPriceType

LAST_PRICE

triggerPriceType

ASK1_PRICE

triggerPriceType

BID1_PRICE

triggerPriceType

OPEN_INTEREST

triggerPriceType

UNRECOGNIZED

schemaresultgetmaxcreateordersize

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise failure.

data

false

none

Response for getting the maximum order size.

errorParam

object

false

none

Parameter information in error messages.

requestTime

string(timestamp)

false

none

Server request receive time.

responseTime

string(timestamp)

false

none

Server response return time.

traceId

string

false

none

Call trace ID.

schemagetmaxcreateordersize

Name
Type
Required
Constraints
Description

maxBuySize

string(decimal)

false

none

Maximum buy size.

maxSellSize

string(decimal)

false

none

Maximum sell size.

ask1Price

string(decimal)

false

none

Best ask price.

bid1Price

string(decimal)

false

none

Best bid price.

schemagetmaxcreateordersizeparam

Name
Type
Required
Constraints
Description

accountId

string(int64)

false

none

Account ID.

contractId

string(int64)

false

none

Contract ID.

price

string

false

none

Order price.

Last updated

+ + Transfer API | edgeX Docs

Transfer API

TransferPrivateApi

POST Create Transfer Out Order

POST /api/v1/private/transfer/createTransferOut

Body Request Parameters

{
+    "accountId": "543429922991899150",
+    "coinId": "1000",
+    "amount": "1.000000",
+    "receiverAccountId": "551109015904453258",
+    "receiverL2Key": "0x03eec711e360695bb44b1170057a25340303c1f16893a8def7450e44294405a8",
+    "clientTransferId": "3877531064364166",
+    "transferReason": "USER_TRANSFER",
+    "l2Nonce": "2280110103",
+    "l2ExpireTime": "1735873200000",
+    "l2Signature": "0141279ec45ce1ea37b11cfa4683cfab8443bcbf8da3f066cef3e437862573f9034efe12eee1be3fc715c7b511f69e3ba32ec67a9ac89538fbb73de46fefc5e5",
+    "extraType": "",
+    "extraDataJson": ""
+}

Request Parameters

Name
Location
Type
Required
Description

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "transferOutId": "564819036077031694"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734663351997",
+    "responseTime": "1734663352035",
+    "traceId": "33728335fc663ba9230e61d4f4b924df"
+}

Response Codes

Status Code
Description
Notes
Schema

200

default response

GET Get Transfer Out Orders by ID

GET /api/v1/private/transfer/getTransferOutById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

transferOutIdList

query

string

No

Transfer out ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564819036077031694",
+            "userId": "543429922866069763",
+            "accountId": "543429922991899150",
+            "coinId": "1000",
+            "amount": "1.000000",
+            "receiverAccountId": "551109015904453258",
+            "receiverL2Key": "0x3eec711e360695bb44b1170057a25340303c1f16893a8def7450e44294405a8",
+            "clientTransferId": "3877531064364166",
+            "isConditionTransfer": false,
+            "conditionFactRegistryAddress": "",
+            "conditionFactErc20Address": "",
+            "conditionFactAmount": "",
+            "conditionFact": "",
+            "transferReason": "USER_TRANSFER",
+            "l2Nonce": "2280110103",
+            "l2ExpireTime": "1735873200000",
+            "l2Signature": {
+                "r": "0x0141279ec45ce1ea37b11cfa4683cfab8443bcbf8da3f066cef3e437862573f9",
+                "s": "0x034efe12eee1be3fc715c7b511f69e3ba32ec67a9ac89538fbb73de46fefc5e5",
+                "v": ""
+            },
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "SUCCESS_CENSOR_SUCCESS",
+            "receiverTransferInId": "564819036173500554",
+            "collateralTransactionId": "564819036223832334",
+            "censorTxId": "893179",
+            "censorTime": "1734663352062",
+            "censorFailCode": "",
+            "censorFailReason": "",
+            "l2TxId": "1084730",
+            "l2RejectTime": "0",
+            "l2RejectCode": "",
+            "l2RejectReason": "",
+            "l2ApprovedTime": "0",
+            "createdTime": "1734663352031",
+            "updatedTime": "1734663352066"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734663607244",
+    "responseTime": "1734663607271",
+    "traceId": "39366eb1153313ba1415851a08762265"
+}

Response Codes

Status Code
Description
Notes
Schema

200

default response

GET Get Available Withdrawal Amount

GET /api/v1/private/transfer/getTransferOutAvailableAmount

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

coinId

query

string

No

Coin ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "availableAmount": "10.964371"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734663286946",
+    "responseTime": "1734663286951",
+    "traceId": "957f0396a8e6059b027b99d232f8b113"
+}

Response Codes

Status Code
Description
Notes
Schema

200

default response

GET Get Transfer In Orders by ID

GET /api/v1/private/transfer/getTransferInById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

transferInIdList

query

string

No

Transfer In ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "id": "564819036173500554",
+            "userId": "543429922866069763",
+            "accountId": "551109015904453258",
+            "coinId": "1000",
+            "amount": "1.000000",
+            "senderAccountId": "543429922991899150",
+            "senderL2Key": "0x5580341e2c99823a0a35356b8ac84e372dd38fd1f4b50f607b931ec8038c211",
+            "senderTransferOutId": "564819036077031694",
+            "clientTransferId": "543429922991899150:3877531064364166",
+            "isConditionTransfer": false,
+            "conditionFactRegistryAddress": "",
+            "conditionFactErc20Address": "",
+            "conditionFactAmount": "",
+            "conditionFact": "",
+            "transferReason": "USER_TRANSFER",
+            "extraType": "",
+            "extraDataJson": "",
+            "status": "SUCCESS_CENSOR_SUCCESS",
+            "collateralTransactionId": "564819036219637898",
+            "censorTxId": "893179",
+            "censorTime": "1734663352062",
+            "censorFailCode": "",
+            "censorFailReason": "",
+            "l2TxId": "1084730",
+            "l2RejectTime": "0",
+            "l2RejectCode": "",
+            "l2RejectReason": "",
+            "l2ApprovedTime": "0",
+            "createdTime": "1734663352054",
+            "updatedTime": "1734663352065"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734663945432",
+    "responseTime": "1734663945452",
+    "traceId": "eb4cfe0a20f14b62b4fdbbd046255171"
+}

Response Codes

Status Code
Description
Notes
Schema

200

default response

Data Models

schemaresultpagedatatransferin

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, other values indicate failure.

data

false

none

Generic paginated response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request receiving timestamp

responseTime

string(timestamp)

false

none

Server response returning timestamp

traceId

string

false

none

Call trace ID

schemapagedatatransferin

Name
Type
Required
Constraints
Description

dataList

false

none

Data list

nextPageOffsetData

string

false

none

Offset to retrieve the next page. If no next page data, the value will be an empty string

schematransferin

Name
Type
Required
Constraints
Description

id

string(int64)

false

none

Transfer In order ID

userId

string(int64)

false

none

User ID

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Coin ID

amount

string

false

none

Transfer amount

senderAccountId

string(int64)

false

none

Sender Account ID

senderL2Key

string

false

none

Sender account L2 key. bigint for hex str

senderTransferOutId

string(int64)

false

none

Sender transfer out order ID

clientTransferId

string

false

none

Client defined ID. Used for idempotent checks and signature generation nonce

isConditionTransfer

boolean

false

none

Whether it is a conditional transfer

conditionFactRegistryAddress

string

false

none

Address of condition fact registry contract. Required when is_condition_transfer=true

conditionFactErc20Address

string

false

none

ERC20 address used to generate the condition fact. Required when is_conditional_transfer=true

conditionFactAmount

string

false

none

Amount used to generate condition fact. Required when is_conditional_transfer=true.

conditionFact

string

false

none

The conditional transfer fact. Required when is_condition_transfer=true

transferReason

string

false

none

Transfer reason

extraType

string

false

none

Additional type. Used by upper layer business

extraDataJson

string

false

none

Additional data in JSON format. Defaults to empty string

status

string

false

none

Transfer status

collateralTransactionId

string(int64)

false

none

ID of related collateral detail. Exists when status=SUCCESS_XXX/FAILED_L2_REJECT/FAILED_L2_REJECT_APPROVED

censorTxId

string(int64)

false

none

Censor processing sequence. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECT/FAILED_L2_REJECT_APPROVED

censorTime

string(int64)

false

none

Censor processing time. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECT/FAILED_L2_REJECT_APPROVED

censorFailCode

string

false

none

Censor failure error code. Exists when status=FAILED_CENSOR_FAILURE

censorFailReason

string

false

none

Censor failure reason. Exists when status=FAILED_CENSOR_FAILURE

l2TxId

string(int64)

false

none

L2 push transaction ID. Exists when censor_status=CENSOR_SUCCESS/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED

l2RejectTime

string(int64)

false

none

L2 rejection time. Exists when censor_status=L2_REJECT/L2_REJECT_APPROVED

l2RejectCode

string

false

none

L2 rejection error code. Exists when censor_status=L2_REJECT/L2_REJECT_APPROVED

l2RejectReason

string

false

none

L2 rejection reason. Exists when censor_status=L2_REJECT/L2_REJECT_APPROVED

l2ApprovedTime

string(int64)

false

none

L2 batch verification time. Exists when status=L2_APPROVED/L2_REJECT_APPROVED

createdTime

string(int64)

false

none

Creation time

updatedTime

string(int64)

false

none

Update time

Enum Values

Property
Value

transferReason

UNKNOWN_TRANSFER_REASON

transferReason

USER_TRANSFER

transferReason

FAST_WITHDRAW

transferReason

CROSS_DEPOSIT

transferReason

CROSS_WITHDRAW

transferReason

UNRECOGNIZED

status

UNKNOWN_TRANSFER_STATUS

status

PENDING_CHECKING

status

PENDING_CENSORING

status

SUCCESS_CENSOR_SUCCESS

status

SUCCESS_L2_APPROVED

status

FAILED_CHECK_INVALID

status

FAILED_CENSOR_FAILURE

status

FAILED_L2_REJECT

status

FAILED_L2_REJECT_APPROVED

status

UNRECOGNIZED

schemaresultpagedatatransferout

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, other values indicate failure.

data

false

none

Generic paginated response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request receiving timestamp

responseTime

string(timestamp)

false

none

Server response returning timestamp

traceId

string

false

none

Call trace ID

schemapagedatatransferout

Name
Type
Required
Constraints
Description

dataList

false

none

Data list

nextPageOffsetData

string

false

none

Offset to retrieve the next page. If no next page data, the value will be an empty string

schematransferout

Name
Type
Required
Constraints
Description

id

string(int64)

false

none

Transfer out order ID

userId

string(int64)

false

none

User ID

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Coin ID

amount

string

false

none

Transfer amount

receiverAccountId

string(int64)

false

none

Receiver Account ID

receiverL2Key

string

false

none

Receiver account L2 key. bigint for hex str

clientTransferId

string

false

none

Client defined ID. Used for idempotent checks and signature generation nonce

isConditionTransfer

boolean

false

none

Whether it is a conditional transfer

conditionFactRegistryAddress

string

false

none

Address of condition fact registry contract. Required when is_conditional_transfer=true

conditionFactErc20Address

string

false

none

ERC20 address used to generate the condition fact. Required when is_conditional_transfer=true

conditionFactAmount

string

false

none

Amount used to generate condition fact. Required when is_conditional_transfer=true.

conditionFact

string

false

none

The conditional transfer fact. Required when is_conditional_transfer=true

transferReason

string

false

none

Transfer reason

l2Nonce

string(int64)

false

none

L2 signature nonce. Take the first 32 bits of sha256(client_transfer_id)

l2ExpireTime

string(int64)

false

none

L2 signature expiration time in milliseconds. When generating/verifying the signature, the hour should be used: l2_expire_time / 3600000

l2Signature

false

none

L2 signature information

extraType

string

false

none

Additional type. Used by upper layer business

extraDataJson

string

false

none

Additional data in JSON format. Defaults to empty string

status

string

false

none

Transfer status

receiverTransferInId

string(int64)

false

none

ID of receiver transfer in order.

collateralTransactionId

string(int64)

false

none

ID of related collateral detail. Exists when status=SUCCESS_XXX/FAILED_L2_REJECT/FAILED_L2_REJECT_APPROVED

censorTxId

string(int64)

false

none

Censor processing sequence. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECT/FAILED_L2_REJECT_APPROVED

censorTime

string(int64)

false

none

Censor processing time. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECT/FAILED_L2_REJECT_APPROVED

censorFailCode

string

false

none

Censor failure error code. Exists when status=FAILED_CENSOR_FAILURE

censorFailReason

string

false

none

Censor failure reason. Exists when status=FAILED_CENSOR_FAILURE

l2TxId

string(int64)

false

none

L2 push transaction ID. Exists when censor_status=CENSOR_SUCCESS/L2_APPROVED/L2_REJECT/L2_REJECT_APPROVED

l2RejectTime

string(int64)

false

none

L2 rejection time. Exists when censor_status=L2_REJECT/L2_REJECT_APPROVED

l2RejectCode

string

false

none

L2 rejection error code. Exists when censor_status=L2_REJECT/L2_REJECT_APPROVED

l2RejectReason

string

false

none

L2 rejection reason. Exists when censor_status=L2_REJECT/L2_REJECT_APPROVED

l2ApprovedTime

string(int64)

false

none

L2 batch verification time. Exists when status=L2_APPROVED/L2_REJECT_APPROVED

createdTime

string(int64)

false

none

Creation time

updatedTime

string(int64)

false

none

Update time

Enum Values

Property
Value

transferReason

UNKNOWN_TRANSFER_REASON

transferReason

USER_TRANSFER

transferReason

FAST_WITHDRAW

transferReason

CROSS_DEPOSIT

transferReason

CROSS_WITHDRAW

transferReason

UNRECOGNIZED

status

UNKNOWN_TRANSFER_STATUS

status

PENDING_CHECKING

status

PENDING_CENSORING

status

SUCCESS_CENSOR_SUCCESS

status

SUCCESS_L2_APPROVED

status

FAILED_CHECK_INVALID

status

FAILED_CENSOR_FAILURE

status

FAILED_L2_REJECT

status

FAILED_L2_REJECT_APPROVED

status

UNRECOGNIZED

schemal2signature

Name
Type
Required
Constraints
Description

r

string

false

none

bigint for hex str

s

string

false

none

bigint for hex str

v

string

false

none

bigint for hex str

schemaresultlisttransferin

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, other values indicate failure.

data

false

none

Correct response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request receiving timestamp

responseTime

string(timestamp)

false

none

Server response returning timestamp

traceId

string

false

none

Call trace ID

schemaresultgettransferoutavailableamount

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, other values indicate failure.

data

false

none

Get Transfer Available Amount - Response

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request receiving timestamp

responseTime

string(timestamp)

false

none

Server response returning timestamp

traceId

string

false

none

Call trace ID

schemagettransferavailableamount

Name
Type
Required
Constraints
Description

availableAmount

string(decimal)

false

none

Available amount

schemaresultlisttransferout

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, other values indicate failure.

data

false

none

Correct response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request receiving timestamp

responseTime

string(timestamp)

false

none

Server response returning timestamp

traceId

string

false

none

Call trace ID

schemaresultcreatetransferout

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, other values indicate failure.

data

false

none

Create Transfer Out Order - Response

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request receiving timestamp

responseTime

string(timestamp)

false

none

Server response returning timestamp

traceId

string

false

none

Call trace ID

schemacreatetransferout

Name
Type
Required
Constraints
Description

transferOutId

string(int64)

false

none

Transfer out order ID

schemacreatetransferoutparam

Name
Type
Required
Constraints
Description

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Asset ID

amount

string

false

none

Transfer amount

receiverAccountId

string

false

none

Receiver account ID

receiverL2Key

string

false

none

Receiver account L2 key. bigint for hex str

clientTransferId

string

false

none

Client defined ID. Used for idempotent checks and signature generation nonce

transferReason

string

false

none

Transfer reason

l2Nonce

string(int64)

false

none

L2 signature nonce. Take the first 32 bits of sha256(client_withdraw_id)

l2ExpireTime

string(int64)

false

none

L2 signature expiration time in milliseconds. When generating/verifying the signature, the hour should be used: l2_expire_time / 3600000

l2Signature

string

false

none

L2 signature

extraType

string

false

none

Additional type. Used by upper layer business

extraDataJson

string

false

none

Additional data in JSON format. Defaults to empty string

Enum Values

Property
Value

transferReason

UNKNOWN_TRANSFER_REASON

transferReason

USER_TRANSFER

transferReason

FAST_WITHDRAW

transferReason

CROSS_DEPOSIT

transferReason

CROSS_WITHDRAW

transferReason

UNRECOGNIZED

Last updated

+ + Asset API | edgeX Docs

Asset API

AssetsPrivateApi

POST Create Normal Withdrawal Order

POST /api/v1/private/assets/createNormalWithdraw

Body Request Parameters

{
+    "accountId": "551109015904453258",
+    "coinId": "1000",
+    "amount": "1.000000",
+    "ethAddress": "0x1fB51aa234287C3CA1F957eA9AD0E148Bb814b7A",
+    "clientWithdrawId": "745410645654877",
+    "expireTime": "1735887600000",
+    "l2Signature": "007bf80407c6a7bb14f5ca3b848a5d908627993f23b073c902e359a6fa4a6a92040cea4c98e25e35ad1d8cc4e18758c463c45bf451299ce55aa49abbdb916d03"
+}

Request Parameters

Name
Location
Type
Required
Description

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "withdrawId": "1054639949233524736"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734674558154",
+    "responseTime": "1734674558171",
+    "traceId": "9e3bfe2b9e1ef82583cb96f36e43e537"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

POST Create Cross-Chain Withdrawal Order

POST /api/v1/private/assets/createCrossWithdraw

Body Request Parameters

{
+  "accountId": "string",
+  "coinId": "string",
+  "amount": "string",
+  "ethAddress": "string",
+  "erc20Address": "string",
+  "lpAccountId": "string",
+  "clientCrossWithdrawId": "string",
+  "expireTime": "string",
+  "l2Signature": "string",
+  "fee": "string",
+  "chainId": "string",
+  "mpcAddress": "string",
+  "mpcSignature": "string",
+  "mpcSignTime": "string"
+}

Request Parameters

Name
Location
Type
Required
Description

Response Example

200 Response

{
+  "code": "string",
+  "msg": "string",
+  "requestTime": "string",
+  "responseTime": "string"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

GET User's Normal Withdrawable Amount

GET /api/v1/private/assets/getNormalWithdrawableAmount

Request Parameters

Name
Location
Type
Required
Description

address

query

string

Yes

User address

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "amount": "0"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734674463329",
+    "responseTime": "1734674464181",
+    "traceId": "6e8a3b8326f00683cf73f701f3edcfb6"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

GET Get Normal Withdrawal Orders by Account ID and Withdrawal ID

GET /api/v1/private/assets/getNormalWithdrawById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

normalWithdrawIdList

query

string

No

Withdrawal ID

Response Example

200 Response

{
+  "code": "string",
+  "msg": "string",
+  "requestTime": "string",
+  "responseTime": "string"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

GET Get Information Required for Cross-Chain Withdrawal Signature

GET /api/v1/private/assets/getCrossWithdrawSignInfo

Request Parameters

Name
Location
Type
Required
Description

chainId

query

string

No

Chain ID

amount

query

string

No

Withdrawal amount

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "lpAccountId": "542076087396467085",
+        "crossWithdrawL2Key": "0x03bf794b4433e0a8b353da361bb7284c670914d27ed04698e6abed0bf1198028",
+        "crossWithdrawMaxAmount": "48799.686154",
+        "fee": "2"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734674557578",
+    "responseTime": "1734674557997",
+    "traceId": "3aa3d5c94c7bc9aef69f590e188058ef"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

GET Get Cross-Chain Withdrawal Orders by Account ID and Withdrawal ID

GET /api/v1/private/assets/getCrossWithdrawById

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

crossWithdrawIdList

query

string

No

Withdrawal ID

Response Example

200 Response

{
+  "code": "string",
+  "msg": "string",
+  "requestTime": "string",
+  "responseTime": "string"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

GET Aggregate Query of All Deposit and Withdrawal Order Records

GET /api/v1/private/assets/getAllOrdersPage

Request Parameters

Name
Location
Type
Required
Description

accountId

query

string

No

Account ID

startTime

query

string

No

Start time, Unix time in seconds

endTime

query

string

No

End time, Unix time in seconds

chainId

query

string

No

Chain ID

typeList

query

string

No

Order type list

size

query

string

No

Number of items per page. Must be > 0 and <= 100.

offsetData

query

string

No

Offset for page retrieval. If not provided, returns first page

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "orderId": "1054639949233524736",
+                "time": "1734674558",
+                "type": "ORDER_TYPE_NORMAL_WITHDRAW",
+                "status": 3,
+                "amount": "1",
+                "fee": "",
+                "txId": "",
+                "chain": "Sepolia - Testnet",
+                "address": "0x1fB51aa234287C3CA1F957eA9AD0E148Bb814b7A",
+                "coin": "USDT",
+                "chainId": "11155111",
+                "transferSenderAccountId": "0",
+                "transferReceiverAccountId": "0"
+            }
+        ],
+        "nextPageOffsetData": "b3fa59aa-8b42-49a3-9729-d7e89d8d9c8d"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734675194541",
+    "responseTime": "1734675194553",
+    "traceId": "d6f1fe9e521e306a49f30158257a07a2"
+}

Response

Status Code
Status Code Description
Description
Data Model

200

Default response

Data Models

schemaresultpagedatassetorder

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

Returns "SUCCESS" for success, otherwise it's a failure.

data

false

none

Generic paginated return

errorParam

object

false

none

Error parameter information

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemapagedataassetorder

Name
Type
Required
Constraints
Description
Notes

dataList

false

none

List of data

nextPageOffsetData

string

false

none

Offset for next page

Empty string when there are no further pages.

schemaassetorder

Name
Type
Required
Constraints
Description
Notes

orderId

string(int64)

false

none

Order ID

time

string(int64)

false

none

Order creation time

type

string

false

none

Order type

status

integer(int32)

false

none

Order status

amount

string

false

none

Order amount

fee

string

false

none

Order fee

txId

string

false

none

Chain tx_id

chain

string

false

none

Chain

address

string

false

none

Address

coin

string

false

none

Coin

chainId

string

false

none

Chain ID

transferSenderAccountId

string

false

none

Transfer out account ID

transferReceiverAccountId

string

false

none

Transfer in account ID

Enum Values

Property
Value

type

UNKNOWN_ORDER_TYPE

type

ORDER_TYPE_NORMAL_DEPOSIT

type

ORDER_TYPE_CROSS_DEPOSIT

type

ORDER_TYPE_NORMAL_WITHDRAW

type

ORDER_TYPE_CROSS_WITHDRAW

type

ORDER_TYPE_FAST_WITHDRAW

type

ORDER_TYPE_TRANSFER_IN

type

ORDER_TYPE_TRANSFER_OUT

type

UNRECOGNIZED

schemaresultlistcrosswithdraw

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

data

false

none

Correct response data

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemacrosswithdraw

Name
Type
Required
Constraints
Description
Notes

id

string(int64)

false

none

Withdrawal order ID

userId

string(int64)

false

none

User ID

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Collateral coin ID

amount

string

false

none

Withdrawal amount

ethAddress

string

false

none

ETH address for withdrawal, may differ from the account's ETH address.

erc20Address

string

false

none

L1 ERC20 contract address for the withdrawn asset

lpAccountId

string(int64)

false

none

LP account ID for L2 receiving user transfers

lpAccountL2Key

string(int64)

false

none

L2 key for the receiving account

clientCrossWithdrawId

string

false

none

Client-defined ID for idempotent checks

fee

string

false

none

Transaction fee

chainId

string

false

none

Chain ID for withdrawal

l2Nonce

string(int64)

false

none

L2 signature nonce. First 32 bits of sha256(client_withdraw_id)

l2ExpireTime

string(int64)

false

none

L2 signature expiration time. Unix time in hours, must be at least 24 hours after order creation.

l2Signature

false

none

L2 signature information

extraType

string

false

none

Additional type for upper-layer business usage

extraDataJson

string

false

none

Extra data, JSON format, defaults to empty string.

status

string

false

none

Normal withdrawal order status

collateralTransactionId

string

false

none

Related collateral detail ID. Exists when status=SUCCESS_XXX/FAILED_L2_REJECTED

censorTxId

string(int64)

false

none

Censorship processing sequence number. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECTED

censorTime

string(int64)

false

none

Censorship processing time. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECTED

censorFailCode

string

false

none

Censorship failure error code. Exists when status=FAILED_CENSOR_FAILURE

censorFailReason

string

false

none

Censorship failure reason. Exists when status=FAILED_CENSOR_FAILURE

l2TxId

string(int64)

false

none

L2 transaction ID. Exists when status=SUCCESS_XXX/FAILED_CENSOR_FAILURE/FAILED_L2_REJECTED

l2HandleTime

string(int64)

false

none

L2 processing time. Exists when status=SUCCESS_L1_CONFIRMING/SUCCESS_L1_WITHDRAWING/SUCCESS_L1_COMPLETED/FAILED_L2_REJECTED

l2RejectCode

string

false

none

L2 reject error code. Exists when status=FAILED_L2_REJECTED

l2RejectReason

string

false

none

L2 reject reason. Exists when status=FAILED_L2_REJECTED

l1ConfirmedTx

false

none

L1 transaction information

l1ConfirmedTime

string(int64)

false

none

L1 transaction confirmation time

l1CompletedTx

false

none

L1 transaction information

l1CompletedEthAddress

string

false

none

L1 withdrawal completion ETH address

l1CompletedTime

string(int64)

false

none

L1 withdrawal completion time

l1RejectedReasonCode

string

false

none

L1 rejection reason code

l1RejectedReasonMsg

string

false

none

L1 rejection reason message

riskSignature

false

none

L2 signature information

transferOutId

string(int64)

false

none

Transfer out order ID

createdTime

string(int64)

false

none

Creation time

updatedTime

string(int64)

false

none

Update time

Enum Values

Property
Value

status

CROSS_WITHDRAW_UNKNOWN

status

CROSS_WITHDRAW_PENDING_RISK_CHECKING

status

CROSS_WITHDRAW_PENDING_CHECKING

status

CROSS_WITHDRAW_SUCCESS_SUBMIT_CENSOR

status

CROSS_WITHDRAW_PENDING_CENSOR_CHECKING_ACCOUNT

status

CROSS_WITHDRAW_PENDING_CENSORING

status

CROSS_WITHDRAW_PENDING_L2_APPROVING

status

CROSS_WITHDRAW_PENDING_L1_SUBMIT

status

CROSS_WITHDRAW_PENDING_L1_CONFIRMING

status

CROSS_WITHDRAW_SUCCESS

status

CROSS_WITHDRAW_FAILED_RISK_CHECK_FAILURE

status

CROSS_WITHDRAW_FAILED_TRANSFER_REJECTED

status

CROSS_WITHDRAW_FAILED_CENSOR_CHECKING_ACCOUNT_REJECTED

status

CROSS_WITHDRAW_FAILED_CENSORING

status

CROSS_WITHDRAW_FAILED_L2_REJECTED

status

CROSS_WITHDRAW_FAILED_L1_SUBMIT_REJECTED

status

CROSS_WITHDRAW_FAILED_L1_REJECTED

status

CROSS_WITHDRAW_FAILED_USER_BALANCE_NOT_ENOUGH

status

UNRECOGNIZED

schemal1tx

Name
Type
Required
Constraints
Description
Notes

hash

string

false

none

Transaction hash

index

integer(int32)

false

none

Index of the tx hash

time

string(int64)

false

none

Tx chain timestamp, in milliseconds

blockHeight

string(int64)

false

none

Block height of the tx

schemal2signature

Name
Type
Required
Constraints
Description
Notes

r

string

false

none

Bigint for hex string

s

string

false

none

Bigint for hex string

v

string

false

none

Bigint for hex string

schemaresultgetcrosswithdrawsigninfo

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

Returns "SUCCESS" for success, otherwise it's a failure.

data

false

none

Get information required for cross-chain withdrawal signature - Response

errorParam

object

false

none

Error parameter information

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemagetcrosswithdrawsigninfo

Name
Type
Required
Constraints
Description
Notes

lpAccountId

string

false

none

LP account ID for L2 receiving user transfers

crossWithdrawL2Key

string

false

none

L2 key for fast withdrawal account

crossWithdrawMaxAmount

string

false

none

Maximum amount for fast cross-chain withdrawal

fee

string

false

none

Transaction fee

schemaresultlistnormalwithdraw

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

Returns "SUCCESS" for success, otherwise it's a failure.

data

false

none

Correct response data

errorParam

object

false

none

Error parameter information

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemanormalwithdraw

Name
Type
Required
Constraints
Description
Notes

id

string(int64)

false

none

Withdrawal order ID

userId

string(int64)

false

none

User ID

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Collateral coin ID

amount

string

false

none

Withdrawal amount

ethAddress

string

false

none

ETH address for withdrawal, may differ from the account's ETH address.

clientWithdrawId

string

false

none

Client-defined ID for idempotent checks

l2Nonce

string(int64)

false

none

L2 signature nonce. First 32 bits of sha256(client_withdraw_id)

l2ExpireTime

string(int64)

false

none

L2 signature expiration time. Unix time in hours, must be at least 24 hours after order creation.

l2Signature

false

none

L2 signature information

status

string

false

none

Normal withdrawal order status

tradeWithdrawId

string(int64)

false

none

Corresponding trading service withdraw order ID

riskSignature

false

none

L2 signature information

l1ConfirmedTx

false

none

L1 transaction information

l1ConfirmedTime

string(int64)

false

none

L1 transaction confirmation time

l1CompletedTime

string(int64)

false

none

L1 withdrawal completion time

createdTime

string(int64)

false

none

Creation time

updatedTime

string(int64)

false

none

Update time

Enum Values

Property
Value

status

NORMAL_WITHDRAW_UNKNOWN

status

NORMAL_WITHDRAW_PENDING_RISK_CHECKING

status

NORMAL_WITHDRAW_PENDING_TRADE_PROCESSING

status

NORMAL_WITHDRAW_PENDING_L2_APPROVING

status

NORMAL_WITHDRAW_PENDING_L1_CONFIRMING

status

NORMAL_WITHDRAW_PENDING_L1_WITHDRAWING

status

NORMAL_WITHDRAW_SUCCESS_L1_COMPLETED

status

NORMAL_WITHDRAW_FAILED_RISK_CHECK_FAILURE

status

NORMAL_WITHDRAW_FAILED_CENSOR_FAILURE

status

NORMAL_WITHDRAW_FAILED_L2_REJECTED

status

UNRECOGNIZED

schemaresultgetnormalwithdrawableamount

| Name | Type Okay, continuing the translated documentation:

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

Returns "SUCCESS" for success, otherwise it's a failure.

data

false

none

Query normal withdrawable claim amount by user address - Response

errorParam

object

false

none

Error parameter information

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemagetnormalwithdrawableamount

Name
Type
Required
Constraints
Description
Notes

amount

string

false

none

Withdrawable amount

schemaresultcreatecrosswithdraw

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

Returns "SUCCESS" for success, otherwise it's a failure.

data

false

none

Create cross-chain withdrawal order - Response

errorParam

object

false

none

Error parameter information

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemacreatecrosswithdraw

Name
Type
Required
Constraints
Description
Notes

crossWithdrawId

string(int64)

false

none

Cross-chain withdrawal order ID

schemacreatecrosswithdrawparam

Name
Type
Required
Constraints
Description
Notes

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Coin ID

amount

string

false

none

Withdrawal amount

ethAddress

string

false

none

Withdrawal address. If empty, withdraw to the corresponding address of the current account.

erc20Address

string

false

none

L1 ERC20 contract address for the withdrawn asset

lpAccountId

string

false

none

LP account ID for L2 receiving user transfers

clientCrossWithdrawId

string

false

none

Client-defined ID, used for signature & idempotent check. Must be filled.

expireTime

string(int64)

false

none

Expiration time

l2Signature

string

false

none

L2 signature

fee

string

false

none

Gas + fee obtained from front-end

chainId

string

false

none

Chain ID for withdrawal

mpcAddress

string

false

none

Which mpc address initiated the withdraw

mpcSignature

string

false

none

Signature of the mpc address to the withdraw field

mpcSignTime

string

false

none

mpc signature timestamp,unix timestamp in seconds

createnormalwithdraw

Name
Type
Required
Constraints
Description
Notes

code

string

false

none

Status code

Returns "SUCCESS" for success, otherwise it's a failure.

data

false

none

Create normal withdrawal order - Response

errorParam

object

false

none

Error parameter information

requestTime

string(timestamp)

false

none

Server request receive time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemacreatenormalwithdraw

Name
Type
Required
Constraints
Description
Notes

withdrawId

string(int64)

false

none

Withdrawal order ID

schemacreatenormalwithdrawparam

Name
Type
Required
Constraints
Description
Notes

accountId

string(int64)

false

none

Account ID

coinId

string(int64)

false

none

Coin ID

amount

string

false

none

Withdrawal amount

ethAddress

string

false

none

Withdrawal address. If empty, withdraw to the corresponding address of the current account.

clientWithdrawId

string

false

none

Client-defined ID, used for signature & idempotent check. Must be filled.

expireTime

string(int64)

false

none

Expiration time

l2Signature

string

false

none

L2 signature

Last updated

\ No newline at end of file diff --git a/docs/edgex/public-api.md b/docs/edgex/public-api.md new file mode 100644 index 0000000..fd89ce8 --- /dev/null +++ b/docs/edgex/public-api.md @@ -0,0 +1,1184 @@ +Meta Data API | edgeX Docs

Meta Data API

MetaDataPublicApi

GET Server Time

GET /api/v1/public/meta/getServerTime

Example Response

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "timeMillis": "1734596189305"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734596189305",
+    "responseTime": "1734596189305",
+    "traceId": "a69e6ec51701d7246cb344a719c99cbf"
+}

Response Body

Status Code
Status Code Description
Description
Data Model

200

default response

GET Meta Data

GET /api/v1/public/meta/getMetaData

Example Response

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "global": {
+            "appName": "edgeX",
+            "appEnv": "testnet",
+            "appOnlySignOn": "https://testnet.edgex.exchange",
+            "feeAccountId": "123456",
+            "feeAccountL2Key": "0x1e240",
+            "poolAccountId": "542076087396467085",
+            "poolAccountL2Key": "0x3bf794b4433e0a8b353da361bb7284c670914d27ed04698e6abed0bf1198028",
+            "fastWithdrawAccountId": "542076087396467085",
+            "fastWithdrawAccountL2Key": "0x3bf794b4433e0a8b353da361bb7284c670914d27ed04698e6abed0bf1198028",
+            "fastWithdrawMaxAmount": "100000",
+            "fastWithdrawRegistryAddress": "0xb2846943C2EdA3830Fb784d2a6de93435267b11D",
+            "starkExChainId": "0xaa36a7",
+            "starkExContractAddress": "0xa3Cb2622C532e46c4376FAd4AbFDf9eDC717BABf",
+            "starkExCollateralCoin": {
+                "coinId": "1000",
+                "coinName": "USDT",
+                "stepSize": "0.000001",
+                "showStepSize": "0.0001",
+                "iconUrl": "https://static.edgex.exchange/icons/coin/USDT.svg",
+                "starkExAssetId": "0x33bda5c923bae4e84825b74762d5482889b9512465fbffc50d1ae4b82e345c3",
+                "starkExResolution": "0xf4240"
+            },
+            "starkExMaxFundingRate": 1120,
+            "starkExOrdersTreeHeight": 64,
+            "starkExPositionsTreeHeight": 64,
+            "starkExFundingValidityPeriod": 604800,
+            "starkExPriceValidityPeriod": 31536000,
+            "maintenanceReason": ""
+        },
+        "coinList": [
+            {
+                "coinId": "1000",
+                "coinName": "USDT",
+                "stepSize": "0.000001",
+                "showStepSize": "0.0001",
+                "iconUrl": "https://static.edgex.exchange/icons/coin/USDT.svg",
+                "starkExAssetId": "0x33bda5c923bae4e84825b74762d5482889b9512465fbffc50d1ae4b82e345c3",
+                "starkExResolution": "0xf4240"
+            },
+            {
+                "coinId": "1001",
+                "coinName": "BTC",
+                "stepSize": "0.001",
+                "showStepSize": "0.001",
+                "iconUrl": "https://static.edgex.exchange/icons/coin/BTC.svg",
+                "starkExAssetId": null,
+                "starkExResolution": null
+            }
+        ],
+        "contractList": [
+            {
+                "contractId": "10000001",
+                "contractName": "BTCUSDT",
+                "baseCoinId": "1001",
+                "quoteCoinId": "1000",
+                "tickSize": "0.1",
+                "stepSize": "0.001",
+                "minOrderSize": "0.001",
+                "maxOrderSize": "50.000",
+                "maxOrderBuyPriceRatio": "0.05",
+                "minOrderSellPriceRatio": "0.05",
+                "maxPositionSize": "60.000",
+                "riskTierList": [
+                    {
+                        "tier": 1,
+                        "positionValueUpperBound": "50000",
+                        "maxLeverage": "100",
+                        "maintenanceMarginRate": "0.005",
+                        "starkExRisk": "21474837",
+                        "starkExUpperBound": "214748364800000000000"
+                    },
+                    {
+                        "tier": 22,
+                        "positionValueUpperBound": "79228162514264337593543",
+                        "maxLeverage": "6",
+                        "maintenanceMarginRate": "0.105",
+                        "starkExRisk": "450971567",
+                        "starkExUpperBound": "340282366920938463463374607431768211455"
+                    }
+                ],
+                "defaultTakerFeeRate": "0.00055",
+                "defaultMakerFeeRate": "0.0002",
+                "defaultLeverage": "50",
+                "liquidateFeeRate": "0.01",
+                "enableTrade": true,
+                "enableDisplay": true,
+                "enableOpenPosition": true,
+                "fundingInterestRate": "0.0003",
+                "fundingImpactMarginNotional": "10",
+                "fundingMaxRate": "0.000234",
+                "fundingMinRate": "-0.000234",
+                "fundingRateIntervalMin": "240",
+                "displayDigitMerge": "0.1,0.5,1,2,5",
+                "displayMaxLeverage": "50",
+                "displayMinLeverage": "1",
+                "displayNewIcon": false,
+                "displayHotIcon": true,
+                "matchServerName": "edgex-match-server",
+                "starkExSyntheticAssetId": "0x425443322d31300000000000000000",
+                "starkExResolution": "0x2540be400",
+                "starkExOraclePriceQuorum": "0x1",
+                "starkExOraclePriceSignedAssetId": [
+                    "0x425443555344000000000000000000004d616b6572",
+                    "0x425443555344000000000000000000005374437277",
+                    "0x4254435553440000000000000000000053746f726b",
+                    "0x425443555344000000000000000000004465787472",
+                    "0x4254435553440000000000000000000053744b6169"
+                ],
+                "starkExOraclePriceSigner": [
+                    "0x28253746dcd68a62df58cda44db2613ab11c8d17deb036feaec5ece1f8a16c1",
+                    "0x41dbe627aeab66504b837b3abd88ae2f58ba6d98ee7bbd7f226c4684d9e6225",
+                    "0xcc85afe4ca87f9628370c432c447e569a01dc96d160015c8039959db8521c4",
+                    "0x2af704df5467285c5d1bd7c08ee33c49057fb2a05ecdc4f949293190f28ce7e",
+                    "0x63f0f8507cc674ff668985a1ea854d3b73835a8181bfbb4564ae422bf68a2c0"
+                ]
+            },
+            {
+                "contractId": "10000002",
+                "contractName": "ETHUSDT",
+                "baseCoinId": "1002",
+                "quoteCoinId": "1000",
+                "tickSize": "0.01",
+                "stepSize": "0.01",
+                "minOrderSize": "0.01",
+                "maxOrderSize": "500.00",
+                "maxOrderBuyPriceRatio": "0.05",
+                "minOrderSellPriceRatio": "0.05",
+                "maxPositionSize": "800.00",
+                "riskTierList": [
+                    {
+                        "tier": 1,
+                        "positionValueUpperBound": "50000",
+                        "maxLeverage": "100",
+                        "maintenanceMarginRate": "0.005",
+                        "starkExRisk": "21474837",
+                        "starkExUpperBound": "214748364800000000000"
+                    },
+                    {
+                        "tier": 22,
+                        "positionValueUpperBound": "79228162514264337593543",
+                        "maxLeverage": "6",
+                        "maintenanceMarginRate": "0.105",
+                        "starkExRisk": "450971567",
+                        "starkExUpperBound": "340282366920938463463374607431768211455"
+                    }
+                ],
+                "defaultTakerFeeRate": "0.00055",
+                "defaultMakerFeeRate": "0.0002",
+                "defaultLeverage": "50",
+                "liquidateFeeRate": "0.01",
+                "enableTrade": true,
+                "enableDisplay": true,
+                "enableOpenPosition": true,
+                "fundingInterestRate": "0.0003",
+                "fundingImpactMarginNotional": "100",
+                "fundingMaxRate": "0.000234",
+                "fundingMinRate": "-0.000234",
+                "fundingRateIntervalMin": "240",
+                "displayDigitMerge": "0.01,0.02,0.04,0.1,0.2",
+                "displayMaxLeverage": "50",
+                "displayMinLeverage": "1",
+                "displayNewIcon": true,
+                "displayHotIcon": false,
+                "matchServerName": "edgex-match-server",
+                "starkExSyntheticAssetId": "0x4554482d3900000000000000000000",
+                "starkExResolution": "0x3b9aca00",
+                "starkExOraclePriceQuorum": "0x1",
+                "starkExOraclePriceSignedAssetId": [
+                    "0x455448555344000000000000000000004d616b6572",
+                    "0x455448555344000000000000000000005374437277",
+                    "0x4554485553440000000000000000000053746f726b",
+                    "0x455448555344000000000000000000004465787472",
+                    "0x4554485553440000000000000000000053744b6169"
+                ],
+                "starkExOraclePriceSigner": [
+                    "0x28253746dcd68a62df58cda44db2613ab11c8d17deb036feaec5ece1f8a16c1",
+                    "0x41dbe627aeab66504b837b3abd88ae2f58ba6d98ee7bbd7f226c4684d9e6225",
+                    "0xcc85afe4ca87f9628370c432c447e569a01dc96d160015c8039959db8521c4",
+                    "0x2af704df5467285c5d1bd7c08ee33c49057fb2a05ecdc4f949293190f28ce7e",
+                    "0x63f0f8507cc674ff668985a1ea854d3b73835a8181bfbb4564ae422bf68a2c0"
+                ]
+            }
+        ],
+        "multiChain": {
+            "coinId": "1000",
+            "maxWithdraw": "100000",
+            "minWithdraw": "0",
+            "minDeposit": "10",
+            "chainList": [
+                {
+                    "chain": "Sepolia - Testnet",
+                    "chainId": "11155111",
+                    "chainIconUrl": "https://static.edgex.exchange/icons/chain/sepolia.svg",
+                    "contractAddress": "0xC820e27D4821071129D4fB04CcD9ae8a370373bc",
+                    "depositGasFeeLess": false,
+                    "feeLess": false,
+                    "feeRate": "0.0001",
+                    "gasLess": false,
+                    "gasToken": "ETH",
+                    "minFee": "2",
+                    "rpcUrl": "https://rpc.edgex.exchange/RMZZpeTnB6hjfcm8xNNyo6cKa9Zn4qgB/eth-sepolia",
+                    "webTxUrl": "https://sepolia.etherscan.io/tx/",
+                    "withdrawGasFeeLess": false,
+                    "tokenList": [
+                        {
+                            "tokenAddress": "0xd98B590ebE0a3eD8C144170bA4122D402182976f",
+                            "decimals": "6",
+                            "iconUrl": "https://static.edgex.exchange/icons/coin/USDT.svg",
+                            "token": "USDT",
+                            "pullOff": false,
+                            "withdrawEnable": true,
+                            "useFixedRate": false,
+                            "fixedRate": ""
+                        }
+                    ],
+                    "txConfirm": "10",
+                    "blockTime": "12",
+                    "allowAaDeposit": true,
+                    "allowAaWithdraw": false,
+                    "appRpcUrl": "https://rpc.edgex.exchange/GujYf2XWDvzXDpQdXno92DGRhfy7HuLK/eth-sepolia"
+                },
+                {
+                    "chain": "BNB - Testnet",
+                    "chainId": "97",
+                    "chainIconUrl": "https://static.edgex.exchange/icons/chain/sepolia.svg",
+                    "contractAddress": "0xBe8dCAE2b5E58BdEe4695F7f366fF0A8B0A414D1",
+                    "depositGasFeeLess": false,
+                    "feeLess": false,
+                    "feeRate": "0.0001",
+                    "gasLess": false,
+                    "gasToken": "BSC",
+                    "minFee": "2",
+                    "rpcUrl": "https://rpc.edgex.exchange/RMZZpeTnB6hjfcm8xNNyo6cKa9Zn4qgB/bsc-testnet",
+                    "webTxUrl": "https://testnet.bscscan.com/tx/",
+                    "withdrawGasFeeLess": false,
+                    "tokenList": [
+                        {
+                            "tokenAddress": "0xda6c748A7593826e410183F05893dbB363D025a1",
+                            "decimals": "6",
+                            "iconUrl": "https://static.edgex.exchange/icons/coin/USDT.svg",
+                            "token": "USDT",
+                            "pullOff": false,
+                            "withdrawEnable": true,
+                            "useFixedRate": false,
+                            "fixedRate": ""
+                        }
+                    ],
+                    "txConfirm": "10",
+                    "blockTime": "3",
+                    "allowAaDeposit": false,
+                    "allowAaWithdraw": false,
+                    "appRpcUrl": "https://rpc.edgex.exchange/GujYf2XWDvzXDpQdXno92DGRhfy7HuLK/bsc-testnet"
+                },
+                {
+                    "chain": "Arbitrum - Testnet",
+                    "chainId": "421614",
+                    "chainIconUrl": "https://static.edgex.exchange/icons/chain/sepolia.svg",
+                    "contractAddress": "0xeeA926DB072E839063321776ddAdaddeECdF9718",
+                    "depositGasFeeLess": false,
+                    "feeLess": false,
+                    "feeRate": "0.0001",
+                    "gasLess": false,
+                    "gasToken": "ETH",
+                    "minFee": "2",
+                    "rpcUrl": "https://rpc.edgex.exchange/RMZZpeTnB6hjfcm8xNNyo6cKa9Zn4qgB/arbitrum-sepolia",
+                    "webTxUrl": "https://sepolia.arbiscan.io/tx/",
+                    "withdrawGasFeeLess": false,
+                    "tokenList": [
+                        {
+                            "tokenAddress": "0x608babb39bb03C038b8DABc3D4bF4e0D02d455Cd",
+                            "decimals": "18",
+                            "iconUrl": "https://static.edgex.exchange/icons/coin/USDT.svg",
+                            "token": "USDT",
+                            "pullOff": false,
+                            "withdrawEnable": true,
+                            "useFixedRate": false,
+                            "fixedRate": ""
+                        }
+                    ],
+                    "txConfirm": "10",
+                    "blockTime": "3",
+                    "allowAaDeposit": true,
+                    "allowAaWithdraw": true,
+                    "appRpcUrl": "https://rpc.edgex.exchange/GujYf2XWDvzXDpQdXno92DGRhfy7HuLK/arbitrum-sepolia"
+                }
+            ]
+        }
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734595526342",
+    "responseTime": "1734595526343",
+    "traceId": "1ee9b62c30925f0df6bd6c8604f32df4"
+}

Response Body

Status Code
Status Code Description
Description
Data Model

200

default response

Data Models

MetadataResult

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failure.

data

false

none

Global metadata

errorParam

object

false

none

Parameter information in error message

requestTime

string(timestamp)

false

none

Server request receiving time

responseTime

string(timestamp)

false

none

Server response returning time

traceId

string

false

none

Call traceId

schemametadata

Name
Type
Required
Constraints
Description

global

false

none

Global meta information

coinList

false

none

All coin meta information

contractList

false

none

All contract meta information

multiChain

false

none

Cross-chain withdrawal related class

schemamultichain

Name
Type
Required
Constraints
Description

coinId

string(int64)

false

none

Asset id for deposit and withdrawal

maxWithdraw

string

false

none

Maximum withdrawal amount

minWithdraw

string

false

none

Minimum withdrawal amount

minDeposit

string

false

none

Minimum deposit amount

chainList

false

none

Supported chains

schemachain

Name
Type
Required
Constraints
Description

chain

string

false

none

Main chain name

chainId

string(int64)

false

none

chainId

chainIconUrl

string

false

none

Main chain icon url

contractAddress

string

false

none

Contract address

depositGasFeeLess

boolean

false

none

Whether to charge deposit fee

feeLess

boolean

false

none

Whether to exempt from fees

feeRate

string

false

none

Fee rate

gasLess

boolean

false

none

Whether to charge gas fee

gasToken

string

false

none

Main chain token name

minFee

string

false

none

Minimum withdrawal fee. If gas + value*fee_rate is less than min_fee, it will be charged according to min_fee

rpcUrl

string

false

none

Online node service of the chain

webTxUrl

string

false

none

Transaction tx link

withdrawGasFeeLess

boolean

false

none

Whether to charge withdrawal fee

tokenList

false

none

Collection of cross-chain related token information

txConfirm

string(int64)

false

none

Number of confirmations for on-chain deposit

blockTime

string

false

none

Block time

appRpcUrl

string

false

none

none

schemamultichaintoken

Name
Type
Required
Constraints
Description

tokenAddress

string

false

none

Token contract address

decimals

string(int64)

false

none

Token precision

iconUrl

string

false

none

Token icon url

token

string

false

none

Token name

pullOff

boolean

false

none

Whether to delist, default is false

withdrawEnable

boolean

false

none

Whether to support withdrawal of this type of asset

useFixedRate

boolean

false

none

Whether to use a fixed exchange rate

fixedRate

string

false

none

Fixed exchange rate

schemacontract

Name
Type
Required
Constraints
Description

contractId

string(int64)

false

none

Perpetual contract pair identifier

contractName

string

false

none

Perpetual contract pair name

baseCoinId

string(int64)

false

none

e.g., 10000001 (BTC)

quoteCoinId

string(int64)

false

none

e.g., 1001 (USD/USDT)

tickSize

string(decimal)

false

none

Minimum price increment (quoteCoinId)

stepSize

string(decimal)

false

none

Minimum quantity increment (baseCoinId)

minOrderSize

string(decimal)

false

none

Minimum order quantity (baseCoinId)

maxOrderSize

string(decimal)

false

none

Maximum order quantity (baseCoinId)

maxOrderBuyPriceRatio

string(decimal)

false

none

Maximum limit buy order price ratio (compared to oracle price), decimal (quote_coin_id)

minOrderSellPriceRatio

string(decimal)

false

none

Minimum limit sell order price ratio (compared to oracle price), decimal (quote_coin_id)

maxPositionSize

string(decimal)

false

none

Maximum position quantity (baseCoinId)

riskTierList

false

none

List of risk limit tiers

defaultTakerFeeRate

string(decimal)

false

none

Default taker fee rate for the contract

defaultMakerFeeRate

string(decimal)

false

none

Default maker fee rate for the contract

defaultLeverage

string(decimal)

false

none

Initial default leverage multiplier when user has not set a trading leverage

liquidateFeeRate

string(decimal)

false

none

Liquidation fee rate

enableTrade

boolean

false

none

Whether trading is allowed. true: allowed, false: not allowed

enableDisplay

boolean

false

none

Whether to display. true: display, false: hide

enableOpenPosition

boolean

false

none

Whether opening positions is allowed. true: allowed to open and close, false: only allowed to close positions

fundingInterestRate

string(decimal)

false

none

Default value of overall interest rate, e.g., 0.0003

fundingImpactMarginNotional

string(decimal)

false

none

Quantity for calculating depth-weighted bid/ask price, e.g., 8000

fundingMaxRate

string(decimal)

false

none

Maximum funding rate, e.g., 0.000234

fundingMinRate

string(decimal)

false

none

Minimum funding rate, e.g., -0.000234

fundingRateIntervalMin

string(decimal)

false

none

Settlement interval of funding rate (in minutes, must be an integer multiple of 60 minutes, settlement starts from 00:00 UTC) decimal

displayDigitMerge

string(decimal)

false

none

Depth merge. e.g., "1,0.1,0.001"

displayMaxLeverage

string(decimal)

false

none

Maximum leverage multiplier to display, decimal. e.g., 20

displayMinLeverage

string(decimal)

false

none

Minimum leverage multiplier to display, decimal. e.g., 1

displayNewIcon

boolean

false

none

Whether it is a newly listed pair

displayHotIcon

boolean

false

none

Whether it is a hot pair

matchServerName

string

false

none

Matching service name, e.g., xxx-match-server-a. This value cannot be changed once configured, otherwise data migration is required.

starkExSyntheticAssetId

string(int64)

false

none

Synthetic asset id of the current pair, bigint for hex str.

starkExResolution

string(int64)

false

none

Processing precision of the quantity held by the current pair, bigint for hex str

starkExOraclePriceQuorum

string(int64)

false

none

Legal number of oracle prices, bigint for hex str

starkExOraclePriceSignedAssetId

[string]

false

none

bigint for hex str

starkExOraclePriceSigner

[string]

false

none

bigint for hex str

schemarisktier

Name
Type
Required
Constraints
Description

tier

integer(int32)

false

none

Tier, starting from 1

positionValueUpperBound

string(decimal)

false

none

Upper limit of position value for the tier (inclusive)

maxLeverage

string(decimal)

false

none

Maximum available leverage for the tier

maintenanceMarginRate

string(decimal)

false

none

Maintenance margin rate for the tier (only for display, the actual maintenance margin rate used is stark_ex_risk / 2^32 as an accurate maintenance margin rate), decimal

starkExRisk

string(int64)

false

none

1 ≤ risk < 2^32

starkExUpperBound

string(int64)

false

none

bigint. 0 ≤ upper_bound ≤ 2^128-1

schemacoin

Name
Type
Required
Constraints
Description

coinId

string(int64)

false

none

Coin id

coinName

string

false

none

Coin name

stepSize

string(decimal)

false

none

Minimum quantity unit

showStepSize

string(decimal)

false

none

Minimum unit displayed to the user

iconUrl

string(url)

false

none

Coin icon url

starkExAssetId

string(int64)

false

none

starkex asset id. If empty, it means it does not exist

starkExResolution

string

false

none

starkex processing precision. If empty, it means it does not exist

schemaglobal

Name
Type
Required
Constraints
Description

appName

string

false

none

xxx

appEnv

string

false

none

dev/testnet/mainnet

appOnlySignOn

string

false

none

https://xxx.exchange

feeAccountId

string(int64)

false

none

Fee account id

feeAccountL2Key

string

false

none

Fee account l2Key, bigint for hex str

poolAccountId

string(int64)

false

none

Asset pool account id

poolAccountL2Key

string

false

none

Asset pool account l2Key, bigint for hex str

fastWithdrawAccountId

string(int64)

false

none

Fast withdrawal account id

fastWithdrawAccountL2Key

string

false

none

Fast withdrawal account l2Key, bigint for hex str

fastWithdrawMaxAmount

string

false

none

Maximum amount for fast withdrawal

fastWithdrawRegistryAddress

string

false

none

Fast withdrawal account address

starkExChainId

string

false

none

Chain id of starkex. bigint for hex str

starkExContractAddress

string

false

none

starkex contract address.

starkExCollateralCoin

false

none

Coin meta information

starkExMaxFundingRate

integer(int32)

false

none

Maximum funding rate per second after starkex precision processing. i.e. stark_ex_max_funding_rate * 2^32 is the actual maximum funding rate per second. E.g.: 1120

starkExOrdersTreeHeight

integer(int32)

false

none

Order merkle tree height. E.g.: 64

starkExPositionsTreeHeight

integer(int32)

false

none

Account merkle tree height. E.g.: 64

starkExFundingValidityPeriod

integer(int32)

false

none

Funding rate submission validity period in seconds. E.g.: 86400

starkExPriceValidityPeriod

integer(int32)

false

none

Oracle price submission validity period in seconds. E.g.: 86400

maintenanceReason

string

false

none

Maintenance reason, empty if no maintenance

getservertime

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failure.

data

false

none

Server time

errorParam

object

false

none

Parameter information in error message

requestTime

string(timestamp)

false

none

Server request receiving time

responseTime

string(timestamp)

false

none

Server response returning time

traceId

string

false

none

Call traceId

schemagetservertime

Name
Type
Required
Constraints
Description

timeMillis

string(int64)

false

none

Server timestamp, milliseconds

Last updated

+ + Quote API | edgeX Docs

Quote API

QuotePublicApi

GET Get Quote Summary

GET /api/v1/public/quote/getTicketSummary

Request Parameters

Name
Location
Type
Required
Description

period

query

string

No

Summary period

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "tickerSummary": {
+            "period": "LAST_DAY_1",
+            "trades": "31450",
+            "value": "201048203.7979",
+            "openInterest": "13.565"
+        }
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734596957000",
+    "responseTime": "1734596957003",
+    "traceId": "574a8b43497ebd0bca55d0b257d034fa"
+}

Response

Status Code
Status Code Meaning
Description
Data Model

200

default response

GET Query 24-Hour Quotes

GET /api/v1/public/quote/getTicker

Request Parameters

Name
Location
Type
Required
Description

contractId

query

string

No

Contract ID

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "contractId": "10000001",
+            "contractName": "BTCUSDT",
+            "priceChange": "-2270.5",
+            "priceChangePercent": "-0.021849",
+            "trades": "79372",
+            "size": "499.487",
+            "value": "50821443.7464",
+            "high": "105331.5",
+            "low": "98755.0",
+            "open": "103913.2",
+            "close": "101642.7",
+            "highTime": "1734524115631",
+            "lowTime": "1734575388228",
+            "startTime": "1734510600000",
+            "endTime": "1734597000000",
+            "lastPrice": "101642.7",
+            "indexPrice": "101676.380723500",
+            "oraclePrice": "101636.3750002346932888031005859375",
+            "openInterest": "0.105",
+            "fundingRate": "-0.00012236",
+            "fundingTime": "1734595200000",
+            "nextFundingTime": "1734609600000"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734597508246",
+    "responseTime": "1734597508250",
+    "traceId": "a49014b0ad76a121193d4717294f85fc"
+}

Response

Status Code
Status Code Meaning
Description
Data Model

200

default response

GET Query Multi-Contract Quantitative K-Line

GET /api/v1/public/quote/getMultiContractKline

Request Parameters

Name
Location
Type
Required
Description

contractIdList

query

string

No

Collection of Contract IDs

priceType

query

string

No

Price type

klineType

query

string

No

K-line type

size

query

string

No

Number to retrieve. Must be greater than 0 and less than or equal to 200

filterBeginKlineTimeInclusive

query

string

No

Query start time (if 0, means from current time). Returns in descending order by time

filterEndKlineTimeExclusive

query

string

No

Query end time

Request Example

https://pro.edgex.exchange/api/v1/public/quote/getMultiContractKline?contractIdList=10000001&klineType=HOUR_1&filterBeginKlineTimeInclusive=1733416860000&filterEndKlineTimeExclusive=1734601200000&priceType=LAST_PRICE

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "contractId": "10000001",
+            "klineList": [
+                {
+                    "klineId": "687194849731486048",
+                    "contractId": "10000001",
+                    "contractName": "BTCUSDT",
+                    "klineType": "HOUR_1",
+                    "klineTime": "1734595200000",
+                    "priceType": "LAST_PRICE",
+                    "trades": "3123",
+                    "size": "7.947",
+                    "value": "807240.1268",
+                    "high": "101798.4",
+                    "low": "101326.3",
+                    "open": "101603.8",
+                    "close": "101605.6",
+                    "makerBuySize": "5.222",
+                    "makerBuyValue": "530431.6634"
+                }
+            ]
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734601896988",
+    "responseTime": "1734601897009",
+    "traceId": "7edd9609a0c5976c1cb58bdee3d08088"
+}
+

Response

Status Code
Status Code Meaning
Description
Data Model

200

default response

Response Data Structure

GET Query K-Line

GET /api/v1/public/quote/getKline

Request Parameters

Name
Location
Type
Required
Description

contractId

query

string

No

Contract ID

priceType

query

string

No

Price type

klineType

query

string

No

K-line type

size

query

string

No

Number to retrieve. Must be greater than 0 and less than or equal to 1000

offsetData

query

string

No

Pagination offset. If empty, get the first page

filterBeginKlineTimeInclusive

query

string

No

Query start time (if 0, means from current time). Returns in descending order by time

filterEndKlineTimeExclusive

query

string

No

Query end time

Request Example

https://pro.edgex.exchange/api/v1/public/quote/getKline?contractId=10000002&klineType=HOUR_1&filterBeginKlineTimeInclusive=1733416860000&filterEndKlineTimeExclusive=1734601200000&priceType=LAST_PRICE

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "klineId": "687194918450962784",
+                "contractId": "10000002",
+                "contractName": "ETHUSDT",
+                "klineType": "HOUR_1",
+                "klineTime": "1734595200000",
+                "priceType": "LAST_PRICE",
+                "trades": "3142",
+                "size": "111.96",
+                "value": "412199.6286",
+                "high": "3694.59",
+                "low": "3667.42",
+                "open": "3694.57",
+                "close": "3670.42",
+                "makerBuySize": "52.21",
+                "makerBuyValue": "192147.4907"
+            }
+        ],
+        "nextPageOffsetData": ""
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734601267556",
+    "responseTime": "1734601267581",
+    "traceId": "72cfd2eeb27fc602aa64990ad84cd8dd"
+}

Response

Status Code
Status Code Meaning
Description
Data Model

200

default response

GET Get Exchange Long Short Ratio

GET /api/v1/public/quote/getExchangeLongShortRatio

Request Parameters

Name
Location
Type
Required
Description

range

query

string

No

If empty, return data with the smallest range

filterContractIdList

query

string

No

If empty, return data for all contracts

filterExchangeList

query

string

No

If empty, return data for all exchanges

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "exchangeLongShortRatioList": [
+            {
+                "range": "30m",
+                "contractId": "10000001",
+                "exchange": "_total_",
+                "buyRatio": "50.9900",
+                "sellRatio": "49.0100",
+                "buyVolUsd": "567855766.2701",
+                "sellVolUsd": "545892952.7900",
+                "createdTime": "1734597018839",
+                "updatedTime": "1734597018839"
+            }
+        ],
+        "allRangeList": [
+            "30m",
+            "1h",
+            "4h",
+            "12h",
+            "24h"
+        ]
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734597836994",
+    "responseTime": "1734597837001",
+    "traceId": "60af97ec1357f9d00da50bada9e4364c"
+}

Response

Status Code
Status Code Meaning
Description
Data Model

200

default response

GET Query Order Book Depth

GET /api/v1/public/quote/getDepth

Request Parameters

Name
Location
Type
Required
Description

contractId

query

string

No

Contract ID

level

query

string

No

Depth level. Currently 15 and 200 levels available

Response Example

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "startVersion": "201223746",
+            "endVersion": "201223747",
+            "level": 15,
+            "contractId": "10000001",
+            "contractName": "BTCUSDT",
+            "asks": [
+                {
+                    "price": "101695.9",
+                    "size": "0.579"
+                },
+                {
+                    "price": "101696.0",
+                    "size": "0.923"
+                },
+                {
+                    "price": "101703.0",
+                    "size": "0.129"
+                }
+            ],
+            "bids": [
+                {
+                    "price": "101695.5",
+                    "size": "1.710"
+                },
+                {
+                    "price": "101694.1",
+                    "size": "0.189"
+                },
+                {
+                    "price": "101692.9",
+                    "size": "0.223"
+                }
+            ],
+            "depthType": "SNAPSHOT"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734598036434",
+    "responseTime": "1734598036435",
+    "traceId": "99b69f04bac0df6e37961f249b9545e4"
+}

Response

Status Code
Status Code Meaning
Description
Data Model

200

default response

Response Data Structure

Data Models

schemaresultlistdepth

Name
Type
Required
Constraints
Chinese Name
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failures

data

false

none

Correct response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemadepth

Name
Type
Required
Constraints
Chinese Name
Description

startVersion

string(int64)

false

none

Start order book version number

endVersion

string(int64)

false

none

End order book version number

level

integer(int32)

false

none

Depth level

contractId

string(int64)

false

none

Contract ID

contractName

string

false

none

Contract name

asks

false

none

Ask list

bids

false

none

Bid list

depthType

string

false

none

Depth type

Enum Values

Property
Value

depthType

UNKNOWN_DEPTH_TYPE

depthType

SNAPSHOT

depthType

CHANGED

depthType

UNRECOGNIZED

schemabookorder

Name
Type
Required
Constraints
Chinese Name
Description

price

string(decimal)

false

none

Price

size

string(decimal)

false

none

Quantity

schemaresultpagedatakline

Name
Type
Required
Constraints
Chinese Name
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failures

data

false

none

Generic paginated response

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemapagedatakline

Name
Type
Required
Constraints
Chinese Name
Description

dataList

false

none

Data list

nextPageOffsetData

string

false

none

Offset for the next page. If there is no next page, it's an empty string

schemakline

Name
Type
Required
Constraints
Chinese Name
Description

klineId

string(int64)

false

none

K-Line ID

contractId

string(int64)

false

none

Perpetual contract ID

contractName

string

false

none

Perpetual contract name

klineType

string

false

none

K-Line type

klineTime

string(int64)

false

none

K-Line time

priceType

string

false

none

Price type of the K-line

trades

string(int64)

false

none

Number of trades

size

string(decimal)

false

none

Volume

value

string(decimal)

false

none

Value

high

string(decimal)

false

none

High price

low

string(decimal)

false

none

Low price

open

string(decimal)

false

none

Opening price

close

string(decimal)

false

none

Closing price

makerBuySize

string(decimal)

false

none

Maker buy volume

makerBuyValue

string(decimal)

false

none

Maker buy value

Enum Values

Property
Value

klineType

UNKNOWN_KLINE_TYPE

klineType

MINUTE_1

klineType

MINUTE_5

klineType

MINUTE_15

klineType

MINUTE_30

klineType

HOUR_1

klineType

HOUR_2

klineType

HOUR_4

klineType

HOUR_6

klineType

HOUR_8

klineType

HOUR_12

klineType

DAY_1

klineType

WEEK_1

klineType

MONTH_1

klineType

UNRECOGNIZED

priceType

UNKNOWN_PRICE_TYPE

priceType

ORACLE_PRICE

priceType

INDEX_PRICE

priceType

LAST_PRICE

priceType

ASK1_PRICE

priceType

BID1_PRICE

priceType

OPEN_INTEREST

priceType

UNRECOGNIZED

schemaresultlistcontractkline

Name
Type
Required
Constraints
Chinese Name
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failures

data

false

none

Correct response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemacontractmultikline

Name
Type
Required
Constraints
Chinese Name
Description

contractId

string(int64)

false

none

Perpetual contract ID

klineList

false

none

Collection of kline data

schemaresultlistticker

Name
Type
Required
Constraints
Chinese Name
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failures

data

false

none

Correct response data

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schematicker

Name
Type
Required
Constraints
Chinese Name
Description

contractId

string(int64)

false

none

Contract ID

contractName

string

false

none

Contract Name

priceChange

string(decimal)

false

none

Price change

priceChangePercent

string(decimal)

false

none

Price change percentage

trades

string(int64)

false

none

24-hour number of trades

size

string(decimal)

false

none

24-hour trading volume

value

string(decimal)

false

none

24-hour trading value

high

string(decimal)

false

none

24-hour high price

low

string(decimal)

false

none

24-hour low price

open

string(decimal)

false

none

24-hour opening price

close

string(decimal)

false

none

24-hour closing price

highTime

string(int64)

false

none

24-hour high price time

lowTime

string(int64)

false

none

24-hour low price time

startTime

string(int64)

false

none

24-hour quote start time

endTime

string(int64)

false

none

24-hour quote end time

lastPrice

string(decimal)

false

none

Latest trade price

indexPrice

string(decimal)

false

none

Current index price

oraclePrice

string(decimal)

false

none

Current oracle price

openInterest

string(decimal)

false

none

Open Interest

fundingRate

string

false

none

Current already settled funding rate

fundingTime

string(int64)

false

none

Funding rate settlement time

nextFundingTime

string(int64)

false

none

Next funding rate settlement time

gettickersummarymodel

Name
Type
Required
Constraints
Chinese Name
Description

code

string

false

none

Status code. "SUCCESS" for success, others for failures

data

false

none

Get quote summary response

errorParam

object

false

none

Parameter information in error messages

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response return time

traceId

string

false

none

Call trace ID

schemagettickersummary

Name
Type
Required
Constraints
Chinese Name
Description

tickerSummary

false

none

Quote summary

schematickersummary

Name
Type
Required
Constraints
Chinese Name
Description

period

string

false

none

Summary period

trades

string

false

none

Total exchange number of trades

value

string

false

none

Total traded value

openInterest

string

false

none

Current total open interest

Enum Values

Property
Value

period

UNKNOWN_PERIOD

period

LAST_DAY_1

period

LAST_DAY_7

period

LAST_DAY_30

period

UNRECOGNIZED

Last updated

+ + Funding API | edgeX Docs

Funding API

FundingPublicApi

GET Get Latest Funding Rate by Contract ID

GET /api/v1/public/funding/getLatestFundingRate

Request Parameters

Name
Location
Type
Required
Description

contractId

query

string

No

Contract ID

Example Response

200 Response

{
+    "code": "SUCCESS",
+    "data": [
+        {
+            "contractId": "10000001",
+            "fundingTime": "1734595200000",
+            "fundingTimestamp": "1734597720000",
+            "oraclePrice": "101559.9220921285450458526611328125",
+            "indexPrice": "101522.558968500",
+            "fundingRate": "-0.00005537",
+            "isSettlement": false,
+            "forecastFundingRate": "-0.00012293",
+            "previousFundingRate": "0.00000567",
+            "previousFundingTimestamp": "1734595140000",
+            "premiumIndex": "-0.00036207",
+            "avgPremiumIndex": "-0.00032293",
+            "premiumIndexTimestamp": "1734597720000",
+            "impactMarginNotional": "100",
+            "impactAskPrice": "101485.8",
+            "impactBidPrice": "101484.7",
+            "interestRate": "0.0003",
+            "predictedFundingRate": "0.00005000",
+            "fundingRateIntervalMin": "240",
+            "starkExFundingIndex": "101559.9220921285450458526611328125"
+        }
+    ],
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734597737870",
+    "responseTime": "1734597737873",
+    "traceId": "5e27ebfb0ae79f51bbd347d2bf3585f9"
+}

Response Codes

Status Code
Status Code Description
Description
Data Model

200

default response

GET Get Funding Rate History by Contract ID with Pagination

GET /api/v1/public/funding/getFundingRatePage

Request Parameters

Name
Location
Type
Required
Description

contractId

query

string

No

Contract ID

size

query

string

No

Number of items to retrieve. Must be greater than 0 and less than or equal to 100

offsetData

query

string

No

Pagination offset. If not provided or empty, retrieves the first page

filterSettlementFundingRate

query

string

No

If true, only query settlement funding rates (funding rate settlement occurs every 8 hours, with a predicted funding rate calculated every minute)

filterBeginTimeInclusive

query

string

No

Start time. If not provided, retrieves the oldest data

filterEndTimeExclusive

query

string

No

End time. If not provided, retrieves the latest data

Example Response

200 Response

{
+    "code": "SUCCESS",
+    "data": {
+        "dataList": [
+            {
+                "contractId": "10000001",
+                "fundingTime": "1733702400000",
+                "fundingTimestamp": "1733702400000",
+                "oraclePrice": "101120.888977311551570892333984375",
+                "indexPrice": "101121.681521500",
+                "fundingRate": "0.00005000",
+                "isSettlement": true,
+                "forecastFundingRate": "",
+                "previousFundingRate": "0.00005000",
+                "previousFundingTimestamp": "1733702340000",
+                "premiumIndex": "0.00022566",
+                "avgPremiumIndex": "0.00017953",
+                "premiumIndexTimestamp": "1733702400000",
+                "impactMarginNotional": "500",
+                "impactAskPrice": "101269.6",
+                "impactBidPrice": "101269.1",
+                "interestRate": "0.0003",
+                "predictedFundingRate": "0.00005000",
+                "fundingRateIntervalMin": "240",
+                "starkExFundingIndex": "101120.888977311551570892333984375"
+            }
+        ],
+        "nextPageOffsetData": "0880A08A97B532"
+    },
+    "msg": null,
+    "errorParam": null,
+    "requestTime": "1734597585432",
+    "responseTime": "1734597586672",
+    "traceId": "02465a59be5d19088ba7e4b5c6b94f6d"
+}

Response Codes

Status Code
Status Code Description
Description
Data Model

200

default response

Data Models

pagedatafundingrate

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise indicates failure

data

false

none

General pagination response

errorParam

object

false

none

Parameter information in the error message

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response time

traceId

string

false

none

Call trace ID

schemapagedatafundingrate

Name
Type
Required
Constraints
Description

dataList

false

none

Data list

nextPageOffsetData

string

false

none

Offset data to retrieve the next page. Empty string if there is no next page

listfundingrate

Name
Type
Required
Constraints
Description

code

string

false

none

Status code. "SUCCESS" for success, otherwise indicates failure

data

false

none

Successful response data

errorParam

object

false

none

Parameter information in the error message

requestTime

string(timestamp)

false

none

Server request reception time

responseTime

string(timestamp)

false

none

Server response time

traceId

string

false

none

Call trace ID

schemafundingrate

Name
Type
Required
Constraints
Description

contractId

string(int64)

false

none

Contract ID

fundingTime

string(int64)

false

none

Funding rate settlement time. E.g., the funding rate for the 08:00-09:00 period is calculated from the previous 07:00-08:00 data, finalized at 08:00, and used for settlement at 09:00

fundingTimestamp

string(int64)

false

none

Funding rate calculation time in milliseconds

oraclePrice

string

false

none

Oracle price

indexPrice

string

false

none

Index price

fundingRate

string

false

none

Funding rate

isSettlement

boolean

false

none

Funding rate settlement flag

forecastFundingRate

string

false

none

Forecast funding rate

previousFundingRate

string

false

none

Previous funding rate

previousFundingTimestamp

string(int64)

false

none

Previous funding rate calculation time in milliseconds

premiumIndex

string

false

none

Premium index

avgPremiumIndex

string

false

none

Average premium index

premiumIndexTimestamp

string

false

none

Premium index calculation time

impactMarginNotional

string

false

none

Quantity required for deep weighted buy/sell price calculation

impactAskPrice

string

false

none

Deep weighted ask price

impactBidPrice

string

false

none

Deep weighted bid price

interestRate

string

false

none

Fixed interest rate

predictedFundingRate

string

false

none

Comprehensive interest rate (interestRate/frequency)

fundingRateIntervalMin

string(int64)

false

none

Funding rate time interval in minutes

starkExFundingIndex

string

false

none

StarkEx funding index

Last updated

\ No newline at end of file diff --git a/docs/edgex/websocket.md b/docs/edgex/websocket.md new file mode 100644 index 0000000..66d322d --- /dev/null +++ b/docs/edgex/websocket.md @@ -0,0 +1,416 @@ +Websocket API | edgeX Docs

Websocket API

WebSocket API Documentation

This document outlines the WebSocket API for both private (user account) and public (market data) information.

(Private WebSocket) User Account Information WebSocket Interface

Description

  • 01. Private WebSocket connections do not require subscriptions; data is automatically pushed after a successful connection. This includes both trading messages and custom messages.

  • 02. Trading messages are identified with the type type-event. Other message types will be defined separately.

  • 03. The event field within the body of a trading message can be one of the following: Snapshot, ACCOUNT_UPDATE, DEPOSIT_UPDATE, WITHDRAW_UPDATE, TRANSFER_IN_UPDATE, TRANSFER_OUT_UPDATE, ORDER_UPDATE, FORCE_WITHDRAW_UPDATE, FORCE_TRADE_UPDATE, FUNDING_SETTLEMENT, ORDER_FILL_FEE_INCOME, START_LIQUIDATING, FINISH_LIQUIDATING, or UNRECOGNIZED.

  • 04. Ping-Pong Mechanism:

    • Server Ping (Heartbeat):

      • After a successful WebSocket connection, the server sends a Ping message at a fixed interval. The message body looks like: {"type":"ping","time":"1693208170000"}. The time field is the server's timestamp when the Ping was sent.

      • The client must respond with a Pong message upon receipt, with a body like: {"type":"pong","time":"1693208170000"}.

      • If the server doesn't receive a Pong response after 5 consecutive Pings, the server will terminate the connection.

    • Client Ping (Latency Measurement):

      • After a successful WebSocket connection, the client can also initiate a Ping message with a body like: {"type":"ping","time":"1693208170000"}. The time field is the client's timestamp when the Ping was sent.

      • The server will immediately respond with a Pong message, with a body like: {"type":"pong","time":"1693208170000"}. The time field in the Pong will match the time field in the client's Ping.

  • 05. Authentication:

    • Web:

      • Browsers don't allow custom headers during WebSocket connections, so special handling is required.

      • Use the same authentication logic as HTTP. Create a JSON string using the X-edgeX-Api-Signature, X-edgeX-Api-Timestamp key-value pairs, for example: {"X-edgeX-Api-Signature": "00e6b34cf9c3c0ca407cc2fe149fad836206c97201f236137c0e89fd079760470672b5257fa372710b5863d1ec6e0215e5bd6b2c3a319eda88886250a100524706ea3dd81a7fc864893c8c6f674e4a4510c369f939bdc0259a0980dfde882c2d", "X-edgeX-Api-Timestamp": "1705720068228"}.

      • Base64 encode this JSON string.

      • During the WebSocket request, pass the base64 encoded value in the SEC_WEBSOCKET_PROTOCOL header.

    • App/API:

      • App/API WebSocket connections can use custom headers. Therefore, Apps/API can continue using the same authentication logic as HTTP, or they can use the Web authentication method described above.

      • WebSocket is a GET request and there is no need to sign the request body.

URL: /api/v1/private/ws

Payload

{
+  // The type for trading messages is "trade-event". Custom messages have their own defined type. "error" indicates an error message sent by the server.
+  "type": "trade-event",
+  // The body of a trading message has the structure below. The message structure for custom messages will be defined separately by the user.
+  "content": {
+    // The event that triggered the data update
+    "event": "ACCOUNT_UPDATE",
+    // Data update version
+    "version": "1000",
+    // Data
+    "data": {
+      // Account information
+      "account": [
+      ],
+      // Collateral information
+      "collateral": [
+      ],
+      // Collateral transaction details
+      "collateralTransaction": [
+      ],
+      // Position information
+      "position": [
+      ],
+      // Position transaction details
+      "positionTransaction": [
+      ],
+       // Deposit records
+      "deposit": [
+      ],
+      // Withdrawal records
+      "withdraw": [
+      ],
+      // Transfer in records
+      "transferIn": [
+      ],
+      // Transfer out records
+      "transferOut": [
+      ],
+      // Order information
+      "order": [
+      ],
+      // Trade details
+      "orderFillTransaction": [
+      ]
+    }
+  }
+}

(Public WebSocket) Market Data WebSocket Interface

URL: /api/v1/public/ws

Description

  • 01. When subscribing or unsubscribing, the server will validate the channel. For invalid channels, the server will respond with an error message, for example: {"type":"error","content":{"code":"INVALID_CONTRACT_ID""msg":"invalid contractId:100000001"}}

  • 02. The message structure for subscribing and unsubscribing is: {"type": "subscribe", "channel": "ticker.10000001"}.

  • 03. Ping-Pong Mechanism:

    • Server Ping (Heartbeat):

      • After a successful WebSocket connection, the server sends a Ping message at a fixed interval. The message body looks like: {"type":"ping","time":"1693208170000"}. The time field is the server's timestamp when the Ping was sent.

      • The client must respond with a Pong message upon receipt, with a body like: {"type":"pong","time":"1693208170000"}.

      • If the server doesn't receive a Pong response after 5 consecutive Pings, the server will terminate the connection.

    • Client Ping (Latency Measurement):

      • After a successful WebSocket connection, the client can also initiate a Ping message with a body like: {"type":"ping","time":"1693208170000"}. The time field is the client's timestamp when the Ping was sent.

      • The server will immediately respond with a Pong message, with a body like: {"type":"pong","time":"1693208170000"}. The time field in the Pong will match the time field in the client's Ping.

Subscription Metadata

Request

{
+  "type": "subscribe",
+  "channel": "metadata"
+}

Response

{
+  "type": "subscribed",
+  "channel": "metadata"
+}

Payload

{
+  // error
+  "type":  "quote-event",
+  "channel": "metadata",
+  "content": {
+    // snapshot quote-event 
+    "dataType": "Snapshot", 
+    // 
+    "channel": "metadata",
+    "data": [
+      {
+        // Coin information
+        "coin": [
+        ],
+        // Contract information
+        "contract": [
+        ]
+      }
+    ]
+  }
+}

Subscribe to 24-Hour Market Ticker

Channel Explanation

Channel
Description

ticker.{contractId}

Subscribe to the ticker of contract contractId

ticker.all

Subscribe to the ticker of all contracts

ticker.all.1s

Subscribe to the ticker of all contracts (periodic push)

Request

{
+  "type": "subscribe",
+  "channel": "ticker.10000001"
+}

Response

{
+  "type": "subscribed",
+  "channel": "ticker.10000001"
+}

Payload

{
+  "type": "payload",
+  "channel": "ticker.10000001",
+  "content": {
+    "dataType": "Snapshot",
+    "channel": "ticker.10000001",
+    "data": [
+      {
+        "contractId": "string",
+        "priceChange": "string",
+        "priceChangePercent": "string",
+        "trades": "string",
+        "size": "string",
+        "value": "string",
+        "high": "string",
+        "low": "string",
+        "open": "string",
+        "close": "string",
+        "highTime": "string",
+        "lowTime": "string",
+        "startTime": "string",
+        "endTime": "string",
+        "lastPrice": "string"
+      }
+    ]
+  }
+}

Subscribe to K-Line Data

Channel Explanation

Channel
Description

kline.{priceType}.{contractId}.{interval}

Subscribe to the interval K-Line of contract contractId based on priceType

priceType Parameter

Value
Description

LAST_PRICE

Last Price K-Line

MARK_PRICE

Mark Price K-Line

interval Parameter

Value
Description

MINUTE_1

1-Minute K-Line

MINUTE_5

5-Minute K-Line

MINUTE_15

15-Minute K-Line

MINUTE_30

30-Minute K-Line

HOUR_1

1-Hour K-Line

HOUR_2

2-Hour K-Line

HOUR_4

4-Hour K-Line

HOUR_6

6-Hour K-Line

HOUR_8

8-Hour K-Line

HOUR_12

12-Hour K-Line

DAY_1

Daily K-Line

WEEK_1

Weekly K-Line

MONTH_1

Monthly K-Line

Request

{
+  "type": "subscribe",
+  "channel": "kline.LAST_PRICE.10000001.MINUTE_1"
+}

Response

{
+  "type": "subscribed",
+  "channel": "kline.LAST_PRICE.10000001.MINUTE_1"
+}

Payload

{
+  "type": "payload",
+  "channel": "kline.LAST_PRICE.10000001.MINUTE_1",
+  "content": {
+    "dataType": "Changed",
+    "channel": "kline.LAST_PRICE.10000001.MINUTE_1",
+    "data": [
+      {
+        "klineId": "1",
+        "contractId": "10000001",
+        "klineType": "MINUTE_1",
+        "klineTime": "1688365544504",
+        "trades": "5",
+        "size": "10.1",
+        "value": "100000",
+        "high": "31200",
+        "low": "31000",
+        "open": "3150",
+        "close": "31010",
+        "makerBuySize": "5",
+        "makerBuyValue": "150000"
+      }
+    ]
+  }
+}

Subscribe to Order Book

Usage Instructions

After a successful subscription, a full dataset is pushed once initially (depthType=SNAPSHOT), and subsequent pushes will be incremental updates (depthType=CHANGED).

Channel Explanation

Channel
Description

depth.{contractId}.{depth}

Subscribe to the order book of contract contractId with a depth of depth

depth Parameter

Value
Description

15

15 levels

200

200 levels

Request

{
+  "type": "subscribe",
+  "channel": "depth.10000001.15"
+}

Response

{
+  "type": "subscribed",
+  "channel": "depth.10000001.15"
+}

Payload

{
+  "type": "payload",
+  "channel": "depth.10000001.15",
+  "content": {
+    "dataType": "Snapshot",
+    "channel": "depth.10000001.15",
+    "data": [
+      {
+        "startVersion": "string",
+        "endVersion": "string",
+        "level": 0,
+        "contractId": "10000001",
+        "depthType": "Snapshot", // Data type: SNAPSHOT for full data, CHANGED for incremental data
+        "bids": [
+          [
+            "26092",
+            // Price
+            "0.9014"
+            // Size. A size of 0 indicates a deletion. Positive numbers mean increase. Negative numbers mean decrease.
+          ],
+          [
+            "26091",
+            "0.9667"
+          ]
+        ],
+        "asks": [
+          [
+            "26093",
+            "0.964"
+          ],
+          [
+            "26094",
+            "1.0213"
+          ]
+        ]
+      }
+    ]
+  }
+}

Subscribe to Latest Trades

Channel Explanation

Channel
Description

trades.{contractId}

Subscribe to the latest trades of contract contractId

Request

{
+  "type": "subscribe",
+  "channel": "trades.10000001"
+}

Response

{
+  "type": "subscribed",
+  "channel": "trades.10000001"
+}

Payload

{
+  "type": "payload",
+  "channel": "trades.10000001",
+  "content": {
+    "dataType": "Changed",
+    "channel": "trades.10000001",
+    "data": [
+      {
+        "ticketId": "1",
+        "time": "1688365544504",
+        "price": "30065.12",
+        "size": "0.01",
+        "value": "300.6512",
+        "takerOrderId": "10",
+        "makerOrderId": "11",
+        "takerAccountId": "3001",
+        "makerAccountId": "3002",
+        "contractId": "10000001",
+        "isBestMatch": true,
+        "isBuyerMaker": false
+      }
+    ]
+  }
+}

Last updated

\ No newline at end of file diff --git a/package.json b/package.json index eef18f8..5701060 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "@grvt/client": "^1.6.4", + "@starkware-industries/starkware-crypto-utils": "^0.2.1", "axios": "^1.12.2", "ccxt": "^4.5.5", "dotenv": "^17.2.2", diff --git a/src/exchanges/create-adapter.ts b/src/exchanges/create-adapter.ts index 93e4909..a446418 100644 --- a/src/exchanges/create-adapter.ts +++ b/src/exchanges/create-adapter.ts @@ -3,6 +3,7 @@ import { AsterExchangeAdapter, type AsterCredentials } from "./aster-adapter"; import { GrvtExchangeAdapter, type GrvtCredentials } from "./grvt/adapter"; import { LighterExchangeAdapter, type LighterCredentials } from "./lighter/adapter"; import { BackpackExchangeAdapter, type BackpackCredentials } from "./backpack/adapter"; +import { EdgeXExchangeAdapter, type EdgeXCredentials } from "./edgex/adapter"; export interface ExchangeFactoryOptions { symbol: string; @@ -11,9 +12,10 @@ export interface ExchangeFactoryOptions { grvt?: GrvtCredentials; lighter?: LighterCredentials; backpack?: BackpackCredentials; + edgex?: EdgeXCredentials; } -export type SupportedExchangeId = "aster" | "grvt" | "lighter" | "backpack"; +export type SupportedExchangeId = "aster" | "grvt" | "lighter" | "backpack" | "edgex"; export function resolveExchangeId(value?: string | null): SupportedExchangeId { const fallback = (value ?? process.env.EXCHANGE ?? process.env.TRADE_EXCHANGE ?? "aster") @@ -23,6 +25,7 @@ export function resolveExchangeId(value?: string | null): SupportedExchangeId { if (fallback === "grvt") return "grvt"; if (fallback === "lighter") return "lighter"; if (fallback === "backpack") return "backpack"; + if (fallback === "edgex") return "edgex"; return "aster"; } @@ -30,6 +33,7 @@ export function getExchangeDisplayName(id: SupportedExchangeId): string { if (id === "grvt") return "GRVT"; if (id === "lighter") return "Lighter"; if (id === "backpack") return "Backpack"; + if (id === "edgex") return "EdgeX"; return "AsterDex"; } @@ -44,5 +48,8 @@ export function createExchangeAdapter(options: ExchangeFactoryOptions): Exchange if (id === "backpack") { return new BackpackExchangeAdapter({ ...options.backpack, symbol: options.symbol }); } + if (id === "edgex") { + return new EdgeXExchangeAdapter(options.symbol, options.edgex); + } return new AsterExchangeAdapter({ ...options.aster, symbol: options.symbol }); } diff --git a/src/exchanges/edgex/adapter.ts b/src/exchanges/edgex/adapter.ts new file mode 100644 index 0000000..a94cb04 --- /dev/null +++ b/src/exchanges/edgex/adapter.ts @@ -0,0 +1,120 @@ +import type { + AccountListener, + DepthListener, + ExchangeAdapter, + KlineListener, + OrderListener, + TickerListener, +} from "../adapter"; +import type { AsterAccountSnapshot, AsterOrder, AsterDepth, AsterTicker, AsterKline, CreateOrderParams } from "../types"; +import { EdgeXGateway } from "./gateway"; + +export interface EdgeXCredentials { + accountId?: string; + privateKey?: string; + positionId?: bigint; + baseUrl?: string; + wsPublicUrl?: string; + wsPrivateUrl?: string; + orderExpirationMs?: number; + logger?: (context: string, error: unknown) => void; +} + +export class EdgeXExchangeAdapter implements ExchangeAdapter { + readonly id = "edgex"; + + private readonly gateway: EdgeXGateway; + private initialized = false; + + constructor(symbol: string, credentials: EdgeXCredentials = {}) { + const accountId = credentials.accountId ?? process.env.EDGEX_ACCOUNT_ID; + const privateKey = credentials.privateKey ?? process.env.EDGEX_PRIVATE_KEY; + const positionIdValue = credentials.positionId ?? parseOptionalBigInt(process.env.EDGEX_POSITION_ID); + + if (!accountId) throw new Error("Missing EDGEX_ACCOUNT_ID environment variable"); + if (!privateKey) throw new Error("Missing EDGEX_PRIVATE_KEY environment variable"); + + this.gateway = new EdgeXGateway({ + accountId, + privateKey, + symbol, + positionId: positionIdValue, + baseUrl: credentials.baseUrl ?? process.env.EDGEX_BASE_URL, + wsPublicUrl: credentials.wsPublicUrl ?? process.env.EDGEX_WS_PUBLIC_URL, + wsPrivateUrl: credentials.wsPrivateUrl ?? process.env.EDGEX_WS_PRIVATE_URL, + orderExpirationMs: + credentials.orderExpirationMs ?? parseOptionalInt(process.env.EDGEX_ORDER_TTL_MS) ?? undefined, + logger: credentials.logger, + }); + } + + supportsTrailingStops(): boolean { + return false; + } + + watchAccount(cb: AccountListener): void { + void this.ensureInitialized(); + this.gateway.onAccount((snapshot: AsterAccountSnapshot) => cb(snapshot)); + } + + watchOrders(cb: OrderListener): void { + void this.ensureInitialized(); + this.gateway.onOrders((orders: AsterOrder[]) => cb(orders)); + } + + watchDepth(_symbol: string, cb: DepthListener): void { + void this.ensureInitialized(); + this.gateway.onDepth(_symbol, (depth: AsterDepth) => cb(depth)); + } + + watchTicker(_symbol: string, cb: TickerListener): void { + void this.ensureInitialized(); + this.gateway.onTicker(_symbol, (ticker: AsterTicker) => cb(ticker)); + } + + watchKlines(_symbol: string, interval: string, cb: KlineListener): void { + void this.ensureInitialized(); + this.gateway.onKlines(interval, (klines: AsterKline[]) => cb(klines)); + } + + async createOrder(params: CreateOrderParams): Promise { + await this.ensureInitialized(); + return this.gateway.createOrder(params); + } + + async cancelOrder(params: { symbol: string; orderId: number | string }): Promise { + await this.ensureInitialized(); + await this.gateway.cancelOrder(String(params.orderId)); + } + + async cancelOrders(params: { symbol: string; orderIdList: Array }): Promise { + await this.ensureInitialized(); + await this.gateway.cancelOrders(params.orderIdList.map(String)); + } + + async cancelAllOrders(params: { symbol: string }): Promise { + await this.ensureInitialized(); + await this.gateway.cancelAllOrders(); + } + + private async ensureInitialized(): Promise { + if (this.initialized) return; + await this.gateway.ensureInitialized(); + this.initialized = true; + } +} + +function parseOptionalInt(value?: string): number | undefined { + if (!value) return undefined; + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : undefined; +} + +function parseOptionalBigInt(value?: string): bigint | undefined { + if (!value) return undefined; + try { + return BigInt(value); + } catch { + return undefined; + } +} diff --git a/src/exchanges/edgex/client.ts b/src/exchanges/edgex/client.ts new file mode 100644 index 0000000..65b5d5c --- /dev/null +++ b/src/exchanges/edgex/client.ts @@ -0,0 +1,130 @@ +import * as crypto from "crypto"; +import type { AxiosInstance, AxiosRequestConfig } from "axios"; +import axios from "axios"; +import { extractMessage } from "../../utils/errors"; +import { EdgeXSignature, buildQueryString } from "./signature"; + +export interface EdgeXHttpClientOptions { + baseUrl: string; + privateKey: string; + timeout?: number; +} + +export interface EdgeXResponse { + code: string; + data: T; + msg?: string | null; + errorParam?: unknown; +} + +export class EdgeXHttpClient { + private readonly axios: AxiosInstance; + private readonly signer: EdgeXSignature; + + constructor(options: EdgeXHttpClientOptions) { + this.axios = axios.create({ + baseURL: options.baseUrl, + timeout: options.timeout ?? 30_000, + }); + this.signer = new EdgeXSignature(options.privateKey); + } + + getSigner(): EdgeXSignature { + return this.signer; + } + + async get(path: string, query?: Record): Promise> { + return this.request({ method: "GET", path, params: query }); + } + + async post(path: string, body?: unknown): Promise> { + return this.request({ method: "POST", path, data: body }); + } + + private buildHeaders(signature: string, timestamp: string): Record { + return { + "Content-Type": "application/json", + "X-edgeX-Api-Timestamp": timestamp, + "X-edgeX-Api-Signature": signature, + }; + } + + private async request(input: { + method: string; + path: string; + data?: unknown; + params?: Record; + }): Promise> { + const normalizedParams = normalizeParams(input.params); + const signature = this.signer.createHttpSignature({ + method: input.method, + path: input.path, + body: input.data, + query: normalizedParams, + }); + const serializedQuery = normalizedParams ? buildQueryString(normalizedParams) : ""; + const url = serializedQuery ? appendQueryString(input.path, serializedQuery) : input.path; + const config: AxiosRequestConfig = { + method: input.method, + url, + data: input.data, + headers: this.buildHeaders(signature.signature, signature.timestamp), + }; + try { + const response = await this.axios.request>(config); + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + const status = error.response?.status; + let detail: string | undefined; + if (error.response?.data != null) { + try { + detail = typeof error.response.data === "string" + ? error.response.data + : JSON.stringify(error.response.data); + } catch { + detail = undefined; + } + } + const statusLabel = status ? ` (${status})` : ""; + const message = detail ?? extractMessage(error); + throw new Error(`EdgeX request failed${statusLabel}: ${message}`); + } + throw new Error(extractMessage(error)); + } + } +} + +function appendQueryString(path: string, query: string): string { + if (!query) return path; + const separator = path.includes("?") + ? path.endsWith("?") || path.endsWith("&") ? "" : "&" + : "?"; + return `${path}${separator}${query}`; +} + +export function computeNonceFromClientOrderId(clientOrderId: string): number { + const hash = crypto.createHash("sha256").update(clientOrderId).digest("hex"); + return parseInt(hash.slice(0, 8), 16); +} + +function normalizeParams(params?: Record): Record | undefined { + if (!params) return undefined; + const normalized: Record = {}; + for (const [key, value] of Object.entries(params)) { + if (value == null) continue; + if (Array.isArray(value)) { + normalized[key] = value.map(stringifyPrimitive).join(","); + continue; + } + normalized[key] = stringifyPrimitive(value); + } + return normalized; +} + +function stringifyPrimitive(value: unknown): string { + if (value == null) return ""; + if (typeof value === "boolean") return value ? "true" : "false"; + if (typeof value === "number") return Number.isFinite(value) ? value.toString() : ""; + return String(value); +} diff --git a/src/exchanges/edgex/decimal.ts b/src/exchanges/edgex/decimal.ts new file mode 100644 index 0000000..9a4d019 --- /dev/null +++ b/src/exchanges/edgex/decimal.ts @@ -0,0 +1,115 @@ +import { strict as assert } from "assert"; + +export function countBase10Scale(resolution: bigint): number { + let scale = 0; + let value = resolution; + while (value % 10n === 0n) { + value /= 10n; + scale += 1; + } + if (value !== 1n) { + throw new Error(`Resolution ${resolution.toString()} is not a power of 10`); + } + return scale; +} + +export function decimalToBigInt(value: number | string, scale: number): bigint { + const normalized = normalizeDecimal(typeof value === "number" ? value.toString() : value); + const [intPart, fracPart = ""] = normalized.split("."); + assert(fracPart.length <= scale, `Value ${value} exceeds scale ${scale}`); + const paddedFraction = (fracPart + "0".repeat(scale)).slice(0, scale); + const digits = stripLeadingZeros(intPart + paddedFraction); + return digits.length === 0 ? 0n : BigInt(digits); +} + +export function bigIntToDecimal(value: bigint, scale: number): string { + const negative = value < 0n; + const abs = negative ? -value : value; + const factor = 10n ** BigInt(scale); + const intPart = abs / factor; + const fracPart = abs % factor; + if (scale === 0) { + return `${negative ? "-" : ""}${intPart.toString()}`; + } + const fracStr = fracPart.toString().padStart(scale, "0").replace(/0+$/, ""); + if (fracStr.length === 0) { + return `${negative ? "-" : ""}${intPart.toString()}`; + } + return `${negative ? "-" : ""}${intPart.toString()}.${fracStr}`; +} + +export function multiplyByDecimal(value: bigint, rate: string, roundUp = false): bigint { + const { numerator, denominator } = decimalToFraction(rate); + const product = value * numerator; + if (!roundUp) { + return product / denominator; + } + return (product + denominator - 1n) / denominator; +} + +export function decimalToFraction(value: string): { numerator: bigint; denominator: bigint } { + const normalized = normalizeDecimal(value); + if (!normalized.includes(".")) { + return { numerator: BigInt(normalized), denominator: 1n }; + } + const negative = normalized.startsWith("-"); + const unsigned = negative ? normalized.slice(1) : normalized; + const parts = unsigned.split("."); + const intPart = parts[0] ?? "0"; + const fracPart = parts[1] ?? ""; + const denominator = 10n ** BigInt(fracPart.length); + const magnitude = BigInt(stripLeadingZeros(intPart + fracPart)); + const numerator = negative ? -magnitude : magnitude; + return { numerator, denominator }; +} + +export function getScaleFromDenominator(denominator: bigint): number { + let scale = 0; + let value = denominator; + while (value > 1n) { + if (value % 10n !== 0n) { + throw new Error(`Denominator ${denominator.toString()} is not a power of 10`); + } + value /= 10n; + scale += 1; + } + return scale; +} + +export function formatDecimal(numerator: bigint, scale: number): string { + const negative = numerator < 0n; + let absValue = negative ? -numerator : numerator; + if (scale === 0) { + return `${negative ? "-" : ""}${absValue.toString()}`; + } + const factor = 10n ** BigInt(scale); + const intPart = absValue / factor; + let fracPart = (absValue % factor).toString().padStart(scale, "0"); + fracPart = fracPart.replace(/0+$/, ""); + if (fracPart.length === 0) { + return `${negative ? "-" : ""}${intPart.toString()}`; + } + return `${negative ? "-" : ""}${intPart.toString()}.${fracPart}`; +} + +function normalizeDecimal(input: string): string { + const trimmed = input.trim(); + if (!/^[-+]?((\d+\.?\d*)|(\.\d+))$/.test(trimmed)) { + throw new Error(`Invalid decimal value: ${input}`); + } + const negative = trimmed.startsWith("-"); + const unsigned = trimmed.replace(/^[-+]/, ""); + const [rawInt = "0", rawFrac = ""] = unsigned.split("."); + const intDigits = stripLeadingZeros(rawInt); + const fracDigits = rawFrac.replace(/0+$/, ""); + const magnitude = fracDigits.length > 0 ? `${intDigits}.${fracDigits}` : intDigits; + if (magnitude === "0") { + return "0"; + } + return negative ? `-${magnitude}` : magnitude; +} + +function stripLeadingZeros(value: string): string { + const stripped = value.replace(/^0+/, ""); + return stripped.length === 0 ? "0" : stripped; +} diff --git a/src/exchanges/edgex/gateway.ts b/src/exchanges/edgex/gateway.ts new file mode 100644 index 0000000..cbca85b --- /dev/null +++ b/src/exchanges/edgex/gateway.ts @@ -0,0 +1,1015 @@ +import { setInterval, clearInterval, setTimeout, clearTimeout } from "timers"; +import WebSocket from "ws"; +import axios from "axios"; +import type { + AsterAccountAsset, + AsterAccountPosition, + AsterAccountSnapshot, + AsterDepth, + AsterKline, + AsterOrder, + AsterTicker, + CreateOrderParams, + OrderSide, + OrderType, +} from "../types"; +import type { EdgeXResponse } from "./client"; +import { EdgeXHttpClient, computeNonceFromClientOrderId } from "./client"; +import { + countBase10Scale, + decimalToBigInt, + decimalToFraction, + formatDecimal, + getScaleFromDenominator, +} from "./decimal"; +import { signLimitOrder } from "./l2"; +import { EdgeXSignature } from "./signature"; +import { extractMessage } from "../../utils/errors"; + +const META_PATH = "/api/v1/public/meta/getMetaData"; +const ACCOUNT_ASSET_PATH = "/api/v1/private/account/getAccountAsset"; +const ACTIVE_ORDER_PATH = "/api/v1/private/order/getActiveOrderPage"; +const CREATE_ORDER_PATH = "/api/v1/private/order/createOrder"; +const CANCEL_ORDER_BY_ID_PATH = "/api/v1/private/order/cancelOrderById"; +const CANCEL_ALL_ORDERS_PATH = "/api/v1/private/order/cancelAllOrder"; + +const PUBLIC_WS_PATH = "/api/v1/public/ws"; +const PRIVATE_WS_PATH = "/api/v1/private/ws"; + +const DEFAULT_BASE_URL = "https://pro.edgex.exchange"; +const DEFAULT_WS_URL = "wss://quote.edgex.exchange"; +const DEFAULT_POLL_INTERVAL_MS = 5_000; +const DEFAULT_ORDER_EXPIRATION_MS = 14 * 24 * 60 * 60 * 1000; // 14 days +const PRIVATE_EXPIRE_OFFSET_MS = 10 * 24 * 60 * 60 * 1000; // 10 days +const EDGE_KLINE_INTERVAL: Record = { + "1m": "MINUTE_1", + "3m": "MINUTE_3", + "5m": "MINUTE_5", + "15m": "MINUTE_15", + "30m": "MINUTE_30", + "1h": "HOUR_1", + "4h": "HOUR_4", + "12h": "HOUR_12", + "1d": "DAY_1", +}; + +interface EdgeXGatewayOptions { + accountId: string; + privateKey: string; + symbol: string; + positionId?: bigint; + baseUrl?: string; + wsPublicUrl?: string; + wsPrivateUrl?: string; + orderExpirationMs?: number; + logger?: (context: string, error: unknown) => void; +} + +interface ContractMetadata { + contractId: string; + contractName: string; + takerFeeRate: string; + makerFeeRate: string; + syntheticResolution: bigint; + collateralResolution: bigint; + syntheticScale: number; + collateralScale: number; + syntheticAssetId: string; + collateralAssetId: string; + feeAssetId: string; + tickSize: string; + stepSize: string; +} + +interface CollateralState { + balance: string; + available: string; + coinId: string; + coinName: string; +} + +interface PositionState { + contractId: string; + size: string; + avgEntryPrice?: string; + unrealizedPnl?: string; + markPrice?: string; +} + +interface AccountState { + totalEquity: string; + availableBalance: string; + collateral: Map; + positions: Map; +} + +export class EdgeXGateway { + readonly symbol: string; + + private readonly accountId: string; + private readonly positionId: bigint; + private readonly privateKey: string; + private readonly http: EdgeXHttpClient; + private readonly signer: EdgeXSignature; + private readonly publicHttp: ReturnType; + private readonly publicWsBase: string; + private readonly privateWsBase: string; + private readonly orderExpirationMs: number; + private readonly logger: (context: string, error: unknown) => void; + + private readonly accountListeners = new Set<(snapshot: AsterAccountSnapshot) => void>(); + private readonly ordersListeners = new Set<(orders: AsterOrder[]) => void>(); + private readonly depthListeners = new Set<(depth: AsterDepth) => void>(); + private readonly tickerListeners = new Set<(ticker: AsterTicker) => void>(); + private readonly klineListeners = new Map void>>(); + + private accountState: AccountState = { + totalEquity: "0", + availableBalance: "0", + collateral: new Map(), + positions: new Map(), + }; + private openOrders: Map = new Map(); + private depthSnapshot: AsterDepth | null = null; + private tickerSnapshot: AsterTicker | null = null; + private klineCache: Map = new Map(); + + private metadata: ContractMetadata | null = null; + + private publicWs: WebSocket | null = null; + private privateWs: WebSocket | null = null; + private publicWsReady = false; + private privateWsReady = false; + private publicReconnectTimer: NodeJS.Timeout | null = null; + private privateReconnectTimer: NodeJS.Timeout | null = null; + + private pollTimer: NodeJS.Timeout | null = null; + + constructor(options: EdgeXGatewayOptions) { + this.symbol = options.symbol.toUpperCase(); + this.accountId = options.accountId; + const defaultPositionId = parseAccountIdToBigInt(options.accountId); + this.positionId = options.positionId ?? defaultPositionId; + this.privateKey = options.privateKey; + const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL; + this.http = new EdgeXHttpClient({ baseUrl, privateKey: options.privateKey }); + this.signer = this.http.getSigner(); + this.publicHttp = axios.create({ baseURL: baseUrl, timeout: 15_000 }); + this.publicWsBase = options.wsPublicUrl ?? DEFAULT_WS_URL; + this.privateWsBase = options.wsPrivateUrl ?? DEFAULT_WS_URL; + this.orderExpirationMs = options.orderExpirationMs ?? DEFAULT_ORDER_EXPIRATION_MS; + this.logger = options.logger ?? ((context, error) => console.error(`[EdgeXGateway] ${context}`, error)); + } + + async ensureInitialized(): Promise { + if (this.metadata) return; + await this.loadMetadata(); + await Promise.all([this.refreshAccount(), this.refreshOpenOrders(), this.refreshDepth(), this.refreshTicker()]); + this.startPolling(); + this.openPublicWs(); + this.openPrivateWs(); + } + + onAccount(listener: (snapshot: AsterAccountSnapshot) => void): () => void { + this.accountListeners.add(listener); + listener(this.buildAccountSnapshot()); + return () => this.accountListeners.delete(listener); + } + + onOrders(listener: (orders: AsterOrder[]) => void): () => void { + this.ordersListeners.add(listener); + listener(this.currentOrders()); + return () => this.ordersListeners.delete(listener); + } + + onDepth(_symbol: string, listener: (depth: AsterDepth) => void): () => void { + this.depthListeners.add(listener); + if (this.depthSnapshot) listener(this.depthSnapshot); + return () => this.depthListeners.delete(listener); + } + + onTicker(_symbol: string, listener: (ticker: AsterTicker) => void): () => void { + this.tickerListeners.add(listener); + if (this.tickerSnapshot) listener(this.tickerSnapshot); + return () => this.tickerListeners.delete(listener); + } + + onKlines(_symbol: string, interval: string, listener: (klines: AsterKline[]) => void): () => void { + if (!this.klineListeners.has(interval)) { + this.klineListeners.set(interval, new Set()); + void this.refreshKlines(interval); + this.subscribePublicChannel(klineChannel(this.contractId, interval)); + } + const listeners = this.klineListeners.get(interval)!; + listeners.add(listener); + const cache = this.klineCache.get(interval); + if (cache) listener(cache); + return () => { + listeners.delete(listener); + }; + } + + async createOrder(params: CreateOrderParams): Promise { + await this.ensureInitialized(); + const meta = this.metadata; + if (!meta) throw new Error("EdgeX metadata not initialized"); + + const side = params.side as OrderSide; + const orderType = mapOrderType(params.type); + const timeInForce = mapTimeInForce(params.timeInForce, orderType); + + const sizeStr = normalizeDecimalInput(params.quantity ?? 0); + const priceStr = orderType === "LIMIT" ? normalizeDecimalInput(params.price ?? 0) : "0"; + + const priceFraction = decimalToFraction(priceStr); + const sizeFraction = decimalToFraction(sizeStr); + const priceScale = getScaleFromDenominator(priceFraction.denominator); + const sizeScale = getScaleFromDenominator(sizeFraction.denominator); + + const valueNumerator = priceFraction.numerator * sizeFraction.numerator; + const valueScale = priceScale + sizeScale; + const valueDecimal = formatDecimal(valueNumerator, valueScale); + + const amountSynthetic = decimalToBigInt(sizeStr, meta.syntheticScale); + const amountCollateral = decimalToBigInt(valueDecimal, meta.collateralScale); + + const feeRate = Number(meta.takerFeeRate || 0); + const feeDecimalValue = Math.ceil(Number(valueDecimal) * feeRate); + const feeDecimalString = feeDecimalValue.toString(); + const amountFee = BigInt(feeDecimalValue) * 10n ** BigInt(meta.collateralScale); + + const clientOrderIdValue = (params as any).clientOrderId; + const clientOrderId = typeof clientOrderIdValue === "string" && clientOrderIdValue.length > 0 + ? clientOrderIdValue + : generateClientOrderId(); + const nonce = computeNonceFromClientOrderId(clientOrderId); + const l2ExpireTime = Date.now() + this.orderExpirationMs; + const expireTime = Math.max(l2ExpireTime - PRIVATE_EXPIRE_OFFSET_MS, Date.now()); + const expirationHours = Math.floor(l2ExpireTime / (60 * 60 * 1000)); + + const signature = signLimitOrder({ + isBuy: side === "BUY", + amountSynthetic, + amountCollateral, + amountFee, + syntheticAssetId: meta.syntheticAssetId, + collateralAssetId: meta.collateralAssetId, + feeAssetId: meta.feeAssetId, + positionId: this.positionId, + nonce, + expirationHours, + privateKey: this.privateKey, + }); + + const body: Record = { + accountId: this.accountId, + contractId: meta.contractId, + price: priceStr, + size: sizeStr, + type: orderType, + timeInForce, + side, + l2Signature: signature.signature, + l2Nonce: nonce.toString(), + l2ExpireTime: l2ExpireTime.toString(), + l2Value: valueDecimal, + l2Size: sizeStr, + l2LimitFee: feeDecimalString, + clientOrderId, + expireTime: expireTime.toString(), + reduceOnly: parseReduceOnly(params.reduceOnly), + }; + + if (params.stopPrice != null) { + body.triggerPrice = normalizeDecimalInput(params.stopPrice); + body.triggerPriceType = "LAST_PRICE"; + } + + const response = await this.http.post<{ orderId: string }>(CREATE_ORDER_PATH, body); + if (response.code !== "SUCCESS") { + throw new Error(`EdgeX create order failed: ${response.code}`); + } + + const orderId = response.data?.orderId ?? ""; + const order = this.mapOrder({ + orderId, + clientOrderId, + contractId: meta.contractId, + accountId: this.accountId, + status: "OPEN", + price: priceStr, + size: sizeStr, + side, + type: orderType, + createdTime: Date.now(), + }); + this.openOrders.set(orderId, order); + this.emitOrders(); + return order; + } + + async cancelOrder(orderId: string): Promise { + await this.http.post(CANCEL_ORDER_BY_ID_PATH, { + accountId: this.accountId, + orderIdList: [orderId], + }); + this.openOrders.delete(orderId); + this.emitOrders(); + } + + async cancelOrders(orderIds: string[]): Promise { + if (orderIds.length === 0) return; + await this.http.post(CANCEL_ORDER_BY_ID_PATH, { + accountId: this.accountId, + orderIdList: orderIds, + }); + for (const id of orderIds) this.openOrders.delete(id); + this.emitOrders(); + } + + async cancelAllOrders(): Promise { + const meta = this.metadata; + if (!meta) return; + await this.http.post(CANCEL_ALL_ORDERS_PATH, { + accountId: this.accountId, + filterContractIdList: [meta.contractId], + }); + this.openOrders.clear(); + this.emitOrders(); + } + + private async loadMetadata(): Promise { + try { + const response = await this.publicHttp.get(META_PATH); + if (response.data.code !== "SUCCESS") { + throw new Error(`Meta request failed: ${response.data.code}`); + } + const data = response.data.data ?? {}; + const contractList: any[] = data.contractList ?? []; + const globalMeta = data.global ?? {}; + const collateralCoin = globalMeta.starkExCollateralCoin ?? {}; + + const contract = findContract(contractList, this.symbol); + if (!contract) { + throw new Error(`EdgeX contract metadata not found for symbol ${this.symbol}`); + } + + const syntheticResolution = parseResolution(contract.starkExResolution ?? "0x1"); + const collateralResolution = parseResolution(collateralCoin.starkExResolution ?? "0xF4240"); + + this.metadata = { + contractId: contract.contractId, + contractName: contract.contractName, + takerFeeRate: contract.defaultTakerFeeRate ?? "0", + makerFeeRate: contract.defaultMakerFeeRate ?? "0", + syntheticResolution, + collateralResolution, + syntheticScale: countBase10Scale(syntheticResolution), + collateralScale: countBase10Scale(collateralResolution), + syntheticAssetId: contract.starkExSyntheticAssetId ?? "", + collateralAssetId: collateralCoin.starkExAssetId ?? "", + feeAssetId: collateralCoin.starkExAssetId ?? "", + tickSize: contract.tickSize ?? "0.1", + stepSize: contract.stepSize ?? "0.001", + }; + } catch (error) { + throw new Error(`Failed to load EdgeX metadata: ${extractMessage(error)}`); + } + } + + private async refreshAccount(): Promise { + try { + const response = await this.http.get(ACCOUNT_ASSET_PATH, { + accountId: this.accountId, + }); + if (response.code !== "SUCCESS") return; + const data = response.data ?? {}; + const account = data.account ?? {}; + const collateralList: any[] = data.collateralList ?? data.collateralAssetList ?? []; + const positionList: any[] = data.positionList ?? []; + const positionAssetList: any[] = data.positionAssetList ?? []; + + const collateralMap = new Map(); + for (const entry of collateralList) { + const coinId = entry.coinId ?? entry.coinName ?? "USDT"; + const coinName = entry.coinName ?? coinId; + const balance = entry.amount ?? entry.totalBalance ?? entry.walletBalance ?? "0"; + const available = entry.availableBalance ?? entry.availableAmount ?? balance; + collateralMap.set(coinId, { + coinId, + coinName, + balance: balance.toString(), + available: available.toString(), + }); + } + + const positionMap = new Map(); + for (const entry of positionList) { + const contractId = entry.contractId ?? entry.symbol; + if (!contractId) continue; + const existing: PositionState = positionMap.get(contractId) ?? { + contractId, + size: "0", + }; + if (entry.openSize != null) existing.size = entry.openSize.toString(); + if (entry.avgEntryPrice != null) existing.avgEntryPrice = entry.avgEntryPrice.toString(); + if (entry.unrealizePnl != null) existing.unrealizedPnl = entry.unrealizePnl.toString(); + positionMap.set(contractId, existing); + } + for (const entry of positionAssetList) { + const contractId = entry.contractId ?? entry.symbol; + if (!contractId) continue; + const existing: PositionState = positionMap.get(contractId) ?? { + contractId, + size: "0", + }; + if (entry.positionValue != null) existing.markPrice = entry.positionValue.toString(); + if (entry.avgEntryPrice != null) existing.avgEntryPrice = entry.avgEntryPrice.toString(); + if (entry.unrealizePnl != null) existing.unrealizedPnl = entry.unrealizePnl.toString(); + positionMap.set(contractId, existing); + } + + this.accountState = { + totalEquity: account.totalEquity ?? account.totalBalance ?? "0", + availableBalance: account.availableBalance ?? account.availableAmount ?? "0", + collateral: collateralMap, + positions: positionMap, + }; + this.emitAccount(); + } catch (error) { + this.logger("refreshAccount", error); + } + } + + private async refreshOpenOrders(): Promise { + try { + const meta = this.metadata; + if (!meta) return; + const response = await this.http.get(ACTIVE_ORDER_PATH, { + accountId: this.accountId, + filterContractIdList: meta.contractId, + }); + if (response.code !== "SUCCESS") return; + + const orders = response.data?.dataList ?? response.data?.orderList ?? []; + this.openOrders.clear(); + for (const order of orders) { + const mapped = this.mapOrder(order); + this.openOrders.set(mapped.orderId.toString(), mapped); + } + this.emitOrders(); + } catch (error) { + this.logger("refreshOpenOrders", error); + } + } + + private async refreshTicker(): Promise { + try { + const meta = this.metadata; + if (!meta) return; + const response = await this.publicHttp.get("/api/v1/public/quote/getTicker", { + params: { contractId: meta.contractId }, + }); + if (response.data.code !== "SUCCESS") return; + const ticker = response.data.data?.[0]; + if (!ticker) return; + this.tickerSnapshot = { + symbol: this.symbol, + lastPrice: ticker.lastPrice ?? ticker.close ?? "0", + openPrice: ticker.open ?? "0", + highPrice: ticker.high ?? "0", + lowPrice: ticker.low ?? "0", + volume: ticker.size ?? "0", + quoteVolume: ticker.value ?? "0", + eventTime: Date.now(), + }; + this.emitTicker(); + } catch (error) { + this.logger("refreshTicker", error); + } + } + + private async refreshDepth(): Promise { + try { + const meta = this.metadata; + if (!meta) return; + const response = await this.publicHttp.get("/api/v1/public/quote/getDepth", { + params: { contractId: meta.contractId, level: 15 }, + }); + if (response.data.code !== "SUCCESS") return; + const info = response.data.data?.[0]; + if (!info) return; + this.depthSnapshot = { + lastUpdateId: Number(info.endVersion ?? Date.now()), + bids: (info.bids ?? []).map((level: [string, string]) => [level[0], level[1]]), + asks: (info.asks ?? []).map((level: [string, string]) => [level[0], level[1]]), + eventTime: Date.now(), + }; + this.emitDepth(); + } catch (error) { + this.logger("refreshDepth", error); + } + } + + private async refreshKlines(interval: string): Promise { + const edgeInterval = EDGE_KLINE_INTERVAL[interval]; + if (!edgeInterval) return; + try { + const meta = this.metadata; + if (!meta) return; + const response = await this.publicHttp.get("/api/v1/public/quote/getKline", { + params: { + contractId: meta.contractId, + klineType: edgeInterval, + priceType: "LAST_PRICE", + }, + }); + if (response.data.code !== "SUCCESS") return; + const klines: AsterKline[] = (response.data.data ?? []).map((item: any) => ({ + eventTime: Date.now(), + symbol: this.symbol, + interval, + openTime: Number(item.klineTime ?? 0), + closeTime: Number(item.klineTime ?? 0) + intervalToMs(interval), + open: item.open ?? "0", + high: item.high ?? "0", + low: item.low ?? "0", + close: item.close ?? "0", + volume: item.size ?? "0", + numberOfTrades: Number(item.trades ?? 0), + })); + this.klineCache.set(interval, klines); + this.emitKlines(interval); + } catch (error) { + this.logger("refreshKlines", error); + } + } + + private openPublicWs(): void { + if (this.publicWs && this.publicWsReady) return; + const url = `${this.publicWsBase}${PUBLIC_WS_PATH}?timestamp=${Date.now()}`; + try { + const ws = new WebSocket(url); + ws.on("open", () => { + this.publicWsReady = true; + this.subscribePublicChannel(`ticker.${this.contractId}`); + this.subscribePublicChannel(`depth.${this.contractId}.15`); + for (const interval of this.klineListeners.keys()) { + this.subscribePublicChannel(klineChannel(this.contractId, interval)); + } + }); + ws.on("message", (data) => this.handlePublicMessage(data)); + ws.on("close", () => { + this.publicWsReady = false; + this.schedulePublicReconnect(); + }); + ws.on("error", (error) => this.logger("publicWs:error", error)); + this.publicWs = ws; + } catch (error) { + this.logger("publicWs", error); + this.schedulePublicReconnect(); + } + } + + private openPrivateWs(): void { + if (this.privateWs && this.privateWsReady) return; + const url = `${this.privateWsBase}${PRIVATE_WS_PATH}?accountId=${this.accountId}`; + const headers = this.signer.createWebsocketHeaders(this.accountId); + try { + const ws = new WebSocket(url, undefined, { + headers: { + "X-edgeX-Api-Timestamp": headers.timestamp, + "X-edgeX-Api-Signature": headers.signature, + }, + }); + ws.on("open", () => { + this.privateWsReady = true; + }); + ws.on("message", (data) => this.handlePrivateMessage(data)); + ws.on("close", () => { + this.privateWsReady = false; + this.schedulePrivateReconnect(); + }); + ws.on("error", (error) => this.logger("privateWs:error", error)); + this.privateWs = ws; + } catch (error) { + this.logger("privateWs", error); + this.schedulePrivateReconnect(); + } + } + + private subscribePublicChannel(channel: string): void { + if (!this.publicWs || this.publicWs.readyState !== WebSocket.OPEN) return; + this.publicWs.send(JSON.stringify({ type: "subscribe", channel })); + } + + private schedulePublicReconnect(): void { + if (this.publicReconnectTimer) return; + this.publicReconnectTimer = setTimeout(() => { + this.publicReconnectTimer = null; + this.openPublicWs(); + }, 2_000); + } + + private schedulePrivateReconnect(): void { + if (this.privateReconnectTimer) return; + this.privateReconnectTimer = setTimeout(() => { + this.privateReconnectTimer = null; + this.openPrivateWs(); + }, 2_000); + } + + private handlePublicMessage(raw: WebSocket.RawData): void { + try { + const text = typeof raw === "string" ? raw : raw.toString("utf8"); + const message = JSON.parse(text); + if (message?.type === "ping") { + this.publicWs?.send(JSON.stringify({ type: "pong", time: message.time })); + return; + } + const quote = extractQuoteEvent(message); + if (!quote) return; + const { channel, payload } = quote; + if (channel.startsWith("ticker")) { + this.tickerSnapshot = { + symbol: this.symbol, + lastPrice: payload?.lastPrice ?? payload?.close ?? "0", + openPrice: payload?.open ?? "0", + highPrice: payload?.high ?? "0", + lowPrice: payload?.low ?? "0", + volume: payload?.size ?? "0", + quoteVolume: payload?.value ?? "0", + eventTime: Date.now(), + }; + this.emitTicker(); + return; + } + if (channel.startsWith("depth")) { + const bids = payload?.bids ?? []; + const asks = payload?.asks ?? []; + this.depthSnapshot = { + lastUpdateId: Number(payload?.endVersion ?? Date.now()), + bids: bids.map((level: [string, string]) => [level[0], level[1]]), + asks: asks.map((level: [string, string]) => [level[0], level[1]]), + eventTime: Date.now(), + }; + this.emitDepth(); + return; + } + if (channel.startsWith("kline")) { + const interval = parseEdgeInterval(channel); + if (!interval) return; + const dataList: any[] = Array.isArray(payload?.data) ? payload.data : payload ? [payload] : []; + const klines: AsterKline[] = dataList.map((item) => ({ + eventTime: Date.now(), + symbol: this.symbol, + interval, + openTime: Number(item.klineTime ?? 0), + closeTime: Number(item.klineTime ?? 0) + intervalToMs(interval), + open: item.open ?? "0", + high: item.high ?? "0", + low: item.low ?? "0", + close: item.close ?? "0", + volume: item.size ?? "0", + numberOfTrades: Number(item.trades ?? 0), + })); + this.klineCache.set(interval, klines); + this.emitKlines(interval); + } + } catch (error) { + this.logger("handlePublicMessage", error); + } + } + + private handlePrivateMessage(raw: WebSocket.RawData): void { + try { + const text = typeof raw === "string" ? raw : raw.toString("utf8"); + const message = JSON.parse(text); + if (message?.type === "ping") { + this.privateWs?.send(JSON.stringify({ type: "pong", time: message.time })); + return; + } + if (message?.type === "trade-event") { + const data = message.content?.data ?? message.data ?? {}; + this.applyTradeEvent(data); + } + } catch (error) { + this.logger("handlePrivateMessage", error); + } + } + + private applyTradeEvent(event: any): void { + if (event?.account) { + for (const accountUpdate of event.account) { + if (accountUpdate?.totalEquity) this.accountState.totalEquity = accountUpdate.totalEquity.toString(); + if (accountUpdate?.availableBalance) this.accountState.availableBalance = accountUpdate.availableBalance.toString(); + } + this.emitAccount(); + } + if (event?.collateral) { + for (const entry of event.collateral) { + const coinId = entry.coinId ?? "USDT"; + this.accountState.collateral.set(coinId, { + coinId, + coinName: entry.coinName ?? coinId, + balance: entry.balance ?? entry.amount ?? "0", + available: entry.availableBalance ?? entry.balance ?? "0", + }); + } + this.emitAccount(); + } + if (event?.position) { + for (const pos of event.position) { + const contractId = pos.contractId; + if (!contractId) continue; + const existing: PositionState = this.accountState.positions.get(contractId) ?? { + contractId, + size: "0", + }; + if (pos.size != null) existing.size = pos.size.toString(); + if (pos.entryPrice != null) existing.avgEntryPrice = pos.entryPrice.toString(); + if (pos.unrealizedPnl != null) existing.unrealizedPnl = pos.unrealizedPnl.toString(); + this.accountState.positions.set(contractId, existing); + } + this.emitAccount(); + } + if (event?.order) { + for (const order of event.order) { + const mapped = this.mapOrder(order); + if (mapped.status === "CANCELED" || mapped.status === "FILLED") { + this.openOrders.delete(mapped.orderId.toString()); + } else { + this.openOrders.set(mapped.orderId.toString(), mapped); + } + } + this.emitOrders(); + } + } + + private startPolling(): void { + this.stopPolling(); + this.pollTimer = setInterval(() => { + void this.refreshAccount(); + void this.refreshOpenOrders(); + void this.refreshTicker(); + }, DEFAULT_POLL_INTERVAL_MS); + } + + private stopPolling(): void { + if (this.pollTimer) { + clearInterval(this.pollTimer); + this.pollTimer = null; + } + } + + private buildAccountSnapshot(): AsterAccountSnapshot { + const assets: AsterAccountAsset[] = []; + for (const entry of this.accountState.collateral.values()) { + assets.push({ + asset: entry.coinName, + walletBalance: entry.balance, + availableBalance: entry.available, + updateTime: Date.now(), + }); + } + + const positions: AsterAccountPosition[] = []; + const meta = this.metadata; + for (const [contractId, position] of this.accountState.positions.entries()) { + if (meta && contractId !== meta.contractId) continue; + positions.push({ + symbol: this.symbol, + positionAmt: position.size ?? "0", + entryPrice: position.avgEntryPrice ?? "0", + unrealizedProfit: position.unrealizedPnl ?? "0", + positionSide: "BOTH", + updateTime: Date.now(), + markPrice: position.markPrice ?? position.avgEntryPrice ?? "0", + }); + } + + return { + canTrade: true, + canDeposit: true, + canWithdraw: true, + updateTime: Date.now(), + totalWalletBalance: this.accountState.totalEquity, + totalUnrealizedProfit: "0", + positions, + assets, + totalMarginBalance: this.accountState.totalEquity, + totalInitialMargin: "0", + totalMaintMargin: "0", + totalOpenOrderInitialMargin: "0", + totalCrossWalletBalance: this.accountState.totalEquity, + totalCrossUnPnl: "0", + } as AsterAccountSnapshot; + } + + private currentOrders(): AsterOrder[] { + return Array.from(this.openOrders.values()); + } + + private emitAccount(): void { + const snapshot = this.buildAccountSnapshot(); + for (const listener of this.accountListeners) listener(snapshot); + } + + private emitOrders(): void { + const orders = this.currentOrders(); + for (const listener of this.ordersListeners) listener(orders); + } + + private emitDepth(): void { + if (!this.depthSnapshot) return; + for (const listener of this.depthListeners) listener(this.depthSnapshot); + } + + private emitTicker(): void { + if (!this.tickerSnapshot) return; + for (const listener of this.tickerListeners) listener(this.tickerSnapshot); + } + + private emitKlines(interval: string): void { + const cache = this.klineCache.get(interval); + if (!cache) return; + const listeners = this.klineListeners.get(interval); + if (!listeners) return; + for (const listener of listeners) listener(cache); + } + + private mapOrder(order: any): AsterOrder { + return { + orderId: order.orderId ?? order.id ?? "", + clientOrderId: order.clientOrderId ?? order.clientId ?? "", + symbol: this.symbol, + side: (order.side ?? "BUY") as OrderSide, + type: (order.type ?? "LIMIT") as OrderType, + status: order.status ?? "OPEN", + price: order.price ?? "0", + origQty: order.size ?? order.orderSize ?? "0", + executedQty: order.filledSize ?? order.executedSize ?? "0", + stopPrice: order.triggerPrice ?? "0", + time: Number(order.createTime ?? order.createdTime ?? Date.now()), + updateTime: Number(order.updateTime ?? order.updatedTime ?? Date.now()), + reduceOnly: Boolean(order.reduceOnly ?? false), + closePosition: false, + }; + } + + private get contractId(): string { + return this.metadata?.contractId ?? ""; + } +} + +function findContract(contractList: any[], symbol: string): any | null { + const upper = symbol.toUpperCase(); + return ( + contractList.find((item) => item.contractName?.toUpperCase() === upper) || + contractList.find((item) => item.contractId?.toUpperCase() === upper) || + null + ); +} + +function parseAccountIdToBigInt(value: string): bigint { + try { + return BigInt(value); + } catch (error) { + throw new Error(`Invalid EdgeX account id: ${value}`); + } +} + +function parseResolution(value: string): bigint { + try { + if (value.startsWith("0x") || value.startsWith("0X")) { + return BigInt(value); + } + return BigInt(value); + } catch { + return 1n; + } +} + +function parseReduceOnly(value: unknown): boolean { + if (typeof value === "boolean") return value; + if (typeof value === "string") return value.toLowerCase() === "true"; + return false; +} + +function mapOrderType(type: string | undefined): string { + const upper = (type ?? "LIMIT").toUpperCase(); + switch (upper) { + case "MARKET": + return "MARKET"; + case "STOP_MARKET": + return "STOP_MARKET"; + case "TAKE_PROFIT_MARKET": + return "TAKE_PROFIT_MARKET"; + default: + return "LIMIT"; + } +} + +function mapTimeInForce(value: string | undefined, orderType: string): string { + const upper = (value ?? "").toUpperCase(); + switch (upper) { + case "IOC": + return "IMMEDIATE_OR_CANCEL"; + case "FOK": + return "FILL_OR_KILL"; + case "GTX": + return "POST_ONLY"; + case "GTC": + return "GOOD_TIL_CANCEL"; + default: + return orderType === "MARKET" ? "IMMEDIATE_OR_CANCEL" : "GOOD_TIL_CANCEL"; + } +} + +function intervalToMs(interval: string): number { + switch (interval) { + case "1m": + return 60 * 1000; + case "3m": + return 3 * 60 * 1000; + case "5m": + return 5 * 60 * 1000; + case "15m": + return 15 * 60 * 1000; + case "30m": + return 30 * 60 * 1000; + case "1h": + return 60 * 60 * 1000; + case "4h": + return 4 * 60 * 60 * 1000; + case "12h": + return 12 * 60 * 60 * 1000; + case "1d": + return 24 * 60 * 60 * 1000; + default: + return 60 * 1000; + } +} + +function parseEdgeInterval(channel: string): string | null { + const parts = channel.split("."); + const edgeInterval = parts[parts.length - 1]; + const entry = Object.entries(EDGE_KLINE_INTERVAL).find(([, value]) => value === edgeInterval); + return entry ? entry[0] : null; +} + +function klineChannel(contractId: string, interval: string): string { + const edge = EDGE_KLINE_INTERVAL[interval] ?? EDGE_KLINE_INTERVAL["1m"]; + return `kline.${contractId}.${edge}`; +} + +function extractQuoteEvent(message: any): { channel: string; payload: any } | null { + if (!message) return null; + if (message.type === "quote-event") { + return { + channel: message.channel ?? "", + payload: message.data ?? message.payload ?? {}, + }; + } + if (message.type === "payload") { + const data = Array.isArray(message.content?.data) ? message.content?.data[0] : message.content?.data ?? message.payload ?? {}; + return { + channel: message.channel ?? "", + payload: data ?? {}, + }; + } + return null; +} + +function normalizeDecimalInput(value: number | string): string { + if (typeof value === "string") return value; + if (!Number.isFinite(value)) return "0"; + const str = value.toString(); + if (!/e/i.test(str)) return str; + const parts = str.toLowerCase().split("e"); + const mantissaRaw = parts[0] ?? "0"; + const exponentRaw = parts[1] ?? "0"; + const mantissa = mantissaRaw; + const exp = Number(exponentRaw); + const negative = mantissa.startsWith("-"); + const unsigned = negative ? mantissa.slice(1) : mantissa; + const [intPart, fracPart = ""] = unsigned.split("."); + let digits = (intPart + fracPart).replace(/^0+/, "") || "0"; + let decimalIndex = intPart.length + exp; + if (decimalIndex <= 0) { + digits = digits.padStart(digits.length + (1 - decimalIndex), "0"); + decimalIndex = 1; + } + if (decimalIndex >= digits.length) { + digits = digits.padEnd(decimalIndex, "0"); + return `${negative ? "-" : ""}${digits}`; + } + const integer = digits.slice(0, decimalIndex) || "0"; + const fraction = digits.slice(decimalIndex).replace(/0+$/, ""); + const sign = negative ? "-" : ""; + return fraction ? `${sign}${integer}.${fraction}` : `${sign}${integer}`; +} + +function generateClientOrderId(): string { + return `${Date.now()}${Math.floor(Math.random() * 1e6).toString().padStart(6, "0")}`; +} diff --git a/src/exchanges/edgex/l2.ts b/src/exchanges/edgex/l2.ts new file mode 100644 index 0000000..84d9a58 --- /dev/null +++ b/src/exchanges/edgex/l2.ts @@ -0,0 +1,133 @@ +import { pedersen } from "@starkware-industries/starkware-crypto-utils"; +import { ec as starkEc, sign as starkSign } from "@starkware-industries/starkware-crypto-utils"; + +const FIELD_PRIME = BigInt("0x080000000000011000000000000000000000000000000000000000000000001"); +const LIMIT_ORDER_WITH_FEE_TYPE = 3n; + +export interface EdgeXL2OrderSignInput { + isBuy: boolean; + amountSynthetic: bigint; + amountCollateral: bigint; + amountFee: bigint; + syntheticAssetId: string; + collateralAssetId: string; + feeAssetId: string; + positionId: bigint; + nonce: number; + expirationHours: number; + privateKey: string; +} + +export interface EdgeXL2SignatureResult { + signature: string; + r: string; + s: string; +} + +export function signLimitOrder(input: EdgeXL2OrderSignInput): EdgeXL2SignatureResult { + const messageHash = calcLimitOrderHash({ + syntheticAssetId: input.syntheticAssetId, + collateralAssetId: input.collateralAssetId, + feeAssetId: input.feeAssetId, + isBuy: input.isBuy, + amountSynthetic: input.amountSynthetic, + amountCollateral: input.amountCollateral, + amountFee: input.amountFee, + nonce: BigInt(input.nonce), + positionId: input.positionId, + expirationHours: BigInt(input.expirationHours), + }); + + const keyPair = starkEc.keyFromPrivate(stripHexPrefix(input.privateKey), "hex"); + const signature = starkSign(keyPair, messageHash, { canonical: true }); + const r = signature.r.toString(16).padStart(64, "0"); + const s = signature.s.toString(16).padStart(64, "0"); + return { + signature: `${r}${s}`, + r, + s, + }; +} + +interface LimitOrderHashParams { + syntheticAssetId: string; + collateralAssetId: string; + feeAssetId: string; + isBuy: boolean; + amountSynthetic: bigint; + amountCollateral: bigint; + amountFee: bigint; + nonce: bigint; + positionId: bigint; + expirationHours: bigint; +} + +function calcLimitOrderHash(params: LimitOrderHashParams): string { + const syntheticAsset = hexToField(params.syntheticAssetId); + const collateralAsset = hexToField(params.collateralAssetId); + const feeAsset = hexToField(params.feeAssetId); + + const amountSynthetic = toField(params.amountSynthetic); + const amountCollateral = toField(params.amountCollateral); + const amountFee = toField(params.amountFee); + const nonce = toField(params.nonce); + const positionId = toField(params.positionId); + const expiration = toField(params.expirationHours); + + const assetSell = params.isBuy ? collateralAsset : syntheticAsset; + const assetBuy = params.isBuy ? syntheticAsset : collateralAsset; + + const amountSell = params.isBuy ? amountCollateral : amountSynthetic; + const amountBuy = params.isBuy ? amountSynthetic : amountCollateral; + + let msg = pedersenPair(assetSell, assetBuy); + msg = pedersenPair(msg, feeAsset); + + let packed0 = amountSell; + packed0 = shiftLeft(packed0, 64n) + amountBuy; + packed0 = shiftLeft(packed0, 64n) + amountFee; + packed0 = shiftLeft(packed0, 32n) + nonce; + packed0 = toField(packed0); + + msg = pedersenPair(msg, packed0); + + let packed1 = LIMIT_ORDER_WITH_FEE_TYPE; + packed1 = shiftLeft(packed1, 64n) + positionId; + packed1 = shiftLeft(packed1, 64n) + positionId; + packed1 = shiftLeft(packed1, 64n) + positionId; + packed1 = shiftLeft(packed1, 32n) + expiration; + packed1 = shiftLeft(packed1, 17n); + packed1 = toField(packed1); + + const final = pedersenPair(msg, packed1); + return final.toString(16); +} + +function pedersenPair(a: bigint, b: bigint): bigint { + const result = pedersen([toHex(a), toHex(b)]); + return BigInt(`0x${result}`); +} + +function toField(value: bigint): bigint { + let normalized = value % FIELD_PRIME; + if (normalized < 0n) normalized += FIELD_PRIME; + return normalized; +} + +function hexToField(value: string): bigint { + const stripped = stripHexPrefix(value); + return toField(BigInt(`0x${stripped || "0"}`)); +} + +function toHex(value: bigint): string { + return toField(value).toString(16); +} + +function stripHexPrefix(value: string): string { + if (!value) return ""; + return value.startsWith("0x") || value.startsWith("0X") ? value.slice(2) : value; +} + +function shiftLeft(value: bigint, bits: bigint): bigint { + return toField(value << bits); +} diff --git a/src/exchanges/edgex/signature.ts b/src/exchanges/edgex/signature.ts new file mode 100644 index 0000000..bafc29a --- /dev/null +++ b/src/exchanges/edgex/signature.ts @@ -0,0 +1,157 @@ +import * as crypto from "crypto"; +import { keccak256 } from "ethereum-cryptography/keccak"; +import { ec as starkEc } from "@starkware-industries/starkware-crypto-utils"; + +export interface HttpSignatureInput { + method: string; + path: string; + query?: Record | URLSearchParams | null; + body?: unknown; + timestamp?: number; +} + +export interface HttpSignatureResult { + signature: string; + timestamp: string; + message: string; +} + +export interface EdgeXWsHeaders { + timestamp: string; + signature: string; +} + +export class EdgeXSignature { + private readonly privateKey: string; + private readonly keyPair: + | ReturnType + | null; + + constructor(privateKey: string) { + this.privateKey = stripHexPrefix(privateKey); + this.keyPair = starkEc.keyFromPrivate(this.privateKey, "hex"); + } + + createHttpSignature(input: HttpSignatureInput): HttpSignatureResult { + const timestamp = input.timestamp ?? Date.now(); + const content = buildSignatureContent({ + timestamp, + method: input.method, + path: input.path, + body: input.body, + query: input.query, + }); + const { r, s } = this.sign(content); + return { + signature: `${r}${s}`, + timestamp: timestamp.toString(), + message: content, + }; + } + + createWebsocketHeaders(accountId: string): EdgeXWsHeaders { + const timestamp = Date.now(); + const path = `/api/v1/private/wsaccountId=${accountId}`; + const content = `${timestamp}GET${path}`; + const { r, s } = this.sign(content); + return { + timestamp: timestamp.toString(), + signature: `${r}${s}`, + }; + } + + signRaw(message: string | Buffer): { r: string; s: string } { + const buffer = typeof message === "string" ? Buffer.from(message, "utf8") : message; + return this.sign(buffer); + } + + randomNonce(max: number = 0xffffffff): number { + return crypto.randomInt(0, max + 1); + } + + private sign(message: string | Buffer): { r: string; s: string } { + if (!this.keyPair) throw new Error("EdgeX signer not initialized"); + const buffer = typeof message === "string" ? Buffer.from(message, "utf8") : message; + const hash = keccak256(buffer); + const msgHex = Buffer.from(hash).toString("hex"); + const signature = this.keyPair.sign(msgHex, { canonical: true }); + const r = signature.r.toString(16).padStart(64, "0"); + const s = signature.s.toString(16).padStart(64, "0"); + return { r, s }; + } +} + +export function buildSignatureContent(input: { + timestamp: number; + method: string; + path: string; + body?: unknown; + query?: Record | URLSearchParams | null; +}): string { + const { timestamp, method } = input; + const upperMethod = method.toUpperCase(); + const normalizedPath = ensureLeadingSlash(input.path); + if (input.body != null && input.body !== "") { + const bodyString = stringifyForSignature(input.body); + return `${timestamp}${upperMethod}${normalizedPath}${bodyString}`; + } + const queryString = buildQueryString(input.query); + if (queryString) { + return `${timestamp}${upperMethod}${normalizedPath}${queryString}`; + } + return `${timestamp}${upperMethod}${normalizedPath}`; +} + +export function buildQueryString(query?: Record | URLSearchParams | null): string { + if (!query) return ""; + const entries: Array<[string, string]> = []; + if (query instanceof URLSearchParams) { + query.forEach((value, key) => { + entries.push([key, value]); + }); + } else { + for (const [key, value] of Object.entries(query)) { + if (value == null) continue; + entries.push([key, stringifyPrimitive(value)]); + } + } + if (entries.length === 0) return ""; + entries.sort(([a], [b]) => a.localeCompare(b)); + return entries.map(([key, value]) => `${key}=${value}`).join("&"); +} + +function stringifyPrimitive(value: unknown): string { + if (value == null) return ""; + if (typeof value === "boolean") return value.toString().toLowerCase(); + if (typeof value === "number") return Number.isFinite(value) ? value.toString() : ""; + return String(value); +} + +export function stringifyForSignature(data: unknown): string { + if (data == null) return ""; + if (typeof data === "string") return data; + if (typeof data === "number") return Number.isFinite(data) ? data.toString() : ""; + if (typeof data === "boolean") return data.toString().toLowerCase(); + if (Array.isArray(data)) { + if (data.length === 0) return ""; + return data.map((item) => stringifyForSignature(item)).join("&"); + } + if (typeof data === "object") { + const map = new Map(); + for (const [key, value] of Object.entries(data as Record)) { + map.set(key, stringifyForSignature(value)); + } + const keys = Array.from(map.keys()).sort((a, b) => a.localeCompare(b)); + return keys.map((key) => `${key}=${map.get(key) ?? ""}`).join("&"); + } + return String(data); +} + +function ensureLeadingSlash(path: string): string { + if (!path.startsWith("/")) return `/${path}`; + return path; +} + +function stripHexPrefix(input: string): string { + return input.startsWith("0x") || input.startsWith("0X") ? input.slice(2) : input; +} diff --git a/src/exchanges/edgex/types.ts b/src/exchanges/edgex/types.ts new file mode 100644 index 0000000..210cbfe --- /dev/null +++ b/src/exchanges/edgex/types.ts @@ -0,0 +1,215 @@ +export interface EdgeXMetaResponse { + code: string; + data: { + global: EdgeXGlobalMeta; + coinList: EdgeXCoinMeta[]; + contractList: EdgeXContractMeta[]; + }; +} + +export interface EdgeXGlobalMeta { + appEnv: string; + starkExCollateralCoin: EdgeXCoinMeta; +} + +export interface EdgeXCoinMeta { + coinId: string; + coinName: string; + stepSize?: string; + starkExAssetId: string; + starkExResolution: string; +} + +export interface EdgeXContractMeta { + contractId: string; + contractName: string; + baseCoinId: string; + quoteCoinId: string; + tickSize: string; + stepSize: string; + minOrderSize: string; + maxOrderSize: string; + defaultTakerFeeRate: string; + defaultMakerFeeRate: string; + displayDigitMerge?: string; + displayMaxLeverage?: string; + displayMinLeverage?: string; + starkExSyntheticAssetId: string; + starkExResolution: string; +} + +export interface EdgeXAccountSnapshotResponse { + code: string; + data: T; +} + +export interface EdgeXOrderRequest { + accountId: string; + contractId: string; + side: "BUY" | "SELL"; + type: "LIMIT" | "MARKET" | "STOP_MARKET" | "TAKE_PROFIT" | string; + timeInForce: string; + price?: string; + size: string; + triggerPrice?: string; + triggerPriceType?: string; + reduceOnly?: boolean; + isPositionTpsl?: boolean; + isSetOpenTp?: boolean; + isSetOpenSl?: boolean; + clientOrderId: string; + expireTime: string; + l2Nonce: string; + l2Value: string; + l2Size: string; + l2LimitFee: string; + l2ExpireTime: string; + l2Signature: string; + extraType?: string; + extraDataJson?: string; +} + +export interface EdgeXCancelOrderRequest { + accountId: string; + orderId: string; +} + +export interface EdgeXCancelAllRequest { + accountId: string; + contractId?: string; +} + +export interface EdgeXOpenOrder { + orderId: string; + clientOrderId: string; + contractId: string; + accountId: string; + side: "BUY" | "SELL"; + type: string; + price: string; + size: string; + filledSize?: string; + status: string; + createTime: string; + updateTime?: string; + l2Nonce?: string; +} + +export interface EdgeXPrivateWsMessage { + type: string; + content?: { + event: string; + version?: string; + data?: T; + }; +} + +export interface EdgeXTradeEvent { + account?: EdgeXAccountUpdate[]; + order?: EdgeXOrderUpdate[]; + position?: EdgeXPositionUpdate[]; + collateral?: EdgeXCollateralUpdate[]; + orderFillTransaction?: EdgeXOrderFillUpdate[]; +} + +export interface EdgeXAccountUpdate { + accountId: string; + totalEquity?: string; + availableBalance?: string; + totalMaintenanceMargin?: string; +} + +export interface EdgeXCollateralUpdate { + coinId: string; + balance: string; + availableBalance: string; +} + +export interface EdgeXPositionUpdate { + contractId: string; + size: string; + averageEntryPrice?: string; + unrealizedPnl?: string; + leverage?: string; + maintenanceMargin?: string; + markPrice?: string; +} + +export interface EdgeXOrderUpdate { + orderId: string; + clientOrderId: string; + contractId: string; + accountId: string; + status: string; + price: string; + size: string; + filledSize?: string; + side: "BUY" | "SELL"; + type: string; + updateTime?: string; + createTime?: string; +} + +export interface EdgeXOrderFillUpdate { + orderId: string; + fillPrice: string; + fillSize: string; + fee: string; + side: "BUY" | "SELL"; + timestamp: string; +} + +export interface EdgeXDepthMessage { + type: string; + channel: string; + content?: { + dataType: "Snapshot" | "Changed" | string; + data: Array<{ + bids: Array<[string, string]>; + asks: Array<[string, string]>; + depthType?: string; + startVersion?: string; + endVersion?: string; + contractId: string; + }>; + }; +} + +export interface EdgeXTickerMessage { + type: string; + channel: string; + content?: { + dataType: "Snapshot" | "Changed" | string; + data: Array<{ + contractId: string; + lastPrice?: string; + high?: string; + low?: string; + open?: string; + close?: string; + size?: string; + value?: string; + trades?: string; + }>; + }; +} + +export interface EdgeXKlineMessage { + type: string; + channel: string; + content?: { + dataType: "Snapshot" | "Changed" | string; + data: Array<{ + contractId: string; + klineType: string; + klineTime: string; + open: string; + high: string; + low: string; + close: string; + size: string; + value: string; + trades: string; + }>; + }; +}