Add Flash.Trade leveraged open and close position

This commit is contained in:
UjjwalGupta49
2025-01-04 18:55:09 +05:30
parent 06ad5ba728
commit 42eb30a3b6
8 changed files with 1066 additions and 4 deletions

View File

@@ -44,20 +44,21 @@
"@pythnetwork/price-service-client": "^1.9.0",
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
"@solana/spl-token": "^0.4.9",
"ai": "^4.0.22",
"@tensor-oss/tensorswap-sdk": "^4.5.0",
"@solana/web3.js": "^1.98.0",
"@tensor-oss/tensorswap-sdk": "^4.5.0",
"@tiplink/api": "^0.3.1",
"ai": "^4.0.22",
"bn.js": "^5.2.1",
"bs58": "^6.0.0",
"chai": "^5.1.2",
"decimal.js": "^10.4.3",
"dotenv": "^16.4.7",
"flash-sdk": "^2.24.3",
"form-data": "^4.0.1",
"zod": "^3.24.1",
"langchain": "^0.3.8",
"openai": "^4.77.0",
"typedoc": "^0.27.6"
"typedoc": "^0.27.6",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/bn.js": "^5.1.6",

220
pnpm-lock.yaml generated
View File

@@ -101,6 +101,9 @@ importers:
dotenv:
specifier: ^16.4.7
version: 16.4.7
flash-sdk:
specifier: ^2.24.3
version: 2.24.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)
form-data:
specifier: ^4.0.1
version: 4.0.1
@@ -217,6 +220,10 @@ packages:
resolution: {integrity: sha512-PxRl+wu5YyptWiR9F2MBHOLLibm87Z4IMUBPreX+DYBtPM+xggvcPi0KAN7+kIL4IrIhXI8ma5V0MCXxSN1pHg==}
engines: {node: '>=11'}
'@coral-xyz/anchor@0.27.0':
resolution: {integrity: sha512-+P/vPdORawvg3A9Wj02iquxb4T0C5m4P6aZBVYysKl4Amk+r6aMPZkUhilBkD6E4Nuxnoajv3CFykUfkGE0n5g==}
engines: {node: '>=11'}
'@coral-xyz/anchor@0.29.0':
resolution: {integrity: sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==}
engines: {node: '>=11'}
@@ -227,6 +234,18 @@ packages:
peerDependencies:
'@solana/web3.js': ^1.68.0
'@coral-xyz/borsh@0.27.0':
resolution: {integrity: sha512-tJKzhLukghTWPLy+n8K8iJKgBq1yLT/AxaNd10yJrX8mI56ao5+OFAKAqW/h0i79KCvb4BK0VGO5ECmmolFz9A==}
engines: {node: '>=10'}
peerDependencies:
'@solana/web3.js': ^1.68.0
'@coral-xyz/borsh@0.28.0':
resolution: {integrity: sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==}
engines: {node: '>=10'}
peerDependencies:
'@solana/web3.js': ^1.68.0
'@coral-xyz/borsh@0.29.0':
resolution: {integrity: sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==}
engines: {node: '>=10'}
@@ -596,6 +615,11 @@ packages:
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@pythnetwork/client@2.22.0':
resolution: {integrity: sha512-Cyv23YqewKUL1pcm99jfmdetUa2aaUXjyRF9jvSeFcY895FddRu7uSWftYiaevsnx7vn4WbJgQR6ExxH+aONow==}
peerDependencies:
'@solana/web3.js': ^1.30.2
'@pythnetwork/price-service-client@1.9.0':
resolution: {integrity: sha512-SLm3IFcfmy9iMqHeT4Ih6qMNZhJEefY14T9yTlpsH2D/FE5+BaGGnfcexUifVlfH6M7mwRC4hEFdNvZ6ebZjJg==}
deprecated: This package is deprecated and is no longer maintained. Please use @pythnetwork/hermes-client instead.
@@ -819,6 +843,9 @@ packages:
'@solana/web3.js@1.95.3':
resolution: {integrity: sha512-O6rPUN0w2fkNqx/Z3QJMB9L225Ex10PRDH8bTaIUPZXMPV0QP8ZpPvjQnXK+upUczlRgzHzd6SjKIha1p+I6og==}
'@solana/web3.js@1.95.8':
resolution: {integrity: sha512-sBHzNh7dHMrmNS5xPD1d0Xa2QffW/RXaxu/OysRXBfwTp+LYqGGmMtCYYwrHPrN5rjAmJCsQRNAwv4FM0t3B6g==}
'@solana/web3.js@1.98.0':
resolution: {integrity: sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA==}
@@ -882,6 +909,9 @@ packages:
'@types/node@18.19.69':
resolution: {integrity: sha512-ECPdY1nlaiO/Y6GUnwgtAAhLNaQ53AyIVz+eILxpEo5OvuqE6yWkqWBIb5dU0DqhKQtMeny+FBD3PK6lm7L5xQ==}
'@types/node@20.17.11':
resolution: {integrity: sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==}
'@types/node@22.10.5':
resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==}
@@ -1228,6 +1258,10 @@ packages:
crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
@@ -1479,6 +1513,10 @@ packages:
fastq@1.18.0:
resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==}
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -1502,6 +1540,9 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
flash-sdk@2.24.3:
resolution: {integrity: sha512-3JdmHZksBgcRlCXVVFZEV64NGKxVHURHoHAMc3+Ev1BdN0Re2S44wxTaQmO6EIvwPYscVG0BPbp6GibpEuMdsw==}
flat-cache@3.2.0:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -1540,9 +1581,16 @@ packages:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'}
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
fs@0.0.1-security:
resolution: {integrity: sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@@ -1753,6 +1801,9 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
jsbi@4.3.0:
resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==}
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -2018,6 +2069,10 @@ packages:
encoding:
optional: true
node-fetch@3.3.2:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
node-gyp-build@4.8.4:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
@@ -2468,6 +2523,9 @@ packages:
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
undici-types@6.19.8:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
@@ -2751,6 +2809,28 @@ snapshots:
- encoding
- utf-8-validate
'@coral-xyz/anchor@0.27.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@coral-xyz/borsh': 0.27.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
base64-js: 1.5.1
bn.js: 5.2.1
bs58: 4.0.1
buffer-layout: 1.2.2
camelcase: 6.3.0
cross-fetch: 3.2.0
crypto-hash: 1.3.0
eventemitter3: 4.0.7
js-sha256: 0.9.0
pako: 2.1.0
snake-case: 3.0.4
superstruct: 0.15.5
toml: 3.0.0
transitivePeerDependencies:
- bufferutil
- encoding
- utf-8-validate
'@coral-xyz/anchor@0.29.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -2778,6 +2858,18 @@ snapshots:
bn.js: 5.2.1
buffer-layout: 1.2.2
'@coral-xyz/borsh@0.27.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
bn.js: 5.2.1
buffer-layout: 1.2.2
'@coral-xyz/borsh@0.28.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)
bn.js: 5.2.1
buffer-layout: 1.2.2
'@coral-xyz/borsh@0.29.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
@@ -3332,6 +3424,17 @@ snapshots:
'@pkgr/core@0.1.1': {}
'@pythnetwork/client@2.22.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@coral-xyz/borsh': 0.28.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)
buffer: 6.0.3
transitivePeerDependencies:
- bufferutil
- encoding
- utf-8-validate
'@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@pythnetwork/price-service-sdk': 1.8.0
@@ -3706,6 +3809,14 @@ snapshots:
- fastestsmallesttextencoderdecoder
- typescript
'@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
dependencies:
'@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
'@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- typescript
'@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)':
dependencies:
'@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)
@@ -3735,6 +3846,20 @@ snapshots:
- encoding
- utf-8-validate
'@solana/spl-token@0.3.11(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)':
dependencies:
'@solana/buffer-layout': 4.0.1
'@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
'@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)
buffer: 6.0.3
transitivePeerDependencies:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
- typescript
- utf-8-validate
'@solana/spl-token@0.3.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10)':
dependencies:
'@solana/buffer-layout': 4.0.1
@@ -3834,6 +3959,28 @@ snapshots:
- encoding
- utf-8-validate
'@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@babel/runtime': 7.26.0
'@noble/curves': 1.8.0
'@noble/hashes': 1.7.0
'@solana/buffer-layout': 4.0.1
agentkeepalive: 4.6.0
bigint-buffer: 1.1.5
bn.js: 5.2.1
borsh: 0.7.0
bs58: 4.0.1
buffer: 6.0.3
fast-stable-stringify: 1.0.0
jayson: 4.1.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
node-fetch: 2.7.0
rpc-websockets: 9.0.4
superstruct: 2.0.2
transitivePeerDependencies:
- bufferutil
- encoding
- utf-8-validate
'@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@babel/runtime': 7.26.0
@@ -3977,6 +4124,10 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/node@20.17.11':
dependencies:
undici-types: 6.19.8
'@types/node@22.10.5':
dependencies:
undici-types: 6.20.0
@@ -4348,6 +4499,8 @@ snapshots:
crypto-js@4.2.0: {}
data-uri-to-buffer@4.0.1: {}
dayjs@1.11.13: {}
debug@4.4.0:
@@ -4622,6 +4775,11 @@ snapshots:
dependencies:
reusify: 1.0.4
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
@@ -4654,6 +4812,34 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
flash-sdk@2.24.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10):
dependencies:
'@coral-xyz/anchor': 0.27.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@pythnetwork/client': 2.22.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@pythnetwork/price-service-client': 1.9.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@solana/spl-token': 0.3.11(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)
'@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@types/node': 20.17.11
bignumber.js: 9.1.2
bs58: 5.0.0
dotenv: 16.4.7
fs: 0.0.1-security
js-sha256: 0.9.0
jsbi: 4.3.0
node-fetch: 3.3.2
rimraf: 5.0.10
ts-node: 10.9.2(@types/node@20.17.11)(typescript@5.7.2)
tweetnacl: 1.0.3
transitivePeerDependencies:
- '@swc/core'
- '@swc/wasm'
- bufferutil
- debug
- encoding
- fastestsmallesttextencoderdecoder
- typescript
- utf-8-validate
flat-cache@3.2.0:
dependencies:
flatted: 3.3.2
@@ -4691,8 +4877,14 @@ snapshots:
node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
fs.realpath@1.0.0: {}
fs@0.0.1-security: {}
function-bind@1.1.2: {}
get-intrinsic@1.2.7:
@@ -4946,6 +5138,8 @@ snapshots:
dependencies:
argparse: 2.0.1
jsbi@4.3.0: {}
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
@@ -5174,6 +5368,12 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
node-fetch@3.3.2:
dependencies:
data-uri-to-buffer: 4.0.1
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
node-gyp-build@4.8.4: {}
number-to-bn@1.7.0:
@@ -5549,6 +5749,24 @@ snapshots:
ts-log@2.2.7: {}
ts-node@10.9.2(@types/node@20.17.11)(typescript@5.7.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 20.17.11
acorn: 8.14.0
acorn-walk: 8.3.4
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 5.7.2
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
ts-node@10.9.2(@types/node@22.10.5)(typescript@5.7.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
@@ -5617,6 +5835,8 @@ snapshots:
undici-types@5.26.5: {}
undici-types@6.19.8: {}
undici-types@6.20.0: {}
unicode-trie@2.0.0:

View File

@@ -59,6 +59,10 @@ import {
fetchTokenReportSummary,
fetchTokenDetailedReport,
OrderParams,
FlashTradeParams,
FlashCloseTradeParams,
flashOpenTrade,
flashCloseTrade,
} from "../tools";
import {
@@ -537,4 +541,22 @@ export class SolanaAgentKit {
async fetchTokenDetailedReport(mint: string): Promise<TokenCheck> {
return fetchTokenDetailedReport(mint);
}
/**
* Opens a new trading position on Flash.Trade
* @param params Flash trade parameters including market, side, collateral, leverage, and pool name
* @returns Transaction signature
*/
async flashOpenTrade(params: FlashTradeParams): Promise<string> {
return flashOpenTrade(this, params);
}
/**
* Closes an existing trading position on Flash.Trade
* @param params Flash trade close parameters
* @returns Transaction signature
*/
async flashCloseTrade(params: FlashCloseTradeParams): Promise<string> {
return flashCloseTrade(this, params);
}
}

View File

@@ -774,6 +774,131 @@ export class SolanaGetWalletAddressTool extends Tool {
}
}
export class SolanaFlashOpenTrade extends Tool {
name = "solana_flash_open_trade";
description = `Opens a new leveraged trading position on Flash.Trade exchange.
Inputs (input is a JSON string):
token: string, one of ["SOL", "BTC", "ETH"] (required)
side: string, either "long" or "short" (required)
collateralUsd: number, amount in USD for collateral eg 10 (required)
leverage: number, eg 5 for 5x leverage (required)
Example:
{
"token": "SOL",
"side": "long",
"collateralUsd": 10,
"leverage": 5
}`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
// Validate input parameters
if (!parsedInput.token) {
throw new Error("Token is required");
}
if (!["SOL", "BTC", "ETH"].includes(parsedInput.token)) {
throw new Error('Token must be one of ["SOL", "BTC", "ETH"]');
}
if (!["long", "short"].includes(parsedInput.side)) {
throw new Error('Side must be either "long" or "short"');
}
if (!parsedInput.collateralUsd || parsedInput.collateralUsd <= 0) {
throw new Error("Collateral USD amount must be positive");
}
if (!parsedInput.leverage || parsedInput.leverage <= 0) {
throw new Error("Leverage must be positive");
}
const tx = await this.solanaKit.flashOpenTrade({
token: parsedInput.token,
side: parsedInput.side,
collateralUsd: parsedInput.collateralUsd,
leverage: parsedInput.leverage,
});
return JSON.stringify({
status: "success",
message: "Flash trade position opened successfully",
transaction: tx,
token: parsedInput.token,
side: parsedInput.side,
collateralUsd: parsedInput.collateralUsd,
leverage: parsedInput.leverage,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaFlashCloseTrade extends Tool {
name = "solana_flash_close_trade";
description = `Closes an existing leveraged trading position on Flash.Trade exchange.
Inputs (input is a JSON string):
token: string, one of ["SOL", "BTC", "ETH"] (required)
side: string, either "long" or "short" (required)
Example:
{
"token": "SOL",
"side": "long"
}`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
// Validate input parameters
if (!parsedInput.token) {
throw new Error("Token is required");
}
if (!["SOL", "BTC", "ETH"].includes(parsedInput.token)) {
throw new Error('Token must be one of ["SOL", "BTC", "ETH"]');
}
if (!["long", "short"].includes(parsedInput.side)) {
throw new Error('Side must be either "long" or "short"');
}
const tx = await this.solanaKit.flashCloseTrade({
token: parsedInput.token,
side: parsedInput.side,
});
return JSON.stringify({
status: "success",
message: "Flash trade position closed successfully",
transaction: tx,
token: parsedInput.token,
side: parsedInput.side,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaPumpfunTokenLaunchTool extends Tool {
name = "solana_launch_pumpfun_token";
@@ -2175,5 +2300,8 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaFetchTokenDetailedReportTool(solanaKit),
new SolanaPerpOpenTradeTool(solanaKit),
new SolanaPerpCloseTradeTool(solanaKit),
new SolanaFlashOpenTrade(solanaKit),
new SolanaFlashCloseTrade(solanaKit),
];
}

View File

@@ -0,0 +1,124 @@
import { PublicKey, ComputeBudgetProgram } from "@solana/web3.js";
import {
PerpetualsClient,
OraclePrice,
PoolConfig,
Privilege,
Side,
} from "flash-sdk";
import { BN } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../index";
import {
CLOSE_POSITION_CU,
marketSdkInfo,
marketTokenMap,
getNftTradingAccountInfo,
fetchOraclePrice,
createPerpClient,
} from "../utils/flashUtils";
export interface FlashCloseTradeParams {
token: string;
side: "long" | "short";
}
/**
* Closes an existing position on Flash.Trade
* @param agent SolanaAgentKit instance
* @param params Trade parameters
* @returns Transaction signature
*/
export async function flashCloseTrade(
agent: SolanaAgentKit,
params: FlashCloseTradeParams,
): Promise<string> {
try {
const { token, side } = params;
// Get market ID from token and side using marketTokenMap
const tokenMarkets = marketTokenMap[token];
if (!tokenMarkets) {
throw new Error(`Token ${token} not supported for trading`);
}
const sideEntry = tokenMarkets[side];
if (!sideEntry) {
throw new Error(`${side} side not available for ${token}`);
}
const market = sideEntry.marketID;
// Validate market data using marketSdkInfo
const marketData = marketSdkInfo[market];
if (!marketData) {
throw new Error(`Invalid market configuration for ${token}/${side}`);
}
// Get token information
const [targetSymbol, collateralSymbol] = marketData.tokenPair.split("/");
// Fetch oracle prices
const [targetPrice, collateralPrice] = await Promise.all([
fetchOraclePrice(targetSymbol),
fetchOraclePrice(collateralSymbol),
]);
// Initialize pool configuration and perpClient
const poolConfig = PoolConfig.fromIdsByName(marketData.pool, "mainnet-beta");
const perpClient = createPerpClient(agent.connection, agent.wallet);
// Calculate price after slippage
const slippageBpsBN = new BN(100); // 1% slippage
const sideEnum = side === "long" ? Side.Long : Side.Short;
const priceWithSlippage = perpClient.getPriceAfterSlippage(
false, // isEntry = false for closing position
slippageBpsBN,
targetPrice.price,
sideEnum,
);
// Get NFT trading account info
const tradingAccounts = await getNftTradingAccountInfo(
agent.wallet_address,
perpClient,
poolConfig,
collateralSymbol,
);
if (
!tradingAccounts.nftTradingAccountPk ||
!tradingAccounts.nftReferralAccountPK ||
!tradingAccounts.nftOwnerRebateTokenAccountPk
) {
throw new Error("Required NFT trading accounts not found");
}
// Build and send transaction
const { instructions, additionalSigners } = await perpClient.closePosition(
targetSymbol,
collateralSymbol,
priceWithSlippage,
sideEnum,
poolConfig,
Privilege.Referral,
tradingAccounts.nftTradingAccountPk,
tradingAccounts.nftReferralAccountPK,
tradingAccounts.nftOwnerRebateTokenAccountPk,
);
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: CLOSE_POSITION_CU,
});
return await perpClient.sendTransaction(
[computeBudgetIx, ...instructions],
{
additionalSigners: additionalSigners,
alts: perpClient.addressLookupTables,
prioritizationFee: 5000000,
},
);
} catch (error) {
throw new Error(`Flash trade close failed: ${error}`);
}
}

View File

@@ -0,0 +1,254 @@
import { PublicKey, ComputeBudgetProgram } from "@solana/web3.js";
import {
PerpetualsClient,
OraclePrice,
PoolConfig,
Privilege,
Side,
CustodyAccount,
Custody,
} from "flash-sdk";
import { BN } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../index";
import {
ALL_TOKENS,
marketSdkInfo,
marketTokenMap,
getNftTradingAccountInfo,
OPEN_POSITION_CU,
fetchOraclePrice,
createPerpClient,
} from "../utils/flashUtils";
export interface FlashTradeParams {
token: string;
side: "long" | "short";
collateralUsd: number;
leverage: number;
}
/**
* Opens a new position on Flash.Trade
* @param agent SolanaAgentKit instance
* @param params Trade parameters
* @returns Transaction signature
*/
export async function flashOpenTrade(
agent: SolanaAgentKit,
params: FlashTradeParams,
): Promise<string> {
try {
const { token, side, collateralUsd, leverage } = params;
// Get market ID from token and side using marketTokenMap
const tokenMarkets = marketTokenMap[token];
if (!tokenMarkets) {
throw new Error(`Token ${token} not supported for trading`);
}
const sideEntry = tokenMarkets[side];
if (!sideEntry) {
throw new Error(`${side} side not available for ${token}`);
}
const market = sideEntry.marketID;
// Validate market data using marketSdkInfo
const marketData = marketSdkInfo[market];
if (!marketData) {
throw new Error(`Invalid market configuration for ${token}/${side}`);
}
// Get token information
const [targetSymbol, collateralSymbol] = marketData.tokenPair.split("/");
const targetToken = ALL_TOKENS.find((t) => t.symbol === targetSymbol);
const collateralToken = ALL_TOKENS.find(
(t) => t.symbol === collateralSymbol,
);
if (!targetToken || !collateralToken) {
throw new Error(`Token not found for pair ${marketData.tokenPair}`);
}
// Fetch oracle prices
const [targetPrice, collateralPrice] = await Promise.all([
fetchOraclePrice(targetSymbol),
fetchOraclePrice(collateralSymbol),
]);
// Initialize pool configuration and perpClient
const poolConfig = PoolConfig.fromIdsByName(marketData.pool, "mainnet-beta");
const perpClient = createPerpClient(agent.connection, agent.wallet);
// Calculate position parameters
const leverageBN = new BN(leverage);
const collateralTokenPrice = convertPriceToNumber(collateralPrice.price);
const collateralAmount = calculateCollateralAmount(
collateralUsd,
collateralTokenPrice,
collateralToken.decimals,
);
// Get custody accounts
const { targetCustody, collateralCustody } = await fetchCustodyAccounts(
perpClient,
poolConfig,
targetSymbol,
collateralSymbol,
);
// Calculate position size
const positionSize = calculatePositionSize(
perpClient,
collateralAmount,
leverageBN,
targetToken,
collateralToken,
side,
targetPrice.price,
collateralPrice.price,
targetCustody,
collateralCustody,
);
// Get NFT trading account info
const tradingAccounts = await getNftTradingAccountInfo(
agent.wallet_address,
perpClient,
poolConfig,
collateralSymbol,
);
if (
!tradingAccounts.nftTradingAccountPk ||
!tradingAccounts.nftReferralAccountPK
) {
throw new Error("Required NFT trading accounts not found");
}
// Prepare transaction
const slippageBps = new BN(1000);
const priceWithSlippage = perpClient.getPriceAfterSlippage(
true,
slippageBps,
targetPrice.price,
side === "long" ? Side.Long : Side.Short,
);
// Build and send transaction
const { instructions, additionalSigners } = await perpClient.openPosition(
targetSymbol,
collateralSymbol,
priceWithSlippage,
collateralAmount,
positionSize,
side === "long" ? Side.Long : Side.Short,
poolConfig,
Privilege.Referral,
tradingAccounts.nftTradingAccountPk,
tradingAccounts.nftReferralAccountPK,
tradingAccounts.nftOwnerRebateTokenAccountPk!,
false,
);
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: OPEN_POSITION_CU,
});
return await perpClient.sendTransaction(
[computeBudgetIx, ...instructions],
{
additionalSigners: additionalSigners,
alts: perpClient.addressLookupTables,
prioritizationFee: 5000000,
},
);
} catch (error) {
throw new Error(`Flash trade failed: ${error}`);
}
}
// Helper functions
function convertPriceToNumber(oraclePrice: OraclePrice): number {
const price = parseInt(oraclePrice.price.toString("hex"), 16);
const exponent = parseInt(oraclePrice.exponent.toString("hex"), 16);
return price * Math.pow(10, exponent);
}
function calculateCollateralAmount(
usdAmount: number,
tokenPrice: number,
decimals: number,
): BN {
return new BN((usdAmount / tokenPrice) * Math.pow(10, decimals));
}
async function fetchCustodyAccounts(
perpClient: PerpetualsClient,
poolConfig: PoolConfig,
targetSymbol: string,
collateralSymbol: string,
) {
const targetConfig = poolConfig.custodies.find(
(c) => c.symbol === targetSymbol,
);
const collateralConfig = poolConfig.custodies.find(
(c) => c.symbol === collateralSymbol,
);
if (!targetConfig || !collateralConfig) {
throw new Error("Custody configuration not found");
}
const accounts = await perpClient.provider.connection.getMultipleAccountsInfo(
[targetConfig.custodyAccount, collateralConfig.custodyAccount],
);
if (!accounts[0] || !accounts[1]) {
throw new Error("Failed to fetch custody accounts");
}
return {
targetCustody: CustodyAccount.from(
targetConfig.custodyAccount,
perpClient.program.coder.accounts.decode<Custody>(
"custody",
accounts[0].data,
),
),
collateralCustody: CustodyAccount.from(
collateralConfig.custodyAccount,
perpClient.program.coder.accounts.decode<Custody>(
"custody",
accounts[1].data,
),
),
};
}
function calculatePositionSize(
perpClient: PerpetualsClient,
collateralAmount: BN,
leverage: BN,
targetToken: any,
collateralToken: any,
side: "long" | "short",
targetPrice: OraclePrice,
collateralPrice: OraclePrice,
targetCustody: CustodyAccount,
collateralCustody: CustodyAccount,
): BN {
return perpClient.getSizeAmountFromLeverageAndCollateral(
collateralAmount,
leverage.toString(),
targetToken,
collateralToken,
side === "long" ? Side.Long : Side.Short,
targetPrice,
targetPrice,
targetCustody,
collateralPrice,
collateralPrice,
collateralCustody,
);
}

View File

@@ -59,3 +59,6 @@ export * from "./create_tiplinks";
export * from "./tensor_trade";
export * from "./rugcheck";
export * from "./flash_open_trade";
export * from "./flash_close_trade";

310
src/utils/flashUtils.ts Normal file
View File

@@ -0,0 +1,310 @@
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
import { OraclePrice } from "flash-sdk";
import { AnchorProvider, BN, Wallet } from "@coral-xyz/anchor";
import { PoolConfig, Token, Referral, PerpetualsClient } from "flash-sdk";
import { Cluster, PublicKey, Connection, Keypair } from "@solana/web3.js";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
const POOL_NAMES = [
"Crypto.1",
"Virtual.1",
"Governance.1",
"Community.1",
"Community.2",
"Community.3",
];
const DEFAULT_CLUSTER: Cluster = "mainnet-beta";
export const POOL_CONFIGS = POOL_NAMES.map((f) =>
PoolConfig.fromIdsByName(f, DEFAULT_CLUSTER),
);
const DUPLICATE_TOKENS = POOL_CONFIGS.map((f) => f.tokens).flat();
const tokenMap = new Map();
for (const token of DUPLICATE_TOKENS) {
tokenMap.set(token.symbol, token);
}
export const ALL_TOKENS: Token[] = Array.from(tokenMap.values());
export const ALL_CUSTODIES = POOL_CONFIGS.map((f) => f.custodies).flat();
const PROGRAM_ID = POOL_CONFIGS[0].programId;
// CU for trade instructions
export const OPEN_POSITION_CU = 150_000;
export const CLOSE_POSITION_CU = 180_000;
const HERMES_URL = "https://hermes.pyth.network"; // Replace with the actual Hermes URL if different
// Create a map of symbol to Pyth price ID
const PRICE_FEED_IDS = ALL_TOKENS.reduce(
(acc, token) => {
acc[token.symbol] = token.pythPriceId;
return acc;
},
{} as { [key: string]: string },
);
const priceServiceConnection = new PriceServiceConnection(HERMES_URL, {
priceFeedRequestConfig: {
binary: true,
},
});
export interface PythPriceEntry {
price: OraclePrice;
emaPrice: OraclePrice;
isStale: boolean;
status: PriceStatus;
}
export enum PriceStatus {
Trading,
Unknown,
Halted,
Auction,
}
export const fetchOraclePrice = async (
symbol: string,
): Promise<PythPriceEntry> => {
const priceFeedId = PRICE_FEED_IDS[symbol];
if (!priceFeedId) {
throw new Error(`Price feed ID not found for symbol: ${symbol}`);
}
try {
const priceFeed = await priceServiceConnection.getLatestPriceFeeds([
priceFeedId,
]);
if (!priceFeed || priceFeed.length === 0) {
throw new Error(`No price feed received for ${symbol}`);
}
const price = priceFeed[0].getPriceUnchecked();
const emaPrice = priceFeed[0].getEmaPriceUnchecked();
const priceOracle = new OraclePrice({
price: new BN(price.price),
exponent: new BN(price.expo),
confidence: new BN(price.conf),
timestamp: new BN(price.publishTime),
});
const emaPriceOracle = new OraclePrice({
price: new BN(emaPrice.price),
exponent: new BN(emaPrice.expo),
confidence: new BN(emaPrice.conf),
timestamp: new BN(emaPrice.publishTime),
});
const token = ALL_TOKENS.find((t) => t.pythPriceId === priceFeedId);
if (!token) {
throw new Error(`Token not found for price feed ID: ${priceFeedId}`);
}
const status = !token.isVirtual ? PriceStatus.Trading : PriceStatus.Unknown;
const pythPriceEntry: PythPriceEntry = {
price: priceOracle,
emaPrice: emaPriceOracle,
isStale: false,
status: status,
};
return pythPriceEntry;
} catch (error) {
console.error(`Error in fetchOraclePrice for ${symbol}:`, error);
throw error;
}
};
// If you need to get all price IDs for subscription or other purposes
export const getAllPriceIds = () => ALL_TOKENS.map((t) => t.pythPriceId);
export const subscribeToPriceFeeds = (
callback: (symbol: string, priceEntry: PythPriceEntry) => void,
) => {
const priceIds = getAllPriceIds();
priceServiceConnection.subscribePriceFeedUpdates(priceIds, (priceFeed) => {
const token = ALL_TOKENS.find((f) => f.pythPriceId === `0x${priceFeed.id}`);
if (token) {
const priceOracle = new OraclePrice({
price: new BN(priceFeed.getPriceUnchecked().price),
exponent: new BN(priceFeed.getPriceUnchecked().expo),
confidence: new BN(priceFeed.getPriceUnchecked().conf),
timestamp: new BN(priceFeed.getPriceUnchecked().publishTime),
});
const emaPriceOracle = new OraclePrice({
price: new BN(priceFeed.getEmaPriceUnchecked().price),
exponent: new BN(priceFeed.getEmaPriceUnchecked().expo),
confidence: new BN(priceFeed.getEmaPriceUnchecked().conf),
timestamp: new BN(priceFeed.getEmaPriceUnchecked().publishTime),
});
const status = !token.isVirtual
? PriceStatus.Trading
: PriceStatus.Unknown;
const priceEntry: PythPriceEntry = {
price: priceOracle,
emaPrice: emaPriceOracle,
isStale: false,
status: status,
};
callback(token.symbol, priceEntry);
}
});
};
export interface MarketInfo {
[key: string]: {
tokenPair: string;
token: string;
side: string;
pool: string;
};
}
const marketSdkInfo: MarketInfo = {};
// Loop through POOL_CONFIGS to process each market
POOL_CONFIGS.forEach((poolConfig) => {
poolConfig.markets.forEach((market) => {
const targetToken = ALL_TOKENS.find(
(token) => token.mintKey.toString() === market.targetMint.toString(),
);
// Find collateral token by matching mintKey
const collateralToken = ALL_TOKENS.find(
(token) => token.mintKey.toString() === market.collateralMint.toString(),
);
if (targetToken?.symbol && collateralToken?.symbol) {
marketSdkInfo[market.marketAccount.toString()] = {
tokenPair: `${targetToken.symbol}/${collateralToken.symbol}`,
token: targetToken.symbol,
side: Object.keys(market.side)[0],
pool: poolConfig.poolName,
};
}
});
});
export { marketSdkInfo };
export interface MarketTokenSides {
[token: string]: {
long?: { marketID: string };
short?: { marketID: string };
};
}
const marketTokenMap: MarketTokenSides = {};
// Convert marketSdkInfo into marketTokenMap
Object.entries(marketSdkInfo).forEach(([marketID, info]) => {
if (!marketTokenMap[info.token]) {
marketTokenMap[info.token] = {};
}
marketTokenMap[info.token][info.side.toLowerCase() as 'long' | 'short'] = {
marketID
};
});
export { marketTokenMap };
interface TradingAccountResult {
nftReferralAccountPK: PublicKey | null;
nftTradingAccountPk: PublicKey | null;
nftOwnerRebateTokenAccountPk: PublicKey | null;
}
export async function getNftTradingAccountInfo(
userPublicKey: PublicKey,
perpClient: PerpetualsClient,
poolConfig: PoolConfig,
collateralCustodySymbol: string,
): Promise<TradingAccountResult> {
const getNFTReferralAccountPK = (publicKey: PublicKey) => {
return PublicKey.findProgramAddressSync(
[Buffer.from("referral"), publicKey.toBuffer()],
PROGRAM_ID,
)[0];
};
const nftReferralAccountPK = getNFTReferralAccountPK(userPublicKey);
const nftReferralAccountInfo =
await perpClient.provider.connection.getAccountInfo(nftReferralAccountPK);
let nftTradingAccountPk: PublicKey | null = null;
let nftOwnerRebateTokenAccountPk: PublicKey | null = null;
if (nftReferralAccountInfo) {
const nftReferralAccountData = perpClient.program.coder.accounts.decode(
"referral",
nftReferralAccountInfo.data,
) as Referral;
nftTradingAccountPk = nftReferralAccountData.refererTradingAccount;
if (nftTradingAccountPk) {
const nftTradingAccountInfo =
await perpClient.provider.connection.getAccountInfo(
nftTradingAccountPk,
);
if (nftTradingAccountInfo) {
const nftTradingAccount = perpClient.program.coder.accounts.decode(
"trading",
nftTradingAccountInfo.data,
) as { owner: PublicKey };
nftOwnerRebateTokenAccountPk = getAssociatedTokenAddressSync(
poolConfig.getTokenFromSymbol(collateralCustodySymbol).mintKey,
nftTradingAccount.owner,
);
// Check if the account exists
const accountExists =
await perpClient.provider.connection.getAccountInfo(
nftOwnerRebateTokenAccountPk,
);
if (!accountExists) {
console.log(
"NFT owner rebate token account does not exist and may need to be created",
);
}
}
}
}
return {
nftReferralAccountPK,
nftTradingAccountPk,
nftOwnerRebateTokenAccountPk,
};
}
/**
* Creates a new PerpetualsClient instance with the given connection and wallet
* @param connection Solana connection
* @param wallet Solana wallet
* @returns PerpetualsClient instance
*/
export function createPerpClient(connection: Connection, wallet: Keypair): PerpetualsClient {
const provider = new AnchorProvider(
connection,
new Wallet(wallet),
{
commitment: 'confirmed',
preflightCommitment: 'confirmed',
skipPreflight: true
}
);
return new PerpetualsClient(
provider,
POOL_CONFIGS[0].programId,
POOL_CONFIGS[0].perpComposibilityProgramId,
POOL_CONFIGS[0].fbNftRewardProgramId,
POOL_CONFIGS[0].rewardDistributionProgram.programId,
{}
);
}