mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-13 23:16:55 +00:00
add airdrop
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
"@langchain/groq": "^0.1.2",
|
||||
"@langchain/langgraph": "^0.2.27",
|
||||
"@langchain/openai": "^0.3.13",
|
||||
"@lightprotocol/compressed-token": "^0.17.1",
|
||||
"@lightprotocol/stateless.js": "^0.17.1",
|
||||
"@metaplex-foundation/mpl-core": "^1.1.1",
|
||||
"@metaplex-foundation/mpl-token-metadata": "^3.3.0",
|
||||
"@metaplex-foundation/umi": "^0.9.2",
|
||||
|
||||
208
pnpm-lock.yaml
generated
208
pnpm-lock.yaml
generated
@@ -23,6 +23,12 @@ importers:
|
||||
'@langchain/openai':
|
||||
specifier: ^0.3.13
|
||||
version: 0.3.14(@langchain/core@0.3.23(openai@4.76.3(zod@3.24.1)))
|
||||
'@lightprotocol/compressed-token':
|
||||
specifier: ^0.17.1
|
||||
version: 0.17.1(@lightprotocol/stateless.js@0.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)
|
||||
'@lightprotocol/stateless.js':
|
||||
specifier: ^0.17.1
|
||||
version: 0.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@metaplex-foundation/mpl-core':
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@metaplex-foundation/umi@0.9.2)(@noble/hashes@1.6.1)
|
||||
@@ -180,9 +186,17 @@ packages:
|
||||
peerDependencies:
|
||||
'@lightprotocol/stateless.js': 0.13.1
|
||||
|
||||
'@lightprotocol/compressed-token@0.17.1':
|
||||
resolution: {integrity: sha512-493KCmZGw1BcHVRJaeRm8EEs+L7gX8dwY7JG13w2pfgOMtZXZ7Wxt261jFJxQJzRLTrUSlrbRJOmfW1+S1Y8SQ==}
|
||||
peerDependencies:
|
||||
'@lightprotocol/stateless.js': 0.17.1
|
||||
|
||||
'@lightprotocol/stateless.js@0.13.1':
|
||||
resolution: {integrity: sha512-3dmsQJwDl/6oQWAvmai8DvYYi0LNi6yLST3WK6XQDSAX4hc8pMd0gjX7feSaX9aMPKrA3xvH6QsljGB5OKCXBw==}
|
||||
|
||||
'@lightprotocol/stateless.js@0.17.1':
|
||||
resolution: {integrity: sha512-EjId1n33A6dBwpce33Wsa/fs/CDKtMtRrkxbApH0alXrnEXmbW6QhIViXOrKYXjZ4uJQM1xsBtsKe0vqJ4nbtQ==}
|
||||
|
||||
'@metaplex-foundation/mpl-core@1.1.1':
|
||||
resolution: {integrity: sha512-h1kLw+cGaV8SiykoHDb1/G01+VYqtJXAt0uGuO5+2Towsdtc6ET4M62iqUnh4EacTVMIW1yYHsKsG/LYWBCKaA==}
|
||||
peerDependencies:
|
||||
@@ -283,6 +297,10 @@ packages:
|
||||
resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
|
||||
'@noble/hashes@1.5.0':
|
||||
resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
|
||||
'@noble/hashes@1.6.0':
|
||||
resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
@@ -320,6 +338,11 @@ packages:
|
||||
'@solana/codecs-core@2.0.0-preview.2':
|
||||
resolution: {integrity: sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg==}
|
||||
|
||||
'@solana/codecs-core@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/codecs-core@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==}
|
||||
peerDependencies:
|
||||
@@ -328,6 +351,11 @@ packages:
|
||||
'@solana/codecs-data-structures@2.0.0-preview.2':
|
||||
resolution: {integrity: sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg==}
|
||||
|
||||
'@solana/codecs-data-structures@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/codecs-data-structures@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==}
|
||||
peerDependencies:
|
||||
@@ -336,6 +364,11 @@ packages:
|
||||
'@solana/codecs-numbers@2.0.0-preview.2':
|
||||
resolution: {integrity: sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw==}
|
||||
|
||||
'@solana/codecs-numbers@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/codecs-numbers@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==}
|
||||
peerDependencies:
|
||||
@@ -346,6 +379,12 @@ packages:
|
||||
peerDependencies:
|
||||
fastestsmallesttextencoderdecoder: ^1.0.22
|
||||
|
||||
'@solana/codecs-strings@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw==}
|
||||
peerDependencies:
|
||||
fastestsmallesttextencoderdecoder: ^1.0.22
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/codecs-strings@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==}
|
||||
peerDependencies:
|
||||
@@ -355,6 +394,11 @@ packages:
|
||||
'@solana/codecs@2.0.0-preview.2':
|
||||
resolution: {integrity: sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA==}
|
||||
|
||||
'@solana/codecs@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/codecs@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==}
|
||||
peerDependencies:
|
||||
@@ -364,6 +408,12 @@ packages:
|
||||
resolution: {integrity: sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA==}
|
||||
hasBin: true
|
||||
|
||||
'@solana/errors@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/errors@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==}
|
||||
hasBin: true
|
||||
@@ -373,6 +423,11 @@ packages:
|
||||
'@solana/options@2.0.0-preview.2':
|
||||
resolution: {integrity: sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w==}
|
||||
|
||||
'@solana/options@2.0.0-preview.4':
|
||||
resolution: {integrity: sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
|
||||
'@solana/options@2.0.0-rc.1':
|
||||
resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==}
|
||||
peerDependencies:
|
||||
@@ -384,6 +439,12 @@ packages:
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.91.6
|
||||
|
||||
'@solana/spl-token-group@0.0.5':
|
||||
resolution: {integrity: sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ==}
|
||||
engines: {node: '>=16'}
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.94.0
|
||||
|
||||
'@solana/spl-token-group@0.0.7':
|
||||
resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -402,6 +463,12 @@ packages:
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.91.6
|
||||
|
||||
'@solana/spl-token@0.4.8':
|
||||
resolution: {integrity: sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA==}
|
||||
engines: {node: '>=16'}
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.94.0
|
||||
|
||||
'@solana/spl-token@0.4.9':
|
||||
resolution: {integrity: sha512-g3wbj4F4gq82YQlwqhPB0gHFXfgsC6UmyGMxtSLf/BozT/oKd59465DbnlUK8L8EcimKMavxsVAMoLcEdeCicg==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -412,6 +479,9 @@ packages:
|
||||
resolution: {integrity: sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@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==}
|
||||
|
||||
@@ -1690,6 +1760,21 @@ snapshots:
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
'@lightprotocol/compressed-token@0.17.1(@lightprotocol/stateless.js@0.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@lightprotocol/stateless.js': 0.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@solana/spl-token': 0.4.8(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)
|
||||
'@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
buffer: 6.0.3
|
||||
tweetnacl: 1.0.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
- fastestsmallesttextencoderdecoder
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
'@lightprotocol/stateless.js@0.13.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
@@ -1703,6 +1788,19 @@ snapshots:
|
||||
- encoding
|
||||
- utf-8-validate
|
||||
|
||||
'@lightprotocol/stateless.js@0.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@noble/hashes': 1.5.0
|
||||
'@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
buffer: 6.0.3
|
||||
superstruct: 2.0.2
|
||||
tweetnacl: 1.0.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
- utf-8-validate
|
||||
|
||||
'@metaplex-foundation/mpl-core@1.1.1(@metaplex-foundation/umi@0.9.2)(@noble/hashes@1.6.1)':
|
||||
dependencies:
|
||||
'@metaplex-foundation/umi': 0.9.2
|
||||
@@ -1817,6 +1915,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@noble/hashes': 1.6.0
|
||||
|
||||
'@noble/hashes@1.5.0': {}
|
||||
|
||||
'@noble/hashes@1.6.0': {}
|
||||
|
||||
'@noble/hashes@1.6.1': {}
|
||||
@@ -1869,6 +1969,11 @@ snapshots:
|
||||
dependencies:
|
||||
'@solana/errors': 2.0.0-preview.2
|
||||
|
||||
'@solana/codecs-core@2.0.0-preview.4(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
|
||||
'@solana/codecs-core@2.0.0-rc.1(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/errors': 2.0.0-rc.1(typescript@5.7.2)
|
||||
@@ -1880,6 +1985,13 @@ snapshots:
|
||||
'@solana/codecs-numbers': 2.0.0-preview.2
|
||||
'@solana/errors': 2.0.0-preview.2
|
||||
|
||||
'@solana/codecs-data-structures@2.0.0-preview.4(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
|
||||
'@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
|
||||
@@ -1892,6 +2004,12 @@ snapshots:
|
||||
'@solana/codecs-core': 2.0.0-preview.2
|
||||
'@solana/errors': 2.0.0-preview.2
|
||||
|
||||
'@solana/codecs-numbers@2.0.0-preview.4(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
|
||||
'@solana/codecs-numbers@2.0.0-rc.1(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
|
||||
@@ -1905,6 +2023,14 @@ snapshots:
|
||||
'@solana/errors': 2.0.0-preview.2
|
||||
fastestsmallesttextencoderdecoder: 1.0.22
|
||||
|
||||
'@solana/codecs-strings@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
|
||||
fastestsmallesttextencoderdecoder: 1.0.22
|
||||
typescript: 5.7.2
|
||||
|
||||
'@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
|
||||
@@ -1923,6 +2049,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
|
||||
'@solana/codecs@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
|
||||
'@solana/options': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
|
||||
'@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
|
||||
@@ -1939,6 +2076,12 @@ snapshots:
|
||||
chalk: 5.3.0
|
||||
commander: 12.1.0
|
||||
|
||||
'@solana/errors@2.0.0-preview.4(typescript@5.7.2)':
|
||||
dependencies:
|
||||
chalk: 5.3.0
|
||||
commander: 12.1.0
|
||||
typescript: 5.7.2
|
||||
|
||||
'@solana/errors@2.0.0-rc.1(typescript@5.7.2)':
|
||||
dependencies:
|
||||
chalk: 5.3.0
|
||||
@@ -1950,6 +2093,17 @@ snapshots:
|
||||
'@solana/codecs-core': 2.0.0-preview.2
|
||||
'@solana/codecs-numbers': 2.0.0-preview.2
|
||||
|
||||
'@solana/options@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.7.2)
|
||||
'@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
|
||||
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
|
||||
'@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
|
||||
@@ -1969,6 +2123,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
|
||||
'@solana/spl-token-group@0.0.5(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@solana/codecs': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
|
||||
'@solana/spl-type-length-value': 0.1.0
|
||||
'@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
- typescript
|
||||
|
||||
'@solana/spl-token-group@0.0.7(@solana/web3.js@1.95.8(bufferutil@4.0.8)(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)
|
||||
@@ -1977,6 +2140,14 @@ snapshots:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
- typescript
|
||||
|
||||
'@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.3(bufferutil@4.0.8)(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.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
- fastestsmallesttextencoderdecoder
|
||||
- typescript
|
||||
|
||||
'@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.8)(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)
|
||||
@@ -2000,6 +2171,21 @@ snapshots:
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
'@solana/spl-token@0.4.8(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(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.8)(utf-8-validate@5.0.10)
|
||||
'@solana/spl-token-group': 0.0.5(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
|
||||
'@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
|
||||
'@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
buffer: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
- fastestsmallesttextencoderdecoder
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
'@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@solana/buffer-layout': 4.0.1
|
||||
@@ -2019,6 +2205,28 @@ snapshots:
|
||||
dependencies:
|
||||
buffer: 6.0.3
|
||||
|
||||
'@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
'@noble/curves': 1.7.0
|
||||
'@noble/hashes': 1.6.1
|
||||
'@solana/buffer-layout': 4.0.1
|
||||
agentkeepalive: 4.5.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.8)(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.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
getTokenDataByAddress,
|
||||
getTokenDataByTicker,
|
||||
stakeWithJup,
|
||||
createCompressedAirdrop,
|
||||
sendCompressedAirdrop,
|
||||
} from "../tools";
|
||||
import { CollectionOptions, PumpFunTokenOptions } from "../types";
|
||||
@@ -142,17 +141,20 @@ export class SolanaAgentKit {
|
||||
return stakeWithJup(this, amount);
|
||||
}
|
||||
|
||||
async airdropCompressedTokens(
|
||||
async sendCompressedAirdrop(
|
||||
mintAddress: string,
|
||||
amount: number,
|
||||
recipients: string[]
|
||||
) {
|
||||
await createCompressedAirdrop(
|
||||
recipients: string[],
|
||||
priorityFeeInLamports: number,
|
||||
shouldLog: boolean
|
||||
): Promise<string[]> {
|
||||
return await sendCompressedAirdrop(
|
||||
this,
|
||||
new PublicKey(mintAddress),
|
||||
BigInt(amount),
|
||||
recipients.map((recipient) => new PublicKey(recipient))
|
||||
amount,
|
||||
recipients.map((recipient) => new PublicKey(recipient)),
|
||||
priorityFeeInLamports,
|
||||
shouldLog
|
||||
);
|
||||
return await sendCompressedAirdrop(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,6 +710,7 @@ export class SolanaAirdropCompressedTokensTool extends Tool {
|
||||
- mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"
|
||||
- amount: number, the amount of tokens to airdrop per recipient, e.g., 42
|
||||
- recipients: string[], the recipient addresses, e.g., ["JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"]
|
||||
- priorityFeeInLamports: number, the priority fee in lamports, e.g., 10_000. Default is 30_000.
|
||||
`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
@@ -719,18 +720,19 @@ export class SolanaAirdropCompressedTokensTool extends Tool {
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const parsedInput = JSON.parse(input);
|
||||
if (parsedInput.recipients.length <= 100) {
|
||||
throw new Error("Recipients array must contain at least 420 addresses");
|
||||
}
|
||||
await this.solanaKit.airdropCompressedTokens(
|
||||
|
||||
const txs = await this.solanaKit.sendCompressedAirdrop(
|
||||
parsedInput.mintAddress,
|
||||
parsedInput.amount,
|
||||
parsedInput.recipients
|
||||
parsedInput.recipients,
|
||||
parsedInput.priorityFeeInLamports || 30_000,
|
||||
false // no logging
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: `Airdropped ${parsedInput.amount} tokens to ${parsedInput.recipients.length} recipients.`,
|
||||
transactionHashes: txs,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
|
||||
@@ -1,228 +1,272 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import type { DrizzleDb, WorkerMessage, WorkerData } from "./types";
|
||||
import {
|
||||
AddressLookupTableAccount,
|
||||
ComputeBudgetProgram,
|
||||
Connection,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../../agent/index.js";
|
||||
import {
|
||||
buildAndSignTx,
|
||||
calculateComputeUnitPrice,
|
||||
createRpc,
|
||||
Rpc,
|
||||
sendAndConfirmTx,
|
||||
sleep,
|
||||
} from "@lightprotocol/stateless.js";
|
||||
import {
|
||||
CompressedTokenProgram,
|
||||
createTokenPool,
|
||||
} from "@lightprotocol/compressed-token";
|
||||
import { Account, getOrCreateAssociatedTokenAccount } from "@solana/spl-token";
|
||||
|
||||
let db: DrizzleDb | null = null;
|
||||
let dbInitPromise: Promise<DrizzleDb | null> | null = null;
|
||||
|
||||
async function configureForBrowser() {
|
||||
try {
|
||||
const [{ SQLocalDrizzle }, { drizzle }, { sql }, heliusCore] =
|
||||
await Promise.all([
|
||||
// @ts-ignore
|
||||
import("sqlocal/drizzle"),
|
||||
import("drizzle-orm/sqlite-proxy"),
|
||||
import("drizzle-orm"),
|
||||
import("helius-airship-core"),
|
||||
]);
|
||||
|
||||
const { databaseFile } = heliusCore;
|
||||
|
||||
const { driver, batchDriver } = new SQLocalDrizzle({
|
||||
databasePath: databaseFile,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
const database = drizzle(driver, batchDriver);
|
||||
await database.run(sql`PRAGMA journal_mode = WAL;`);
|
||||
await database.run(sql`PRAGMA synchronous = normal;`);
|
||||
|
||||
return database;
|
||||
} catch (error) {
|
||||
console.error("Browser database configuration failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function configureForNode() {
|
||||
try {
|
||||
const [{ drizzle }, { default: Database }, { databaseFile }] =
|
||||
await Promise.all([
|
||||
import("drizzle-orm/better-sqlite3"),
|
||||
import("better-sqlite3"),
|
||||
import("helius-airship-core"),
|
||||
]);
|
||||
|
||||
const sqlite = new Database(databaseFile);
|
||||
sqlite.exec("PRAGMA journal_mode = WAL;");
|
||||
sqlite.exec("PRAGMA synchronous = normal;");
|
||||
|
||||
return drizzle(sqlite);
|
||||
} catch (error) {
|
||||
console.error("Node database configuration failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function configureDatabase(): Promise<DrizzleDb> {
|
||||
if (!dbInitPromise) {
|
||||
dbInitPromise = (async () => {
|
||||
if (!db) {
|
||||
db =
|
||||
typeof window !== "undefined"
|
||||
? await configureForBrowser()
|
||||
: await configureForNode();
|
||||
}
|
||||
return db;
|
||||
})();
|
||||
}
|
||||
|
||||
const database = await dbInitPromise;
|
||||
if (!database) throw new Error("Database initialization failed");
|
||||
return database;
|
||||
}
|
||||
async function createWorker(): Promise<
|
||||
Worker | import("worker_threads").Worker
|
||||
> {
|
||||
if (typeof window !== "undefined") {
|
||||
const origin = new URL(window.location.href).origin;
|
||||
if (
|
||||
!origin.startsWith("https://") &&
|
||||
!origin.startsWith("http://localhost")
|
||||
) {
|
||||
throw new Error("Invalid origin protocol");
|
||||
}
|
||||
|
||||
const workerCode = `
|
||||
self.importScripts('${origin}/airdrop-worker.js'.replace(/[<>'"]/g, ''));
|
||||
`;
|
||||
|
||||
const blobOptions = {
|
||||
type: "application/javascript",
|
||||
headers: {
|
||||
"Content-Security-Policy": "default-src 'self'",
|
||||
},
|
||||
};
|
||||
|
||||
const blob = new Blob([workerCode], blobOptions);
|
||||
const workerUrl = URL.createObjectURL(blob);
|
||||
const worker = new Worker(workerUrl);
|
||||
URL.revokeObjectURL(workerUrl);
|
||||
return worker;
|
||||
} else {
|
||||
// Node
|
||||
const { Worker } = await import("worker_threads");
|
||||
const path = await import("path");
|
||||
|
||||
return new Worker(path.resolve(__dirname, "worker.js"));
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_AIRDROP_RECIPIENTS = 1000;
|
||||
const MAX_CONCURRENT = 30;
|
||||
/**
|
||||
* Create airdrop with zk compression
|
||||
* @param agent Agent
|
||||
* @param mintAddress Token mint public key (non token-2022)
|
||||
* @param amount amount of tokens to airdrop per recipient
|
||||
* @param recipients Recipient public keys
|
||||
*/
|
||||
export async function createCompressedAirdrop(
|
||||
agent: SolanaAgentKit,
|
||||
mintAddress: PublicKey,
|
||||
amount: bigint,
|
||||
recipients: PublicKey[]
|
||||
): Promise<void> {
|
||||
try {
|
||||
const database = await configureDatabase();
|
||||
const { create, init } = await import("helius-airship-core");
|
||||
|
||||
await init({
|
||||
db: database as any,
|
||||
});
|
||||
|
||||
await create({
|
||||
db: database as any,
|
||||
signer: agent.wallet.publicKey,
|
||||
addresses: recipients,
|
||||
amount,
|
||||
mintAddress,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Create operation failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send airdrop. must be called after `createCompressedAirdrop`
|
||||
* @param agent Agent
|
||||
* @param onProgress Callback for progress updates
|
||||
* @param onError Callback for error handling
|
||||
* Send airdrop with ZK Compressed Tokens.
|
||||
* @param agent Agent
|
||||
* @param mintAddress SPL Mint address
|
||||
* @param amount Amount to send per recipient
|
||||
* @param recipients Recipient wallet addresses (no ATAs)
|
||||
* @param shouldLog Whether to log progress to stdout. Defaults to false.
|
||||
*/
|
||||
export async function sendCompressedAirdrop(
|
||||
agent: SolanaAgentKit,
|
||||
onProgress?: (progress: number) => void,
|
||||
onError?: (error: Error) => void
|
||||
): Promise<void> {
|
||||
let worker: Worker | import("worker_threads").Worker | null = null;
|
||||
const { databaseFile } = await import("helius-airship-core");
|
||||
mintAddress: PublicKey,
|
||||
amount: number,
|
||||
recipients: PublicKey[],
|
||||
prioFeeInLamports: number,
|
||||
shouldLog: boolean = false
|
||||
): Promise<string[]> {
|
||||
if (recipients.length > MAX_AIRDROP_RECIPIENTS) {
|
||||
throw new Error(
|
||||
`Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.`
|
||||
);
|
||||
}
|
||||
let sourceTokenAccount: Account;
|
||||
try {
|
||||
sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
|
||||
agent.connection,
|
||||
agent.wallet,
|
||||
mintAddress,
|
||||
agent.wallet.publicKey
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
"Source token account not found and failed to create it. Please add funds to your wallet and try again."
|
||||
);
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const cleanup = () => {
|
||||
if (worker) {
|
||||
try {
|
||||
if ("terminate" in worker) {
|
||||
worker.terminate();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[Main] Worker cleanup error:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
await createTokenPool(
|
||||
agent.connection as unknown as Rpc,
|
||||
agent.wallet,
|
||||
mintAddress
|
||||
);
|
||||
} catch (error: any) {
|
||||
if (error.message.includes("already in use")) {
|
||||
// skip
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
worker = await createWorker();
|
||||
return await processAll(
|
||||
agent,
|
||||
amount,
|
||||
mintAddress,
|
||||
recipients,
|
||||
prioFeeInLamports,
|
||||
shouldLog
|
||||
);
|
||||
}
|
||||
async function processAll(
|
||||
agent: SolanaAgentKit,
|
||||
amount: number,
|
||||
mint: PublicKey,
|
||||
recipients: PublicKey[],
|
||||
prioFeeInLamports: number,
|
||||
shouldLog: boolean
|
||||
): Promise<string[]> {
|
||||
const mintAddress = mint;
|
||||
const payer = agent.wallet;
|
||||
|
||||
const message: WorkerData = {
|
||||
type: "send",
|
||||
data: {
|
||||
secretKey: Array.from(agent.wallet.secretKey),
|
||||
url: agent.connection.rpcEndpoint,
|
||||
dbPath: databaseFile,
|
||||
},
|
||||
};
|
||||
const sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
|
||||
agent.connection,
|
||||
agent.wallet,
|
||||
mintAddress,
|
||||
agent.wallet.publicKey
|
||||
);
|
||||
|
||||
const handleMessage = (event: MessageEvent<WorkerMessage>) => {
|
||||
if (event.data === undefined) {
|
||||
cleanup();
|
||||
reject(new Error());
|
||||
return;
|
||||
}
|
||||
const { type, data, error } = event.data;
|
||||
switch (type) {
|
||||
case "progress":
|
||||
onProgress?.(data!);
|
||||
break;
|
||||
case "error":
|
||||
cleanup();
|
||||
const errorObj = new Error(error);
|
||||
onError?.(errorObj);
|
||||
reject(errorObj);
|
||||
break;
|
||||
case "complete":
|
||||
cleanup();
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
};
|
||||
const maxRecipientsPerInstruction = 5;
|
||||
const maxIxs = 3; // empirically determined (as of 12/15/2024)
|
||||
const lookupTableAddress = new PublicKey(
|
||||
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ"
|
||||
);
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
(worker as Worker).onmessage = handleMessage;
|
||||
} else {
|
||||
(worker as import("worker_threads").Worker).on(
|
||||
"message",
|
||||
handleMessage
|
||||
const lookupTableAccount = (
|
||||
await agent.connection.getAddressLookupTable(lookupTableAddress)
|
||||
).value!;
|
||||
|
||||
const batches: PublicKey[][] = [];
|
||||
for (
|
||||
let i = 0;
|
||||
i < recipients.length;
|
||||
i += maxRecipientsPerInstruction * maxIxs
|
||||
) {
|
||||
batches.push(recipients.slice(i, i + maxRecipientsPerInstruction * maxIxs));
|
||||
}
|
||||
|
||||
const instructionSets = await Promise.all(
|
||||
batches.map(async (recipientBatch) => {
|
||||
const instructions: TransactionInstruction[] = [
|
||||
ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }),
|
||||
ComputeBudgetProgram.setComputeUnitPrice({
|
||||
microLamports: calculateComputeUnitPrice(prioFeeInLamports, 500_000),
|
||||
}),
|
||||
];
|
||||
|
||||
const compressIxPromises = [];
|
||||
for (
|
||||
let i = 0;
|
||||
i < recipientBatch.length;
|
||||
i += maxRecipientsPerInstruction
|
||||
) {
|
||||
const batch = recipientBatch.slice(i, i + maxRecipientsPerInstruction);
|
||||
compressIxPromises.push(
|
||||
CompressedTokenProgram.compress({
|
||||
payer: payer.publicKey,
|
||||
owner: payer.publicKey,
|
||||
source: sourceTokenAccount.address,
|
||||
toAddress: batch,
|
||||
amount: batch.map(() => amount),
|
||||
mint: mintAddress,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
worker.postMessage(message);
|
||||
} catch (error) {
|
||||
cleanup();
|
||||
console.error("[Main] Send operation failed:", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
});
|
||||
onError?.(error instanceof Error ? error : new Error(String(error)));
|
||||
reject(error);
|
||||
const compressIxs = await Promise.all(compressIxPromises);
|
||||
return [...instructions, ...compressIxs];
|
||||
})
|
||||
);
|
||||
|
||||
const url = agent.connection.rpcEndpoint;
|
||||
if (url.includes("devnet")) {
|
||||
throw new Error("Devnet is not supported for airdrop. Please use mainnet.");
|
||||
}
|
||||
if (!url.includes("helius")) {
|
||||
console.warn(
|
||||
"Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt."
|
||||
);
|
||||
}
|
||||
const rpc = createRpc(url, url, url);
|
||||
|
||||
const results = [];
|
||||
let confirmedCount = 0;
|
||||
const totalBatches = instructionSets.length;
|
||||
|
||||
const renderProgressBar = (current: number, total: number) => {
|
||||
const percentage = Math.floor((current / total) * 100);
|
||||
const filled = Math.floor((percentage / 100) * 20);
|
||||
const empty = 20 - filled;
|
||||
const bar = "█".repeat(filled) + "░".repeat(empty);
|
||||
return `Airdropped to ${Math.min(current * 15, recipients.length)}/${
|
||||
recipients.length
|
||||
} recipients [${bar}] ${percentage}%`;
|
||||
};
|
||||
|
||||
const log = (message: string) => {
|
||||
if (shouldLog && typeof process !== "undefined" && process.stdout) {
|
||||
process.stdout.write(message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
for (let i = 0; i < instructionSets.length; i += MAX_CONCURRENT) {
|
||||
const batchPromises = instructionSets
|
||||
.slice(i, i + MAX_CONCURRENT)
|
||||
.map((instructions, idx) =>
|
||||
sendTransactionWithRetry(
|
||||
rpc,
|
||||
instructions,
|
||||
payer,
|
||||
lookupTableAccount,
|
||||
i + idx
|
||||
).then((signature) => {
|
||||
confirmedCount++;
|
||||
log("\r" + renderProgressBar(confirmedCount, totalBatches));
|
||||
return signature;
|
||||
})
|
||||
);
|
||||
|
||||
const batchResults = await Promise.allSettled(batchPromises);
|
||||
results.push(...batchResults);
|
||||
}
|
||||
|
||||
log("\n");
|
||||
|
||||
const failures = results
|
||||
.filter((r) => r.status === "rejected")
|
||||
.map((r, idx) => ({
|
||||
index: idx,
|
||||
error: (r as PromiseRejectedResult).reason,
|
||||
}));
|
||||
|
||||
if (failures.length > 0) {
|
||||
throw new Error(
|
||||
`Failed to process ${failures.length} batches: ${failures
|
||||
.map((f) => f.error)
|
||||
.join(", ")}`
|
||||
);
|
||||
}
|
||||
|
||||
return results.map((r) => (r as PromiseFulfilledResult<string>).value);
|
||||
}
|
||||
|
||||
async function sendTransactionWithRetry(
|
||||
connection: Rpc,
|
||||
instructions: TransactionInstruction[],
|
||||
payer: Keypair,
|
||||
lookupTableAccount: AddressLookupTableAccount,
|
||||
batchIndex: number
|
||||
): Promise<string> {
|
||||
const MAX_RETRIES = 3;
|
||||
const INITIAL_BACKOFF = 500; // ms
|
||||
|
||||
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
const { blockhash } = await connection.getLatestBlockhash();
|
||||
const tx = buildAndSignTx(
|
||||
instructions,
|
||||
payer,
|
||||
blockhash,
|
||||
[],
|
||||
[lookupTableAccount]
|
||||
);
|
||||
|
||||
const signature = await sendAndConfirmTx(connection, tx);
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
const isRetryable =
|
||||
error.message?.includes("blockhash not found") ||
|
||||
error.message?.includes("timeout") ||
|
||||
error.message?.includes("rate limit") ||
|
||||
error.message?.includes("too many requests");
|
||||
|
||||
if (!isRetryable || attempt === MAX_RETRIES - 1) {
|
||||
throw new Error(
|
||||
`Batch ${batchIndex} failed after ${attempt + 1} attempts: ${
|
||||
error.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
const backoff =
|
||||
INITIAL_BACKOFF * Math.pow(2, attempt) * (0.5 + Math.random());
|
||||
await sleep(backoff);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
|
||||
import type { SqliteRemoteDatabase } from "drizzle-orm/sqlite-proxy";
|
||||
|
||||
export interface Closeable {
|
||||
close(): Promise<void> | void;
|
||||
}
|
||||
|
||||
export type DrizzleDb = BetterSQLite3Database | SqliteRemoteDatabase;
|
||||
|
||||
export interface WorkerMessage {
|
||||
type: "progress" | "error" | "complete";
|
||||
data?: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface WorkerData {
|
||||
type: "send" | "poll";
|
||||
data: {
|
||||
dbPath: string;
|
||||
url: string;
|
||||
secretKey?: number[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface WorkerError extends Error {
|
||||
code?: string;
|
||||
type: "worker_error";
|
||||
}
|
||||
|
||||
export interface DatabaseError extends Error {
|
||||
code?: string;
|
||||
type: "database_error";
|
||||
}
|
||||
|
||||
export function isWorkerMessage(message: any): message is WorkerMessage {
|
||||
return (
|
||||
message &&
|
||||
typeof message === "object" &&
|
||||
"type" in message &&
|
||||
(message.type === "progress" ||
|
||||
message.type === "error" ||
|
||||
message.type === "complete")
|
||||
);
|
||||
}
|
||||
|
||||
export function isWorkerData(data: any): data is WorkerData {
|
||||
return (
|
||||
data &&
|
||||
typeof data === "object" &&
|
||||
"type" in data &&
|
||||
(data.type === "send" || data.type === "poll") &&
|
||||
"data" in data &&
|
||||
typeof data.data === "object" &&
|
||||
typeof data.data.dbPath === "string" &&
|
||||
typeof data.data.url === "string"
|
||||
);
|
||||
}
|
||||
|
||||
export function isWorkerError(error: any): error is WorkerError {
|
||||
return (
|
||||
error instanceof Error && "type" in error && error.type === "worker_error"
|
||||
);
|
||||
}
|
||||
|
||||
export function isDatabaseError(error: any): error is DatabaseError {
|
||||
return (
|
||||
error instanceof Error && "type" in error && error.type === "database_error"
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import { send } from "helius-airship-core";
|
||||
import { Keypair } from "@solana/web3.js";
|
||||
import type { WorkerMessage, WorkerData, DrizzleDb, Closeable } from "./types";
|
||||
|
||||
let db: DrizzleDb | null = null;
|
||||
let dbInitPromise: Promise<DrizzleDb | null> | null = null;
|
||||
|
||||
async function initializeDb(dbPath: string): Promise<DrizzleDb> {
|
||||
if (!dbInitPromise) {
|
||||
dbInitPromise = (async () => {
|
||||
if (!db) {
|
||||
try {
|
||||
if (typeof window !== "undefined") {
|
||||
const [{ SQLocalDrizzle }, { drizzle }, { sql }] =
|
||||
await Promise.all([
|
||||
// @ts-ignore
|
||||
import("sqlocal/drizzle"),
|
||||
import("drizzle-orm/sqlite-proxy"),
|
||||
import("drizzle-orm"),
|
||||
]);
|
||||
|
||||
const { driver, batchDriver } = new SQLocalDrizzle({
|
||||
databasePath: dbPath,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
db = drizzle(driver, batchDriver);
|
||||
await db.run(sql`PRAGMA journal_mode = WAL;`);
|
||||
await db.run(sql`PRAGMA synchronous = normal;`);
|
||||
} else {
|
||||
const [{ drizzle }, { default: Database }] = await Promise.all([
|
||||
import("drizzle-orm/better-sqlite3"),
|
||||
import("better-sqlite3"),
|
||||
]);
|
||||
|
||||
const sqlite = new Database(dbPath);
|
||||
sqlite.exec("PRAGMA journal_mode = WAL;");
|
||||
sqlite.exec("PRAGMA synchronous = normal;");
|
||||
db = drizzle(sqlite);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Worker database initialization failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return db;
|
||||
})();
|
||||
}
|
||||
|
||||
const database = await dbInitPromise;
|
||||
if (!database) throw new Error("Worker database initialization failed");
|
||||
return database;
|
||||
}
|
||||
|
||||
function postMessage(message: WorkerMessage) {
|
||||
if (typeof window !== "undefined") {
|
||||
self.postMessage(message);
|
||||
} else {
|
||||
const { parentPort } = require("worker_threads");
|
||||
parentPort?.postMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMessage(data: WorkerData) {
|
||||
let database: DrizzleDb | null = null;
|
||||
|
||||
try {
|
||||
database = await initializeDb(data.data.dbPath);
|
||||
|
||||
switch (data.type) {
|
||||
case "send":
|
||||
if (!data.data.secretKey) {
|
||||
throw new Error("Secret key is required for send operation");
|
||||
}
|
||||
|
||||
await send({
|
||||
db: database,
|
||||
keypair: Keypair.fromSecretKey(new Uint8Array(data.data.secretKey)),
|
||||
url: data.data.url,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
postMessage({ type: "complete" });
|
||||
} catch (error) {
|
||||
console.error("[Worker] Operation failed:", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
type: data.type,
|
||||
url: data.data.url,
|
||||
});
|
||||
|
||||
postMessage({
|
||||
type: "error",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
} finally {
|
||||
if (database && "close" in database) {
|
||||
try {
|
||||
await (database as Closeable).close();
|
||||
} catch (error) {
|
||||
console.error("Error closing database connection:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
self.onmessage = (event: MessageEvent<WorkerData>) => {
|
||||
handleMessage(event.data).catch((error) => {
|
||||
postMessage({
|
||||
type: "error",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
});
|
||||
};
|
||||
} else {
|
||||
const { parentPort } = require("worker_threads");
|
||||
parentPort?.on("message", (data: WorkerData) => {
|
||||
handleMessage(data).catch((error) => {
|
||||
postMessage({
|
||||
type: "error",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user