@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.
- package/.eslintrc +38 -0
- package/.prettierrc.js +1 -0
- package/.python-version +1 -0
- package/Dockerfile +15 -0
- package/README.md +3 -0
- package/babel.config.js +3 -0
- package/data-contracts/ABIs/AdminUpgradeabilityProxy.json +132 -0
- package/data-contracts/ABIs/BaseAdminUpgradeabilityProxy.json +113 -0
- package/data-contracts/ABIs/BaseUpgradeabilityProxy.json +22 -0
- package/data-contracts/ABIs/DiscoveryProviderFactory.json +189 -0
- package/data-contracts/ABIs/DiscoveryProviderFactoryInterface.json +61 -0
- package/data-contracts/ABIs/DiscoveryProviderStorage.json +205 -0
- package/data-contracts/ABIs/DiscoveryProviderStorageInterface.json +65 -0
- package/data-contracts/ABIs/ECDSA.json +4 -0
- package/data-contracts/ABIs/IPLDBlacklistFactory.json +168 -0
- package/data-contracts/ABIs/Initializable.json +4 -0
- package/data-contracts/ABIs/Migrations.json +67 -0
- package/data-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
- package/data-contracts/ABIs/Ownable.json +79 -0
- package/data-contracts/ABIs/PlaylistFactory.json +669 -0
- package/data-contracts/ABIs/PlaylistFactoryInterface.json +42 -0
- package/data-contracts/ABIs/PlaylistStorage.json +250 -0
- package/data-contracts/ABIs/PlaylistStorageInterface.json +129 -0
- package/data-contracts/ABIs/Proxy.json +10 -0
- package/data-contracts/ABIs/Registry.json +240 -0
- package/data-contracts/ABIs/RegistryContract.json +102 -0
- package/data-contracts/ABIs/RegistryContractInterface.json +28 -0
- package/data-contracts/ABIs/RegistryInterface.json +66 -0
- package/data-contracts/ABIs/SigningLogic.json +43 -0
- package/data-contracts/ABIs/SigningLogicInitializable.json +46 -0
- package/data-contracts/ABIs/SocialFeatureFactory.json +460 -0
- package/data-contracts/ABIs/SocialFeatureStorage.json +225 -0
- package/data-contracts/ABIs/SocialFeatureStorageInterface.json +123 -0
- package/data-contracts/ABIs/TestContract.json +135 -0
- package/data-contracts/ABIs/TestContractInterface.json +19 -0
- package/data-contracts/ABIs/TestContractWithStorage.json +165 -0
- package/data-contracts/ABIs/TestContractWithStorageInterface.json +24 -0
- package/data-contracts/ABIs/TestStorage.json +144 -0
- package/data-contracts/ABIs/TestStorageInterface.json +42 -0
- package/data-contracts/ABIs/TestUserReplicaSetManager.json +432 -0
- package/data-contracts/ABIs/TrackFactory.json +391 -0
- package/data-contracts/ABIs/TrackFactoryInterface.json +73 -0
- package/data-contracts/ABIs/TrackStorage.json +223 -0
- package/data-contracts/ABIs/TrackStorageInterface.json +121 -0
- package/data-contracts/ABIs/UpgradeabilityProxy.json +37 -0
- package/data-contracts/ABIs/UserFactory.json +657 -0
- package/data-contracts/ABIs/UserFactoryInterface.json +65 -0
- package/data-contracts/ABIs/UserLibraryFactory.json +334 -0
- package/data-contracts/ABIs/UserReplicaSetManager.json +418 -0
- package/data-contracts/ABIs/UserStorage.json +233 -0
- package/data-contracts/ABIs/UserStorageInterface.json +93 -0
- package/data-contracts/signatureSchemas.ts +1236 -0
- package/dist/core.d.ts +446 -0
- package/dist/core.js +769 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +689 -0
- package/dist/index.js +72850 -0
- package/dist/index.js.map +1 -0
- package/eth-contracts/ABIs/Address.json +4 -0
- package/eth-contracts/ABIs/AudiusAdminUpgradeabilityProxy.json +105 -0
- package/eth-contracts/ABIs/AudiusClaimDistributor.json +4968 -0
- package/eth-contracts/ABIs/AudiusToken.json +724 -0
- package/eth-contracts/ABIs/BaseUpgradeabilityProxy.json +23 -0
- package/eth-contracts/ABIs/Checkpointing.json +4 -0
- package/eth-contracts/ABIs/ClaimsManager.json +539 -0
- package/eth-contracts/ABIs/Context.json +11 -0
- package/eth-contracts/ABIs/DelegateManager.json +989 -0
- package/eth-contracts/ABIs/DelegateManagerV2.json +1049 -0
- package/eth-contracts/ABIs/DelegateManagerV2Bad.json +1049 -0
- package/eth-contracts/ABIs/ERC20.json +252 -0
- package/eth-contracts/ABIs/ERC20Burnable.json +287 -0
- package/eth-contracts/ABIs/ERC20Detailed.json +270 -0
- package/eth-contracts/ABIs/ERC20Mintable.json +364 -0
- package/eth-contracts/ABIs/ERC20Pausable.json +397 -0
- package/eth-contracts/ABIs/EthRewardsManager.json +174 -0
- package/eth-contracts/ABIs/Governance.json +938 -0
- package/eth-contracts/ABIs/GovernanceUpgraded.json +953 -0
- package/eth-contracts/ABIs/GovernanceV2.json +938 -0
- package/eth-contracts/ABIs/IERC20.json +200 -0
- package/eth-contracts/ABIs/Initializable.json +4 -0
- package/eth-contracts/ABIs/InitializableV2.json +14 -0
- package/eth-contracts/ABIs/Migrations.json +71 -0
- package/eth-contracts/ABIs/MinterRole.json +91 -0
- package/eth-contracts/ABIs/MockAccount.json +62 -0
- package/eth-contracts/ABIs/MockDelegateManager.json +55 -0
- package/eth-contracts/ABIs/MockStakingCaller.json +259 -0
- package/eth-contracts/ABIs/MockWormhole.json +106 -0
- package/eth-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
- package/eth-contracts/ABIs/Ownable.json +93 -0
- package/eth-contracts/ABIs/Pausable.json +150 -0
- package/eth-contracts/ABIs/PauserRole.json +91 -0
- package/eth-contracts/ABIs/Proxy.json +10 -0
- package/eth-contracts/ABIs/Registry.json +288 -0
- package/eth-contracts/ABIs/Roles.json +4 -0
- package/eth-contracts/ABIs/SafeERC20.json +4 -0
- package/eth-contracts/ABIs/SafeMath.json +4 -0
- package/eth-contracts/ABIs/ServiceProviderFactory.json +1153 -0
- package/eth-contracts/ABIs/ServiceTypeManager.json +337 -0
- package/eth-contracts/ABIs/Staking.json +555 -0
- package/eth-contracts/ABIs/StakingUpgraded.json +570 -0
- package/eth-contracts/ABIs/TestContract.json +44 -0
- package/eth-contracts/ABIs/TrustedNotifierManager.json +265 -0
- package/eth-contracts/ABIs/Uint256Helpers.json +4 -0
- package/eth-contracts/ABIs/UpgradeabilityProxy.json +40 -0
- package/eth-contracts/ABIs/Wormhole.json +45 -0
- package/eth-contracts/ABIs/WormholeClient.json +155 -0
- package/examples/file.mp3 +0 -0
- package/examples/initAudiusLibs.js +86 -0
- package/examples/initializeVersions.js +95 -0
- package/examples/pic.jpg +0 -0
- package/initScripts/configureLocalDiscProv.js +167 -0
- package/initScripts/helpers/claim.js +43 -0
- package/initScripts/helpers/distributeTokens.js +24 -0
- package/initScripts/helpers/spRegistration.js +138 -0
- package/initScripts/helpers/utils.js +34 -0
- package/initScripts/helpers/version.js +93 -0
- package/initScripts/local.js +617 -0
- package/initScripts/mainnet.js +131 -0
- package/initScripts/manageProdRelayerWallets.js +191 -0
- package/package.json +125 -0
- package/rollup.config.js +164 -0
- package/scripts/AudiusClaimDistributor.json +4968 -0
- package/scripts/Wormhole.json +155 -0
- package/scripts/addCIDToIpldBlacklist.js +124 -0
- package/scripts/circleci-test.sh +53 -0
- package/scripts/communityRewards/transferCommunityRewardsToSolana.js +222 -0
- package/scripts/ipfs.sh +58 -0
- package/scripts/migrate_contracts.sh +25 -0
- package/scripts/reset.sh +65 -0
- package/scripts/test.sh +77 -0
- package/src/api/account.js +670 -0
- package/src/api/base.js +122 -0
- package/src/api/file.js +168 -0
- package/src/api/playlist.js +328 -0
- package/src/api/rewards.d.ts +4 -0
- package/src/api/rewards.js +682 -0
- package/src/api/serviceProvider.js +154 -0
- package/src/api/track.js +604 -0
- package/src/api/user.js +888 -0
- package/src/api/user.test.js +172 -0
- package/src/constants.ts +7 -0
- package/src/core.ts +3 -0
- package/src/index.js +6 -0
- package/src/libs.d.ts +3 -0
- package/src/libs.js +619 -0
- package/src/sanityChecks/addSecondaries.js +40 -0
- package/src/sanityChecks/assignReplicaSetIfNecessary.js +10 -0
- package/src/sanityChecks/index.d.ts +9 -0
- package/src/sanityChecks/index.js +31 -0
- package/src/sanityChecks/isCreator.js +73 -0
- package/src/sanityChecks/needsRecoveryEmail.js +20 -0
- package/src/sanityChecks/rolloverNodes.js +74 -0
- package/src/sanityChecks/sanitizeNodes.js +24 -0
- package/src/sanityChecks/syncNodes.js +28 -0
- package/src/sdk/constants.ts +10 -0
- package/src/sdk/index.ts +1 -0
- package/src/sdk/oauth/Oauth.ts +265 -0
- package/src/sdk/oauth/index.ts +1 -0
- package/src/sdk/sdk.ts +102 -0
- package/src/service-selection/ServiceSelection.test.ts +320 -0
- package/src/service-selection/ServiceSelection.ts +460 -0
- package/src/service-selection/constants.ts +14 -0
- package/src/service-selection/index.ts +1 -0
- package/src/services/ABIDecoder/AudiusABIDecoder.ts +71 -0
- package/src/services/ABIDecoder/index.ts +1 -0
- package/src/services/comstock/Comstock.ts +39 -0
- package/src/services/comstock/index.ts +1 -0
- package/src/services/contracts/ContractClient.ts +227 -0
- package/src/services/contracts/GovernedContractClient.ts +53 -0
- package/src/services/contracts/ProviderSelection.ts +42 -0
- package/src/services/creatorNode/CreatorNode.ts +1065 -0
- package/src/services/creatorNode/CreatorNodeSelection.test.ts +997 -0
- package/src/services/creatorNode/CreatorNodeSelection.ts +488 -0
- package/src/services/creatorNode/constants.ts +10 -0
- package/src/services/creatorNode/index.ts +2 -0
- package/src/services/dataContracts/AudiusContracts.ts +234 -0
- package/src/services/dataContracts/IPLDBlacklistFactoryClient.ts +73 -0
- package/src/services/dataContracts/PlaylistFactoryClient.ts +370 -0
- package/src/services/dataContracts/RegistryClient.ts +95 -0
- package/src/services/dataContracts/SocialFeatureFactoryClient.ts +196 -0
- package/src/services/dataContracts/TrackFactoryClient.ts +131 -0
- package/src/services/dataContracts/UserFactoryClient.ts +351 -0
- package/src/services/dataContracts/UserLibraryFactoryClient.ts +115 -0
- package/src/services/dataContracts/UserReplicaSetManagerClient.ts +206 -0
- package/src/services/dataContracts/index.ts +1 -0
- package/src/services/discoveryProvider/DiscoveryProvider.ts +1168 -0
- package/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +536 -0
- package/src/services/discoveryProvider/DiscoveryProviderSelection.ts +383 -0
- package/src/services/discoveryProvider/constants.ts +13 -0
- package/src/services/discoveryProvider/index.ts +1 -0
- package/src/services/discoveryProvider/requests.ts +629 -0
- package/src/services/ethContracts/AudiusTokenClient.ts +163 -0
- package/src/services/ethContracts/ClaimDistributionClient.ts +45 -0
- package/src/services/ethContracts/ClaimsManagerClient.ts +102 -0
- package/src/services/ethContracts/DelegateManagerClient.ts +480 -0
- package/src/services/ethContracts/EthContracts.ts +359 -0
- package/src/services/ethContracts/EthRewardsManagerClient.ts +33 -0
- package/src/services/ethContracts/GovernanceClient.ts +451 -0
- package/src/services/ethContracts/RegistryClient.ts +33 -0
- package/src/services/ethContracts/ServiceProviderFactoryClient.ts +691 -0
- package/src/services/ethContracts/ServiceTypeManagerClient.ts +112 -0
- package/src/services/ethContracts/StakingProxyClient.ts +97 -0
- package/src/services/ethContracts/TrustedNotifierManagerClient.ts +101 -0
- package/src/services/ethContracts/WormholeClient.ts +97 -0
- package/src/services/ethContracts/index.ts +1 -0
- package/src/services/ethWeb3Manager/EthWeb3Manager.ts +239 -0
- package/src/services/ethWeb3Manager/index.ts +1 -0
- package/src/services/hedgehog/Hedgehog.ts +96 -0
- package/src/services/hedgehog/index.ts +1 -0
- package/src/services/identity/IdentityService.ts +551 -0
- package/src/services/identity/index.ts +1 -0
- package/src/services/identity/requests.ts +65 -0
- package/src/services/schemaValidator/SchemaValidator.ts +105 -0
- package/src/services/schemaValidator/index.ts +1 -0
- package/src/services/schemaValidator/schemas/trackSchema.json +267 -0
- package/src/services/schemaValidator/schemas/userSchema.json +230 -0
- package/src/services/solanaAudiusData/errors.ts +20 -0
- package/src/services/solanaAudiusData/index.ts +1189 -0
- package/src/services/solanaWeb3Manager/errors.js +101 -0
- package/src/services/solanaWeb3Manager/index.d.ts +46 -0
- package/src/services/solanaWeb3Manager/index.js +655 -0
- package/src/services/solanaWeb3Manager/padBNToUint8Array.ts +7 -0
- package/src/services/solanaWeb3Manager/rewards.js +941 -0
- package/src/services/solanaWeb3Manager/rewardsAttester.ts +1093 -0
- package/src/services/solanaWeb3Manager/tokenAccount.js +149 -0
- package/src/services/solanaWeb3Manager/transactionHandler.js +345 -0
- package/src/services/solanaWeb3Manager/transfer.js +272 -0
- package/src/services/solanaWeb3Manager/userBank.js +160 -0
- package/src/services/solanaWeb3Manager/utils.d.ts +31 -0
- package/src/services/solanaWeb3Manager/utils.js +163 -0
- package/src/services/solanaWeb3Manager/wAudio.js +28 -0
- package/src/services/solanaWeb3Manager/wAudio.test.js +30 -0
- package/src/services/web3Manager/Web3Config.ts +14 -0
- package/src/services/web3Manager/Web3Manager.ts +360 -0
- package/src/services/web3Manager/XMLHttpRequest.ts +11 -0
- package/src/services/web3Manager/index.ts +2 -0
- package/src/services/wormhole/index.js +424 -0
- package/src/types.ts +8 -0
- package/src/userStateManager.ts +53 -0
- package/src/utils/apiSigning.ts +51 -0
- package/src/utils/captcha.ts +97 -0
- package/src/utils/estimateGas.ts +64 -0
- package/src/utils/fileHasher.ts +278 -0
- package/src/utils/importContractABI.d.ts +9 -0
- package/src/utils/importContractABI.js +19 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/multiProvider.ts +72 -0
- package/src/utils/network.test.ts +127 -0
- package/src/utils/network.ts +308 -0
- package/src/utils/promiseFight.test.ts +87 -0
- package/src/utils/promiseFight.ts +36 -0
- package/src/utils/signatures.ts +139 -0
- package/src/utils/types.ts +34 -0
- package/src/utils/utils.test.ts +36 -0
- package/src/utils/utils.ts +235 -0
- package/src/utils/uuid.ts +14 -0
- package/src/web3.d.ts +9 -0
- package/src/web3.js +8 -0
- package/tests/assets/static_image.png +0 -0
- package/tests/assets/static_text.txt +1 -0
- package/tests/audiusTokenClientTest.js +37 -0
- package/tests/creatorNodeTest.js +19 -0
- package/tests/fileHasherTest.js +125 -0
- package/tests/governanceTest.js +382 -0
- package/tests/helpers.js +105 -0
- package/tests/index.js +14 -0
- package/tests/playlistClientTest.js +157 -0
- package/tests/providerSelectionTest.js +241 -0
- package/tests/registryClientTest.js +19 -0
- package/tests/rewardsAttesterTest.js +373 -0
- package/tests/serviceTypeManagerClientTest.js +33 -0
- package/tests/socialFeatureClientTest.js +79 -0
- package/tests/stakingTest.js +302 -0
- package/tests/trackClientTest.js +86 -0
- package/tests/userClientTest.js +121 -0
- package/tsconfig.json +10 -0
- package/types/@audius-hedgehog/index.d.ts +39 -0
- 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 }
|