@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,424 @@
|
|
|
1
|
+
const bs58 = require('bs58')
|
|
2
|
+
const { toBuffer } = require('ethereumjs-util')
|
|
3
|
+
const { zeroPad } = require('ethers/lib/utils')
|
|
4
|
+
const { providers } = require('ethers/lib/index')
|
|
5
|
+
const wormholeSDK = require('@certusone/wormhole-sdk')
|
|
6
|
+
|
|
7
|
+
const SolanaUtils = require('../solanaWeb3Manager/utils')
|
|
8
|
+
const { Utils } = require('../../utils')
|
|
9
|
+
const { wAudioFromWeiAudio } = require('../solanaWeb3Manager/wAudio')
|
|
10
|
+
const { sign, getTransferTokensDigest } = require('../../utils/signatures')
|
|
11
|
+
/** Singleton state-manager for Audius Eth Contracts */
|
|
12
|
+
|
|
13
|
+
class Wormhole {
|
|
14
|
+
/**
|
|
15
|
+
* Wormhole constructor
|
|
16
|
+
* @param {object} hedgehog
|
|
17
|
+
* @param {object} ethWeb3Manager
|
|
18
|
+
* @param {object} ethContracts
|
|
19
|
+
* @param {object} identityService
|
|
20
|
+
* @param {object} solanaWeb3Manager
|
|
21
|
+
* @param {Array<string>} rpcHosts
|
|
22
|
+
* @param {string} solBridgeAddress
|
|
23
|
+
* @param {string} solTokenBridgeAddress
|
|
24
|
+
* @param {string} ethBridgeAddress
|
|
25
|
+
* @param {string} ethTokenBridgeAddress
|
|
26
|
+
* @param {boolean} isServer
|
|
27
|
+
*/
|
|
28
|
+
constructor (
|
|
29
|
+
hedgehog,
|
|
30
|
+
ethWeb3Manager,
|
|
31
|
+
ethContracts,
|
|
32
|
+
identityService,
|
|
33
|
+
solanaWeb3Manager,
|
|
34
|
+
rpcHosts,
|
|
35
|
+
solBridgeAddress,
|
|
36
|
+
solTokenBridgeAddress,
|
|
37
|
+
ethBridgeAddress,
|
|
38
|
+
ethTokenBridgeAddress,
|
|
39
|
+
isServer
|
|
40
|
+
) {
|
|
41
|
+
// Wormhole service dependecies
|
|
42
|
+
this.hedgehog = hedgehog
|
|
43
|
+
this.ethWeb3Manager = ethWeb3Manager
|
|
44
|
+
this.ethContracts = ethContracts
|
|
45
|
+
this.identityService = identityService
|
|
46
|
+
this.solanaWeb3Manager = solanaWeb3Manager
|
|
47
|
+
|
|
48
|
+
// Wormhole config
|
|
49
|
+
this.rpcHosts = rpcHosts
|
|
50
|
+
this.solBridgeAddress = solBridgeAddress
|
|
51
|
+
this.solTokenBridgeAddress = solTokenBridgeAddress
|
|
52
|
+
this.ethBridgeAddress = ethBridgeAddress
|
|
53
|
+
this.ethTokenBridgeAddress = ethTokenBridgeAddress
|
|
54
|
+
this.wormholeSDK = wormholeSDK
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getSignedVAAWithRetry (
|
|
58
|
+
hosts,
|
|
59
|
+
emitterChain,
|
|
60
|
+
emitterAddress,
|
|
61
|
+
sequence,
|
|
62
|
+
extraGrpcOpts = {},
|
|
63
|
+
retryTimeout = 5000,
|
|
64
|
+
retryAttempts = 60
|
|
65
|
+
) {
|
|
66
|
+
let currentWormholeRpcHost = -1
|
|
67
|
+
const getNextRpcHost = () => ++currentWormholeRpcHost % hosts.length
|
|
68
|
+
let result
|
|
69
|
+
let attempts = 0
|
|
70
|
+
while (!result) {
|
|
71
|
+
attempts++
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, retryTimeout))
|
|
73
|
+
try {
|
|
74
|
+
result = await this.wormholeSDK.getSignedVAA(
|
|
75
|
+
hosts[getNextRpcHost()],
|
|
76
|
+
emitterChain,
|
|
77
|
+
emitterAddress,
|
|
78
|
+
sequence,
|
|
79
|
+
extraGrpcOpts
|
|
80
|
+
)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (retryAttempts !== undefined && attempts > retryAttempts) {
|
|
83
|
+
throw e
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sends `amount` tokens to `solanaAccount` by way of the wormhole
|
|
92
|
+
* @param {string} ethTxReceipt The tx receipt
|
|
93
|
+
* @param {function} [customSignTransaction] Optional custom sign transaction parameter
|
|
94
|
+
* @param {Object?} options The grpc options passed to get signed VAA for different transport
|
|
95
|
+
*
|
|
96
|
+
* else will attempt to relay
|
|
97
|
+
* @returns {Promise} Promise object of {
|
|
98
|
+
transactionSignature: string,
|
|
99
|
+
error: Error,
|
|
100
|
+
phase: string,
|
|
101
|
+
logs: Array<string>
|
|
102
|
+
}
|
|
103
|
+
*/
|
|
104
|
+
async attestAndCompleteTransferEthToSol (ethTxReceipt, customSignTransaction, options = {}) {
|
|
105
|
+
const phases = {
|
|
106
|
+
GET_RECEIPT: 'GET_RECEIPT',
|
|
107
|
+
GET_SIGNED_VAA: 'GET_SIGNED_VAA',
|
|
108
|
+
POST_VAA_SOLANA: 'POST_VAA_SOLANA',
|
|
109
|
+
REDEEM_ON_SOLANA: 'REDEEM_ON_SOLANA'
|
|
110
|
+
}
|
|
111
|
+
let phase = phases.GET_RECEIPT
|
|
112
|
+
const logs = [`Attest and complete transfer for eth to sol for reciept ${ethTxReceipt}`]
|
|
113
|
+
try {
|
|
114
|
+
const receipt = await this.ethWeb3Manager.web3.eth.getTransactionReceipt(ethTxReceipt)
|
|
115
|
+
const sequence = this.wormholeSDK.parseSequenceFromLogEth(receipt, this.ethBridgeAddress)
|
|
116
|
+
const emitterAddress = this.wormholeSDK.getEmitterAddressEth(this.ethTokenBridgeAddress)
|
|
117
|
+
phase = phases.GET_SIGNED_VAA
|
|
118
|
+
const { vaaBytes } = await this.getSignedVAAWithRetry(
|
|
119
|
+
this.rpcHosts,
|
|
120
|
+
this.wormholeSDK.CHAIN_ID_ETH,
|
|
121
|
+
emitterAddress,
|
|
122
|
+
sequence,
|
|
123
|
+
options
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const connection = this.solanaWeb3Manager.connection
|
|
127
|
+
let signTransaction
|
|
128
|
+
if (customSignTransaction) {
|
|
129
|
+
signTransaction = customSignTransaction
|
|
130
|
+
} else {
|
|
131
|
+
signTransaction = async (transaction) => {
|
|
132
|
+
const { blockhash } = await connection.getLatestBlockhash()
|
|
133
|
+
// Must call serialize message to set the correct signatures on the transaction
|
|
134
|
+
transaction.serializeMessage()
|
|
135
|
+
const transactionData = {
|
|
136
|
+
recentBlockhash: blockhash,
|
|
137
|
+
instructions: transaction.instructions.map(SolanaUtils.prepareInstructionForRelay),
|
|
138
|
+
signatures: transaction.signatures.map(sig => ({
|
|
139
|
+
publicKey: sig.publicKey.toString(),
|
|
140
|
+
signature: sig.signature
|
|
141
|
+
}))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { transactionSignature } = await this.identityService.solanaRelayRaw(transactionData)
|
|
145
|
+
logs.push(`Relay sol tx for postVAA with signature ${transactionSignature}`)
|
|
146
|
+
return {
|
|
147
|
+
serialize: () => {}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
connection.sendRawTransaction = async () => ''
|
|
151
|
+
connection.confirmTransaction = async () => ''
|
|
152
|
+
}
|
|
153
|
+
phase = phases.POST_VAA_SOLANA
|
|
154
|
+
await this.wormholeSDK.postVaaSolana(
|
|
155
|
+
connection,
|
|
156
|
+
signTransaction,
|
|
157
|
+
this.solBridgeAddress,
|
|
158
|
+
this.solanaWeb3Manager.feePayerAddress.toString(), // payerAddress
|
|
159
|
+
vaaBytes
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Finally, redeem on Solana
|
|
163
|
+
phase = phases.REDEEM_ON_SOLANA
|
|
164
|
+
const transaction = await this.wormholeSDK.redeemOnSolana(
|
|
165
|
+
connection,
|
|
166
|
+
this.solBridgeAddress,
|
|
167
|
+
this.solTokenBridgeAddress,
|
|
168
|
+
this.solanaWeb3Manager.feePayerAddress.toString(), // payerAddress,
|
|
169
|
+
vaaBytes
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
let finalTxSignature
|
|
173
|
+
// Must call serialize message to set the correct signatures on the transaction
|
|
174
|
+
if (customSignTransaction) {
|
|
175
|
+
const signedTransaction = await signTransaction(transaction)
|
|
176
|
+
const txid = await connection.sendRawTransaction(signedTransaction.serialize())
|
|
177
|
+
finalTxSignature = txid
|
|
178
|
+
|
|
179
|
+
await connection.confirmTransaction(txid)
|
|
180
|
+
} else {
|
|
181
|
+
transaction.serializeMessage()
|
|
182
|
+
|
|
183
|
+
const { blockhash } = await connection.getLatestBlockhash()
|
|
184
|
+
const transactionData = {
|
|
185
|
+
recentBlockhash: blockhash,
|
|
186
|
+
instructions: transaction.instructions.map(SolanaUtils.prepareInstructionForRelay),
|
|
187
|
+
signatures: transaction.signatures.map(sig => ({
|
|
188
|
+
publicKey: sig.publicKey.toString(),
|
|
189
|
+
signature: sig.signature
|
|
190
|
+
}))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const { transactionSignature } = await this.identityService.solanaRelayRaw(transactionData)
|
|
194
|
+
finalTxSignature = transactionSignature
|
|
195
|
+
}
|
|
196
|
+
logs.push(`Complete redeem on sol with signature ${finalTxSignature}`)
|
|
197
|
+
return {
|
|
198
|
+
transactionSignature: finalTxSignature,
|
|
199
|
+
error: null,
|
|
200
|
+
phase,
|
|
201
|
+
logs
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return {
|
|
205
|
+
error: error.message,
|
|
206
|
+
phase,
|
|
207
|
+
logs
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Sends `amount` tokens to `solanaAccount` by way of the wormhole
|
|
214
|
+
* @param {BN} amount The amount of AUDIO to send in Wrapped Audio (8 decimals)
|
|
215
|
+
* @param {string} ethTargetAddress The eth address to transfer AUDIO
|
|
216
|
+
* @param {Object?} options The grpc options passed to get signed VAA for different transport
|
|
217
|
+
*/
|
|
218
|
+
async sendTokensFromSolToEthViaWormhole (amount, ethTargetAddress, options = {}) {
|
|
219
|
+
const phases = {
|
|
220
|
+
GENERATE_SOL_ROOT_ACCT: 'GENERATE_SOL_ROOT_ACCT',
|
|
221
|
+
TRANSFER_WAUDIO_TO_ROOT: 'TRANSFER_WAUDIO_TO_ROOT',
|
|
222
|
+
TRANFER_FROM_SOL: 'TRANFER_FROM_SOL',
|
|
223
|
+
GET_SIGNED_VAA: 'GET_SIGNED_VAA',
|
|
224
|
+
GET_EMITTER_ADDR: 'GET_EMITTER_ADDR',
|
|
225
|
+
REDEEM_ON_ETH: 'REDEEM_ON_ETH'
|
|
226
|
+
}
|
|
227
|
+
let phase = phases.GENERATE_SOL_ROOT_ACCT
|
|
228
|
+
const logs = [`Transferring ${amount} WAUDIO to ${ethTargetAddress}`]
|
|
229
|
+
try {
|
|
230
|
+
const wAudioAmount = wAudioFromWeiAudio(amount)
|
|
231
|
+
// Generate a solana keypair derived from the hedgehog private key
|
|
232
|
+
// NOTE: The into to fromSeed is a 32 bytes Uint8Array
|
|
233
|
+
const rootSolanaAccount = this.solanaWeb3Manager.solanaWeb3.Keypair.fromSeed(
|
|
234
|
+
this.hedgehog.wallet.getPrivateKey()
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
const solanaAddress = rootSolanaAccount.publicKey.toString()
|
|
238
|
+
logs.push(`Root Solana Account: ${solanaAddress}`)
|
|
239
|
+
|
|
240
|
+
// Find the token account owned by the root solana account and get the token account's info
|
|
241
|
+
const associatedTokenAccount = await this.solanaWeb3Manager.findAssociatedTokenAddress(solanaAddress)
|
|
242
|
+
const tokenAccountInfo = await this.solanaWeb3Manager.getAssociatedTokenAccountInfo(associatedTokenAccount.toString())
|
|
243
|
+
|
|
244
|
+
// If it's not a valid token account, create the token account
|
|
245
|
+
if (!tokenAccountInfo) {
|
|
246
|
+
logs.push(`Creating Associated Token Account: ${associatedTokenAccount.toString()}`)
|
|
247
|
+
await this.solanaWeb3Manager.createAssociatedTokenAccount(solanaAddress)
|
|
248
|
+
} else {
|
|
249
|
+
logs.push(`Associated Token Account Exits: ${associatedTokenAccount.toString()}`)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
phase = phases.TRANSFER_WAUDIO_TO_ROOT
|
|
253
|
+
// Move wrapped audio from then user bank account to the user's token wallet
|
|
254
|
+
await this.solanaWeb3Manager.transferWAudio(tokenAccountInfo.address.toString(), amount)
|
|
255
|
+
logs.push(`Transferred waudio ${wAudioAmount.toString()} balance to associated token account`)
|
|
256
|
+
phase = phases.TRANFER_FROM_SOL
|
|
257
|
+
|
|
258
|
+
const connection = this.solanaWeb3Manager.connection
|
|
259
|
+
|
|
260
|
+
// Submit transaction - results in a Wormhole message being published
|
|
261
|
+
const tx = await this.wormholeSDK.transferFromSolana(
|
|
262
|
+
connection, // solana web3 Connection
|
|
263
|
+
this.solBridgeAddress, // bridge address
|
|
264
|
+
this.solTokenBridgeAddress, // token bridge address
|
|
265
|
+
this.solanaWeb3Manager.feePayerAddress, // payerAddress
|
|
266
|
+
tokenAccountInfo.address.toString(), // fromAddress
|
|
267
|
+
this.solanaWeb3Manager.mintAddress, // mintAddress
|
|
268
|
+
wAudioAmount, // BigInt
|
|
269
|
+
zeroPad(toBuffer(ethTargetAddress), 32), // Uint8Array of length 32 targetAddress
|
|
270
|
+
this.wormholeSDK.CHAIN_ID_ETH, // ChainId targetChain
|
|
271
|
+
zeroPad(toBuffer(this.ethContracts.AudiusTokenClient.contractAddress), 32), // Uint8Array of length 32 originAddress
|
|
272
|
+
this.wormholeSDK.CHAIN_ID_ETH, // ChainId originChain
|
|
273
|
+
solanaAddress // from owner address
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
// Must call serialize message to set the correct signatures on the transaction
|
|
277
|
+
tx.serializeMessage()
|
|
278
|
+
tx.partialSign(rootSolanaAccount)
|
|
279
|
+
|
|
280
|
+
const { blockhash } = await connection.getLatestBlockhash()
|
|
281
|
+
const transactionData = {
|
|
282
|
+
recentBlockhash: blockhash,
|
|
283
|
+
instructions: tx.instructions.map(SolanaUtils.prepareInstructionForRelay),
|
|
284
|
+
signatures: tx.signatures.map(sig => ({
|
|
285
|
+
publicKey: sig.publicKey.toString(),
|
|
286
|
+
signature: sig.signature
|
|
287
|
+
}))
|
|
288
|
+
}
|
|
289
|
+
const { transactionSignature } = await this.identityService.solanaRelayRaw(transactionData)
|
|
290
|
+
logs.push(`Transferred to wormhole with signature: ${transactionSignature}`)
|
|
291
|
+
phase = phases.GET_EMITTER_ADDR
|
|
292
|
+
|
|
293
|
+
// Get the sequence number and emitter address required to fetch the signedVAA of our message
|
|
294
|
+
const info = await connection.getTransaction(transactionSignature)
|
|
295
|
+
const sequence = this.wormholeSDK.parseSequenceFromLogSolana(info)
|
|
296
|
+
const emitterAddress = await this.wormholeSDK.getEmitterAddressSolana(this.solTokenBridgeAddress)
|
|
297
|
+
// Fetch the signedVAA from the Wormhole Network (this may require retries while you wait for confirmation)
|
|
298
|
+
phase = phases.GET_SIGNED_VAA
|
|
299
|
+
const { vaaBytes } = await this.getSignedVAAWithRetry(
|
|
300
|
+
this.rpcHosts,
|
|
301
|
+
this.wormholeSDK.CHAIN_ID_SOLANA,
|
|
302
|
+
emitterAddress,
|
|
303
|
+
sequence,
|
|
304
|
+
options
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
// Redeem on Ethereum
|
|
308
|
+
// NOTE: The signer should be the user's personal wallet
|
|
309
|
+
phase = phases.REDEEM_ON_ETH
|
|
310
|
+
const signer = (new providers.Web3Provider(window.ethereum)).getSigner()
|
|
311
|
+
await this.wormholeSDK.redeemOnEth(this.ethTokenBridgeAddress, signer, vaaBytes)
|
|
312
|
+
logs.push('Redeemed on eth')
|
|
313
|
+
return { phase, logs, error: null }
|
|
314
|
+
} catch (error) {
|
|
315
|
+
return {
|
|
316
|
+
error: error.message,
|
|
317
|
+
phase,
|
|
318
|
+
logs
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Locks assets owned by `fromAccount` into the Solana wormhole with a target
|
|
325
|
+
* solanaAccount destination via the provided relayer wallet.
|
|
326
|
+
* @param {string} fromAccount the account holding the ETH AUDIO to transfer
|
|
327
|
+
* @param {BN} amount The amount of AUDIO to send in WEI (18 decimals)
|
|
328
|
+
* @param {string} solanaAccount The solana token account
|
|
329
|
+
*/
|
|
330
|
+
async _getTransferTokensToEthWormholeParams (fromAccount, amount, solanaAccount) {
|
|
331
|
+
const web3 = this.ethWeb3Manager.getWeb3()
|
|
332
|
+
const wormholeClientAddress = this.ethContracts.WormholeClient.contractAddress
|
|
333
|
+
|
|
334
|
+
const chainId = await web3.eth.getChainId()
|
|
335
|
+
|
|
336
|
+
const currentBlockNumber = await web3.eth.getBlockNumber()
|
|
337
|
+
const currentBlock = await web3.eth.getBlock(currentBlockNumber)
|
|
338
|
+
|
|
339
|
+
// 1 hour, sufficiently far in future
|
|
340
|
+
const deadline = currentBlock.timestamp + (60 * 60 * 1)
|
|
341
|
+
const solanaB58 = bs58.decode(solanaAccount).toString('hex')
|
|
342
|
+
const recipient = toBuffer(`0x${solanaB58}`)
|
|
343
|
+
const nonce = await this.ethContracts.WormholeClient.nonces(fromAccount)
|
|
344
|
+
const arbiterFee = Utils.toBN('0')
|
|
345
|
+
|
|
346
|
+
const digest = getTransferTokensDigest(
|
|
347
|
+
web3,
|
|
348
|
+
'AudiusWormholeClient',
|
|
349
|
+
wormholeClientAddress,
|
|
350
|
+
chainId,
|
|
351
|
+
{
|
|
352
|
+
from: fromAccount,
|
|
353
|
+
amount,
|
|
354
|
+
recipientChain: chainId,
|
|
355
|
+
recipient,
|
|
356
|
+
arbiterFee
|
|
357
|
+
},
|
|
358
|
+
nonce,
|
|
359
|
+
deadline
|
|
360
|
+
)
|
|
361
|
+
const myPrivateKey = this.hedgehog.wallet._privKey
|
|
362
|
+
const signedDigest = sign(digest, myPrivateKey)
|
|
363
|
+
return {
|
|
364
|
+
chainId,
|
|
365
|
+
deadline,
|
|
366
|
+
recipient,
|
|
367
|
+
arbiterFee,
|
|
368
|
+
signedDigest
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Locks assets owned by `fromAccount` into the Solana wormhole with a target
|
|
374
|
+
* solanaAccount destination via the provided relayer wallet.
|
|
375
|
+
* @param {string} fromAccount the account holding the ETH AUDIO to transfer
|
|
376
|
+
* @param {BN} amount The amount of AUDIO to send in WEI (18 decimals)
|
|
377
|
+
* @param {string} solanaAccount The solana token account
|
|
378
|
+
* @param {string} relayer The eth relayer to permission to aprrove and transfer
|
|
379
|
+
*/
|
|
380
|
+
async transferTokensToEthWormhole (fromAccount, amount, solanaAccount, relayer) {
|
|
381
|
+
const {
|
|
382
|
+
chainId,
|
|
383
|
+
deadline,
|
|
384
|
+
recipient,
|
|
385
|
+
arbiterFee,
|
|
386
|
+
signedDigest
|
|
387
|
+
} = await this._getTransferTokensToEthWormholeParams(fromAccount, amount, solanaAccount)
|
|
388
|
+
const tx = await this.ethContracts.WormholeClient.transferTokens(
|
|
389
|
+
fromAccount,
|
|
390
|
+
amount,
|
|
391
|
+
chainId,
|
|
392
|
+
recipient,
|
|
393
|
+
arbiterFee,
|
|
394
|
+
deadline,
|
|
395
|
+
signedDigest,
|
|
396
|
+
relayer
|
|
397
|
+
)
|
|
398
|
+
return tx
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async getTransferTokensToEthWormholeMethod (fromAccount, amount, solanaAccount, relayer) {
|
|
402
|
+
const {
|
|
403
|
+
chainId,
|
|
404
|
+
deadline,
|
|
405
|
+
recipient,
|
|
406
|
+
arbiterFee,
|
|
407
|
+
signedDigest
|
|
408
|
+
} = await this._getTransferTokensToEthWormholeParams(fromAccount, amount, solanaAccount)
|
|
409
|
+
const method = await this.ethContracts.WormholeClient.WormholeContract.methods.transferTokens(
|
|
410
|
+
fromAccount,
|
|
411
|
+
amount,
|
|
412
|
+
chainId,
|
|
413
|
+
recipient,
|
|
414
|
+
arbiterFee,
|
|
415
|
+
deadline,
|
|
416
|
+
signedDigest.v,
|
|
417
|
+
signedDigest.r,
|
|
418
|
+
signedDigest.s
|
|
419
|
+
)
|
|
420
|
+
return method
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
module.exports = Wormhole
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CURRENT_USER_EXISTS_LOCAL_STORAGE_KEY } from './constants'
|
|
2
|
+
|
|
3
|
+
const supportsLocalStorage = () =>
|
|
4
|
+
typeof window !== 'undefined' && window && window.localStorage
|
|
5
|
+
|
|
6
|
+
export type CurrentUser = {
|
|
7
|
+
user_id: string
|
|
8
|
+
wallet: string
|
|
9
|
+
blocknumber: number
|
|
10
|
+
track_blocknumber: number
|
|
11
|
+
creator_node_endpoint: string
|
|
12
|
+
is_creator: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Singleton class to store the current user if initialized.
|
|
17
|
+
* Some instances of AudiusLibs and services require a current user to
|
|
18
|
+
* return valid queries, e.g. requesting the a discprov to return a reposted track.
|
|
19
|
+
*/
|
|
20
|
+
export class UserStateManager {
|
|
21
|
+
currentUser: CurrentUser | null
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
// Should reflect the same fields as discovery node's /users?handle=<handle>
|
|
25
|
+
this.currentUser = null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sets this.currentUser with currentUser
|
|
30
|
+
* @param {Object} currentUser fields to override this.currentUser with
|
|
31
|
+
*/
|
|
32
|
+
setCurrentUser(currentUser: CurrentUser) {
|
|
33
|
+
this.currentUser = currentUser
|
|
34
|
+
if (supportsLocalStorage()) {
|
|
35
|
+
window.localStorage.setItem(CURRENT_USER_EXISTS_LOCAL_STORAGE_KEY, 'true')
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getCurrentUser() {
|
|
40
|
+
return this.currentUser
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getCurrentUserId() {
|
|
44
|
+
return this.currentUser ? this.currentUser.user_id : null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
clearUser() {
|
|
48
|
+
this.currentUser = null
|
|
49
|
+
if (supportsLocalStorage()) {
|
|
50
|
+
window.localStorage.removeItem(CURRENT_USER_EXISTS_LOCAL_STORAGE_KEY)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import type Web3 from 'web3'
|
|
3
|
+
|
|
4
|
+
interface WalletResponse {
|
|
5
|
+
signature: string
|
|
6
|
+
signer: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Recover the public wallet address given the response contains the signature and timestamp
|
|
11
|
+
* @param {object} response entire service provider response (not axios)
|
|
12
|
+
*/
|
|
13
|
+
export function recoverWallet(web3: Web3, response: WalletResponse) {
|
|
14
|
+
let recoveredDelegateWallet = null
|
|
15
|
+
|
|
16
|
+
const dataForRecovery = JSON.parse(JSON.stringify(response))
|
|
17
|
+
delete dataForRecovery.signature
|
|
18
|
+
const dataForRecoveryStr = JSON.stringify(sortObjectKeys(dataForRecovery))
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const hashedData = web3.utils.keccak256(dataForRecoveryStr)
|
|
22
|
+
recoveredDelegateWallet = web3.eth.accounts.recover(
|
|
23
|
+
hashedData,
|
|
24
|
+
response.signature
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
assert.strictEqual(response.signer, recoveredDelegateWallet)
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.error(`Issue with recovering public wallet address: ${e}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return recoveredDelegateWallet
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type ValueOrArray<T> = undefined | string | T | Array<ValueOrArray<T>>
|
|
36
|
+
type SortObject = ValueOrArray<Record<string, string>>
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Recursively sorts object keys alphabetically
|
|
40
|
+
*/
|
|
41
|
+
export function sortObjectKeys(x: SortObject): SortObject {
|
|
42
|
+
if (typeof x !== 'object' || !x) {
|
|
43
|
+
return x
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(x)) {
|
|
46
|
+
return x.map(sortObjectKeys)
|
|
47
|
+
}
|
|
48
|
+
return Object.keys(x)
|
|
49
|
+
.sort()
|
|
50
|
+
.reduce((o, k) => ({ ...o, [k]: sortObjectKeys(x[k]) }), {})
|
|
51
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import FormData from 'form-data'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
grecaptcha: {
|
|
7
|
+
ready: (callback: () => void) => Promise<void>
|
|
8
|
+
execute: (siteKey: string, config: { action: string }) => Promise<string>
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const VERIFY_ENDPOINT = 'https://www.google.com/recaptcha/api/siteverify'
|
|
14
|
+
const IS_BROWSER = typeof window !== 'undefined' && window !== null
|
|
15
|
+
|
|
16
|
+
interface CaptchaConfig {
|
|
17
|
+
siteKey: string
|
|
18
|
+
serviceKey: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class Captcha {
|
|
22
|
+
siteKey: string
|
|
23
|
+
serviceKey: string
|
|
24
|
+
constructor({ siteKey, serviceKey }: CaptchaConfig) {
|
|
25
|
+
this.siteKey = siteKey
|
|
26
|
+
this.serviceKey = serviceKey
|
|
27
|
+
|
|
28
|
+
this.generate = this.generate.bind(this)
|
|
29
|
+
this.verify = this.verify.bind(this)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Intended to be called by clients. Will generate a token used to calculate recaptcha score.
|
|
34
|
+
* @param action name for this "action" for grouping
|
|
35
|
+
*/
|
|
36
|
+
async generate(action: string) {
|
|
37
|
+
if (!this.siteKey) {
|
|
38
|
+
throw new Error('No siteKey provided')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!IS_BROWSER) {
|
|
42
|
+
throw new Error('Expected a browser/client context')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!window.grecaptcha) {
|
|
46
|
+
throw new Error('No captcha found, did you forget to import it?')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return await new Promise<string>((resolve) => {
|
|
50
|
+
window.grecaptcha.ready(() => {
|
|
51
|
+
window.grecaptcha.execute(this.siteKey, { action }).then((token) => {
|
|
52
|
+
resolve(token)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Intended to be called by services. According to recaptcha v3 docs:
|
|
60
|
+
* A score of 1.0 is very likely a good interaction, 0.0 is very likely a bot
|
|
61
|
+
* @param token
|
|
62
|
+
* @param minScore score must be >= minScore to be ok
|
|
63
|
+
* @returns
|
|
64
|
+
* {boolean | null} ok - whether score > minScore (false if something went wrong)
|
|
65
|
+
* {number | null} score - the raw score [0, 1] (or null if a score was not computed)
|
|
66
|
+
*/
|
|
67
|
+
async verify(token: string, minScore = 0.5) {
|
|
68
|
+
if (!this.serviceKey) {
|
|
69
|
+
throw new Error('No serviceKey provided')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let score, ok, hostname
|
|
73
|
+
|
|
74
|
+
const formData = new FormData()
|
|
75
|
+
formData.append('response', token)
|
|
76
|
+
formData.append('secret', this.serviceKey)
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const resp = await axios.post(VERIFY_ENDPOINT, formData, {
|
|
80
|
+
headers: formData.getHeaders(),
|
|
81
|
+
adapter: IS_BROWSER
|
|
82
|
+
? require('axios/lib/adapters/xhr')
|
|
83
|
+
: require('axios/lib/adapters/http')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
score = resp.data.score
|
|
87
|
+
ok = score >= minScore
|
|
88
|
+
hostname = resp.data.hostname
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.error('Error with verifying captcha request', e)
|
|
91
|
+
score = null
|
|
92
|
+
ok = true
|
|
93
|
+
hostname = null
|
|
94
|
+
}
|
|
95
|
+
return { score, ok, hostname }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type Wallet from 'ethereumjs-wallet'
|
|
2
|
+
|
|
3
|
+
// Default multiplier on top of gas estimate to be extra safe that txns
|
|
4
|
+
// will go through
|
|
5
|
+
const GAS_LIMIT_MULTIPLIER = 1.05
|
|
6
|
+
|
|
7
|
+
export interface ContractMethod {
|
|
8
|
+
arguments: string[]
|
|
9
|
+
estimateGas: (config: {
|
|
10
|
+
from: Wallet | string | undefined
|
|
11
|
+
gas: number | undefined
|
|
12
|
+
}) => Promise<number>
|
|
13
|
+
_method: {
|
|
14
|
+
name: string
|
|
15
|
+
inputs: Array<{ type: string }>
|
|
16
|
+
}
|
|
17
|
+
encodeABI: () => string
|
|
18
|
+
send: <Tx>(config: {
|
|
19
|
+
from: Wallet | string | undefined
|
|
20
|
+
gas: number
|
|
21
|
+
gasPrice?: number
|
|
22
|
+
}) => Tx
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface EstimateGasConfig {
|
|
26
|
+
method: ContractMethod
|
|
27
|
+
from?: Wallet | string
|
|
28
|
+
gasLimitMaximum: number
|
|
29
|
+
multiplier?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns estimated gas use for a txn for a contract method
|
|
34
|
+
* @param options
|
|
35
|
+
* @param options.method the contract method
|
|
36
|
+
* @param options.from address the method will be sent from (required if the contract requires a certain sender, e.g. guardian)
|
|
37
|
+
* @param options.gasLimitMaximum the maximum amount of gas we will allow
|
|
38
|
+
* (likely will return a number much smaller than this)
|
|
39
|
+
* @param options.multipler the multiplier to safe-guard against estimates that are too low
|
|
40
|
+
*/
|
|
41
|
+
export const estimateGas = async ({
|
|
42
|
+
method,
|
|
43
|
+
from,
|
|
44
|
+
gasLimitMaximum,
|
|
45
|
+
multiplier = GAS_LIMIT_MULTIPLIER
|
|
46
|
+
}: EstimateGasConfig) => {
|
|
47
|
+
try {
|
|
48
|
+
const estimatedGas = await method.estimateGas({
|
|
49
|
+
from,
|
|
50
|
+
gas: gasLimitMaximum
|
|
51
|
+
})
|
|
52
|
+
// Rounding is necessary here as fractional gas limits will break
|
|
53
|
+
const safeEstimatedGas = Math.ceil(estimatedGas * multiplier)
|
|
54
|
+
console.info(
|
|
55
|
+
`Estimated gas limit ${safeEstimatedGas} for method ${method._method.name}`
|
|
56
|
+
)
|
|
57
|
+
return safeEstimatedGas
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error(
|
|
60
|
+
`Unable to estimate gas for transaction ${method._method.name}, using ${gasLimitMaximum}`
|
|
61
|
+
)
|
|
62
|
+
return gasLimitMaximum
|
|
63
|
+
}
|
|
64
|
+
}
|