@audius/sdk 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/.eslintrc +38 -0
  2. package/.prettierrc.js +1 -0
  3. package/.python-version +1 -0
  4. package/Dockerfile +15 -0
  5. package/README.md +3 -0
  6. package/babel.config.js +3 -0
  7. package/data-contracts/ABIs/AdminUpgradeabilityProxy.json +132 -0
  8. package/data-contracts/ABIs/BaseAdminUpgradeabilityProxy.json +113 -0
  9. package/data-contracts/ABIs/BaseUpgradeabilityProxy.json +22 -0
  10. package/data-contracts/ABIs/DiscoveryProviderFactory.json +189 -0
  11. package/data-contracts/ABIs/DiscoveryProviderFactoryInterface.json +61 -0
  12. package/data-contracts/ABIs/DiscoveryProviderStorage.json +205 -0
  13. package/data-contracts/ABIs/DiscoveryProviderStorageInterface.json +65 -0
  14. package/data-contracts/ABIs/ECDSA.json +4 -0
  15. package/data-contracts/ABIs/IPLDBlacklistFactory.json +168 -0
  16. package/data-contracts/ABIs/Initializable.json +4 -0
  17. package/data-contracts/ABIs/Migrations.json +67 -0
  18. package/data-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
  19. package/data-contracts/ABIs/Ownable.json +79 -0
  20. package/data-contracts/ABIs/PlaylistFactory.json +669 -0
  21. package/data-contracts/ABIs/PlaylistFactoryInterface.json +42 -0
  22. package/data-contracts/ABIs/PlaylistStorage.json +250 -0
  23. package/data-contracts/ABIs/PlaylistStorageInterface.json +129 -0
  24. package/data-contracts/ABIs/Proxy.json +10 -0
  25. package/data-contracts/ABIs/Registry.json +240 -0
  26. package/data-contracts/ABIs/RegistryContract.json +102 -0
  27. package/data-contracts/ABIs/RegistryContractInterface.json +28 -0
  28. package/data-contracts/ABIs/RegistryInterface.json +66 -0
  29. package/data-contracts/ABIs/SigningLogic.json +43 -0
  30. package/data-contracts/ABIs/SigningLogicInitializable.json +46 -0
  31. package/data-contracts/ABIs/SocialFeatureFactory.json +460 -0
  32. package/data-contracts/ABIs/SocialFeatureStorage.json +225 -0
  33. package/data-contracts/ABIs/SocialFeatureStorageInterface.json +123 -0
  34. package/data-contracts/ABIs/TestContract.json +135 -0
  35. package/data-contracts/ABIs/TestContractInterface.json +19 -0
  36. package/data-contracts/ABIs/TestContractWithStorage.json +165 -0
  37. package/data-contracts/ABIs/TestContractWithStorageInterface.json +24 -0
  38. package/data-contracts/ABIs/TestStorage.json +144 -0
  39. package/data-contracts/ABIs/TestStorageInterface.json +42 -0
  40. package/data-contracts/ABIs/TestUserReplicaSetManager.json +432 -0
  41. package/data-contracts/ABIs/TrackFactory.json +391 -0
  42. package/data-contracts/ABIs/TrackFactoryInterface.json +73 -0
  43. package/data-contracts/ABIs/TrackStorage.json +223 -0
  44. package/data-contracts/ABIs/TrackStorageInterface.json +121 -0
  45. package/data-contracts/ABIs/UpgradeabilityProxy.json +37 -0
  46. package/data-contracts/ABIs/UserFactory.json +657 -0
  47. package/data-contracts/ABIs/UserFactoryInterface.json +65 -0
  48. package/data-contracts/ABIs/UserLibraryFactory.json +334 -0
  49. package/data-contracts/ABIs/UserReplicaSetManager.json +418 -0
  50. package/data-contracts/ABIs/UserStorage.json +233 -0
  51. package/data-contracts/ABIs/UserStorageInterface.json +93 -0
  52. package/data-contracts/signatureSchemas.ts +1236 -0
  53. package/dist/core.d.ts +446 -0
  54. package/dist/core.js +769 -0
  55. package/dist/core.js.map +1 -0
  56. package/dist/index.d.ts +689 -0
  57. package/dist/index.js +72850 -0
  58. package/dist/index.js.map +1 -0
  59. package/eth-contracts/ABIs/Address.json +4 -0
  60. package/eth-contracts/ABIs/AudiusAdminUpgradeabilityProxy.json +105 -0
  61. package/eth-contracts/ABIs/AudiusClaimDistributor.json +4968 -0
  62. package/eth-contracts/ABIs/AudiusToken.json +724 -0
  63. package/eth-contracts/ABIs/BaseUpgradeabilityProxy.json +23 -0
  64. package/eth-contracts/ABIs/Checkpointing.json +4 -0
  65. package/eth-contracts/ABIs/ClaimsManager.json +539 -0
  66. package/eth-contracts/ABIs/Context.json +11 -0
  67. package/eth-contracts/ABIs/DelegateManager.json +989 -0
  68. package/eth-contracts/ABIs/DelegateManagerV2.json +1049 -0
  69. package/eth-contracts/ABIs/DelegateManagerV2Bad.json +1049 -0
  70. package/eth-contracts/ABIs/ERC20.json +252 -0
  71. package/eth-contracts/ABIs/ERC20Burnable.json +287 -0
  72. package/eth-contracts/ABIs/ERC20Detailed.json +270 -0
  73. package/eth-contracts/ABIs/ERC20Mintable.json +364 -0
  74. package/eth-contracts/ABIs/ERC20Pausable.json +397 -0
  75. package/eth-contracts/ABIs/EthRewardsManager.json +174 -0
  76. package/eth-contracts/ABIs/Governance.json +938 -0
  77. package/eth-contracts/ABIs/GovernanceUpgraded.json +953 -0
  78. package/eth-contracts/ABIs/GovernanceV2.json +938 -0
  79. package/eth-contracts/ABIs/IERC20.json +200 -0
  80. package/eth-contracts/ABIs/Initializable.json +4 -0
  81. package/eth-contracts/ABIs/InitializableV2.json +14 -0
  82. package/eth-contracts/ABIs/Migrations.json +71 -0
  83. package/eth-contracts/ABIs/MinterRole.json +91 -0
  84. package/eth-contracts/ABIs/MockAccount.json +62 -0
  85. package/eth-contracts/ABIs/MockDelegateManager.json +55 -0
  86. package/eth-contracts/ABIs/MockStakingCaller.json +259 -0
  87. package/eth-contracts/ABIs/MockWormhole.json +106 -0
  88. package/eth-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
  89. package/eth-contracts/ABIs/Ownable.json +93 -0
  90. package/eth-contracts/ABIs/Pausable.json +150 -0
  91. package/eth-contracts/ABIs/PauserRole.json +91 -0
  92. package/eth-contracts/ABIs/Proxy.json +10 -0
  93. package/eth-contracts/ABIs/Registry.json +288 -0
  94. package/eth-contracts/ABIs/Roles.json +4 -0
  95. package/eth-contracts/ABIs/SafeERC20.json +4 -0
  96. package/eth-contracts/ABIs/SafeMath.json +4 -0
  97. package/eth-contracts/ABIs/ServiceProviderFactory.json +1153 -0
  98. package/eth-contracts/ABIs/ServiceTypeManager.json +337 -0
  99. package/eth-contracts/ABIs/Staking.json +555 -0
  100. package/eth-contracts/ABIs/StakingUpgraded.json +570 -0
  101. package/eth-contracts/ABIs/TestContract.json +44 -0
  102. package/eth-contracts/ABIs/TrustedNotifierManager.json +265 -0
  103. package/eth-contracts/ABIs/Uint256Helpers.json +4 -0
  104. package/eth-contracts/ABIs/UpgradeabilityProxy.json +40 -0
  105. package/eth-contracts/ABIs/Wormhole.json +45 -0
  106. package/eth-contracts/ABIs/WormholeClient.json +155 -0
  107. package/examples/file.mp3 +0 -0
  108. package/examples/initAudiusLibs.js +86 -0
  109. package/examples/initializeVersions.js +95 -0
  110. package/examples/pic.jpg +0 -0
  111. package/initScripts/configureLocalDiscProv.js +167 -0
  112. package/initScripts/helpers/claim.js +43 -0
  113. package/initScripts/helpers/distributeTokens.js +24 -0
  114. package/initScripts/helpers/spRegistration.js +138 -0
  115. package/initScripts/helpers/utils.js +34 -0
  116. package/initScripts/helpers/version.js +93 -0
  117. package/initScripts/local.js +617 -0
  118. package/initScripts/mainnet.js +131 -0
  119. package/initScripts/manageProdRelayerWallets.js +191 -0
  120. package/package.json +125 -0
  121. package/rollup.config.js +164 -0
  122. package/scripts/AudiusClaimDistributor.json +4968 -0
  123. package/scripts/Wormhole.json +155 -0
  124. package/scripts/addCIDToIpldBlacklist.js +124 -0
  125. package/scripts/circleci-test.sh +53 -0
  126. package/scripts/communityRewards/transferCommunityRewardsToSolana.js +222 -0
  127. package/scripts/ipfs.sh +58 -0
  128. package/scripts/migrate_contracts.sh +25 -0
  129. package/scripts/reset.sh +65 -0
  130. package/scripts/test.sh +77 -0
  131. package/src/api/account.js +670 -0
  132. package/src/api/base.js +122 -0
  133. package/src/api/file.js +168 -0
  134. package/src/api/playlist.js +328 -0
  135. package/src/api/rewards.d.ts +4 -0
  136. package/src/api/rewards.js +682 -0
  137. package/src/api/serviceProvider.js +154 -0
  138. package/src/api/track.js +604 -0
  139. package/src/api/user.js +888 -0
  140. package/src/api/user.test.js +172 -0
  141. package/src/constants.ts +7 -0
  142. package/src/core.ts +3 -0
  143. package/src/index.js +6 -0
  144. package/src/libs.d.ts +3 -0
  145. package/src/libs.js +619 -0
  146. package/src/sanityChecks/addSecondaries.js +40 -0
  147. package/src/sanityChecks/assignReplicaSetIfNecessary.js +10 -0
  148. package/src/sanityChecks/index.d.ts +9 -0
  149. package/src/sanityChecks/index.js +31 -0
  150. package/src/sanityChecks/isCreator.js +73 -0
  151. package/src/sanityChecks/needsRecoveryEmail.js +20 -0
  152. package/src/sanityChecks/rolloverNodes.js +74 -0
  153. package/src/sanityChecks/sanitizeNodes.js +24 -0
  154. package/src/sanityChecks/syncNodes.js +28 -0
  155. package/src/sdk/constants.ts +10 -0
  156. package/src/sdk/index.ts +1 -0
  157. package/src/sdk/oauth/Oauth.ts +265 -0
  158. package/src/sdk/oauth/index.ts +1 -0
  159. package/src/sdk/sdk.ts +102 -0
  160. package/src/service-selection/ServiceSelection.test.ts +320 -0
  161. package/src/service-selection/ServiceSelection.ts +460 -0
  162. package/src/service-selection/constants.ts +14 -0
  163. package/src/service-selection/index.ts +1 -0
  164. package/src/services/ABIDecoder/AudiusABIDecoder.ts +71 -0
  165. package/src/services/ABIDecoder/index.ts +1 -0
  166. package/src/services/comstock/Comstock.ts +39 -0
  167. package/src/services/comstock/index.ts +1 -0
  168. package/src/services/contracts/ContractClient.ts +227 -0
  169. package/src/services/contracts/GovernedContractClient.ts +53 -0
  170. package/src/services/contracts/ProviderSelection.ts +42 -0
  171. package/src/services/creatorNode/CreatorNode.ts +1065 -0
  172. package/src/services/creatorNode/CreatorNodeSelection.test.ts +997 -0
  173. package/src/services/creatorNode/CreatorNodeSelection.ts +488 -0
  174. package/src/services/creatorNode/constants.ts +10 -0
  175. package/src/services/creatorNode/index.ts +2 -0
  176. package/src/services/dataContracts/AudiusContracts.ts +234 -0
  177. package/src/services/dataContracts/IPLDBlacklistFactoryClient.ts +73 -0
  178. package/src/services/dataContracts/PlaylistFactoryClient.ts +370 -0
  179. package/src/services/dataContracts/RegistryClient.ts +95 -0
  180. package/src/services/dataContracts/SocialFeatureFactoryClient.ts +196 -0
  181. package/src/services/dataContracts/TrackFactoryClient.ts +131 -0
  182. package/src/services/dataContracts/UserFactoryClient.ts +351 -0
  183. package/src/services/dataContracts/UserLibraryFactoryClient.ts +115 -0
  184. package/src/services/dataContracts/UserReplicaSetManagerClient.ts +206 -0
  185. package/src/services/dataContracts/index.ts +1 -0
  186. package/src/services/discoveryProvider/DiscoveryProvider.ts +1168 -0
  187. package/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +536 -0
  188. package/src/services/discoveryProvider/DiscoveryProviderSelection.ts +383 -0
  189. package/src/services/discoveryProvider/constants.ts +13 -0
  190. package/src/services/discoveryProvider/index.ts +1 -0
  191. package/src/services/discoveryProvider/requests.ts +629 -0
  192. package/src/services/ethContracts/AudiusTokenClient.ts +163 -0
  193. package/src/services/ethContracts/ClaimDistributionClient.ts +45 -0
  194. package/src/services/ethContracts/ClaimsManagerClient.ts +102 -0
  195. package/src/services/ethContracts/DelegateManagerClient.ts +480 -0
  196. package/src/services/ethContracts/EthContracts.ts +359 -0
  197. package/src/services/ethContracts/EthRewardsManagerClient.ts +33 -0
  198. package/src/services/ethContracts/GovernanceClient.ts +451 -0
  199. package/src/services/ethContracts/RegistryClient.ts +33 -0
  200. package/src/services/ethContracts/ServiceProviderFactoryClient.ts +691 -0
  201. package/src/services/ethContracts/ServiceTypeManagerClient.ts +112 -0
  202. package/src/services/ethContracts/StakingProxyClient.ts +97 -0
  203. package/src/services/ethContracts/TrustedNotifierManagerClient.ts +101 -0
  204. package/src/services/ethContracts/WormholeClient.ts +97 -0
  205. package/src/services/ethContracts/index.ts +1 -0
  206. package/src/services/ethWeb3Manager/EthWeb3Manager.ts +239 -0
  207. package/src/services/ethWeb3Manager/index.ts +1 -0
  208. package/src/services/hedgehog/Hedgehog.ts +96 -0
  209. package/src/services/hedgehog/index.ts +1 -0
  210. package/src/services/identity/IdentityService.ts +551 -0
  211. package/src/services/identity/index.ts +1 -0
  212. package/src/services/identity/requests.ts +65 -0
  213. package/src/services/schemaValidator/SchemaValidator.ts +105 -0
  214. package/src/services/schemaValidator/index.ts +1 -0
  215. package/src/services/schemaValidator/schemas/trackSchema.json +267 -0
  216. package/src/services/schemaValidator/schemas/userSchema.json +230 -0
  217. package/src/services/solanaAudiusData/errors.ts +20 -0
  218. package/src/services/solanaAudiusData/index.ts +1189 -0
  219. package/src/services/solanaWeb3Manager/errors.js +101 -0
  220. package/src/services/solanaWeb3Manager/index.d.ts +46 -0
  221. package/src/services/solanaWeb3Manager/index.js +655 -0
  222. package/src/services/solanaWeb3Manager/padBNToUint8Array.ts +7 -0
  223. package/src/services/solanaWeb3Manager/rewards.js +941 -0
  224. package/src/services/solanaWeb3Manager/rewardsAttester.ts +1093 -0
  225. package/src/services/solanaWeb3Manager/tokenAccount.js +149 -0
  226. package/src/services/solanaWeb3Manager/transactionHandler.js +345 -0
  227. package/src/services/solanaWeb3Manager/transfer.js +272 -0
  228. package/src/services/solanaWeb3Manager/userBank.js +160 -0
  229. package/src/services/solanaWeb3Manager/utils.d.ts +31 -0
  230. package/src/services/solanaWeb3Manager/utils.js +163 -0
  231. package/src/services/solanaWeb3Manager/wAudio.js +28 -0
  232. package/src/services/solanaWeb3Manager/wAudio.test.js +30 -0
  233. package/src/services/web3Manager/Web3Config.ts +14 -0
  234. package/src/services/web3Manager/Web3Manager.ts +360 -0
  235. package/src/services/web3Manager/XMLHttpRequest.ts +11 -0
  236. package/src/services/web3Manager/index.ts +2 -0
  237. package/src/services/wormhole/index.js +424 -0
  238. package/src/types.ts +8 -0
  239. package/src/userStateManager.ts +53 -0
  240. package/src/utils/apiSigning.ts +51 -0
  241. package/src/utils/captcha.ts +97 -0
  242. package/src/utils/estimateGas.ts +64 -0
  243. package/src/utils/fileHasher.ts +278 -0
  244. package/src/utils/importContractABI.d.ts +9 -0
  245. package/src/utils/importContractABI.js +19 -0
  246. package/src/utils/index.ts +11 -0
  247. package/src/utils/multiProvider.ts +72 -0
  248. package/src/utils/network.test.ts +127 -0
  249. package/src/utils/network.ts +308 -0
  250. package/src/utils/promiseFight.test.ts +87 -0
  251. package/src/utils/promiseFight.ts +36 -0
  252. package/src/utils/signatures.ts +139 -0
  253. package/src/utils/types.ts +34 -0
  254. package/src/utils/utils.test.ts +36 -0
  255. package/src/utils/utils.ts +235 -0
  256. package/src/utils/uuid.ts +14 -0
  257. package/src/web3.d.ts +9 -0
  258. package/src/web3.js +8 -0
  259. package/tests/assets/static_image.png +0 -0
  260. package/tests/assets/static_text.txt +1 -0
  261. package/tests/audiusTokenClientTest.js +37 -0
  262. package/tests/creatorNodeTest.js +19 -0
  263. package/tests/fileHasherTest.js +125 -0
  264. package/tests/governanceTest.js +382 -0
  265. package/tests/helpers.js +105 -0
  266. package/tests/index.js +14 -0
  267. package/tests/playlistClientTest.js +157 -0
  268. package/tests/providerSelectionTest.js +241 -0
  269. package/tests/registryClientTest.js +19 -0
  270. package/tests/rewardsAttesterTest.js +373 -0
  271. package/tests/serviceTypeManagerClientTest.js +33 -0
  272. package/tests/socialFeatureClientTest.js +79 -0
  273. package/tests/stakingTest.js +302 -0
  274. package/tests/trackClientTest.js +86 -0
  275. package/tests/userClientTest.js +121 -0
  276. package/tsconfig.json +10 -0
  277. package/types/@audius-hedgehog/index.d.ts +39 -0
  278. package/types/abi-decoder/index.d.ts +41 -0
@@ -0,0 +1,149 @@
1
+ const { ASSOCIATED_TOKEN_PROGRAM_ID, Token } = require('@solana/spl-token')
2
+ const {
3
+ PublicKey,
4
+ SystemProgram,
5
+ SYSVAR_RENT_PUBKEY,
6
+ Keypair
7
+ } = require('@solana/web3.js')
8
+
9
+ /**
10
+ * Finds the associated token address given a solana wallet public key
11
+ * @param {PublicKey} solanaWalletKey Public Key for a given solana account (a wallet)
12
+ * @param {PublicKey} mintKey
13
+ * @param {PublicKey} solanaTokenProgramKey
14
+ * @returns {PublicKey} token account public key
15
+ */
16
+ async function findAssociatedTokenAddress ({
17
+ solanaWalletKey,
18
+ mintKey,
19
+ solanaTokenProgramKey
20
+ }) {
21
+ const addresses = await PublicKey.findProgramAddress(
22
+ [
23
+ solanaWalletKey.toBuffer(),
24
+ solanaTokenProgramKey.toBuffer(),
25
+ mintKey.toBuffer()
26
+ ],
27
+ ASSOCIATED_TOKEN_PROGRAM_ID
28
+ )
29
+ return addresses[0]
30
+ }
31
+
32
+ /**
33
+ * Gets token account information (e.g. balance, ownership, etc.)
34
+ * @param {PublicKey} tokenAccountAddressKey
35
+ * @param {PublicKey} mintKey
36
+ * @param {PublicKey} solanaTokenProgramKey
37
+ * @param {Connection} connection
38
+ * @returns {AccountInfo}
39
+ */
40
+ async function getAssociatedTokenAccountInfo ({
41
+ tokenAccountAddressKey,
42
+ mintKey,
43
+ solanaTokenProgramKey,
44
+ connection
45
+ }) {
46
+ const token = new Token(
47
+ connection,
48
+ mintKey,
49
+ solanaTokenProgramKey,
50
+ Keypair.generate()
51
+ )
52
+ const info = await token.getAccountInfo(tokenAccountAddressKey)
53
+ return info
54
+ }
55
+
56
+ /**
57
+ * Creates an associated token account for a given solana account (a wallet)
58
+ * @param {PublicKey} feePayerKey
59
+ * @param {PublicKey} solanaWalletKey the wallet we wish to create a token account for
60
+ * @param {PublicKey} mintKey
61
+ * @param {PublicKey} solanaTokenProgramKey
62
+ * @param {Connection} connection
63
+ * @param {IdentityService} identityService
64
+ */
65
+ async function createAssociatedTokenAccount ({
66
+ feePayerKey,
67
+ solanaWalletKey,
68
+ mintKey,
69
+ solanaTokenProgramKey,
70
+ connection,
71
+ identityService
72
+ }) {
73
+ const associatedTokenAddress = await findAssociatedTokenAddress({
74
+ solanaWalletKey,
75
+ mintKey,
76
+ solanaTokenProgramKey
77
+ })
78
+
79
+ const accounts = [
80
+ // 0. `[sw]` Funding account (must be a system account)
81
+ {
82
+ pubkey: feePayerKey,
83
+ isSigner: true,
84
+ isWritable: true
85
+ },
86
+ // 1. `[w]` Associated token account address to be created
87
+ {
88
+ pubkey: associatedTokenAddress,
89
+ isSigner: false,
90
+ isWritable: true
91
+ },
92
+ // 2. `[r]` Wallet address for the new associated token account
93
+ {
94
+ pubkey: solanaWalletKey,
95
+ isSigner: false,
96
+ isWritable: false
97
+ },
98
+ // 3. `[r]` The token mint for the new associated token account
99
+ {
100
+ pubkey: mintKey,
101
+ isSigner: false,
102
+ isWritable: false
103
+ },
104
+ // 4. `[r]` System program
105
+ {
106
+ pubkey: SystemProgram.programId,
107
+ isSigner: false,
108
+ isWritable: false
109
+ },
110
+ // 5. `[r]` SPL Token program
111
+ {
112
+ pubkey: solanaTokenProgramKey,
113
+ isSigner: false,
114
+ isWritable: false
115
+ },
116
+ // 6. `[r]` Rent sysvar
117
+ {
118
+ pubkey: SYSVAR_RENT_PUBKEY,
119
+ isSigner: false,
120
+ isWritable: false
121
+ }
122
+ ]
123
+
124
+ const { blockhash } = await connection.getLatestBlockhash()
125
+
126
+ const transactionData = {
127
+ recentBlockhash: blockhash,
128
+ instructions: [{
129
+ keys: accounts.map(account => {
130
+ return {
131
+ pubkey: account.pubkey.toString(),
132
+ isSigner: account.isSigner,
133
+ isWritable: account.isWritable
134
+ }
135
+ }),
136
+ programId: ASSOCIATED_TOKEN_PROGRAM_ID.toString(),
137
+ data: Buffer.from([])
138
+ }]
139
+ }
140
+
141
+ const response = await identityService.solanaRelay(transactionData)
142
+ return response
143
+ }
144
+
145
+ module.exports = {
146
+ findAssociatedTokenAddress,
147
+ getAssociatedTokenAccountInfo,
148
+ createAssociatedTokenAccount
149
+ }
@@ -0,0 +1,345 @@
1
+ const SolanaUtils = require('./utils')
2
+ const {
3
+ Transaction,
4
+ PublicKey,
5
+ } = require('@solana/web3.js')
6
+
7
+ /**
8
+ * Handles sending Solana transactions, either directly via `sendAndConfirmTransaction`,
9
+ * or via IdentityService's relay.
10
+ */
11
+ class TransactionHandler {
12
+ /**
13
+ * Creates an instance of TransactionHandler.
14
+ *
15
+ * @param {{
16
+ * connection: Connection,
17
+ * useRelay: boolean,
18
+ * identityService: Object,
19
+ * feePayerKeypairs: KeyPair[]
20
+ * skipPreflight: boolean
21
+ * }} {
22
+ * connection,
23
+ * useRelay,
24
+ * identityService = null,
25
+ * feePayerKeypairs = null,
26
+ * skipPreflight = true
27
+ * }
28
+ * @memberof TransactionHandler
29
+ */
30
+ constructor ({
31
+ connection,
32
+ useRelay,
33
+ identityService = null,
34
+ feePayerKeypairs = null,
35
+ skipPreflight = true,
36
+ retryTimeoutMs = 60000,
37
+ pollingFrequencyMs = 300,
38
+ sendingFrequencyMs = 300
39
+ }) {
40
+ this.connection = connection
41
+ this.useRelay = useRelay
42
+ this.identityService = identityService
43
+ this.feePayerKeypairs = feePayerKeypairs
44
+ this.skipPreflight = skipPreflight
45
+ this.retryTimeoutMs = retryTimeoutMs
46
+ this.pollingFrequencyMs = pollingFrequencyMs
47
+ this.sendingFrequencyMs = sendingFrequencyMs
48
+ }
49
+
50
+ /**
51
+ * Primary method to send a Solana transaction.
52
+ *
53
+ * @typedef {Object} HandleTransactionReturn
54
+ * @property {Object} res the result
55
+ * @property {string} [error=null] the optional error
56
+ * Will be a string if `errorMapping` is passed to the handler.
57
+ * @property {string|number} [error_code=null] the optional error code.
58
+ * @property {string} [recentBlockhash=null] optional recent blockhash to prefer over fetching
59
+ * @property {boolean} [skipPreflight=null] optional per transaction override to skipPreflight
60
+ * @property {any} [logger=console] optional logger
61
+ * @property {any} [feePayerOverride=null] optional fee payer override
62
+ *
63
+ * @param {Array<TransactionInstruction>} instructions an array of `TransactionInstructions`
64
+ * @param {*} [errorMapping=null] an optional error mapping. Should expose a `fromErrorCode` method.
65
+ * @param {Array<{publicKey: string, signature: Buffer}>} [signature=null] optional signatures
66
+ * @returns {Promise<HandleTransactionReturn>}
67
+ * @memberof TransactionHandler
68
+ */
69
+ async handleTransaction ({ instructions, errorMapping = null, recentBlockhash = null, logger = console, skipPreflight = null, feePayerOverride = null, sendBlockhash = true, signatures = null, retry = true }) {
70
+ let result = null
71
+ if (this.useRelay) {
72
+ result = await this._relayTransaction(instructions, recentBlockhash, skipPreflight, feePayerOverride, sendBlockhash, signatures, retry)
73
+ } else {
74
+ result = await this._locallyConfirmTransaction(instructions, recentBlockhash, logger, skipPreflight, feePayerOverride, signatures, retry)
75
+ }
76
+ if (result.error && result.errorCode !== null && errorMapping) {
77
+ result.errorCode = errorMapping.fromErrorCode(result.errorCode)
78
+ }
79
+ return result
80
+ }
81
+
82
+ async _relayTransaction (
83
+ instructions,
84
+ recentBlockhash,
85
+ skipPreflight,
86
+ feePayerOverride = null,
87
+ sendBlockhash,
88
+ signatures,
89
+ retry
90
+ ) {
91
+ const relayable = instructions.map(SolanaUtils.prepareInstructionForRelay)
92
+
93
+ const transactionData = {
94
+ signatures,
95
+ instructions: relayable,
96
+ skipPreflight:
97
+ skipPreflight === null ? this.skipPreflight : skipPreflight,
98
+ feePayerOverride: feePayerOverride ? feePayerOverride.toString() : null,
99
+ retry
100
+ }
101
+
102
+ if (sendBlockhash || Array.isArray(signatures)) {
103
+ transactionData.recentBlockhash = (recentBlockhash || (await this.connection.getLatestBlockhash('confirmed')).blockhash)
104
+ }
105
+
106
+ try {
107
+ const response = await this.identityService.solanaRelay(transactionData)
108
+ return { res: response, error: null, errorCode: null }
109
+ } catch (e) {
110
+ const error =
111
+ (e.response && e.response.data && e.response.data.error) || e.message
112
+ const errorCode = this._parseSolanaErrorCode(error)
113
+ return { res: null, error, errorCode }
114
+ }
115
+ }
116
+
117
+ async _locallyConfirmTransaction (instructions, recentBlockhash, logger, skipPreflight, feePayerOverride = null, signatures = null, retry = true) {
118
+ const feePayerKeypairOverride = (() => {
119
+ if (feePayerOverride && this.feePayerKeypairs) {
120
+ const stringFeePayer = feePayerOverride.toString()
121
+ return this.feePayerKeypairs.find(
122
+ (keypair) => keypair.publicKey.toString() === stringFeePayer
123
+ )
124
+ }
125
+ return null
126
+ })()
127
+
128
+ const feePayerAccount = feePayerKeypairOverride || (this.feePayerKeypairs && this.feePayerKeypairs[0])
129
+ if (!feePayerAccount) {
130
+ logger.error('transactionHandler: Local feepayer keys missing for direct confirmation!')
131
+ return {
132
+ res: null,
133
+ error: 'Missing keys',
134
+ errorCode: null
135
+ }
136
+ }
137
+
138
+ // Get blockhash
139
+
140
+ recentBlockhash =
141
+ recentBlockhash ||
142
+ (await this.connection.getLatestBlockhash('confirmed')).blockhash
143
+
144
+ // Construct the txn
145
+
146
+ const tx = new Transaction({ recentBlockhash })
147
+ instructions.forEach((i) => tx.add(i))
148
+ tx.feePayer = feePayerAccount.publicKey
149
+ tx.sign(feePayerAccount)
150
+
151
+ if (Array.isArray(signatures)) {
152
+ signatures.forEach(({ publicKey, signature }) => {
153
+ tx.addSignature(new PublicKey(publicKey), signature)
154
+ })
155
+ }
156
+
157
+ const rawTransaction = tx.serialize()
158
+
159
+ // Send the txn
160
+
161
+ const sendRawTransaction = async () => {
162
+ return this.connection.sendRawTransaction(rawTransaction, {
163
+ skipPreflight:
164
+ skipPreflight === null ? this.skipPreflight : skipPreflight,
165
+ commitment: 'processed',
166
+ preflightCommitment: 'processed',
167
+ maxRetries: retry ? 0 : undefined
168
+ })
169
+ }
170
+
171
+ let txid
172
+ try {
173
+ txid = await sendRawTransaction()
174
+ } catch (e) {
175
+ // Rarely, this intiial send will fail
176
+ logger.warn(`transactionHandler: Initial send failed: ${e}`)
177
+ const { message: error } = e
178
+ const errorCode = this._parseSolanaErrorCode(error)
179
+ return {
180
+ res: null,
181
+ error,
182
+ errorCode
183
+ }
184
+ }
185
+
186
+ let done = false
187
+
188
+ // Start up resubmission loop
189
+ let sendCount = 0
190
+ const startTime = Date.now()
191
+ if (retry) {
192
+ ;(async () => {
193
+ let elapsed = Date.now() - startTime
194
+ // eslint-disable-next-line no-unmodified-loop-condition
195
+ while (!done && elapsed < this.retryTimeoutMs) {
196
+ try {
197
+ sendRawTransaction()
198
+ } catch (e) {
199
+ logger.warn(`transactionHandler: error in send loop: ${e} for txId ${txid}`)
200
+ }
201
+ sendCount++
202
+ await delay(this.sendingFrequencyMs)
203
+ elapsed = Date.now() - startTime
204
+ }
205
+ })()
206
+ }
207
+
208
+ // Await for tx confirmation
209
+ try {
210
+ await this._awaitTransactionSignatureConfirmation(txid, logger)
211
+ done = true
212
+ logger.info(`transactionHandler: finished for txid ${txid} with ${sendCount} retries`)
213
+ return {
214
+ res: txid,
215
+ error: null,
216
+ errorCode: null
217
+ }
218
+ } catch (e) {
219
+ logger.warn(`transactionHandler: error in awaitTransactionSignature: ${JSON.stringify(e)}, ${txid}`)
220
+ done = true
221
+ const { message: error } = e
222
+ const errorCode = this._parseSolanaErrorCode(error)
223
+ return {
224
+ res: null,
225
+ error,
226
+ errorCode
227
+ }
228
+ }
229
+ }
230
+
231
+ async _awaitTransactionSignatureConfirmation (txid, logger) {
232
+ let done = false
233
+
234
+ const result = await new Promise((resolve, reject) => {
235
+ ;(async () => {
236
+ // Setup timeout if nothing else finishes
237
+ setTimeout(() => {
238
+ if (done) {
239
+ return
240
+ }
241
+ done = true
242
+ const message = `transactionHandler: Timed out in await, ${txid}`
243
+ logger.warn(message)
244
+ reject(new Error(message))
245
+ }, this.retryTimeoutMs)
246
+
247
+ // Setup WS listener
248
+ try {
249
+ this.connection.onSignature(
250
+ txid,
251
+ (result) => {
252
+ if (done) return
253
+ done = true
254
+ if (result.err) {
255
+ const err = JSON.stringify(result.err)
256
+ logger.warn(`transactionHandler: Error in onSignature ${txid}, ${err}`)
257
+ reject(new Error(err))
258
+ } else {
259
+ resolve(txid)
260
+ }
261
+ },
262
+ 'processed'
263
+ )
264
+ } catch (e) {
265
+ done = true
266
+ logger.error(`transactionHandler: WS error in setup ${txid}, ${e}`)
267
+ }
268
+
269
+ // Setup polling
270
+ while (!done) {
271
+ ;(async () => {
272
+ try {
273
+ const signatureStatuses =
274
+ await this.connection.getSignatureStatuses([txid])
275
+ const result = signatureStatuses?.value[0]
276
+
277
+ // Early return this iteration if already done, or no result
278
+ if (done || !result) return
279
+
280
+ // End loop if error
281
+ if (result.err) {
282
+ const err = JSON.stringify(result.err)
283
+ logger.error(
284
+ `transactionHandler: polling saw result error: ${err}, tx: ${txid}`
285
+ )
286
+ done = true
287
+ reject(new Error(err))
288
+ return
289
+ }
290
+
291
+ // Early return if response without confirmation
292
+ if (
293
+ !(
294
+ result.confirmations ||
295
+ result.confirmationStatus === 'confirmed' ||
296
+ result.confirmationStatus === 'finalized'
297
+ )
298
+ ) {
299
+ return
300
+ }
301
+
302
+ // Otherwise, we made it
303
+ done = true
304
+ resolve(txid)
305
+ } catch (e) {
306
+ if (!done) {
307
+ logger.error(
308
+ `transactionHandler: REST polling connection error: ${e}, tx: ${txid}`
309
+ )
310
+ }
311
+ }
312
+ })()
313
+
314
+ await delay(this.pollingFrequencyMs)
315
+ }
316
+ })()
317
+ })
318
+ done = true
319
+ return result
320
+ }
321
+
322
+ /**
323
+ * Attempts to parse an error code out of a message of the form:
324
+ * "... custom program error: 0x1", where the return in this case would be the number 1.
325
+ * Returns null for unparsable strings.
326
+ */
327
+ _parseSolanaErrorCode (errorMessage) {
328
+ if (!errorMessage) return null
329
+ // Match on custom solana program errors
330
+ const matcher = /(?:custom program error: 0x)(.*)$/
331
+ const res = errorMessage.match(matcher)
332
+ if (res && res.length === 2) return parseInt(res[1], 16) || null
333
+ // Match on custom anchor errors
334
+ const matcher2 = /(?:"Custom":)(\d+)/
335
+ const res2 = errorMessage.match(matcher2)
336
+ if (res2 && res2.length === 2) return parseInt(res2[1], 10) || null
337
+ return null
338
+ }
339
+ }
340
+
341
+ async function delay (ms) {
342
+ return new Promise((resolve) => setTimeout(resolve, ms))
343
+ }
344
+
345
+ module.exports = { TransactionHandler }