@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,682 @@
1
+ const axios = require('axios')
2
+ const { sampleSize } = require('lodash')
3
+
4
+ const { Base, Services } = require('./base')
5
+ const BN = require('bn.js')
6
+ const { RewardsManagerError } = require('../services/solanaWeb3Manager/errors')
7
+ const { WAUDIO_DECMIALS } = require('../constants')
8
+ const { Utils } = require('../utils/utils')
9
+
10
+ const { decodeHashId } = Utils
11
+
12
+ const GetAttestationError = Object.freeze({
13
+ CHALLENGE_INCOMPLETE: 'CHALLENGE_INCOMPLETE',
14
+ ALREADY_DISBURSED: 'ALREADY_DISBURSED',
15
+ INVALID_ORACLE: 'INVALID_ORACLE',
16
+ MISSING_CHALLENGES: 'MISSING_CHALLENGES',
17
+ INVALID_INPUT: 'INVALID_INPUT',
18
+ USER_NOT_FOUND: 'USER_NOT_FOUND',
19
+ HCAPTCHA: 'HCAPTCHA',
20
+ COGNITO_FLOW: 'COGNITO_FLOW',
21
+ DISCOVERY_NODE_ATTESTATION_ERROR: 'DISCOVERY_NODE_ATTESTATION_ERROR',
22
+ DISCOVERY_NODE_UNKNOWN_RESPONSE: 'DISCOVERY_NODE_UNKNOWN_RESPONSE',
23
+ AAO_ATTESTATION_ERROR: 'AAO_ATTESTATION_ERROR',
24
+ AAO_ATTESTATION_REJECTION: 'AAO_ATTESTATION_REJECTION',
25
+ AAO_ATTESTATION_UNKNOWN_RESPONSE: 'AAO_ATTESTATION_UNKNOWN_RESPONSE',
26
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR'
27
+ })
28
+
29
+ const AggregateAttestationError = Object.freeze(
30
+ {
31
+ INSUFFICIENT_DISCOVERY_NODE_COUNT: 'INSUFFICIENT_DISCOVERY_NODE_COUNT',
32
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR'
33
+ }
34
+ )
35
+
36
+ const GetSenderAttestationError = Object.freeze({
37
+ REQUEST_FOR_ATTESTATION_FAILED: 'REQUEST_FOR_ATTESTATION_FAILED'
38
+ })
39
+
40
+ /**
41
+ * Combined error type for `SubmitAndEvaluate`
42
+ */
43
+ const SubmitAndEvaluateError = Object.freeze({
44
+ ...GetAttestationError,
45
+ ...AggregateAttestationError,
46
+ ...RewardsManagerError
47
+ })
48
+
49
+ const AttestationPhases = Object.freeze({
50
+ SANITY_CHECKS: 'SANITY_CHECKS',
51
+ AGGREGATE_ATTESTATIONS: 'AGGREGATE_ATTESTATIONS',
52
+ SUBMIT_ATTESTATIONS: 'SUBMIT_ATTESTATIONS',
53
+ EVALUATE_ATTESTATIONS: 'EVALUATE_ATTESTATIONS'
54
+ })
55
+
56
+ const AAO_REQUEST_TIMEOUT_MS = 15 * 1000
57
+ const WRAPPED_AUDIO_PRECISION = 10 ** WAUDIO_DECMIALS
58
+
59
+ /**
60
+ * @typedef {import("../services/solanaWeb3Manager/rewards.js").AttestationMeta} AttestationMeta
61
+ */
62
+
63
+ class Rewards extends Base {
64
+ constructor (ServiceProvider, ...args) {
65
+ super(...args)
66
+ this.ServiceProvider = ServiceProvider
67
+ }
68
+
69
+ /**
70
+ *
71
+ * Top level method to aggregate attestations, submit them to RewardsManager, and evalute the result.
72
+ *
73
+ * @typedef {Object} GetSubmitAndEvaluateAttestationsReturn
74
+ * @property {Boolean} success
75
+ * @property {GetAttestationError} error
76
+ *
77
+ * @param {{
78
+ * challengeId: string,
79
+ * encodedUserId: string,
80
+ * handle: string,
81
+ * recipientEthAddress: string,
82
+ * specifier: string,
83
+ * oracleEthAddress: string,
84
+ * amount: number,
85
+ * quorumSize: number,
86
+ * AAOEndpoint: string,
87
+ * endpoints: Array<string>,
88
+ * instructionsPerTransaction?: number,
89
+ * maxAggregationAttempts?: number
90
+ * logger: any
91
+ * feePayerOverride: string | null
92
+ * }} {
93
+ * challengeId,
94
+ * encodedUserId,
95
+ * handle,
96
+ * recipientEthAddress,
97
+ * specifier,
98
+ * oracleEthAddress,
99
+ * amount,
100
+ * quorumSize,
101
+ * AAOEndpoint,
102
+ * endpoints,
103
+ * maxAggregationAttempts,
104
+ * instructionsPerTransaction,
105
+ * logger,
106
+ * feePayerOverride
107
+ * }
108
+ * @returns {Promise<GetSubmitAndEvaluateAttestationsReturn>}
109
+ * @memberof Challenge
110
+ */
111
+ async submitAndEvaluate ({
112
+ challengeId, encodedUserId, handle, recipientEthAddress, specifier, oracleEthAddress, amount, quorumSize, AAOEndpoint, instructionsPerTransaction, maxAggregationAttempts = 20, endpoints = null, logger = console, feePayerOverride = null
113
+ }) {
114
+ let phase
115
+ let nodesToReselect = null
116
+ try {
117
+ phase = AttestationPhases.SANITY_CHECKS
118
+
119
+ // fail if amount is a decimal
120
+ if ((Number(amount) !== amount) || (amount % 1 !== 0)) {
121
+ throw new Error('Invalid amount')
122
+ }
123
+
124
+ // Aggregate
125
+
126
+ logger.info(`submitAndEvaluate: aggregating attestations for userId [${decodeHashId(encodedUserId)}], challengeId [${challengeId}]`)
127
+ phase = AttestationPhases.AGGREGATE_ATTESTATIONS
128
+ const { discoveryNodeAttestations, aaoAttestation, error: aggregateError, erroringNodes } = await this.aggregateAttestations({
129
+ challengeId, encodedUserId, handle, specifier, oracleEthAddress, amount, quorumSize, AAOEndpoint, endpoints, logger, maxAttempts: maxAggregationAttempts
130
+ })
131
+ if (aggregateError) {
132
+ nodesToReselect = erroringNodes
133
+ throw new Error(aggregateError)
134
+ }
135
+
136
+ // Submit
137
+
138
+ logger.info(`submitAndEvaluate: submitting for challenge [${challengeId}], userId: [${decodeHashId(encodedUserId)}] with [${discoveryNodeAttestations.length}] DN and [${aaoAttestation ? 1 : 0}] oracle attestations.`)
139
+ const fullTokenAmount = new BN(amount * WRAPPED_AUDIO_PRECISION)
140
+ phase = AttestationPhases.SUBMIT_ATTESTATIONS
141
+ const { errorCode: submitErrorCode, error: submitError } = await this.solanaWeb3Manager.submitChallengeAttestations({
142
+ attestations: discoveryNodeAttestations,
143
+ oracleAttestation: aaoAttestation,
144
+ challengeId,
145
+ specifier,
146
+ recipientEthAddress,
147
+ tokenAmount: fullTokenAmount,
148
+ instructionsPerTransaction,
149
+ logger,
150
+ feePayerOverride
151
+ })
152
+
153
+ // In the case of an unparseable error,
154
+ // we'll only have the error, not the code.
155
+ if (submitErrorCode || submitError) {
156
+ const shouldRetryInSeperateTransactions = (
157
+ submitErrorCode === RewardsManagerError.REPEATED_SENDERS ||
158
+ submitErrorCode === RewardsManagerError.SIGN_COLLISION ||
159
+ submitErrorCode === RewardsManagerError.OPERATOR_COLLISION
160
+ )
161
+ // If we have sender collisions, we should
162
+ // submit one attestation per transaction and try to get
163
+ // into a good state.
164
+ // TODO: in the case this retry fails, we still proceed
165
+ // to evaluate phase and will error there (not ideal)
166
+ if (shouldRetryInSeperateTransactions) {
167
+ logger.warn(`submitAndEvaluate: saw repeat senders for userId [${decodeHashId(encodedUserId)}] challengeId: [${challengeId}] with err: ${submitErrorCode}, breaking up into individual transactions`)
168
+ await this.solanaWeb3Manager.submitChallengeAttestations({
169
+ attestations: discoveryNodeAttestations,
170
+ oracleAttestation: aaoAttestation,
171
+ challengeId,
172
+ specifier,
173
+ recipientEthAddress,
174
+ tokenAmount: fullTokenAmount,
175
+ instructionsPerTransaction: 2, // SECP + Attestation
176
+ logger,
177
+ feePayerOverride
178
+ })
179
+ } else {
180
+ throw new Error(submitErrorCode || submitError)
181
+ }
182
+ }
183
+
184
+ // Evaluate
185
+
186
+ logger.info(`submitAndEvaluate: evaluating for challenge [${challengeId}], userId: [${decodeHashId(encodedUserId)}]`)
187
+ phase = AttestationPhases.EVALUATE_ATTESTATIONS
188
+ const { errorCode: evaluateErrorCode, error: evaluateError } = await this.solanaWeb3Manager.evaluateChallengeAttestations({
189
+ challengeId,
190
+ specifier,
191
+ recipientEthAddress,
192
+ oracleEthAddress,
193
+ tokenAmount: fullTokenAmount,
194
+ logger,
195
+ feePayerOverride
196
+ })
197
+
198
+ if (evaluateErrorCode || evaluateError) {
199
+ throw new Error(evaluateErrorCode || evaluateError)
200
+ }
201
+
202
+ return { success: true, error: null, phase: null, nodesToReselect: null }
203
+ } catch (e) {
204
+ const err = e.message
205
+ const log = (err === GetAttestationError.COGNITO_FLOW || err === GetAttestationError.HCAPTCHA) ? logger.info : logger.error
206
+ log(`submitAndEvaluate: failed for userId: [${decodeHashId(encodedUserId)}] challenge-id [${challengeId}] at phase [${phase}] with err: ${err}`)
207
+ return { success: false, error: err, phase, nodesToReselect }
208
+ }
209
+ }
210
+
211
+ /**
212
+ *
213
+ * Aggregates attestations from Discovery Nodes and AAO.
214
+ *
215
+ * @typedef {Object} AttestationsReturn
216
+ * @property {Array<AttestationMeta>} discoveryNodeAttestations
217
+ * @property {AttestationMeta} aaoAttestation
218
+ * @property {GetAttestationError} error
219
+ *
220
+ * @param {{
221
+ * challengeId: string,
222
+ * encodedUserId: string,
223
+ * handle: string,
224
+ * specifier: string,
225
+ * oracleEthAddress: string,
226
+ * amount: number,
227
+ * quorumSize: number,
228
+ * AAOEndpoint: string,
229
+ * maxAttempts: number
230
+ * endpoints = null
231
+ * logger: any
232
+ * }} {
233
+ * challengeId,
234
+ * encodedUserId,
235
+ * handle,
236
+ * specifier,
237
+ * oracleEthAddress,
238
+ * amount,
239
+ * quorumSize,
240
+ * AAOEndpoint,
241
+ * maxAttempts
242
+ * endpoints = null,
243
+ * logger
244
+ * }
245
+ * @returns {Promise<AttestationsReturn>}
246
+ * @memberof Rewards
247
+ */
248
+ async aggregateAttestations ({ challengeId, encodedUserId, handle, specifier, oracleEthAddress, amount, quorumSize, AAOEndpoint, maxAttempts, endpoints = null, logger = console }) {
249
+ this.REQUIRES(Services.DISCOVERY_PROVIDER)
250
+
251
+ if (endpoints) {
252
+ endpoints = sampleSize(endpoints, quorumSize)
253
+ } else {
254
+ // If no endpoints array provided, select here
255
+ endpoints = await this.ServiceProvider.getUniquelyOwnedDiscoveryNodes(quorumSize)
256
+ }
257
+
258
+ if (endpoints.length < quorumSize) {
259
+ logger.error(`Tried to fetch [${quorumSize}] attestations, but only found [${endpoints.length}] registered nodes.`)
260
+
261
+ return {
262
+ discoveryNodeAttestations: null,
263
+ aaoAttestation: null,
264
+ error: AggregateAttestationError.INSUFFICIENT_DISCOVERY_NODE_COUNT,
265
+ erroringNodes: null
266
+ }
267
+ }
268
+
269
+ // First attempt AAO
270
+
271
+ let aaoAttestation = null
272
+
273
+ try {
274
+ const { success, error: aaoAttestationError } = await this.getAAOAttestation({
275
+ challengeId,
276
+ specifier,
277
+ handle,
278
+ amount,
279
+ AAOEndpoint,
280
+ oracleEthAddress
281
+ })
282
+
283
+ if (aaoAttestationError) {
284
+ return {
285
+ discoveryNodeAttestations: null,
286
+ aaoAttestation: null,
287
+ error: aaoAttestationError,
288
+ erroringNodes: null
289
+ }
290
+ }
291
+ aaoAttestation = success
292
+ } catch (e) {
293
+ const err = e.message
294
+ logger.error(`Failed to aggregate attestations for user [${decodeHashId(encodedUserId)}], challenge-id: [${challengeId}] with err: ${err}`)
295
+ return {
296
+ discoveryNodeAttestations: null,
297
+ aaoAttestation: null,
298
+ error: GetAttestationError.AAO_ATTESTATION_ERROR,
299
+ erroringNodes: null
300
+ }
301
+ }
302
+
303
+ // Then attempt DNs
304
+
305
+ try {
306
+ const discoveryNodeAttestationResults = await this._getDiscoveryAttestationsWithRetries({
307
+ endpoints,
308
+ challengeId,
309
+ encodedUserId,
310
+ specifier,
311
+ oracleEthAddress,
312
+ logger,
313
+ maxAttempts
314
+ })
315
+
316
+ const discoveryNodeSuccesses = discoveryNodeAttestationResults.map(r => r.success)
317
+ const discoveryNodeErrors = discoveryNodeAttestationResults.map(r => r.error)
318
+ const error = discoveryNodeErrors.find(Boolean)
319
+ if (error) {
320
+ // Propagate out the specific nodes that errored
321
+ const erroringNodes = discoveryNodeAttestationResults.filter(r => r.error).map(r => r.endpoint)
322
+ return {
323
+ discoveryNodeAttestations: null,
324
+ aaoAttestation: null,
325
+ error,
326
+ erroringNodes
327
+ }
328
+ }
329
+
330
+ return {
331
+ discoveryNodeAttestations: discoveryNodeSuccesses,
332
+ aaoAttestation,
333
+ error: null,
334
+ erroringNodes: null
335
+ }
336
+ } catch (e) {
337
+ const err = e.message
338
+ logger.error(`Failed to aggregate attestations for user [${decodeHashId(encodedUserId)}], challenge-id: [${challengeId}] with err: ${err}`)
339
+ return {
340
+ discoveryNodeAttestations: null,
341
+ aaoAttestation: null,
342
+ error: GetAttestationError.DISCOVERY_NODE_ATTESTATION_ERROR,
343
+ erroringNodes: null
344
+ }
345
+ }
346
+ }
347
+
348
+ /**
349
+ *
350
+ * Retrieves a Discovery Node attestation for a given userId.
351
+ *
352
+ * @typedef {Object} GetAttestationReturn
353
+ * @property {AttestationMeta} success
354
+ * @property {GetAttestationError} error
355
+ *
356
+ * @param {{
357
+ * challengeId: string,
358
+ * encodedUserId: string,
359
+ * specifier: string,
360
+ * oracleEthAddress: string,
361
+ * discoveryProviderEndpoint: string
362
+ * logger: any
363
+ * }} {
364
+ * challengeId,
365
+ * encodedUserId,
366
+ * specifier,
367
+ * oracleEthAddress,
368
+ * discoveryProviderEndpoint
369
+ * logger
370
+ * }
371
+ * @returns {Promise<GetAttestationReturn>}
372
+ * @memberof Challenge
373
+ */
374
+ async getChallengeAttestation ({ challengeId, encodedUserId, specifier, oracleEthAddress, discoveryProviderEndpoint, logger = console }) {
375
+ this.REQUIRES(Services.DISCOVERY_PROVIDER)
376
+ try {
377
+ const res = await this.discoveryProvider.getChallengeAttestation(
378
+ challengeId,
379
+ encodedUserId,
380
+ specifier,
381
+ oracleEthAddress,
382
+ discoveryProviderEndpoint
383
+ )
384
+
385
+ const meta = {
386
+ ethAddress: res.owner_wallet,
387
+ signature: res.attestation
388
+ }
389
+
390
+ return { success: meta, error: null }
391
+ } catch (e) {
392
+ const err = e.message
393
+ logger.error(`Failed to get challenge attestation for userId [${decodeHashId(encodedUserId)}] challengeId [${challengeId}]from ${discoveryProviderEndpoint} with ${err}`)
394
+ const mappedErr = GetAttestationError[err] || GetAttestationError.DISCOVERY_NODE_UNKNOWN_RESPONSE
395
+ return {
396
+ success: null,
397
+ error: mappedErr
398
+ }
399
+ }
400
+ }
401
+
402
+ async getUndisbursedChallenges ({ limit, offset, completedBlockNumber, encodedUserId, logger = console } = { logger: console }) {
403
+ this.REQUIRES(Services.DISCOVERY_PROVIDER)
404
+ try {
405
+ const res = await this.discoveryProvider.getUndisbursedChallenges(
406
+ limit,
407
+ offset,
408
+ completedBlockNumber,
409
+ encodedUserId
410
+ )
411
+ return { success: res, error: null }
412
+ } catch (e) {
413
+ const error = e.message
414
+ logger.error(`Failed to get undisbursed challenges with error: ${error}`)
415
+ return {
416
+ success: null,
417
+ error
418
+ }
419
+ }
420
+ }
421
+
422
+ /**
423
+ *
424
+ * Retrieves an AAO attestation for a given user handle.
425
+ *
426
+ * @typedef {Object} GetAAOAttestationReturn
427
+ * @property {AttestationMeta} success
428
+ * @property {GetAttestationError} error
429
+ *
430
+ * @param {{
431
+ * challengeId: string,
432
+ * specifier: string,
433
+ * handle: string,
434
+ * amount: number,
435
+ * AAOEndpoint: string,
436
+ * oracleEthAddress: string
437
+ * logger: any
438
+ * }} {
439
+ * challengeId,
440
+ * specifier,
441
+ * handle,
442
+ * amount,
443
+ * AAOEndpoint,
444
+ * oracleEthAddress,
445
+ * logger
446
+ * }
447
+ * @returns {Promise<GetAAOAttestationReturn>}
448
+ * @memberof Challenge
449
+ */
450
+ async getAAOAttestation ({ challengeId, specifier, handle, amount, AAOEndpoint, oracleEthAddress, logger = console }) {
451
+ const data = {
452
+ challengeId,
453
+ challengeSpecifier: specifier,
454
+ amount
455
+ }
456
+ const request = {
457
+ method: 'post',
458
+ headers: {
459
+ 'Content-Type': 'application/json'
460
+ },
461
+ url: `${AAOEndpoint}/attestation/${handle}`,
462
+ timeout: AAO_REQUEST_TIMEOUT_MS,
463
+ data
464
+ }
465
+
466
+ try {
467
+ const response = await axios(request)
468
+ // if attestation is successful, 'result' represents a signature
469
+ // otherwise, 'result' is false
470
+ // - there may or may not be a value for `needs` if the attestation fails
471
+ // - depending on whether the user can take an action to attempt remediation
472
+ const { result, needs } = response.data
473
+
474
+ if (!result) {
475
+ logger.error(`Failed to get AAO attestation${needs ? `: needs ${needs}` : ''}`)
476
+ const mappedErr = needs
477
+ ? GetAttestationError[needs] || GetAttestationError.AAO_ATTESTATION_UNKNOWN_RESPONSE
478
+ : GetAttestationError.AAO_ATTESTATION_REJECTION
479
+ return {
480
+ success: null,
481
+ error: mappedErr
482
+ }
483
+ }
484
+
485
+ return {
486
+ success: {
487
+ signature: result,
488
+ ethAddress: oracleEthAddress
489
+ },
490
+ error: null
491
+ }
492
+ } catch (e) {
493
+ const err = e.message
494
+ logger.error(`Failed to get AAO attestation: ${err}`)
495
+ return {
496
+ success: null,
497
+ error: GetAttestationError.AAO_ATTESTATION_ERROR
498
+ }
499
+ }
500
+ }
501
+
502
+ async _getDiscoveryAttestationsWithRetries ({
503
+ endpoints,
504
+ challengeId,
505
+ encodedUserId,
506
+ specifier,
507
+ oracleEthAddress,
508
+ logger,
509
+ maxAttempts
510
+ }) {
511
+ let retryCount = 0
512
+ let unrecoverableError = false
513
+ const completedAttestations = []
514
+ let needsAttestations = endpoints
515
+
516
+ do {
517
+ logger.info(`Aggregating attestations with retries challenge: ${challengeId}, userId: ${encodedUserId}, endpoints: ${needsAttestations}, attempt ${retryCount}`)
518
+ if (retryCount > 0) {
519
+ await (new Promise(resolve => setTimeout(resolve, 2000)))
520
+ }
521
+
522
+ const attestations = await Promise.all(needsAttestations.map(async endpoint => {
523
+ const res = await this.getChallengeAttestation({
524
+ challengeId,
525
+ encodedUserId,
526
+ specifier,
527
+ oracleEthAddress,
528
+ discoveryProviderEndpoint: endpoint,
529
+ logger
530
+ })
531
+ return { endpoint, res }
532
+ }))
533
+
534
+ needsAttestations = []
535
+ attestations.forEach(a => {
536
+ // If it's a retryable error
537
+ const isRetryable = a.res.error === GetAttestationError.CHALLENGE_INCOMPLETE ||
538
+ a.res.error === GetAttestationError.MISSING_CHALLENGES
539
+
540
+ if (isRetryable) {
541
+ needsAttestations.push(a.endpoint)
542
+ logger.info(`Node ${a.endpoint} challenge still incomplete for challenge [${challengeId}], userId: ${encodedUserId}`)
543
+ // If final attempt, make sure we return the result
544
+ if (retryCount === maxAttempts) {
545
+ completedAttestations.push({ ...a.res, endpoint: a.endpoint })
546
+ }
547
+ } else {
548
+ completedAttestations.push({ ...a.res, endpoint: a.endpoint })
549
+ if (a.res.error) {
550
+ unrecoverableError = true
551
+ }
552
+ }
553
+ })
554
+
555
+ retryCount++
556
+ }
557
+ while (needsAttestations.length && retryCount <= maxAttempts)
558
+
559
+ if (needsAttestations.length || unrecoverableError) {
560
+ logger.info(`Failed to aggregate attestations for challenge [${challengeId}], userId: [${decodeHashId(encodedUserId)}]`)
561
+ } else {
562
+ logger.info(`Successfully aggregated attestations for challenge [${challengeId}], userId: [${decodeHashId(encodedUserId)}]`)
563
+ }
564
+ return completedAttestations
565
+ }
566
+
567
+ /**
568
+ *
569
+ * Creates a new discovery node sender for rewards. A sender may
570
+ * attest in user challenge completion to issue rewards.
571
+ *
572
+ * This method queries other discovery nodes asking for attestation of
573
+ * a given new senderEthAddress (delegate wallet) and operatorEthAddress (owner wallet).
574
+ * Those attestations are bundled
575
+ *
576
+ * @param {{
577
+ * senderEthAddress: string
578
+ * operatorEthAddress: string
579
+ * senderEndpoint: string
580
+ * endpoints?: string[]
581
+ * numAttestations?: number
582
+ * feePayerOverride?: string
583
+ * }} {
584
+ * senderEthAddress: the new sender eth address to add. The delegate wallet.
585
+ * operatorEthAddress: the unique address of the operator that runs this service
586
+ * senderEndpoint: the new sender's service endpoint
587
+ * endpoints: optional endpoints from other nodes. If not provided, nodes are selected from chain.
588
+ * numAttestations: optional number of attestations to get from other nodes, default 3
589
+ * feePayerOverride: optional override feepayer
590
+ * }
591
+ * @memberof Rewards
592
+ */
593
+ async createSenderPublic ({
594
+ senderEthAddress,
595
+ operatorEthAddress,
596
+ senderEndpoint,
597
+ endpoints,
598
+ numAttestations = 3,
599
+ feePayerOverride
600
+ }) {
601
+ let attestEndpoints
602
+ if (endpoints) {
603
+ attestEndpoints = sampleSize(endpoints, numAttestations)
604
+ } else {
605
+ attestEndpoints = await this.ServiceProvider.getUniquelyOwnedDiscoveryNodes(numAttestations, [], async (node) => {
606
+ const isRegistered = await this.solanaWeb3Manager.getIsDiscoveryNodeRegistered(node.delegateOwnerWallet)
607
+ return isRegistered
608
+ })
609
+ }
610
+
611
+ if (attestEndpoints.length < numAttestations) {
612
+ throw new Error(`Not enough other nodes found, need ${numAttestations}, found ${attestEndpoints.length}`)
613
+ }
614
+
615
+ let error = null
616
+ const attestations = await Promise.all(attestEndpoints.map(async attestEndpoint => {
617
+ try {
618
+ const res = await this.discoveryProvider.getCreateSenderAttestation(
619
+ senderEthAddress,
620
+ attestEndpoint
621
+ )
622
+ return {
623
+ ethAddress: res.owner_wallet,
624
+ signature: res.attestation
625
+ }
626
+ } catch (e) {
627
+ console.error(e)
628
+ error = true
629
+ }
630
+ }))
631
+ if (error) {
632
+ console.error(`Failed to get attestations from other nodes ${attestEndpoints}`)
633
+ return {
634
+ success: null,
635
+ error: GetSenderAttestationError.REQUEST_FOR_ATTESTATION_FAILED
636
+ }
637
+ }
638
+
639
+ // Register the server as a sender on the rewards manager
640
+ const receipt = await this.solanaWeb3Manager.createSender({
641
+ senderEthAddress,
642
+ operatorEthAddress,
643
+ attestations,
644
+ feePayerOverride
645
+ })
646
+ return receipt
647
+ }
648
+
649
+ /**
650
+ * Logs results of an attestation to identity.
651
+ *
652
+ * @param {{
653
+ * status: string,
654
+ * userId: string,
655
+ * challengeId: string,
656
+ * amount: number,
657
+ * source: string
658
+ * specifier: string
659
+ * error?: string,
660
+ * phase?: string,
661
+ * reason?: string
662
+ * }} { status, userId, challengeId, amount, error, phase, specifier, reason }
663
+ * @memberof IdentityService
664
+ */
665
+ async sendAttestationResult ({ status, userId, challengeId, amount, error, phase, source, specifier, reason }) {
666
+ await this.identityService.sendAttestationResult({
667
+ status,
668
+ userId,
669
+ challengeId,
670
+ amount,
671
+ error,
672
+ phase,
673
+ source,
674
+ specifier,
675
+ reason
676
+ })
677
+ }
678
+ }
679
+
680
+ module.exports = Rewards
681
+ module.exports.SubmitAndEvaluateError = SubmitAndEvaluateError
682
+ module.exports.AttestationPhases = AttestationPhases