From d6460d796e944484a1ad055e5d57f1172c8d8ce3 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Mon, 18 Apr 2022 21:23:27 -0700 Subject: [PATCH 01/14] Refactor all the underlying ethContracts --- libs/package-lock.json | 9 + libs/package.json | 1 + .../services/ABIDecoder/AudiusABIDecoder.ts | 71 ++ libs/src/services/contracts/ContractClient.ts | 19 +- .../contracts/GovernedContractClient.ts | 53 ++ .../services/contracts/ProviderSelection.ts | 4 +- .../DiscoveryProvider.test.ts | 74 ++ .../src/services/ethContracts/EthContracts.ts | 284 ++++++++ ...iusTokenClient.js => audiusTokenClient.ts} | 90 ++- .../ethContracts/claimDistributionClient.js | 45 -- .../ethContracts/claimDistributionClient.ts | 45 ++ .../ethContracts/claimsManagerClient.js | 124 ---- .../ethContracts/claimsManagerClient.ts | 99 +++ .../ethContracts/delegateManagerClient.ts | 451 ++++++++++++ ...erClient.js => ethRewardsManagerClient.ts} | 14 +- ...overnanceClient.js => governanceClient.ts} | 253 ++++--- libs/src/services/ethContracts/index.js | 22 +- .../services/ethContracts/registryClient.js | 21 - .../services/ethContracts/registryClient.ts | 33 + .../serviceProviderFactoryClient.ts | 687 ++++++++++++++++++ .../ethContracts/serviceTypeManagerClient.js | 90 --- .../ethContracts/serviceTypeManagerClient.ts | 112 +++ .../ethContracts/stakingProxyClient.ts | 97 +++ ...ent.js => trustedNotifierManagerClient.ts} | 42 +- .../{wormholeClient.js => wormholeClient.ts} | 77 +- .../services/ethWeb3Manager/EthWeb3Manager.ts | 22 +- libs/src/services/identity/IdentityService.ts | 14 +- libs/src/services/web3Manager/Web3Manager.ts | 353 +++++++++ libs/src/utils/estimateGas.ts | 10 +- libs/src/utils/utils.ts | 2 +- 30 files changed, 2689 insertions(+), 529 deletions(-) create mode 100644 libs/src/services/ABIDecoder/AudiusABIDecoder.ts create mode 100644 libs/src/services/contracts/GovernedContractClient.ts create mode 100644 libs/src/services/discoveryProvider/DiscoveryProvider.test.ts create mode 100644 libs/src/services/ethContracts/EthContracts.ts rename libs/src/services/ethContracts/{audiusTokenClient.js => audiusTokenClient.ts} (61%) delete mode 100644 libs/src/services/ethContracts/claimDistributionClient.js create mode 100644 libs/src/services/ethContracts/claimDistributionClient.ts delete mode 100644 libs/src/services/ethContracts/claimsManagerClient.js create mode 100644 libs/src/services/ethContracts/claimsManagerClient.ts create mode 100644 libs/src/services/ethContracts/delegateManagerClient.ts rename libs/src/services/ethContracts/{ethRewardsManagerClient.js => ethRewardsManagerClient.ts} (69%) rename libs/src/services/ethContracts/{governanceClient.js => governanceClient.ts} (62%) delete mode 100644 libs/src/services/ethContracts/registryClient.js create mode 100644 libs/src/services/ethContracts/registryClient.ts create mode 100644 libs/src/services/ethContracts/serviceProviderFactoryClient.ts delete mode 100644 libs/src/services/ethContracts/serviceTypeManagerClient.js create mode 100644 libs/src/services/ethContracts/serviceTypeManagerClient.ts create mode 100644 libs/src/services/ethContracts/stakingProxyClient.ts rename libs/src/services/ethContracts/{trustedNotifierManagerClient.js => trustedNotifierManagerClient.ts} (68%) rename libs/src/services/ethContracts/{wormholeClient.js => wormholeClient.ts} (56%) create mode 100644 libs/src/services/web3Manager/Web3Manager.ts diff --git a/libs/package-lock.json b/libs/package-lock.json index 8bd90a0f409..46a85aa4923 100644 --- a/libs/package-lock.json +++ b/libs/package-lock.json @@ -3757,6 +3757,15 @@ "@types/range-parser": "*" } }, + "@types/form-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.0.tgz", + "integrity": "sha512-23/wYiuckYYtFpL+4RPWiWmRQH2BjFuqCUi2+N3amB1a1Drv+i/byTrGvlLwRVLFNAZbwpbQ7JvTK+VCAPMbcg==", + "dev": true, + "requires": { + "form-data": "*" + } + }, "@types/hashids": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/hashids/-/hashids-2.0.1.tgz", diff --git a/libs/package.json b/libs/package.json index 6ad1f70b1f3..587ce370559 100644 --- a/libs/package.json +++ b/libs/package.json @@ -78,6 +78,7 @@ "@types/async-retry": "1.4.3", "@types/bs58": "4.0.1", "@types/expect": "24.3.0", + "@types/form-data": "^2.5.0", "@types/hashids": "2.0.1", "@types/mocha": "9.1.0", "@types/node-localstorage": "1.3.0", diff --git a/libs/src/services/ABIDecoder/AudiusABIDecoder.ts b/libs/src/services/ABIDecoder/AudiusABIDecoder.ts new file mode 100644 index 00000000000..3f561861609 --- /dev/null +++ b/libs/src/services/ABIDecoder/AudiusABIDecoder.ts @@ -0,0 +1,71 @@ +import abiDecoder from 'abi-decoder' +import { Utils } from '../../utils' +import type { AbiItem, AbiInput } from 'web3-utils' +import type { Log } from 'web3-core' + +const abiMap: Record = {} + +function loadABI(abiFile: string) { + const contract = Utils.importDataContractABI(abiFile) + abiDecoder.addABI(contract.abi) + abiMap[contract.contractName] = contract.abi +} + +loadABI('Registry.json') +loadABI('UserFactory.json') +loadABI('TrackFactory.json') +loadABI('DiscoveryProviderFactory.json') +loadABI('SocialFeatureFactory.json') +loadABI('PlaylistFactory.json') +loadABI('UserLibraryFactory.json') +loadABI('UserReplicaSetManager.json') + +// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- should just use esm +export class AudiusABIDecoder { + static decodeMethod(contractName: string, encodedABI: string) { + const decoded = abiDecoder.decodeMethod(encodedABI) + if (!decoded) { + throw new Error('No Audius ABI matches given data') + } + + // hack around abi-decoder's lack of contract-specific support (only one global + // namespace of functions) + const abi = abiMap[contractName] + if (!abi) { + throw new Error('Unrecognized contract name') + } + + let foundFunction: AbiItem | undefined + abi.forEach((item) => { + if (item.type === 'function' && item.name === decoded.name) { + foundFunction = item + } + }) + + if (!foundFunction) { + throw new Error( + `Unrecognized function ${decoded.name} for contract ${contractName}` + ) + } + + const paramSpecs = foundFunction.inputs as AbiInput[] + decoded.params.forEach((param, idx) => { + if (idx >= paramSpecs.length) { + throw new Error('Extra parameter') + } + + const paramSpec = paramSpecs[idx] + if (paramSpec?.name !== param.name || paramSpec.type !== param.type) { + throw new Error( + `Invalid name or value for param ${paramSpec?.name}: ${paramSpec?.type}` + ) + } + }) + + return decoded + } + + static decodeLogs(_: string | unknown, logs: Log[]) { + return abiDecoder.decodeLogs(logs) + } +} diff --git a/libs/src/services/contracts/ContractClient.ts b/libs/src/services/contracts/ContractClient.ts index f34d28e538b..772e1b856e3 100644 --- a/libs/src/services/contracts/ContractClient.ts +++ b/libs/src/services/contracts/ContractClient.ts @@ -4,22 +4,25 @@ import retry from 'async-retry' import type { ContractABI, Nullable, Logger } from '../../utils' import type { Contract } from 'web3-eth-contract' import type { HttpProvider } from 'web3-core' +import type { EthWeb3Manager } from '../ethWeb3Manager' const CONTRACT_INITIALIZING_INTERVAL = 100 const CONTRACT_INITIALIZING_TIMEOUT = 10000 const CONTRACT_INIT_MAX_ATTEMPTS = 5 const METHOD_CALL_MAX_RETRIES = 5 +export type GetRegistryAddress = (key: string) => Promise + /* * Base class for instantiating contracts. * Performs a single init of the eth contract the first * time a method on the contract is invoked. */ export class ContractClient { - web3Manager: Web3Manager + web3Manager: Web3Manager | EthWeb3Manager contractABI: ContractABI['abi'] contractRegistryKey: string - getRegistryAddress: (key: string) => Promise + getRegistryAddress: GetRegistryAddress _contractAddress: Nullable _contract: Nullable _isInitialized: boolean @@ -29,10 +32,10 @@ export class ContractClient { logger: Logger constructor( - web3Manager: Web3Manager, + web3Manager: Web3Manager | EthWeb3Manager, contractABI: ContractABI['abi'], contractRegistryKey: string, - getRegistryAddress: (key: string) => Promise, + getRegistryAddress: GetRegistryAddress, logger: Logger = console, contractAddress: Nullable = null ) { @@ -162,7 +165,8 @@ export class ContractClient { /** Gets the contract address and ensures that the contract has initted. */ async getAddress() { await this.init() - return this._contractAddress + // calling init first ensures _contactAddress is present + return this._contractAddress as string } /** @@ -170,7 +174,7 @@ export class ContractClient { * The contract can then be invoked with .call() or be passed to a sendTransaction. * @param methodName the name of the contract method */ - async getMethod(methodName: string, ...args: unknown[]) { + async getMethod(methodName: string, ...args: any[]) { await this.init() if (!(methodName in this._contract?.methods)) { throw new Error( @@ -217,6 +221,7 @@ export class ContractClient { async getContract() { await this.init() - return this._contract + // init ensures _contract is set + return this._contract as Contract } } diff --git a/libs/src/services/contracts/GovernedContractClient.ts b/libs/src/services/contracts/GovernedContractClient.ts new file mode 100644 index 00000000000..60ce4df130b --- /dev/null +++ b/libs/src/services/contracts/GovernedContractClient.ts @@ -0,0 +1,53 @@ +import type { ContractABI } from '../../utils' +import type { GovernanceClient } from '../ethContracts/governanceClient' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { Web3Manager } from '../web3Manager' +import type { GetRegistryAddress } from './ContractClient' + +import { ContractClient } from './ContractClient' + +/** + * Contract class that extends a ContractClient and provides an interface + * to retrieve governed methods that cannot be executed directly. + */ +export class GovernedContractClient extends ContractClient { + governanceClient: GovernanceClient + + constructor( + web3Manager: Web3Manager | EthWeb3Manager, + contractABI: ContractABI['abi'], + contractRegistryKey: string, + getRegistryAddress: GetRegistryAddress, + governanceClient: GovernanceClient, + logger = console + ) { + super( + web3Manager, + contractABI, + contractRegistryKey, + getRegistryAddress, + logger + ) + this.governanceClient = governanceClient + } + + /** + * Gets a governed version of a method and allows a single transaction + * to be sent to the governance client with the appropriate payload. + * Similar to `getMethod` + */ + async getGovernedMethod(methodName: string, ...args: unknown[]) { + const contractMethod = await this.getMethod(methodName, ...args) + const { signature, callData } = + this.governanceClient.getSignatureAndCallData(methodName, contractMethod) + const contractRegistryKey = this.web3Manager + .getWeb3() + .utils.utf8ToHex(this.contractRegistryKey) + const method = await this.governanceClient.guardianExecuteTransaction( + contractRegistryKey, + signature, + callData + ) + return method + } +} diff --git a/libs/src/services/contracts/ProviderSelection.ts b/libs/src/services/contracts/ProviderSelection.ts index 8df56f716f6..ffecbeee385 100644 --- a/libs/src/services/contracts/ProviderSelection.ts +++ b/libs/src/services/contracts/ProviderSelection.ts @@ -1,5 +1,6 @@ import Web3 from 'web3' import { ServiceSelection } from '../../service-selection' +import type { Web3Manager } from '../web3Manager' import type { ContractClient } from './ContractClient' /** @@ -24,13 +25,14 @@ export class ProviderSelection extends ServiceSelection { * @param client object used for making transaction calls */ override async select(client: ContractClient) { - const web3Manager = client.web3Manager + const web3Manager = client.web3Manager as Web3Manager const filteredServices = this.filterOutKnownUnhealthy( await this.getServices() ) const web3 = new Web3( web3Manager.provider(filteredServices[0] as string, 10000) ) + web3Manager.setWeb3(web3) } diff --git a/libs/src/services/discoveryProvider/DiscoveryProvider.test.ts b/libs/src/services/discoveryProvider/DiscoveryProvider.test.ts new file mode 100644 index 00000000000..5fac623c667 --- /dev/null +++ b/libs/src/services/discoveryProvider/DiscoveryProvider.test.ts @@ -0,0 +1,74 @@ +export {} +// const DiscoveryProvider = require('.') +// const helpers = require('../../../tests/helpers') +// const { DISCOVERY_PROVIDER_TIMESTAMP } = require('./constants') +// +// let audiusInstance = helpers.audiusInstance +// +// // eslint-disable-next-line mocha/no-skipped-tests -- probably should be an integration test +// xit('will reselect a healthy disc prov if initialized disc prov becomes unhealthy', async () => { +// const LocalStorage = require('node-localstorage').LocalStorage +// const localStorage = new LocalStorage('./local-storage') +// +// // Make healthyThenUnhealthy respond with 200 initially +// const healthyThenUnhealthy = 'https://healthyThenUnhealthy.audius.co' +// const healthyThenUnhealthyInterceptor = nock(healthyThenUnhealthy) +// .persist() +// .get(uri => true) // hitting any route will respond with 200 +// .reply(200, { +// data: { +// service: 'discovery-node', +// version: '1.2.3', +// block_difference: 0 +// } +// }) +// +// const healthy = 'https://healthy.audius.co' +// nock(healthy) +// .persist() +// .get(uri => true) +// .delay(100) +// .reply(200, { +// data: { +// service: 'discovery-node', +// version: '1.2.3', +// block_difference: 0 +// } +// }) +// +// // Initialize libs and then set disc prov instance with eth contracts mock +// await audiusInstance.init() +// localStorage.removeItem(DISCOVERY_PROVIDER_TIMESTAMP) +// const mockDP = new DiscoveryProvider( +// null, // whitelist +// null, // blacklist +// audiusInstance.userStateManager, +// mockEthContracts([healthyThenUnhealthy, healthy], '1.2.3'), +// audiusInstance.web3Manager, +// null, // reselectTimeout +// null // selectionCallback +// ) +// await mockDP.init() +// audiusInstance.discoveryProvider = mockDP +// +// // Check that healthyThenUnhealthy was chosen and set in local stoarge +// const discProvLocalStorageData1 = JSON.parse(localStorage.getItem(DISCOVERY_PROVIDER_TIMESTAMP)) +// assert.strictEqual(audiusInstance.discoveryProvider.discoveryProviderEndpoint, healthyThenUnhealthy) +// assert.strictEqual(discProvLocalStorageData1.endpoint, healthyThenUnhealthy) +// +// // Then make healthyThenUnhealthy fail on successive requests +// // To remove any persistent interceptors: https://stackoverflow.com/a/59885361 +// nock.removeInterceptor(healthyThenUnhealthyInterceptor.interceptors[0]) +// nock(healthyThenUnhealthy) +// .persist() +// .get(uri => true) +// .reply(500, { unhealthy: true }) +// +// // Make a libs request to disc prov; should use healthy +// await audiusInstance.discoveryProvider.getUsers(1) +// const discProvLocalStorageData2 = JSON.parse(localStorage.getItem(DISCOVERY_PROVIDER_TIMESTAMP)) +// +// // Check that healthy was chosen and set in local stoarge +// assert.strictEqual(audiusInstance.discoveryProvider.discoveryProviderEndpoint, healthy) +// assert.strictEqual(discProvLocalStorageData2.endpoint, healthy) +// }) diff --git a/libs/src/services/ethContracts/EthContracts.ts b/libs/src/services/ethContracts/EthContracts.ts new file mode 100644 index 00000000000..ba88b84d851 --- /dev/null +++ b/libs/src/services/ethContracts/EthContracts.ts @@ -0,0 +1,284 @@ +export {} +// import semver from 'semver' +// import { AudiusTokenClient } from './audiusTokenClient' +// import { RegistryClient } from './registryClient' +// import { GovernanceClient } from './governanceClient' +// import { ServiceTypeManagerClient } from './serviceTypeManagerClient' +// import { ServiceProviderFactoryClient } from './serviceProviderFactoryClient' +// import { StakingProxyClient } from './stakingProxyClient' +// import { DelegateManagerClient } from './delegateManagerClient' +// import { ClaimsManagerClient } from './claimsManagerClient' +// import { ClaimDistributionClient } from './claimDistributionClient' +// import { WormholeClient } from './wormholeClient' +// import { EthRewardsManagerClient } from './ethRewardsManagerClient' +// import { TrustedNotifierManagerClient } from './trustedNotifierManagerClient' +// import { Utils } from '../../utils' +// +// const AudiusTokenABI = Utils.importEthContractABI('AudiusToken.json').abi +// const RegistryABI = Utils.importEthContractABI('Registry.json').abi +// const GovernanceABI = Utils.importEthContractABI('Governance.json').abi +// const ServiceTypeManagerABI = Utils.importEthContractABI('ServiceTypeManager.json').abi +// const ServiceProviderFactoryABI = Utils.importEthContractABI('ServiceProviderFactory.json').abi +// const StakingABI = Utils.importEthContractABI('Staking.json').abi +// const DelegateManagerABI = Utils.importEthContractABI('DelegateManager.json').abi +// const ClaimsManagerABI = Utils.importEthContractABI('ClaimsManager.json').abi +// const ClaimDistributionABI = Utils.importEthContractABI('AudiusClaimDistributor.json').abi +// const WormholeClientABI = Utils.importEthContractABI('WormholeClient.json').abi +// const EthRewardsManagerABI = Utils.importEthContractABI('EthRewardsManager.json').abi +// const TrustedNotifierManagerABI = Utils.importEthContractABI('TrustedNotifierManager.json').abi +// +// const GovernanceRegistryKey = 'Governance' +// const ServiceTypeManagerProxyKey = 'ServiceTypeManagerProxy' +// const ServiceProviderFactoryRegistryKey = 'ServiceProviderFactory' +// const StakingProxyKey = 'StakingProxy' +// const DelegateManagerRegistryKey = 'DelegateManager' +// const ClaimsManagerProxyKey = 'ClaimsManagerProxy' +// const ClaimDistributionRegistryKey = 'ClaimDistribution' +// const EthRewardsManagerProxyKey = 'EthRewardsManagerProxy' +// const TrustedNotifierManagerProxyKey = 'TrustedNotifierManagerProxy' +// +// const TWO_MINUTES = 2 * 60 * 1000 +// +// const serviceType = Object.freeze({ +// DISCOVERY_PROVIDER: 'discovery-node', +// CREATOR_NODE: 'content-node' +// }) +// const serviceTypeList = Object.values(serviceType) +// +// class EthContracts { +// constructor ( +// ethWeb3Manager, +// tokenContractAddress, +// registryAddress, +// claimDistributionContractAddress, +// wormholeContractAddress, +// isServer, +// isDebug = false +// ) { +// this.ethWeb3Manager = ethWeb3Manager +// this.tokenContractAddress = tokenContractAddress +// this.claimDistributionContractAddress = claimDistributionContractAddress +// this.wormholeContractAddress = wormholeContractAddress +// this.registryAddress = registryAddress +// this.isServer = isServer +// this.isDebug = isDebug +// this.expectedServiceVersions = null +// +// this.AudiusTokenClient = new AudiusTokenClient( +// this.ethWeb3Manager, +// AudiusTokenABI, +// this.tokenContractAddress +// ) +// this.RegistryClient = new RegistryClient( +// this.ethWeb3Manager, +// RegistryABI, +// this.registryAddress +// ) +// this.getRegistryAddressForContract = this.getRegistryAddressForContract.bind(this) +// +// this.StakingProxyClient = new StakingProxyClient( +// this.ethWeb3Manager, +// StakingABI, +// StakingProxyKey, +// this.getRegistryAddressForContract, +// this.AudiusTokenClient +// ) +// +// this.GovernanceClient = new GovernanceClient( +// this.ethWeb3Manager, +// GovernanceABI, +// GovernanceRegistryKey, +// this.getRegistryAddressForContract, +// this.AudiusTokenClient, +// this.StakingProxyClient +// ) +// +// this.ClaimsManagerClient = new ClaimsManagerClient( +// this.ethWeb3Manager, +// ClaimsManagerABI, +// ClaimsManagerProxyKey, +// this.getRegistryAddressForContract +// ) +// +// this.EthRewardsManagerClient = new EthRewardsManagerClient( +// this.ethWeb3Manager, +// EthRewardsManagerABI, +// EthRewardsManagerProxyKey, +// this.getRegistryAddressForContract +// ) +// +// this.ServiceTypeManagerClient = new ServiceTypeManagerClient( +// this.ethWeb3Manager, +// ServiceTypeManagerABI, +// ServiceTypeManagerProxyKey, +// this.getRegistryAddressForContract, +// this.GovernanceClient +// ) +// +// this.ServiceProviderFactoryClient = new ServiceProviderFactoryClient( +// this.ethWeb3Manager, +// ServiceProviderFactoryABI, +// ServiceProviderFactoryRegistryKey, +// this.getRegistryAddressForContract, +// this.AudiusTokenClient, +// this.StakingProxyClient, +// this.GovernanceClient, +// this.isDebug +// ) +// +// this.DelegateManagerClient = new DelegateManagerClient( +// this.ethWeb3Manager, +// DelegateManagerABI, +// DelegateManagerRegistryKey, +// this.getRegistryAddressForContract, +// this.AudiusTokenClient, +// this.StakingProxyClient, +// this.GovernanceClient +// ) +// +// if (this.claimDistributionContractAddress) { +// this.ClaimDistributionClient = new ClaimDistributionClient( +// this.ethWeb3Manager, +// ClaimDistributionABI, +// ClaimDistributionRegistryKey, +// this.getRegistryAddressForContract, +// this.claimDistributionContractAddress +// ) +// } +// +// this.WormholeClient = new WormholeClient( +// this.ethWeb3Manager, +// WormholeClientABI, +// this.wormholeContractAddress, +// this.AudiusTokenClient +// ) +// +// this.TrustedNotifierManagerClient = new TrustedNotifierManagerClient( +// this.ethWeb3Manager, +// TrustedNotifierManagerABI, +// TrustedNotifierManagerProxyKey, +// this.getRegistryAddressForContract, +// this.GovernanceClient +// ) +// +// this.contractClients = [ +// this.ServiceTypeManagerClient, +// this.StakingProxyClient, +// this.ServiceProviderFactoryClient +// ] +// +// // Whether or not we are running in `regressed` mode, meaning we were +// // unable to select a discovery provider that was up-to-date. Clients may +// // want to consider blocking writes. +// this._regressedMode = false +// } +// +// async init () { +// if (!this.ethWeb3Manager || !this.tokenContractAddress || !this.registryAddress) throw new Error('Failed to initialize EthContracts') +// +// if (this.isServer) { +// await Promise.all(this.contractClients.map(client => client.init())) +// } +// } +// +// /** +// * Estabilishes that connection to discovery providers has regressed +// */ +// enterRegressedMode () { +// console.info('Entering regressed mode') +// this._regressedMode = true +// setTimeout(() => { +// console.info('Leaving regressed mode') +// this._regressedMode = false +// }, TWO_MINUTES) +// } +// +// isInRegressedMode () { +// return this._regressedMode +// } +// +// async getRegistryAddressForContract (contractName) { +// // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names +// this.contracts = this.contracts || { [this.registryAddress]: 'registry' } +// this.contractAddresses = this.contractAddresses || { registry: this.registryAddress } +// if (!this.contractAddresses[contractName]) { +// const address = await this.RegistryClient.getContract(contractName) +// this.contracts[address] = contractName +// this.contractAddresses[contractName] = address +// } +// return this.contractAddresses[contractName] +// } +// +// async getCurrentVersion (serviceType) { +// try { +// const version = await this.ServiceTypeManagerClient.getCurrentVersion(serviceType) +// return version +// } catch (e) { +// console.log(`Error retrieving version for ${serviceType}`) +// return null +// } +// } +// +// /* +// * Determine the latest version for deployed services such as discovery provider and cache +// */ +// async getExpectedServiceVersions () { +// const versions = await Promise.all( +// serviceTypeList.map(serviceType => this.getCurrentVersion(serviceType)) +// ) +// const expectedVersions = serviceTypeList.reduce((map, serviceType, i) => { +// if (versions[i]) { +// map[serviceType] = versions[i] +// } +// return map +// }, {}) +// return expectedVersions +// } +// +// /* +// * Determine whether major and minor versions match for two version strings +// * Version string 2 must have equivalent major/minor versions and a patch >= version1 +// * @param {string} version string 1 +// * @param {string} version string 2 +// */ +// isValidSPVersion (version1, version2) { +// return ( +// semver.major(version1) === semver.major(version2) && +// semver.minor(version1) === semver.minor(version2) && +// semver.patch(version2) >= semver.patch(version1) +// ) +// } +// +// /** +// * Determines whether the major and minor versions are equal +// * @param {string} version string 1 +// * @param {string} version string 2 +// */ +// hasSameMajorAndMinorVersion (version1, version2) { +// return ( +// semver.major(version1) === semver.major(version2) && +// semver.minor(version1) === semver.minor(version2) +// ) +// } +// +// async getServiceProviderList (spType) { +// return this.ServiceProviderFactoryClient.getServiceProviderList(spType) +// } +// +// async getNumberOfVersions (spType) { +// return this.ServiceTypeManagerClient.getNumberOfVersions(spType) +// } +// +// async getVersion (spType, queryIndex) { +// return this.ServiceTypeManagerClient.getVersion(spType, queryIndex) +// } +// +// async getServiceTypeInfo (spType) { +// return this.ServiceTypeManagerClient.getServiceTypeInfo(spType) +// } +// } +// +// module.exports = EthContracts +// module.exports.serviceType = serviceType +// +// diff --git a/libs/src/services/ethContracts/audiusTokenClient.js b/libs/src/services/ethContracts/audiusTokenClient.ts similarity index 61% rename from libs/src/services/ethContracts/audiusTokenClient.js rename to libs/src/services/ethContracts/audiusTokenClient.ts index 38c01dabc2b..c575710ff7a 100644 --- a/libs/src/services/ethContracts/audiusTokenClient.js +++ b/libs/src/services/ethContracts/audiusTokenClient.ts @@ -1,38 +1,60 @@ -class AudiusTokenClient { - constructor (ethWeb3Manager, contractABI, contractAddress) { +import type Web3 from 'web3' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { Contract } from 'web3-eth-contract' +import type { AbiItem } from 'web3-utils' +import type Wallet from 'ethereumjs-wallet' + +export class AudiusTokenClient { + ethWeb3Manager: EthWeb3Manager + contractABI: AbiItem + contractAddress: string + web3: Web3 + AudiusTokenContract: Contract + bustCacheNonce: number + + constructor( + ethWeb3Manager: EthWeb3Manager, + contractABI: AbiItem, + contractAddress: string + ) { this.ethWeb3Manager = ethWeb3Manager this.contractABI = contractABI this.contractAddress = contractAddress this.web3 = this.ethWeb3Manager.getWeb3() - this.AudiusTokenContract = new this.web3.eth.Contract(this.contractABI, this.contractAddress) + this.AudiusTokenContract = new this.web3.eth.Contract( + this.contractABI, + this.contractAddress + ) this.bustCacheNonce = 0 } /* ------- GETTERS ------- */ - async bustCache () { + async bustCache() { this.bustCacheNonce += 1 } - async balanceOf (account) { + async balanceOf(account: string) { let args if (this.bustCacheNonce > 0) { args = { _audiusBustCache: this.bustCacheNonce } } - const balance = await this.AudiusTokenContract.methods.balanceOf(account).call(args) + const balance = await this.AudiusTokenContract.methods + .balanceOf(account) + .call(args) return this.web3.utils.toBN(balance) } // Get the name of the contract - async name () { + async name() { const name = await this.AudiusTokenContract.methods.name().call() return name } // Get the name of the contract - async nonces (wallet) { + async nonces(wallet: Wallet) { // Pass along a unique param so the nonce value is always not cached const nonce = await this.AudiusTokenContract.methods.nonces(wallet).call({ _audiusBustCache: Date.now() @@ -43,14 +65,26 @@ class AudiusTokenClient { /* ------- SETTERS ------- */ - async transfer (recipient, amount) { - const contractMethod = this.AudiusTokenContract.methods.transfer(recipient, amount) + async transfer(recipient: string, amount: number) { + const contractMethod = this.AudiusTokenContract.methods.transfer( + recipient, + amount + ) const tx = await this.ethWeb3Manager.sendTransaction(contractMethod) return { txReceipt: tx } } - async transferFrom (owner, recipient, relayer, amount) { - const method = this.AudiusTokenContract.methods.transferFrom(owner, recipient, amount) + async transferFrom( + owner: Wallet, + recipient: string, + relayer: Wallet, + amount: number + ) { + const method = this.AudiusTokenContract.methods.transferFrom( + owner, + recipient, + amount + ) const tx = await this.ethWeb3Manager.relayTransaction( method, this.contractAddress, @@ -62,14 +96,14 @@ class AudiusTokenClient { } // Permit meta transaction of balance transfer - async permit ( - owner, // address - spender, // address - value, // uint - deadline, // uint - v, // uint8 - r, // bytes32 - s // bytes32 + async permit( + owner: Wallet, // address + spender: Wallet, // address + value: number, // uint + deadline: number, // uint + v: number, // uint8 + r: BinaryData, // bytes32 + s: BinaryData // bytes32 ) { const contractMethod = this.AudiusTokenContract.methods.permit( owner, @@ -92,8 +126,11 @@ class AudiusTokenClient { // Allow spender to withdraw from calling account up to value amount // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md - async approve (spender, value, privateKey = null) { - const contractMethod = this.AudiusTokenContract.methods.approve(spender, value) + async approve(spender: string, value: number, privateKey = null) { + const contractMethod = this.AudiusTokenContract.methods.approve( + spender, + value + ) let tx if (privateKey === null) { tx = await this.ethWeb3Manager.sendTransaction(contractMethod) @@ -107,7 +144,12 @@ class AudiusTokenClient { return { txReceipt: tx } } - async approveProxyTokens (owner, spender, value, relayer) { + async approveProxyTokens( + owner: Wallet, + spender: string, + value: number, + relayer: Wallet + ) { const method = this.AudiusTokenContract.methods.approve(spender, value) const tx = await this.ethWeb3Manager.relayTransaction( method, @@ -119,5 +161,3 @@ class AudiusTokenClient { return { txReceipt: tx } } } - -module.exports = AudiusTokenClient diff --git a/libs/src/services/ethContracts/claimDistributionClient.js b/libs/src/services/ethContracts/claimDistributionClient.js deleted file mode 100644 index c0c19e4e5d3..00000000000 --- a/libs/src/services/ethContracts/claimDistributionClient.js +++ /dev/null @@ -1,45 +0,0 @@ -const { ContractClient } = require('../contracts/ContractClient') - -class ClaimDistributionClient extends ContractClient { - // ===================== Contract Methods ===================== - /** - * Calls the contract method to check if the claim index has been claimed - * @param {number} index - * @returns {boolean} isClaimed - */ - async isClaimed (index) { - const method = await this.getMethod( - 'isClaimed', - index - ) - const isClaimed = await method.call() - return isClaimed - } - - /** - * Proxies the calls the contract method to make a claim - * @param {number} index - * @param {string} account - * @param {string} amount - * @param {Array} merkleProof - * @returns {Object} transaction - */ - async claim (index, account, amount, merkleProof) { - const method = await this.getMethod( - 'claim', - index, - account, - amount, - merkleProof - ) - const contractAddress = await this.getAddress() - const tx = await this.web3Manager.relayTransaction( - method, - contractAddress, - account - ) - return tx - } -} - -module.exports = ClaimDistributionClient diff --git a/libs/src/services/ethContracts/claimDistributionClient.ts b/libs/src/services/ethContracts/claimDistributionClient.ts new file mode 100644 index 00000000000..12703235aa5 --- /dev/null +++ b/libs/src/services/ethContracts/claimDistributionClient.ts @@ -0,0 +1,45 @@ +import type Wallet from 'ethereumjs-wallet' +import { ContractClient } from '../contracts/ContractClient' +import type { EthWeb3Manager } from '../ethWeb3Manager' + +export class ClaimDistributionClient extends ContractClient { + // ===================== Contract Methods ===================== + /** + * Calls the contract method to check if the claim index has been claimed + */ + async isClaimed(index: number) { + const method = await this.getMethod('isClaimed', index) + const isClaimed = await method.call() + return isClaimed + } + + /** + * Proxies the calls the contract method to make a claim + * @param index + * @param account + * @param amount + * @param merkleProof + * @returns transaction + */ + async claim( + index: number, + account: Wallet, + amount: string, + merkleProof: string[] + ) { + const method = await this.getMethod( + 'claim', + index, + account, + amount, + merkleProof + ) + const contractAddress = await this.getAddress() + const tx = await (this.web3Manager as EthWeb3Manager).relayTransaction( + method, + contractAddress, + account + ) + return tx + } +} diff --git a/libs/src/services/ethContracts/claimsManagerClient.js b/libs/src/services/ethContracts/claimsManagerClient.js deleted file mode 100644 index 6b1c42131ab..00000000000 --- a/libs/src/services/ethContracts/claimsManagerClient.js +++ /dev/null @@ -1,124 +0,0 @@ -const { Utils } = require('../../utils') -const { ContractClient } = require('../contracts/ContractClient') - -class ClaimsManagerClient extends ContractClient { - /* ------- GETTERS ------- */ - - // Get the duration of a funding round in blocks - async getFundingRoundBlockDiff () { - const method = await this.getMethod( - 'getFundingRoundBlockDiff' - ) - const info = await method.call() - return parseInt(info) - } - - // Get the last block where a funding round was initiated - async getLastFundedBlock () { - const method = await this.getMethod( - 'getLastFundedBlock' - ) - const info = await method.call() - return parseInt(info) - } - - // Get the amount funded per round in wei - async getFundsPerRound () { - const method = await this.getMethod( - 'getFundsPerRound' - ) - const info = await method.call() - return Utils.toBN(info) - } - - // Get the total amount claimed in the current round - async getTotalClaimedInRound () { - const method = await this.getMethod( - 'getTotalClaimedInRound' - ) - const info = await method.call() - return Utils.toBN(info) - } - - // Get the Governance address - async getGovernanceAddress () { - const method = await this.getMethod( - 'getGovernanceAddress' - ) - const info = await method.call() - return info - } - - // Get the ServiceProviderFactory address - async getServiceProviderFactoryAddress () { - const method = await this.getMethod( - 'getServiceProviderFactoryAddress' - ) - const info = await method.call() - return info - } - - // Get the DelegateManager address - async getDelegateManagerAddress () { - const method = await this.getMethod( - 'getDelegateManagerAddress' - ) - const info = await method.call() - return info - } - - // Get the Staking address - async getStakingAddress () { - const method = await this.getMethod( - 'getStakingAddress' - ) - const info = await method.call() - return info - } - - // Returns boolean indicating whether a claim is considered pending - async claimPending (address) { - const method = await this.getMethod( - 'claimPending', - address - ) - const info = await method.call() - return info - } - - // Returns boolean indicating whether a claim is considered pending - async initiateRound (txRetries = 5) { - const method = await this.getMethod( - 'initiateRound' - ) - return this.web3Manager.sendTransaction( - method, - null, - null, - txRetries - ) - } - - // Fetches the claim processed events - async getClaimProcessedEvents ({ - claimer, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('ClaimProcessed', { - fromBlock: queryStartBlock, - filter: { - _claimer: claimer - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - claimer: event.returnValues._claimer, - rewards: Utils.toBN(event.returnValues._rewards), - oldTotal: Utils.toBN(event.returnValues._oldTotal), - newTotal: Utils.toBN(event.returnValues._newTotal) - })) - } -} - -module.exports = ClaimsManagerClient diff --git a/libs/src/services/ethContracts/claimsManagerClient.ts b/libs/src/services/ethContracts/claimsManagerClient.ts new file mode 100644 index 00000000000..2cb939f4fd1 --- /dev/null +++ b/libs/src/services/ethContracts/claimsManagerClient.ts @@ -0,0 +1,99 @@ +import { Utils } from '../../utils' +import { ContractClient } from '../contracts/ContractClient' + +export class ClaimsManagerClient extends ContractClient { + /* ------- GETTERS ------- */ + + // Get the duration of a funding round in blocks + async getFundingRoundBlockDiff() { + const method = await this.getMethod('getFundingRoundBlockDiff') + const info = await method.call() + return parseInt(info) + } + + // Get the last block where a funding round was initiated + async getLastFundedBlock() { + const method = await this.getMethod('getLastFundedBlock') + const info = await method.call() + return parseInt(info) + } + + // Get the amount funded per round in wei + async getFundsPerRound() { + const method = await this.getMethod('getFundsPerRound') + const info = await method.call() + return Utils.toBN(info) + } + + // Get the total amount claimed in the current round + async getTotalClaimedInRound() { + const method = await this.getMethod('getTotalClaimedInRound') + const info = await method.call() + return Utils.toBN(info) + } + + // Get the Governance address + async getGovernanceAddress() { + const method = await this.getMethod('getGovernanceAddress') + const info = await method.call() + return info + } + + // Get the ServiceProviderFactory address + async getServiceProviderFactoryAddress() { + const method = await this.getMethod('getServiceProviderFactoryAddress') + const info = await method.call() + return info + } + + // Get the DelegateManager address + async getDelegateManagerAddress() { + const method = await this.getMethod('getDelegateManagerAddress') + const info = await method.call() + return info + } + + // Get the Staking address + async getStakingAddress() { + const method = await this.getMethod('getStakingAddress') + const info = await method.call() + return info + } + + // Returns boolean indicating whether a claim is considered pending + async claimPending(address: string) { + const method = await this.getMethod('claimPending', address) + const info = await method.call() + return info + } + + // Returns boolean indicating whether a claim is considered pending + async initiateRound(txRetries = 5) { + const method = await this.getMethod('initiateRound') + return await this.web3Manager.sendTransaction(method, null, null, txRetries) + } + + // Fetches the claim processed events + async getClaimProcessedEvents({ + claimer, + queryStartBlock = 0 + }: { + claimer: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents('ClaimProcessed', { + fromBlock: queryStartBlock, + filter: { + _claimer: claimer + } + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + claimer: event.returnValues['_claimer'], + rewards: Utils.toBN(event.returnValues['_rewards']), + oldTotal: Utils.toBN(event.returnValues['_oldTotal']), + newTotal: Utils.toBN(event.returnValues['_newTotal']) + })) + } +} diff --git a/libs/src/services/ethContracts/delegateManagerClient.ts b/libs/src/services/ethContracts/delegateManagerClient.ts new file mode 100644 index 00000000000..91e76583c38 --- /dev/null +++ b/libs/src/services/ethContracts/delegateManagerClient.ts @@ -0,0 +1,451 @@ +import { ContractABI, Utils } from '../../utils' +import type { GetRegistryAddress } from '../contracts/ContractClient' +import { GovernedContractClient } from '../contracts/GovernedContractClient' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { AudiusTokenClient } from './audiusTokenClient' +import type { GovernanceClient } from './governanceClient' +import type { StakingProxyClient } from './stakingProxyClient' + +type GetEvent = { + delegator: string + serviceProvider: string + queryStartBlock: number +} + +export class DelegateManagerClient extends GovernedContractClient { + audiusTokenClient: AudiusTokenClient + stakingProxyClient: StakingProxyClient + + constructor( + ethWeb3Manager: EthWeb3Manager, + contractABI: ContractABI['abi'], + contractRegistryKey: string, + getRegistryAddress: GetRegistryAddress, + audiusTokenClient: AudiusTokenClient, + stakingProxyClient: StakingProxyClient, + governanceClient: GovernanceClient, + logger = console + ) { + super( + ethWeb3Manager, + contractABI, + contractRegistryKey, + getRegistryAddress, + governanceClient, + logger + ) + this.audiusTokenClient = audiusTokenClient + this.stakingProxyClient = stakingProxyClient + } + + async delegateStake(targetSP: string, amount: number) { + // Approve token transfer operation + const contractAddress = await this.stakingProxyClient.getAddress() + const tx0 = await this.audiusTokenClient.approve(contractAddress, amount) + const method = await this.getMethod('delegateStake', targetSP, amount) + const tx = await this.web3Manager.sendTransaction(method) + + const returnValues = tx.events?.['IncreaseDelegatedStake']?.returnValues + + return { + txReceipt: tx, + tokenApproveReceipt: tx0, + delegator: returnValues?._delegator, + serviceProvider: returnValues?._serviceProvider, + increaseAmount: Utils.toBN(returnValues?._increaseAmount) + } + } + + /* Pass either delegator or serviceProvider filters */ + async getIncreaseDelegateStakeEvents({ + delegator, + serviceProvider, + queryStartBlock = 0 + }: GetEvent) { + const contract = await this.getContract() + const filter: { _delegator?: string; _serviceProvider?: string } = {} + if (delegator) { + filter._delegator = delegator + } else { + filter._serviceProvider = serviceProvider + } + const events = await contract.getPastEvents('IncreaseDelegatedStake', { + fromBlock: queryStartBlock, + filter + }) + + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + delegator: event.returnValues['_delegator'], + increaseAmount: Utils.toBN(event.returnValues['_increaseAmount']), + serviceProvider: event.returnValues['_serviceProvider'] + })) + } + + async getDecreaseDelegateStakeEvents({ + delegator, + serviceProvider, + queryStartBlock = 0 + }: GetEvent) { + const contract = await this.getContract() + const filter: { _delegator?: string; _serviceProvider?: string } = {} + if (delegator) { + filter._delegator = delegator + } + if (serviceProvider) { + filter._serviceProvider = serviceProvider + } + + const events = await contract.getPastEvents( + 'UndelegateStakeRequestEvaluated', + { + fromBlock: queryStartBlock, + filter + } + ) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + delegator: event.returnValues['_delegator'], + amount: Utils.toBN(event.returnValues['_amount']), + serviceProvider: event.returnValues['_serviceProvider'] + })) + } + + async getUndelegateStakeRequestedEvents({ + delegator, + serviceProvider, + queryStartBlock = 0 + }: GetEvent) { + const contract = await this.getContract() + const filter: { _delegator?: string; _serviceProvider?: string } = {} + if (delegator) { + filter._delegator = delegator + } + if (serviceProvider) { + filter._serviceProvider = serviceProvider + } + + const events = await contract.getPastEvents('UndelegateStakeRequested', { + fromBlock: queryStartBlock, + filter + }) + + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + lockupExpiryBlock: parseInt(event.returnValues['_lockupExpiryBlock']), + delegator: event.returnValues['_delegator'], + amount: Utils.toBN(event.returnValues['_amount']), + serviceProvider: event.returnValues['_serviceProvider'] + })) + } + + async getUndelegateStakeCancelledEvents({ + delegator, + serviceProvider, + queryStartBlock = 0 + }: GetEvent) { + const contract = await this.getContract() + const filter: { _delegator?: string; _serviceProvider?: string } = {} + if (delegator) { + filter._delegator = delegator + } + if (serviceProvider) { + filter._serviceProvider = serviceProvider + } + + const events = await contract.getPastEvents( + 'UndelegateStakeRequestCancelled', + { + fromBlock: queryStartBlock, + filter + } + ) + + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + delegator: event.returnValues['_delegator'], + amount: Utils.toBN(event.returnValues['_amount']), + serviceProvider: event.returnValues['_serviceProvider'] + })) + } + + async getClaimEvents({ + claimer, + queryStartBlock = 0 + }: { + claimer: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents('Claim', { + fromBlock: queryStartBlock, + filter: { + _claimer: claimer + } + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + claimer: event.returnValues['_claimer'], + rewards: Utils.toBN(event.returnValues['_rewards']), + newTotal: Utils.toBN(event.returnValues['_newTotal']) + })) + } + + async getSlashEvents({ + target, + queryStartBlock = 0 + }: { + target: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents('Slash', { + fromBlock: queryStartBlock, + filter: { + _target: target + } + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + target: event.returnValues['_target'], + amount: Utils.toBN(event.returnValues['_amount']), + newTotal: Utils.toBN(event.returnValues['_newTotal']) + })) + } + + async getDelegatorRemovedEvents({ + target, + queryStartBlock = 0 + }: { + target: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents('DelegatorRemoved', { + fromBlock: queryStartBlock, + filter: { + _target: target + } + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + serviceProvider: event.returnValues['_serviceProvider'], + delegator: event.returnValues['_delegator'], + unstakedAmount: Utils.toBN(event.returnValues['_unstakedAmount']) + })) + } + + async requestUndelegateStake(targetSP: string, amount: number) { + const method = await this.getMethod( + 'requestUndelegateStake', + targetSP, + amount + ) + return await this.web3Manager.sendTransaction(method) + } + + async cancelUndelegateStakeRequest() { + const method = await this.getMethod('cancelUndelegateStakeRequest') + return await this.web3Manager.sendTransaction(method) + } + + async undelegateStake() { + const method = await this.getMethod('undelegateStake') + + const tx = await this.web3Manager.sendTransaction(method) + + const returnValues = + tx.events?.['UndelegateStakeRequestEvaluated']?.returnValues + + return { + txReceipt: tx, + delegator: returnValues._delegator, + serviceProvider: returnValues._serviceProvider, + decreaseAmount: Utils.toBN(returnValues._amount) + } + } + + async claimRewards(serviceProvider: string, txRetries = 5) { + const method = await this.getMethod('claimRewards', serviceProvider) + return await this.web3Manager.sendTransaction(method, null, null, txRetries) + } + + async requestRemoveDelegator(serviceProvider: string, delegator: string) { + const method = await this.getMethod( + 'requestRemoveDelegator', + serviceProvider, + delegator + ) + return await this.web3Manager.sendTransaction(method) + } + + async cancelRemoveDelegatorRequest( + serviceProvider: string, + delegator: string + ) { + const method = await this.getMethod( + 'cancelRemoveDelegatorRequest', + serviceProvider, + delegator + ) + return await this.web3Manager.sendTransaction(method) + } + + async removeDelegator(serviceProvider: string, delegator: string) { + const method = await this.getMethod( + 'removeDelegator', + serviceProvider, + delegator + ) + const tx = await this.web3Manager.sendTransaction(method) + const returnValues = + tx.events?.['RemoveDelegatorRequestEvaluated']?.returnValues + return { + txReceipt: tx, + delegator: returnValues._delegator, + serviceProvider: returnValues._serviceProvider, + unstakedAmount: Utils.toBN(returnValues._unstakedAmount) + } + } + + // ========================================= View Functions ========================================= + + async getDelegatorsList(serviceProvider: string) { + const method = await this.getMethod('getDelegatorsList', serviceProvider) + const info = await method.call() + return info + } + + async getTotalDelegatedToServiceProvider(serviceProvider: string) { + const method = await this.getMethod( + 'getTotalDelegatedToServiceProvider', + serviceProvider + ) + const info = await method.call() + return Utils.toBN(info) + } + + async getTotalDelegatorStake(delegator: string) { + const method = await this.getMethod('getTotalDelegatorStake', delegator) + const info = await method.call() + return Utils.toBN(info) + } + + async getTotalLockedDelegationForServiceProvider(serviceProvider: string) { + const method = await this.getMethod( + 'getTotalLockedDelegationForServiceProvider', + serviceProvider + ) + const info = await method.call() + return Utils.toBN(info) + } + + async getDelegatorStakeForServiceProvider( + delegator: string, + serviceProvider: string + ) { + const method = await this.getMethod( + 'getDelegatorStakeForServiceProvider', + delegator, + serviceProvider + ) + const info = await method.call() + return Utils.toBN(info) + } + + async getPendingUndelegateRequest(delegator: string) { + const method = await this.getMethod( + 'getPendingUndelegateRequest', + delegator + ) + const info = await method.call() + return { + amount: Utils.toBN(info.amount), + lockupExpiryBlock: parseInt(info.lockupExpiryBlock), + target: info.target + } + } + + async getPendingRemoveDelegatorRequest( + serviceProvider: string, + delegator: string + ) { + const method = await this.getMethod( + 'getPendingRemoveDelegatorRequest', + serviceProvider, + delegator + ) + const info = await method.call() + return { lockupExpiryBlock: parseInt(info) } + } + + async getUndelegateLockupDuration() { + const method = await this.getMethod('getUndelegateLockupDuration') + const info = await method.call() + return parseInt(info) + } + + async getMaxDelegators() { + const method = await this.getMethod('getMaxDelegators') + const info = await method.call() + return parseInt(info) + } + + async getMinDelegationAmount() { + const method = await this.getMethod('getMinDelegationAmount') + const info = await method.call() + return Utils.toBN(info) + } + + async getRemoveDelegatorLockupDuration() { + const method = await this.getMethod('getRemoveDelegatorLockupDuration') + const info = await method.call() + return parseInt(info) + } + + async getRemoveDelegatorEvalDuration() { + const method = await this.getMethod('getRemoveDelegatorEvalDuration') + const info = await method.call() + return parseInt(info) + } + + async getGovernanceAddress() { + const method = await this.getMethod('getGovernanceAddress') + const info = await method.call() + return info + } + + async getServiceProviderFactoryAddress() { + const method = await this.getMethod('getServiceProviderFactoryAddress') + const info = await method.call() + return info + } + + async getClaimsManagerAddress() { + const method = await this.getMethod('getClaimsManagerAddress') + const info = await method.call() + return info + } + + async getStakingAddress() { + const method = await this.getMethod('getStakingAddress') + const info = await method.call() + return info + } + + async updateRemoveDelegatorLockupDuration(duration: string) { + const method = await this.getGovernedMethod( + 'updateRemoveDelegatorLockupDuration', + duration + ) + return await this.web3Manager.sendTransaction(method) + } + + async updateUndelegateLockupDuration(duration: string) { + const method = await this.getGovernedMethod( + 'updateUndelegateLockupDuration', + duration + ) + return await this.web3Manager.sendTransaction(method) + } +} diff --git a/libs/src/services/ethContracts/ethRewardsManagerClient.js b/libs/src/services/ethContracts/ethRewardsManagerClient.ts similarity index 69% rename from libs/src/services/ethContracts/ethRewardsManagerClient.js rename to libs/src/services/ethContracts/ethRewardsManagerClient.ts index 99910dbadc3..0000bf03289 100644 --- a/libs/src/services/ethContracts/ethRewardsManagerClient.js +++ b/libs/src/services/ethContracts/ethRewardsManagerClient.ts @@ -1,35 +1,33 @@ -const { ContractClient } = require('../contracts/ContractClient') +import { ContractClient } from '../contracts/ContractClient' -class EthRewardsManagerClient extends ContractClient { +export class EthRewardsManagerClient extends ContractClient { /* ------- GETTERS ------- */ // Get the token used by the contract - async token () { + async token() { const method = await this.getMethod('token') const info = await method.call() return info } // Get the Governance address - async getGovernanceAddress () { + async getGovernanceAddress() { const method = await this.getMethod('getGovernanceAddress') const info = await method.call() return info } // Get the recipient address - async getRecipientAddress () { + async getRecipientAddress() { const method = await this.getMethod('getRecipientAddress') const info = await method.call() return info } // Get the anti abuse oracle addresses - async getAntiAbuseOracleAddresses () { + async getAntiAbuseOracleAddresses() { const method = await this.getMethod('getAntiAbuseOracleAddresses') const info = await method.call() return info } } - -module.exports = EthRewardsManagerClient diff --git a/libs/src/services/ethContracts/governanceClient.js b/libs/src/services/ethContracts/governanceClient.ts similarity index 62% rename from libs/src/services/ethContracts/governanceClient.js rename to libs/src/services/ethContracts/governanceClient.ts index 14a62310a18..2e60f14e831 100644 --- a/libs/src/services/ethContracts/governanceClient.js +++ b/libs/src/services/ethContracts/governanceClient.ts @@ -1,36 +1,65 @@ -const { ContractClient } = require('../contracts/ContractClient') -const { Utils } = require('../../utils') +import { ContractClient, GetRegistryAddress } from '../contracts/ContractClient' +import { ContractABI, ContractMethod, Utils } from '../../utils' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { AudiusTokenClient } from './audiusTokenClient' +import type { StakingProxyClient } from './stakingProxyClient' +import type { EventLog } from 'web3-core' + +type ProposalTxn = { + proposalId: string + proposer: string + submissionBlockNumber: string + targetContractRegistryKey: string + targetContractAddress: string + callValue: string + functionSignature: string + callData: string + outcome: string + numVotes: string + voteMagnitudeYes: string + voteMagnitudeNo: string +} /** * Transform a method name and its argument types into a string-composed * signature, e.g. someMethod(bytes32, int32) - * @param {string} methodName - * @param {Array} argumentTypes + * @param methodName + * @param argumentTypes */ -const createMethodSignature = (methodName, argumentTypes) => { +const createMethodSignature = (methodName: string, argumentTypes: string[]) => { return `${methodName}(${argumentTypes.join(',')})` } /** * Represent an instance of a proposal vote. */ -const Vote = Object.freeze({ +export const Vote = Object.freeze({ no: 1, yes: 2 }) -class GovernanceClient extends ContractClient { - constructor ( - ethWeb3Manager, - contractABI, - contractRegistryKey, - getRegistryAddress, - audiusTokenClient, - stakingProxyClient, +export class GovernanceClient extends ContractClient { + audiusTokenClient: AudiusTokenClient + stakingProxyClient: StakingProxyClient + isDebug: boolean + + constructor( + ethWeb3Manager: EthWeb3Manager, + contractABI: ContractABI['abi'], + contractRegistryKey: string, + getRegistryAddress: GetRegistryAddress, + audiusTokenClient: AudiusTokenClient, + stakingProxyClient: StakingProxyClient, logger = console, isDebug = false ) { - super(ethWeb3Manager, contractABI, contractRegistryKey, getRegistryAddress, logger) + super( + ethWeb3Manager, + contractABI, + contractRegistryKey, + getRegistryAddress, + logger + ) this.audiusTokenClient = audiusTokenClient this.stakingProxyClient = stakingProxyClient this.isDebug = isDebug @@ -43,11 +72,11 @@ class GovernanceClient extends ContractClient { * Gets the function signature and call data for a contract method. * The signature and call data are passed to other contracts (like governance) * as arguments. - * @param {string} methodName - * @param {Contract.method} contractMethod + * @param methodName + * @param contractMethod */ - getSignatureAndCallData (methodName, contractMethod) { - const argumentTypes = contractMethod._method.inputs.map(i => i.type) + getSignatureAndCallData(methodName: string, contractMethod: ContractMethod) { + const argumentTypes = contractMethod._method.inputs.map((i) => i.type) const argumentValues = contractMethod.arguments const signature = createMethodSignature(methodName, argumentTypes) @@ -56,14 +85,14 @@ class GovernanceClient extends ContractClient { return { signature, callData } } - async guardianExecuteTransaction ( - contractRegistryKey, - functionSignature, - callData - ) { + async guardianExecuteTransaction( + contractRegistryKey: string, + functionSignature: string, + callData: string + ): Promise { // 0 eth valued transaction. We don't anticipate needed to attach // value to this txn, so default to 0. - const callValue0 = this.toBN(0) + const callValue0 = this.toBN('0') const method = await this.getMethod( 'guardianExecuteTransaction', @@ -75,85 +104,81 @@ class GovernanceClient extends ContractClient { return method } - async getVotingPeriod () { + async getVotingPeriod() { const method = await this.getMethod('getVotingPeriod') const period = await method.call() return parseInt(period) } - async setVotingPeriod ( - period - ) { + async setVotingPeriod(period: string) { const methodName = 'setVotingPeriod' const contractMethod = await this.getMethod(methodName, period) - const { signature, callData } = this.getSignatureAndCallData(methodName, contractMethod) - const contractRegistryKey = this.web3Manager.getWeb3().utils.utf8ToHex(this.contractRegistryKey) + const { signature, callData } = this.getSignatureAndCallData( + methodName, + contractMethod + ) + const contractRegistryKey = this.web3Manager + .getWeb3() + .utils.utf8ToHex(this.contractRegistryKey) const method = await this.guardianExecuteTransaction( contractRegistryKey, signature, callData ) - return this.web3Manager.sendTransaction(method) + return await this.web3Manager.sendTransaction(method) } - async getVotingQuorumPercent () { + async getVotingQuorumPercent() { const method = await this.getMethod('getVotingQuorumPercent') const percent = await method.call() return parseInt(percent) } - async getExecutionDelay () { + async getExecutionDelay() { const method = await this.getMethod('getExecutionDelay') const delay = await method.call() return parseInt(delay) } - async setExecutionDelay ( - delay - ) { + async setExecutionDelay(delay: number) { const methodName = 'setExecutionDelay' const contractMethod = await this.getMethod(methodName, delay) - const { signature, callData } = this.getSignatureAndCallData(methodName, contractMethod) - const contractRegistryKey = this.web3Manager.getWeb3().utils.utf8ToHex(this.contractRegistryKey) + const { signature, callData } = this.getSignatureAndCallData( + methodName, + contractMethod + ) + const contractRegistryKey = this.web3Manager + .getWeb3() + .utils.utf8ToHex(this.contractRegistryKey) const method = await this.guardianExecuteTransaction( contractRegistryKey, signature, callData ) - return this.web3Manager.sendTransaction(method) + return await this.web3Manager.sendTransaction(method) } - async getProposalById ( - id - ) { - const method = await this.getMethod( - 'getProposalById', - id - ) + async getProposalById(id: number) { + const method = await this.getMethod('getProposalById', id) const proposal = await method.call() const formattedProposal = this.formatProposal(proposal) return formattedProposal } - async getProposalTargetContractHash ( - id - ) { - const method = await this.getMethod( - 'getProposalTargetContractHash', - id - ) + async getProposalTargetContractHash(id: string) { + const method = await this.getMethod('getProposalTargetContractHash', id) return method.call() } - async getProposals (queryStartBlock = 0) { + async getProposals(queryStartBlock = 0) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalSubmitted', { fromBlock: queryStartBlock }) - return events.map(this.formatProposalEvent) + return events?.map(this.formatProposalEvent) } - async getProposalsForAddresses (addresses, queryStartBlock = 0) { + async getProposalsForAddresses(addresses: string[], queryStartBlock = 0) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalSubmitted', { fromBlock: queryStartBlock, @@ -164,10 +189,7 @@ class GovernanceClient extends ContractClient { return events.map(this.formatProposalEvent) } - async getProposalSubmission ( - proposalId, - queryStartBlock = 0 - ) { + async getProposalSubmission(proposalId: number, queryStartBlock = 0) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalSubmitted', { fromBlock: queryStartBlock, @@ -175,25 +197,34 @@ class GovernanceClient extends ContractClient { _proposalId: proposalId } }) - return this.formatProposalEvent(events[0]) + return this.formatProposalEvent(events[0] as EventLog) } - async getInProgressProposals () { + async getInProgressProposals() { const method = await this.getMethod('getInProgressProposals') const ids = await method.call() return ids } - async submitProposal ({ + async submitProposal({ targetContractRegistryKey, callValue, functionSignature, callData, // array of args, e.g. [slashAmount, targetAddress] name, description + }: { + targetContractRegistryKey: string + callValue: string + functionSignature: string + callData: string[] // array of args, e.g. [slashAmount, targetAddress] + name: string + description: string }) { - const argumentTypes = functionSignature.match(/.*\((?.*)\)/).groups.args.split(',') - const encodedCallData = this.abiEncode(argumentTypes, callData) + const argumentTypes = functionSignature + .match(/.*\((?.*)\)/) + ?.groups?.['args']?.split(',') + const encodedCallData = this.abiEncode(argumentTypes as string[], callData) const method = await this.getMethod( 'submitProposal', @@ -205,52 +236,30 @@ class GovernanceClient extends ContractClient { description ) const tx = await this.web3Manager.sendTransaction(method) - if (tx && tx.events && tx.events.ProposalSubmitted && tx.events.ProposalSubmitted.returnValues) { - const id = tx.events.ProposalSubmitted.returnValues._proposalId + const id = tx.events?.['ProposalSubmitted']?.returnValues?._proposalId + if (id) { return id } throw new Error('submitProposal: txn malformed') } - async submitVote ({ - proposalId, - vote - }) { - const method = await this.getMethod( - 'submitVote', - proposalId, - vote - ) + async submitVote({ proposalId, vote }: { proposalId: number; vote: string }) { + const method = await this.getMethod('submitVote', proposalId, vote) await this.web3Manager.sendTransaction(method) } - async updateVote ({ - proposalId, - vote - }) { - const method = await this.getMethod( - 'updateVote', - proposalId, - vote - ) + async updateVote({ proposalId, vote }: { proposalId: number; vote: string }) { + const method = await this.getMethod('updateVote', proposalId, vote) await this.web3Manager.sendTransaction(method) } - async evaluateProposalOutcome ( - proposalId - ) { - const method = await this.getMethod( - 'evaluateProposalOutcome', - proposalId - ) + async evaluateProposalOutcome(proposalId: number) { + const method = await this.getMethod('evaluateProposalOutcome', proposalId) const outcome = await this.web3Manager.sendTransaction(method) return outcome } - async getProposalEvaluation ( - proposalId, - queryStartBlock = 0 - ) { + async getProposalEvaluation(proposalId: number, queryStartBlock = 0) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalOutcomeEvaluated', { fromBlock: queryStartBlock, @@ -261,9 +270,12 @@ class GovernanceClient extends ContractClient { return events } - async getVotes ({ + async getVotes({ proposalId, queryStartBlock = 0 + }: { + proposalId: number + queryStartBlock: number }) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalVoteSubmitted', { @@ -275,9 +287,12 @@ class GovernanceClient extends ContractClient { return events.map(this.formatVote) } - async getVoteUpdates ({ + async getVoteUpdates({ proposalId, queryStartBlock = 0 + }: { + proposalId: number + queryStartBlock: number }) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalVoteUpdated', { @@ -289,9 +304,12 @@ class GovernanceClient extends ContractClient { return events.map(this.formatVote) } - async getVoteSubmissionsByAddress ({ + async getVoteSubmissionsByAddress({ addresses, queryStartBlock = 0 + }: { + addresses: string[] + queryStartBlock: number }) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalVoteSubmitted', { @@ -303,9 +321,12 @@ class GovernanceClient extends ContractClient { return events.map(this.formatVote) } - async getVoteUpdatesByAddress ({ + async getVoteUpdatesByAddress({ addresses, queryStartBlock = 0 + }: { + addresses: string[] + queryStartBlock: number }) { const contract = await this.getContract() const events = await contract.getPastEvents('ProposalVoteUpdated', { @@ -317,9 +338,12 @@ class GovernanceClient extends ContractClient { return events.map(this.formatVote) } - async getVoteByProposalAndVoter ({ + async getVoteByProposalAndVoter({ proposalId, voterAddress + }: { + proposalId: number[] + voterAddress: number }) { const method = await this.getMethod( 'getVoteInfoByProposalAndVoter', @@ -334,21 +358,19 @@ class GovernanceClient extends ContractClient { /** * ABI encodes argument types and values together into one encoded string - * @param {Array} types - * @param {Array} values */ - abiEncode (types, values) { + abiEncode(types: string[], values: string[]) { return this.web3Manager.getWeb3().eth.abi.encodeParameters(types, values) } - toBN (val) { + toBN(val: string) { return this.web3Manager.getWeb3().utils.toBN(val) } /** * Prune off extraneous fields from proposal returned by txn */ - formatProposal (proposal) { + formatProposal(proposal: ProposalTxn) { return { proposalId: parseInt(proposal.proposalId), proposer: proposal.proposer, @@ -368,7 +390,7 @@ class GovernanceClient extends ContractClient { /** * Formats a proposal event */ - formatProposalEvent (proposalEvent) { + formatProposalEvent(proposalEvent: EventLog) { const event = proposalEvent.returnValues return { proposalId: parseInt(event._proposalId), @@ -382,7 +404,7 @@ class GovernanceClient extends ContractClient { /** * Prune off extraneous fields from vote event */ - formatVote (voteEvent) { + formatVote(voteEvent: EventLog) { const event = voteEvent.returnValues return { proposalId: parseInt(event._proposalId), @@ -398,19 +420,24 @@ class GovernanceClient extends ContractClient { * @param {Number} proposalId id of the governance proposal * @returns {BN} amount of tokens in wei required to reach quorum */ - async calculateQuorum (proposalId) { + async calculateQuorum(proposalId: number) { const { submissionBlockNumber } = await this.getProposalById(proposalId) // represented as a value > 0, eg 5% is 5 const quoroumPercent = await this.getVotingQuorumPercent() // retrieve stake at the time of proposal from Staking client - const totalStakeAtProposal = await this.stakingProxyClient.totalStakedAt(submissionBlockNumber) + const totalStakeAtProposal = await this.stakingProxyClient.totalStakedAt( + submissionBlockNumber + ) // quorum = (total staked at proposal * quorum percent) / 100 // the divmod function returns an object with both the quotient (div) and the remainder (mod) // { div, mod } - const quorumStakeDivMod = (totalStakeAtProposal.mul(Utils.toBN(quoroumPercent))).divmod(Utils.toBN(100)) + const quorumStakeDivMod = totalStakeAtProposal + .mul(Utils.toBN(quoroumPercent)) + // @ts-expect-error divmod not in types for some reason + .divmod(Utils.toBN(100)) let quorumStake = quorumStakeDivMod.div @@ -422,5 +449,3 @@ class GovernanceClient extends ContractClient { return quorumStake } } - -module.exports = { GovernanceClient, Vote } diff --git a/libs/src/services/ethContracts/index.js b/libs/src/services/ethContracts/index.js index a66826251ea..0e4797dde28 100644 --- a/libs/src/services/ethContracts/index.js +++ b/libs/src/services/ethContracts/index.js @@ -1,18 +1,18 @@ const semver = require('semver') let urlJoin = require('proper-url-join') -const AudiusTokenClient = require('./audiusTokenClient') -const RegistryClient = require('./registryClient') +const { AudiusTokenClient } = require('./audiusTokenClient') +const { RegistryClient } = require('./registryClient') const { GovernanceClient } = require('./governanceClient') -const ServiceTypeManagerClient = require('./serviceTypeManagerClient') -const ServiceProviderFactoryClient = require('./serviceProviderFactoryClient') -const StakingProxyClient = require('./stakingProxyClient') -const DelegateManagerClient = require('./delegateManagerClient') -const ClaimsManagerClient = require('./claimsManagerClient') -const ClaimDistributionClient = require('./claimDistributionClient') -const WormholeClient = require('./wormholeClient') -const EthRewardsManagerClient = require('./ethRewardsManagerClient') -const TrustedNotifierManagerClient = require('./trustedNotifierManagerClient') +const { ServiceTypeManagerClient } = require('./serviceTypeManagerClient') +const { ServiceProviderFactoryClient } = require('./serviceProviderFactoryClient') +const { StakingProxyClient } = require('./stakingProxyClient') +const { DelegateManagerClient } = require('./delegateManagerClient') +const { ClaimsManagerClient } = require('./claimsManagerClient') +const { ClaimDistributionClient } = require('./claimDistributionClient') +const { WormholeClient } = require('./wormholeClient') +const { EthRewardsManagerClient } = require('./ethRewardsManagerClient') +const { TrustedNotifierManagerClient } = require('./trustedNotifierManagerClient') const { Utils } = require('../../utils') const AudiusTokenABI = Utils.importEthContractABI('AudiusToken.json').abi diff --git a/libs/src/services/ethContracts/registryClient.js b/libs/src/services/ethContracts/registryClient.js deleted file mode 100644 index b1fc549579a..00000000000 --- a/libs/src/services/ethContracts/registryClient.js +++ /dev/null @@ -1,21 +0,0 @@ -const { Utils } = require('../../utils') - -class RegistryClient { - constructor (web3Manager, contractABI, contractAddress) { - this.web3Manager = web3Manager - this.contractABI = contractABI - this.contractAddress = contractAddress - - this.web3 = this.web3Manager.getWeb3() - this.Registry = new this.web3.eth.Contract(contractABI, contractAddress) - } - - async getContract (contractRegistryKey) { - Utils.checkStrLen(contractRegistryKey, 32) - return this.Registry.methods.getContract( - Utils.utf8ToHex(contractRegistryKey) - ).call() - } -} - -module.exports = RegistryClient diff --git a/libs/src/services/ethContracts/registryClient.ts b/libs/src/services/ethContracts/registryClient.ts new file mode 100644 index 00000000000..a9f18ad3ed2 --- /dev/null +++ b/libs/src/services/ethContracts/registryClient.ts @@ -0,0 +1,33 @@ +import { Utils } from '../../utils' +import type { Web3Manager } from '../web3Manager' +import type { AbiItem } from 'web3-utils' +import type Web3 from 'web3' +import type { Contract } from 'web3-eth-contract' + +export class RegistryClient { + web3Manager: Web3Manager + contractABI: AbiItem + contractAddress: string + web3: Web3 + Registry: Contract + + constructor( + web3Manager: Web3Manager, + contractABI: AbiItem, + contractAddress: string + ) { + this.web3Manager = web3Manager + this.contractABI = contractABI + this.contractAddress = contractAddress + + this.web3 = this.web3Manager.getWeb3() + this.Registry = new this.web3.eth.Contract(contractABI, contractAddress) + } + + async getContract(contractRegistryKey: string) { + Utils.checkStrLen(contractRegistryKey, 32) + return this.Registry.methods + .getContract(Utils.utf8ToHex(contractRegistryKey)) + .call() + } +} diff --git a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts new file mode 100644 index 00000000000..7017d7ce461 --- /dev/null +++ b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts @@ -0,0 +1,687 @@ +// TODO: a lot of extra parseInt's that result in incorrect (as unknown as string) typecasting + +import { ContractABI, Utils } from '../../utils' +import { GovernedContractClient } from '../contracts/GovernedContractClient' +import axios, { AxiosRequestConfig } from 'axios' +import { range } from 'lodash' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { GetRegistryAddress } from '../contracts/ContractClient' +import type { AudiusTokenClient } from './audiusTokenClient' +import type { StakingProxyClient } from './stakingProxyClient' +import type { GovernanceClient } from './governanceClient' +import urlJoin from 'proper-url-join' + +type GetEvent = { + serviceType: string + owner: string + queryStartBlock: number +} + +type Filter = { _owner?: string; _serviceType?: string } + +export class ServiceProviderFactoryClient extends GovernedContractClient { + audiusTokenClient: AudiusTokenClient + stakingProxyClient: StakingProxyClient + isDebug: boolean + + constructor( + ethWeb3Manager: EthWeb3Manager, + contractABI: ContractABI['abi'], + contractRegistryKey: string, + getRegistryAddress: GetRegistryAddress, + audiusTokenClient: AudiusTokenClient, + stakingProxyClient: StakingProxyClient, + governanceClient: GovernanceClient, + logger = console, + isDebug = false + ) { + super( + ethWeb3Manager, + contractABI, + contractRegistryKey, + getRegistryAddress, + governanceClient, + logger + ) + this.audiusTokenClient = audiusTokenClient + this.stakingProxyClient = stakingProxyClient + this.isDebug = isDebug + } + + async registerWithDelegate( + serviceType: string, + endpoint: string, + amount: number, + delegateOwnerWallet: string + ) { + const sanitizedEndpoint = endpoint.replace(/\/$/, '') + + if (!this.isDebug && !Utils.isHttps(sanitizedEndpoint)) { + throw new Error('Domain name not using https protocol!') + } + + if (!this.isDebug && !Utils.isFQDN(sanitizedEndpoint)) { + throw new Error('Not a fully qualified domain name!') + } + if (!Number.isInteger(amount) && !Utils.isBN(amount)) { + throw new Error('Invalid amount') + } + + const requestUrl = urlJoin(sanitizedEndpoint, 'health_check') + const axiosRequestObj: AxiosRequestConfig = { + url: requestUrl, + method: 'get', + timeout: 1000 + } + const resp = await axios(axiosRequestObj) + const endpointServiceType = resp.data.data.service + + if (serviceType !== endpointServiceType) { + throw new Error( + 'Attempting to register endpoint with mismatched service type' + ) + } + + // Approve token transfer operation + const contractAddress = await this.stakingProxyClient.getAddress() + const tx0 = await this.audiusTokenClient.approve(contractAddress, amount) + + // Register and stake + const method = await this.getMethod( + 'register', + Utils.utf8ToHex(serviceType), + sanitizedEndpoint, + amount, + delegateOwnerWallet + ) + // @ts-expect-error TODO: this seems incorrect + const tx = await this.web3Manager.sendTransaction(method, 1000000) + const returnValues = tx.events?.['RegisteredServiceProvider']?.returnValues + return { + txReceipt: tx, + spID: parseInt(returnValues._spID), + serviceType: Utils.hexToUtf8(returnValues._serviceType), + owner: returnValues._owner, + endpoint: returnValues._endpoint, + tokenApproveReceipt: tx0 + } + } + + async register(serviceType: string, endpoint: string, amount: number) { + return await this.registerWithDelegate( + serviceType, + endpoint, + amount, + this.web3Manager.getWalletAddress() + ) + } + + async getRegisteredServiceProviderEvents({ + serviceType, + owner, + queryStartBlock = 0 + }: GetEvent) { + const contract = await this.getContract() + const filter: Filter = {} + if (owner) { + filter._owner = owner + } + if (serviceType) { + filter._serviceType = serviceType + } + const events = await contract.getPastEvents('RegisteredServiceProvider', { + fromBlock: queryStartBlock, + filter + }) + + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + spID: parseInt(event.returnValues['_spID']), + serviceType: Utils.hexToUtf8(event.returnValues['_serviceType']), + owner: event.returnValues['_owner'], + endpoint: event.returnValues['_endpoint'], + stakeAmount: Utils.toBN(event.returnValues['_stakeAmout']) + })) + } + + async getDeregisteredServiceProviderEvents({ + serviceType, + owner, + queryStartBlock = 0 + }: GetEvent) { + const contract = await this.getContract() + const filter: Filter = {} + if (owner) { + filter._owner = owner + } + if (serviceType) { + filter._serviceType = serviceType + } + const events = await contract.getPastEvents('DeregisteredServiceProvider', { + fromBlock: queryStartBlock, + filter + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + spID: parseInt(event.returnValues['_spID']), + serviceType: Utils.hexToUtf8(event.returnValues['_serviceType']), + owner: event.returnValues['_owner'], + endpoint: event.returnValues['_endpoint'], + stakeAmount: Utils.toBN(event.returnValues['_stakeAmount']) + })) + } + + async getIncreasedStakeEvents({ + owner, + queryStartBlock = 0 + }: { + owner: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents('IncreasedStake', { + fromBlock: queryStartBlock, + filter: { + _owner: owner + } + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + owner: event.returnValues['_owner'], + increaseAmount: Utils.toBN(event.returnValues['_increaseAmount']), + newStakeAmount: Utils.toBN(event.returnValues['_newStakeAmount']) + })) + } + + async getDecreasedStakeEvaluatedEvents({ + owner, + queryStartBlock = 0 + }: { + owner: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents( + 'DecreaseStakeRequestEvaluated', + { + fromBlock: queryStartBlock, + filter: { + _owner: owner + } + } + ) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + owner: event.returnValues['_owner'], + decreaseAmount: Utils.toBN(event.returnValues['_decreaseAmount']), + newStakeAmount: Utils.toBN(event.returnValues['_newStakeAmount']) + })) + } + + async getDecreasedStakeRequestedEvents({ + owner, + queryStartBlock = 0 + }: { + owner: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents('DecreaseStakeRequested', { + fromBlock: queryStartBlock, + filter: { + _owner: owner + } + }) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + owner: event.returnValues['_owner'], + decreaseAmount: Utils.toBN(event.returnValues['_decreaseAmount']), + lockupExpiryBlock: parseInt(event.returnValues['_lockupExpiryBlock']) + })) + } + + async getDecreasedStakeCancelledEvents({ + owner, + queryStartBlock = 0 + }: { + owner: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const events = await contract.getPastEvents( + 'DecreaseStakeRequestCancelled', + { + fromBlock: queryStartBlock, + filter: { + _owner: owner + } + } + ) + return events.map((event) => ({ + blockNumber: parseInt(event.blockNumber as unknown as string), + owner: event.returnValues['_owner'], + decreaseAmount: Utils.toBN(event.returnValues['_decreaseAmount']), + lockupExpiryBlock: parseInt(event.returnValues['_lockupExpiryBlock']) + })) + } + + // Get the deregistered service's most recent endpoint and delegate owner wallet + async getDeregisteredService({ + serviceType, + spID, + queryStartBlock = 0 + }: { + serviceType: string + spID: string + queryStartBlock: number + }) { + const contract = await this.getContract() + const service: { + endpoint: string + delegateOwnerWallet: string + owner?: string + } = { endpoint: '', delegateOwnerWallet: '' } + const registerEvents = await contract.getPastEvents( + 'RegisteredServiceProvider', + { + fromBlock: queryStartBlock, + filter: { + _spID: spID, + _serviceType: Utils.utf8ToHex(serviceType) + } + } + ) + + if (registerEvents.length > 0) { + const { _endpoint, _owner } = registerEvents[registerEvents.length - 1] + ?.returnValues as { _endpoint: string; _owner: string } + service.endpoint = _endpoint + service.owner = _owner + } + + const endpointUpdateEvents = await contract.getPastEvents( + 'EndpointUpdated', + { + fromBlock: queryStartBlock, + filter: { + _spID: spID, + _serviceType: Utils.utf8ToHex(serviceType) + } + } + ) + + if (endpointUpdateEvents.length > 0) { + const { _newEndpoint } = endpointUpdateEvents[ + endpointUpdateEvents.length - 1 + ]?.returnValues as { _newEndpoint: string } + service.endpoint = _newEndpoint + } + + const walletEvents = await contract.getPastEvents( + 'DelegateOwnerWalletUpdated', + { + fromBlock: queryStartBlock, + filter: { + _spID: spID, + _serviceType: Utils.utf8ToHex(serviceType) + } + } + ) + + if (walletEvents.length > 0) { + const { _updatedWallet } = walletEvents[walletEvents.length - 1] + ?.returnValues as { _updatedWallet: string } + service.delegateOwnerWallet = _updatedWallet + } + + return service + } + + async increaseStake(amount: number) { + const contractAddress = await this.stakingProxyClient.getAddress() + const tx0 = await this.audiusTokenClient.approve(contractAddress, amount) + const method = await this.getMethod('increaseStake', amount) + // @ts-expect-error TODO: sendTransaction's signature seems pretty different + const tx = await this.web3Manager.sendTransaction(method, 1000000) + return { + txReceipt: tx, + tokenApproveReceipt: tx0 + } + } + + /** + * Makes a request to decrease stake + * @param amount + * @returns decrease stake lockup expiry block + */ + async requestDecreaseStake(amount: number) { + const requestDecreaseMethod = await this.getMethod( + 'requestDecreaseStake', + amount + ) + await this.web3Manager.sendTransaction( + requestDecreaseMethod, + // @ts-expect-error TODO: sendTransaction's signature seems pretty different + 1000000 + ) + + const account = this.web3Manager.getWalletAddress() + const lockupExpiryBlock = await this.getLockupExpiry(account) + return parseInt(lockupExpiryBlock as unknown as string) + } + + /** + * Gets the pending decrease stake request for a given account + * @param account wallet address to fetch for + */ + async getPendingDecreaseStakeRequest(account: string) { + const requestInfoMethod = await this.getMethod( + 'getPendingDecreaseStakeRequest', + account + ) + const { amount, lockupExpiryBlock } = await requestInfoMethod.call() + return { + amount: Utils.toBN(amount), + lockupExpiryBlock: parseInt(lockupExpiryBlock) + } + } + + /** + * Gets the pending decrease stake lockup duration + */ + async getDecreaseStakeLockupDuration() { + const requestInfoMethod = await this.getMethod( + 'getDecreaseStakeLockupDuration' + ) + const info = await requestInfoMethod.call() + return parseInt(info) + } + + /** + * Gets the deployer cut lockup duration + */ + async getDeployerCutLockupDuration() { + const requestInfoMethod = await this.getMethod( + 'getDeployerCutLockupDuration' + ) + const info = await requestInfoMethod.call() + return parseInt(info) + } + + /** + * Cancels the pending decrease stake request + * @param account wallet address to cancel request for + */ + async cancelDecreaseStakeRequest(account: string) { + const requestCancelDecreaseMethod = await this.getMethod( + 'cancelDecreaseStakeRequest', + account + ) + await this.web3Manager.sendTransaction( + requestCancelDecreaseMethod, + // @ts-expect-error TODO: double check sendTransaction + 1000000 + ) + } + + /** + * Fetches the pending decrease stake lockup expiry block for a user + * @param account wallet address to fetch for + */ + async getLockupExpiry(account: string) { + const { lockupExpiryBlock } = await this.getPendingDecreaseStakeRequest( + account + ) + return parseInt(lockupExpiryBlock as unknown as string) + } + + async decreaseStake() { + const method = await this.getMethod('decreaseStake') + // @ts-expect-error TODO: double check sendTransaction + const tx = await this.web3Manager.sendTransaction(method, 1000000) + + return { + txReceipt: tx + } + } + + /** + * Deregisters a service + * @param serviceType + * @param endpoint + */ + async deregister(serviceType: string, endpoint: string) { + const method = await this.getMethod( + 'deregister', + Utils.utf8ToHex(serviceType), + endpoint + ) + const tx = await this.web3Manager.sendTransaction(method) + const returnValues = + tx.events?.['DeregisteredServiceProvider']?.returnValues + + return { + txReceipt: tx, + spID: parseInt(returnValues._spID), + serviceType: Utils.hexToUtf8(returnValues._serviceType), + owner: returnValues._owner, + endpoint: returnValues._endpoint + } + } + + async getTotalServiceTypeProviders(serviceType: string) { + const method = await this.getMethod( + 'getTotalServiceTypeProviders', + Utils.utf8ToHex(serviceType) + ) + const count = await method.call() + return parseInt(count) + } + + async getServiceProviderIdFromEndpoint(endpoint: string) { + const method = await this.getMethod( + 'getServiceProviderIdFromEndpoint', + endpoint + ) + const info = await method.call() + return parseInt(info) + } + + // TODO: Remove this method after all consumers are using + // `getServiceEndpointInfo` directly + async getServiceProviderInfo(serviceType: string, serviceId: number) { + return await this.getServiceEndpointInfo(serviceType, serviceId) + } + + async getServiceEndpointInfo(serviceType: string, serviceId: number) { + const method = await this.getMethod( + 'getServiceEndpointInfo', + Utils.utf8ToHex(serviceType), + serviceId + ) + const info = await method.call() + return { + owner: info.owner, + endpoint: info.endpoint.replace(/\/$/, ''), + spID: parseInt(serviceId as unknown as string), + type: serviceType, + blockNumber: parseInt(info.blockNumber), + delegateOwnerWallet: info.delegateOwnerWallet + } + } + + async getServiceProviderInfoFromEndpoint(endpoint: string) { + const requestUrl = urlJoin(endpoint, 'health_check') + const axiosRequestObj: AxiosRequestConfig = { + url: requestUrl, + method: 'get', + timeout: 1000 + } + + const resp = await axios(axiosRequestObj) + const serviceType = resp.data.data.service + + const serviceProviderId = await this.getServiceProviderIdFromEndpoint( + endpoint + ) + const info = await this.getServiceEndpointInfo( + serviceType, + serviceProviderId + ) + return info + } + + async getServiceProviderIdsFromAddress( + ownerAddress: string, + serviceType: string + ) { + const method = await this.getMethod( + 'getServiceProviderIdsFromAddress', + ownerAddress, + Utils.utf8ToHex(serviceType) + ) + const info: string[] = await method.call() + return info.map((id) => parseInt(id)) + } + + async getServiceProviderIdFromAddress( + ownerAddress: string, + serviceType: string + ) { + const infos = await this.getServiceProviderIdsFromAddress( + ownerAddress, + serviceType + ) + return infos[0] as number + } + + async getServiceEndpointInfoFromAddress( + ownerAddress: string, + serviceType: string + ) { + const spId = await this.getServiceProviderIdFromAddress( + ownerAddress, + serviceType + ) + + // cast this as an array for backwards compatibility because everything expects an array + const spInfo = [await this.getServiceEndpointInfo(serviceType, spId)] + return spInfo + } + + /** + * Returns all service providers of requested `serviceType` + * Returns array of objects with schema { blockNumber, delegateOwnerWallet, endpoint, owner, spID, type } + */ + async getServiceProviderList(serviceType: string) { + const numberOfProviders = await this.getTotalServiceTypeProviders( + serviceType + ) + + const providerList = await Promise.all( + range(1, numberOfProviders + 1).map( + async (i) => await this.getServiceEndpointInfo(serviceType, i) + ) + ) + return providerList.filter((provider) => provider.endpoint !== '') + } + + async updateDecreaseStakeLockupDuration(duration: string) { + const method = await this.getGovernedMethod( + 'updateDecreaseStakeLockupDuration', + duration + ) + return await this.web3Manager.sendTransaction(method) + } + + async getServiceProviderDetails(serviceProviderAddress: string) { + const method = await this.getMethod( + 'getServiceProviderDetails', + serviceProviderAddress + ) + const info = await method.call() + return { + deployerCut: parseInt(info.deployerCut), + deployerStake: Utils.toBN(info.deployerStake), + maxAccountStake: Utils.toBN(info.maxAccountStake), + minAccountStake: Utils.toBN(info.minAccountStake), + numberOfEndpoints: parseInt(info.numberOfEndpoints), + validBounds: info.validBounds + } + } + + async updateDelegateOwnerWallet( + serviceType: string, + endpoint: string, + updatedDelegateOwnerWallet: string + ) { + const method = await this.getMethod( + 'updateDelegateOwnerWallet', + Utils.utf8ToHex(serviceType), + endpoint, + updatedDelegateOwnerWallet + ) + + const tx = await this.web3Manager.sendTransaction(method) + return tx + } + + async updateEndpoint( + serviceType: string, + oldEndpoint: string, + newEndpoint: string + ) { + const method = await this.getMethod( + 'updateEndpoint', + Utils.utf8ToHex(serviceType), + oldEndpoint, + newEndpoint + ) + const tx = await this.web3Manager.sendTransaction(method) + return tx + } + + async requestUpdateDeployerCut(ownerAddress: string, deployerCut: string) { + const method = await this.getMethod( + 'requestUpdateDeployerCut', + ownerAddress, + deployerCut + ) + const tx = await this.web3Manager.sendTransaction(method) + return tx + } + + async getPendingUpdateDeployerCutRequest(ownerAddress: string) { + const method = await this.getMethod( + 'getPendingUpdateDeployerCutRequest', + ownerAddress + ) + const { lockupExpiryBlock, newDeployerCut } = await method.call() + return { + lockupExpiryBlock: parseInt(lockupExpiryBlock), + newDeployerCut: parseInt(newDeployerCut) + } + } + + async cancelUpdateDeployerCut(ownerAddress: string) { + const method = await this.getMethod('cancelUpdateDeployerCut', ownerAddress) + const tx = await this.web3Manager.sendTransaction(method) + return tx + } + + async updateDeployerCut(ownerAddress: string) { + const method = await this.getMethod('updateDeployerCut', ownerAddress) + const tx = await this.web3Manager.sendTransaction(method) + return tx + } + + async updateServiceProviderStake(ownerAddress: string, newAmount: string) { + const method = await this.getMethod( + 'updateServiceProviderStake', + ownerAddress, + newAmount + ) + const tx = await this.web3Manager.sendTransaction(method) + return tx + } +} diff --git a/libs/src/services/ethContracts/serviceTypeManagerClient.js b/libs/src/services/ethContracts/serviceTypeManagerClient.js deleted file mode 100644 index cca3ce2b391..00000000000 --- a/libs/src/services/ethContracts/serviceTypeManagerClient.js +++ /dev/null @@ -1,90 +0,0 @@ -const { Utils } = require('../../utils') -const GovernedContractClient = require('../contracts/GovernedContractClient') - -class ServiceTypeManagerClient extends GovernedContractClient { - /** - * - * @param {String} serviceType Type of service to set the version, either `discovery-node` or `content-node` - * @param {String} serviceVersion Version string to set on chain - * @param {String?} privateKey Optional privateKey to pass along to web3Manager sendTransaction - * @param {Boolean?} dryRun Optional parameter to return the generated parameters without sending tx - * @returns comma-separated String of serviceType and serviceVersion if dryRun; else response from web3Manager.sendTransaction - */ - async setServiceVersion (serviceType, serviceVersion, privateKey = null, dryRun = false) { - const method = await this.getGovernedMethod( - 'setServiceVersion', - Utils.utf8ToHex(serviceType), - Utils.utf8ToHex(serviceVersion) - ) - - if (dryRun) { - return `${Utils.utf8ToHex(serviceType)},${Utils.utf8ToHex(serviceVersion)}` - } - - return this.web3Manager.sendTransaction( - method, - (await this.governanceClient.getAddress()), - privateKey - ) - } - - async addServiceType (serviceType, serviceTypeMin, serviceTypeMax, privateKey = null) { - const method = await this.getGovernedMethod( - 'addServiceType', - Utils.utf8ToHex(serviceType), - serviceTypeMin, - serviceTypeMax - ) - - return this.web3Manager.sendTransaction( - method, - (await this.governanceClient.getAddress()), - privateKey - ) - } - - async getValidServiceTypes () { - const method = await this.getMethod('getValidServiceTypes') - const types = await method.call() - return types.map(t => Utils.hexToUtf8(t)) - } - - async getCurrentVersion (serviceType) { - const method = await this.getMethod('getCurrentVersion', Utils.utf8ToHex(serviceType)) - const hexVersion = await method.call() - return Utils.hexToUtf8(hexVersion) - } - - async getVersion (serviceType, serviceTypeIndex) { - const serviceTypeBytes32 = Utils.utf8ToHex(serviceType) - const method = await this.getMethod('getVersion', serviceTypeBytes32, serviceTypeIndex) - const version = await method.call() - return Utils.hexToUtf8(version) - } - - async getNumberOfVersions (serviceType) { - const method = await this.getMethod('getNumberOfVersions', Utils.utf8ToHex(serviceType)) - return parseInt(await method.call()) - } - - /** - * @notice Add a new service type - * @param serviceType - * @returns { - * isValid: boolean - Is the types type is isValid - * minStake: number - minimum stake for service type - * maxStake: number - minimum stake for service type - * } - */ - async getServiceTypeInfo (serviceType) { - const method = await this.getMethod('getServiceTypeInfo', Utils.utf8ToHex(serviceType)) - const response = await method.call() - return { - isValid: response[0], - minStake: Utils.toBN(response[1]), - maxStake: Utils.toBN(response[2]) - } - } -} - -module.exports = ServiceTypeManagerClient diff --git a/libs/src/services/ethContracts/serviceTypeManagerClient.ts b/libs/src/services/ethContracts/serviceTypeManagerClient.ts new file mode 100644 index 00000000000..e760b56b255 --- /dev/null +++ b/libs/src/services/ethContracts/serviceTypeManagerClient.ts @@ -0,0 +1,112 @@ +import { Utils } from '../../utils' +import { GovernedContractClient } from '../contracts/GovernedContractClient' + +export class ServiceTypeManagerClient extends GovernedContractClient { + /** + * + * @param serviceType Type of service to set the version, either `discovery-node` or `content-node` + * @param serviceVersion Version string to set on chain + * @param privateKey Optional privateKey to pass along to web3Manager sendTransaction + * @param dryRun Optional parameter to return the generated parameters without sending tx + * @returns comma-separated String of serviceType and serviceVersion if dryRun; else response from web3Manager.sendTransaction + */ + async setServiceVersion( + serviceType: string, + serviceVersion: string, + privateKey: string | null = null, + dryRun = false + ) { + const method = await this.getGovernedMethod( + 'setServiceVersion', + Utils.utf8ToHex(serviceType), + Utils.utf8ToHex(serviceVersion) + ) + + if (dryRun) { + return `${Utils.utf8ToHex(serviceType)},${Utils.utf8ToHex( + serviceVersion + )}` + } + + return await this.web3Manager.sendTransaction( + method, + await this.governanceClient.getAddress(), + privateKey + ) + } + + async addServiceType( + serviceType: string, + serviceTypeMin: string, + serviceTypeMax: string, + privateKey: string | null = null + ) { + const method = await this.getGovernedMethod( + 'addServiceType', + Utils.utf8ToHex(serviceType), + serviceTypeMin, + serviceTypeMax + ) + + return await this.web3Manager.sendTransaction( + method, + await this.governanceClient.getAddress(), + privateKey + ) + } + + async getValidServiceTypes() { + const method = await this.getMethod('getValidServiceTypes') + const types: string[] = await method.call() + return types.map((t) => Utils.hexToUtf8(t)) + } + + async getCurrentVersion(serviceType: string) { + const method = await this.getMethod( + 'getCurrentVersion', + Utils.utf8ToHex(serviceType) + ) + const hexVersion = await method.call() + return Utils.hexToUtf8(hexVersion) + } + + async getVersion(serviceType: string, serviceTypeIndex: number) { + const serviceTypeBytes32 = Utils.utf8ToHex(serviceType) + const method = await this.getMethod( + 'getVersion', + serviceTypeBytes32, + serviceTypeIndex + ) + const version = await method.call() + return Utils.hexToUtf8(version) + } + + async getNumberOfVersions(serviceType: string) { + const method = await this.getMethod( + 'getNumberOfVersions', + Utils.utf8ToHex(serviceType) + ) + return parseInt(await method.call()) + } + + /** + * @notice Add a new service type + * @returns { + * isValid: Is the types type is isValid + * minStake: minimum stake for service type + * maxStake: minimum stake for service type + * } + */ + async getServiceTypeInfo(serviceType: string) { + const method = await this.getMethod( + 'getServiceTypeInfo', + Utils.utf8ToHex(serviceType) + ) + const response = await method.call() + return { + isValid: response[0], + minStake: Utils.toBN(response[1]), + maxStake: Utils.toBN(response[2]) + } + } +} diff --git a/libs/src/services/ethContracts/stakingProxyClient.ts b/libs/src/services/ethContracts/stakingProxyClient.ts new file mode 100644 index 00000000000..9c4314c152d --- /dev/null +++ b/libs/src/services/ethContracts/stakingProxyClient.ts @@ -0,0 +1,97 @@ +import type { ContractABI } from '../../utils' +import { ContractClient, GetRegistryAddress } from '../contracts/ContractClient' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { AudiusTokenClient } from './audiusTokenClient' +import type BN from 'bn.js' + +export class StakingProxyClient extends ContractClient { + audiusTokenClient: AudiusTokenClient + toBN: (value: string | number) => BN + + constructor( + ethWeb3Manager: EthWeb3Manager, + contractABI: ContractABI['abi'], + contractRegistryKey: string, + getRegistryAddress: GetRegistryAddress, + audiusTokenClient: AudiusTokenClient, + logger = console + ) { + super( + ethWeb3Manager, + contractABI, + contractRegistryKey, + getRegistryAddress, + logger + ) + this.audiusTokenClient = audiusTokenClient + this.toBN = ethWeb3Manager.getWeb3().utils.toBN + } + + async token() { + const method = await this.getMethod('token') + return method.call() + } + + async totalStaked() { + const method = await this.getMethod('totalStaked') + return this.toBN(await method.call()) + } + + async supportsHistory() { + const method = await this.getMethod('supportsHistory') + return method.call() + } + + async totalStakedFor(account: string) { + const method = await this.getMethod('totalStakedFor', account) + return this.toBN(await method.call()) + } + + async totalStakedForAt(account: string, blockNumber: string) { + const method = await this.getMethod( + 'totalStakedForAt', + account, + blockNumber + ) + return this.toBN(await method.call()) + } + + async totalStakedAt(blockNumber: number) { + const method = await this.getMethod('totalStakedAt', blockNumber) + return this.toBN(await method.call()) + } + + async isStaker(account: string) { + const method = await this.getMethod('isStaker', account) + return method.call() + } + + async getDelegateManagerAddress() { + const method = await this.getMethod('getDelegateManagerAddress') + return method.call() + } + + async getClaimsManagerAddress() { + const method = await this.getMethod('getClaimsManagerAddress') + return method.call() + } + + async getServiceProviderFactoryAddress() { + const method = await this.getMethod('getServiceProviderFactoryAddress') + return method.call() + } + + async getGovernanceAddress() { + const method = await this.getMethod('getGovernanceAddress') + return method.call() + } + + async getLastClaimedBlockForUser() { + const method = await this.getMethod( + 'lastClaimedFor', + this.web3Manager.getWalletAddress() + ) + const tx = await method.call() + return tx + } +} diff --git a/libs/src/services/ethContracts/trustedNotifierManagerClient.js b/libs/src/services/ethContracts/trustedNotifierManagerClient.ts similarity index 68% rename from libs/src/services/ethContracts/trustedNotifierManagerClient.js rename to libs/src/services/ethContracts/trustedNotifierManagerClient.ts index cd68055825e..139b18e4239 100644 --- a/libs/src/services/ethContracts/trustedNotifierManagerClient.js +++ b/libs/src/services/ethContracts/trustedNotifierManagerClient.ts @@ -1,6 +1,6 @@ -const GovernedContractClient = require('../contracts/GovernedContractClient') +import { GovernedContractClient } from '../contracts/GovernedContractClient' -class TrustedNotifierManagerClient extends GovernedContractClient { +export class TrustedNotifierManagerClient extends GovernedContractClient { /** * Register Trusted Notifier with specified fields (wallet, endpoint, email) * @notice Only callable by Governance contract @@ -8,16 +8,21 @@ class TrustedNotifierManagerClient extends GovernedContractClient { * @notice New Trusted Notifier is assigned an auto-incremented integer ID * @returns Newly assigned integer ID */ - async registerNotifier (wallet, endpoint, email, privateKey = null) { + async registerNotifier( + wallet: string, + endpoint: string, + email: string, + privateKey: string | null = null + ) { const method = await this.getGovernedMethod( 'registerNotifier', wallet, endpoint, email ) - return this.web3Manager.sendTransaction( + return await this.web3Manager.sendTransaction( method, - (await this.governanceClient.getAddress()), + await this.governanceClient.getAddress(), privateKey ) } @@ -27,19 +32,16 @@ class TrustedNotifierManagerClient extends GovernedContractClient { * @notice Only callable by Governance contract or wallet * @returns ID of deregistered Trusted Notifier */ - async deregisterNotifier (wallet, privateKey = null) { - const method = await this.getGovernedMethod( - 'deregisterNotifier', - wallet - ) - return this.web3Manager.sendTransaction( + async deregisterNotifier(wallet: string, privateKey: string | null = null) { + const method = await this.getGovernedMethod('deregisterNotifier', wallet) + return await this.web3Manager.sendTransaction( method, - (await this.governanceClient.getAddress()), + await this.governanceClient.getAddress(), privateKey ) } - async getLatestNotifierID () { + async getLatestNotifierID() { const method = await this.getMethod('getLatestNotifierID') const ID = await method.call() return parseInt(ID) @@ -47,9 +49,8 @@ class TrustedNotifierManagerClient extends GovernedContractClient { /** * Returns all TrustedNotifier info associated with ID - * @returns {Object} { wallet, endpoint, email } */ - async getNotifierForID (ID) { + async getNotifierForID(ID: string) { const method = await this.getMethod('getNotifierForID', ID) const notifierInfo = await method.call() return { @@ -61,9 +62,8 @@ class TrustedNotifierManagerClient extends GovernedContractClient { /** * Returns all TrustedNotifier info associated with wallet - * @returns {Object} { ID, endpoint, email } */ - async getNotifierForWallet (wallet) { + async getNotifierForWallet(wallet: string) { const method = await this.getMethod('getNotifierForWallet', wallet) const notifierInfo = await method.call() return { @@ -75,9 +75,8 @@ class TrustedNotifierManagerClient extends GovernedContractClient { /** * Returns all TrustedNotifier info associated with endpoint - * @returns {Object} { ID, wallet, email } */ - async getNotifierForEndpoint (endpoint) { + async getNotifierForEndpoint(endpoint: string) { const method = await this.getMethod('getNotifierForEndpoint', endpoint) const notifierInfo = await method.call() return { @@ -89,9 +88,8 @@ class TrustedNotifierManagerClient extends GovernedContractClient { /** * Returns all TrustedNotifier info associated with email - * @returns {Object} { ID, wallet, endpoint } */ - async getNotifierForEmail (email) { + async getNotifierForEmail(email: string) { const method = await this.getMethod('getNotifierForEmail', email) const notifierInfo = await method.call() return { @@ -101,5 +99,3 @@ class TrustedNotifierManagerClient extends GovernedContractClient { } } } - -module.exports = TrustedNotifierManagerClient diff --git a/libs/src/services/ethContracts/wormholeClient.js b/libs/src/services/ethContracts/wormholeClient.ts similarity index 56% rename from libs/src/services/ethContracts/wormholeClient.js rename to libs/src/services/ethContracts/wormholeClient.ts index 15b4f876f0b..9bdf105c385 100644 --- a/libs/src/services/ethContracts/wormholeClient.js +++ b/libs/src/services/ethContracts/wormholeClient.ts @@ -1,16 +1,39 @@ -class WormholeClient { - constructor (ethWeb3Manager, contractABI, contractAddress, audiusTokenClient) { +import type Web3 from 'web3' +import type { ContractABI } from '../../utils' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { AudiusTokenClient } from './audiusTokenClient' +import type { Contract } from 'web3-eth-contract' +import type Wallet from 'ethereumjs-wallet' +import type BN from 'bn.js' + +export class WormholeClient { + ethWeb3Manager: EthWeb3Manager + contractABI: ContractABI['abi'] + contractAddress: string + web3: Web3 + audiusTokenClient: AudiusTokenClient + WormholeContract: Contract + + constructor( + ethWeb3Manager: EthWeb3Manager, + contractABI: ContractABI['abi'], + contractAddress: string, + audiusTokenClient: AudiusTokenClient + ) { this.ethWeb3Manager = ethWeb3Manager this.contractABI = contractABI this.contractAddress = contractAddress this.web3 = this.ethWeb3Manager.getWeb3() this.audiusTokenClient = audiusTokenClient - this.WormholeContract = new this.web3.eth.Contract(this.contractABI, this.contractAddress) + this.WormholeContract = new this.web3.eth.Contract( + this.contractABI, + this.contractAddress + ) } // Get the name of the contract - async nonces (wallet) { + async nonces(wallet: string) { // Pass along a unique param so the nonce value is always not cached const nonce = await this.WormholeContract.methods.nonces(wallet).call({ _audiusBustCache: Date.now() @@ -21,11 +44,7 @@ class WormholeClient { /* ------- SETTERS ------- */ - async initialize ( - fromAcct, - wormholeAddress, - relayer - ) { + async initialize(fromAcct: Wallet, wormholeAddress: string, relayer: Wallet) { const method = this.WormholeContract.methods.initialize( this.audiusTokenClient.contractAddress, wormholeAddress @@ -44,36 +63,16 @@ class WormholeClient { /** * Transfers in eth from the user's wallet to the wormhole contract and * specifies a solana wallet to realized the tokens in SOL - * - * @param {string} fromAcct - * @param {BN} amount - * @param {number} chainId - * @param {*} solanaAccount - * @param {*} arbiterFee - * @param {*} deadline - * @param {string} signedDigest - * @param {string} relayer - * @returns { - * txHash: string, - * txParams: { - * data: string - * gasLimit: string - * gasPrice: number - * nonce: string - * to: string - * value: string - * } - * } */ - async transferTokens ( - fromAcct, - amount, - chainId, - solanaAccount, - arbiterFee, - deadline, - signedDigest, - relayer + async transferTokens( + fromAcct: Wallet, + amount: BN, + chainId: number, + solanaAccount: string, + arbiterFee: string, + deadline: string, + signedDigest: { v: string; r: string; s: string }, + relayer: Wallet ) { const method = this.WormholeContract.methods.transferTokens( fromAcct, @@ -97,5 +96,3 @@ class WormholeClient { return tx } } - -module.exports = WormholeClient diff --git a/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts b/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts index d61f0ccd63c..0a8feac16d6 100644 --- a/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts +++ b/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts @@ -12,17 +12,15 @@ import retry from 'async-retry' import type { IdentityService, RelayTransaction } from '../identity' import type { Hedgehog } from '@audius/hedgehog' import type { AxiosError } from 'axios' +import type { Web3Config } from '../web3Manager' +import type Wallet from 'ethereumjs-wallet' +import type { TransactionReceipt } from 'web3-core' const MIN_GAS_PRICE = Math.pow(10, 9) // 1 GWei, ETH minimum allowed gas price const HIGH_GAS_PRICE = 250 * MIN_GAS_PRICE // 250 GWei const DEFAULT_GAS_PRICE = 100 * MIN_GAS_PRICE // 100 Gwei is a reasonably average gas price const MAX_GAS_LIMIT = 5000000 // We've seen prod tx's take up to 4M. Set to the highest we've observed + a buffer -type Web3Config = { - ownerWallet: string - providers: Providers -} - /** Singleton state-manager for Audius Eth Contracts */ export class EthWeb3Manager { web3Config: Web3Config @@ -82,11 +80,11 @@ export class EthWeb3Manager { async sendTransaction( contractMethod: ContractMethod, - contractAddress = null, - privateKey = null, + contractAddress: string | null = null, + privateKey: string | null = null, txRetries = 5, txGasLimit: number | null = null - ) { + ): Promise { const gasLimit = txGasLimit ?? (await estimateGas({ @@ -147,8 +145,8 @@ export class EthWeb3Manager { } const gasPrice = parseInt(await this.web3.eth.getGasPrice()) - return contractMethod.send({ - from: this.ownerWallet as string, + return await contractMethod.send({ + from: this.ownerWallet, gas: gasLimit, gasPrice: gasPrice }) @@ -162,8 +160,8 @@ export class EthWeb3Manager { async relayTransaction( contractMethod: ContractMethod, contractAddress: string, - ownerWallet: string, - relayerWallet: string, + ownerWallet: Wallet, + relayerWallet?: Wallet, txRetries = 5, txGasLimit: number | null = null ): Promise> { diff --git a/libs/src/services/identity/IdentityService.ts b/libs/src/services/identity/IdentityService.ts index 8f4fa42406e..ecdb57938e8 100644 --- a/libs/src/services/identity/IdentityService.ts +++ b/libs/src/services/identity/IdentityService.ts @@ -4,7 +4,9 @@ import { uuid } from '../../utils/uuid' import type { Captcha } from '../../utils' import { getTrackListens, TimeFrame } from './requests' -import type Web3Manager from '../web3Manager' +import type { Web3Manager } from '../web3Manager' +import type { Log, TransactionReceipt } from 'web3-core' +import type Wallet from 'ethereumjs-wallet' type Data = Record @@ -351,12 +353,12 @@ export class IdentityService { } async relay( - contractRegistryKey: string, - contractAddress: string, + contractRegistryKey: string | null | undefined, + contractAddress: string | null | undefined, senderAddress: string, encodedABI: string, - gasLimit: string - ) { + gasLimit: number + ): Promise<{ receipt: TransactionReceipt }> { const shouldCaptcha = Math.random() < RELAY_CAPTCHA_SAMPLE_RATE let token if (this.captcha && shouldCaptcha) { @@ -383,7 +385,7 @@ export class IdentityService { async ethRelay( contractAddress: string, - senderAddress: string, + senderAddress: Wallet, encodedABI: string, gasLimit: string ): Promise { diff --git a/libs/src/services/web3Manager/Web3Manager.ts b/libs/src/services/web3Manager/Web3Manager.ts new file mode 100644 index 00000000000..04691a34de2 --- /dev/null +++ b/libs/src/services/web3Manager/Web3Manager.ts @@ -0,0 +1,353 @@ +import Web3 from '../../web3' +import sigUtil from 'eth-sig-util' +import retry from 'async-retry' +import { ContractMethod, estimateGas } from '../../utils' +import { AudiusABIDecoder } from '../ABIDecoder' +import EthereumWallet from 'ethereumjs-wallet' +import { XMLHttpRequest } from './XMLHttpRequest' +import type { Web3Config } from './Web3Config' +import type { IdentityService } from '../identity' +import type { Hedgehog } from '@audius/hedgehog' +import type Web3Type from 'web3' +import type { HttpProvider, TransactionReceipt, EventLog } from 'web3-core' +import type { EIP712TypedData } from 'eth-sig-util' +import type { DecodedLog } from 'abi-decoder' + +const DEFAULT_GAS_LIMIT = 2000000 + +/** singleton class to be instantiated and persisted with every AudiusLibs */ +export class Web3Manager { + web3Config: Web3Config + isServer: boolean + identityService: IdentityService + hedgehog: Hedgehog + AudiusABIDecoder: typeof AudiusABIDecoder + web3: Web3Type | undefined + useExternalWeb3: boolean | undefined + // @ts-expect-error an error is thrown if it's not provided + ownerWallet: EthereumWallet + + constructor( + web3Config: Web3Config, + identityService: IdentityService, + hedgehog: Hedgehog, + isServer: boolean + ) { + this.web3Config = web3Config + this.isServer = isServer + + // Unset if externalWeb3 = true + this.identityService = identityService + this.hedgehog = hedgehog + this.AudiusABIDecoder = AudiusABIDecoder + } + + async init() { + const web3Config = this.web3Config + if (!web3Config) throw new Error('Failed to initialize Web3Manager') + + if ( + // External Web3 + web3Config?.useExternalWeb3 && + web3Config.externalWeb3Config?.web3 && + web3Config.externalWeb3Config.ownerWallet + ) { + this.web3 = web3Config.externalWeb3Config.web3 + this.useExternalWeb3 = true + this.ownerWallet = web3Config.externalWeb3Config.ownerWallet + } else if ( + // Internal Web3 + web3Config && + !web3Config.useExternalWeb3 && + web3Config.internalWeb3Config?.web3ProviderEndpoints + ) { + // either user has external web3 but it's not configured, or doesn't have web3 + this.web3 = new Web3( + this.provider( + web3Config.internalWeb3Config.web3ProviderEndpoints[0] as string, + 10000 + ) + ) + this.useExternalWeb3 = false + + if (web3Config.internalWeb3Config.privateKey) { + const pkeyBuffer = Buffer.from( + web3Config.internalWeb3Config.privateKey, + 'hex' + ) + this.ownerWallet = EthereumWallet.fromPrivateKey(pkeyBuffer) + return + } + + // create private key pair here if it doesn't already exist + const storedWallet = this.hedgehog.getWallet() + if (storedWallet) { + this.ownerWallet = storedWallet + } else { + const passwordEntropy = `audius-dummy-pkey-${Math.floor( + Math.random() * 1000000 + )}` + this.ownerWallet = await this.hedgehog.createWalletObj(passwordEntropy) + } + } else { + throw new Error("web3ProviderEndpoint isn't passed into constructor") + } + } + + getWeb3() { + return this.web3 as Web3Type + } + + setWeb3(web3: Web3Type) { + this.web3 = web3 + } + + getWalletAddress() { + if (this.useExternalWeb3) { + // Lowercase the owner wallet. Consider using the checksum address. + // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md. + // @ts-expect-error Wallet type doesn't have `toLowerCase` method? + return this.ownerWallet.toLowerCase() + } else { + return this.ownerWallet.getAddressString() + } + } + + setOwnerWallet(ownerWallet: EthereumWallet) { + this.ownerWallet = ownerWallet + } + + web3IsExternal() { + return this.useExternalWeb3 + } + + getOwnerWalletPrivateKey() { + if (this.useExternalWeb3) { + throw new Error("Can't get owner wallet private key for external web3") + } else { + return this.ownerWallet.getPrivateKey() + } + } + + /** + * Signs provided string data (should be timestamped). + * @param data + */ + async sign(data: string) { + if (this.useExternalWeb3) { + const account = this.getWalletAddress() + if (this.isServer) { + return await this.web3?.eth.sign( + this.web3.utils.fromUtf8(data), + account + ) + } else { + return await this.web3?.eth.personal.sign( + this.web3.utils.fromUtf8(data), + account, + '' + ) + } + } + + return sigUtil.personalSign(this.getOwnerWalletPrivateKey(), { data }) + } + + /** + * Given a data payload and signature, verifies that signature is valid, and returns + * Ethereum wallet address used to sign data. + * @param data information that was signed + * @param signature hex-formatted signature of data generated by web3 personalSign method + */ + async verifySignature(data: string, signature: string) { + return sigUtil.recoverPersonalSignature({ data: data, sig: signature }) + } + + async signTypedData(signatureData: EIP712TypedData) { + if (this.useExternalWeb3) { + return await ethSignTypedData( + this.getWeb3(), + this.getWalletAddress(), + signatureData + ) + } else { + // Due to changes in ethereumjs-util's toBuffer method as of v6.2.0 + // non hex-prefixed string values are not permitted and need to be + // provided directly as a buffer. + // https://github.com/ethereumjs/ethereumjs-util/releases/tag/v6.2.0 + Object.keys(signatureData.message).forEach((key) => { + const message = signatureData.message[key] + if (typeof message === 'string' && !message.startsWith('0x')) { + signatureData.message[key] = Buffer.from(message) + } + }) + return sigUtil.signTypedData(this.ownerWallet.getPrivateKey(), { + data: signatureData + }) + } + } + + async sendTransaction( + contractMethod: ContractMethod, + contractRegistryKey?: string | null, + contractAddress?: string | null, + txRetries = 5, + txGasLimit?: number + ): Promise { + const gasLimit = + txGasLimit ?? + (await estimateGas({ + method: contractMethod, + gasLimitMaximum: DEFAULT_GAS_LIMIT + })) + if (this.useExternalWeb3) { + return await contractMethod.send({ + from: this.ownerWallet, + gas: gasLimit + }) + } else { + const encodedABI = contractMethod.encodeABI() + + const response = await retry( + async () => { + return await this.identityService.relay( + contractRegistryKey, + contractAddress, + this.ownerWallet.getAddressString(), + encodedABI, + gasLimit + ) + }, + { + // Retry function 5x by default + // 1st retry delay = 500ms, 2nd = 1500ms, 3rd...nth retry = 4000 ms (capped) + minTimeout: 500, + maxTimeout: 4000, + factor: 3, + retries: txRetries, + onRetry: (err) => { + if (err) { + console.log( + // eslint-disable-next-line @typescript-eslint/no-base-to-string + `libs web3Manager transaction send retry error : ${err}` + ) + } + } + } + ) + + const receipt = response.receipt + + // interestingly, using contractMethod.send from Metamask's web3 (eg. like in the if + // above) parses the event log into an 'events' key on the transaction receipt and + // blows away the 'logs' key. However, using sendRawTransaction as our + // relayer does, returns only the logs. Here, we replicate the part of the 'events' + // key that our code consumes, but we may want to change our functions to consume + // this data in a different way in future (this parsing is messy). + // More on Metamask's / Web3.js' behavior here: + // https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-send + if (receipt.logs) { + const events: TransactionReceipt['events'] = {} + // TODO: decodeLogs appears to return DecodedLog, not DecodedLog[] so maybe a type/version issue + const decoded = this.AudiusABIDecoder.decodeLogs( + contractRegistryKey, + receipt.logs + ) as unknown as DecodedLog[] + decoded.forEach((evt) => { + const returnValues: Record = {} + evt.events.forEach((arg) => { + returnValues[arg.name] = arg.value + }) + const eventLog = { returnValues } + events[evt.name] = eventLog as EventLog + }) + receipt.events = events + } + return response.receipt + } + } + + // TODO - Remove this. Adapted from https://github.com/raiden-network/webui/pull/51/files + // Vendored code below + provider(url: string, timeout: number) { + return this.monkeyPatchProvider( + new Web3.providers.HttpProvider(url, { timeout }) + ) + } + + // TODO: Workaround for https://github.com/ethereum/web3.js/issues/1803 it should be immediately removed + // as soon as the issue is fixed upstream. + // Issue is also documented here https://github.com/ethereum/web3.js/issues/1802 + monkeyPatchProvider(httpProvider: HttpProvider) { + // @ts-expect-error overriding a private method not appearing in types + override(httpProvider, '_prepareRequest', function () { + return function ( + this: HttpProvider & { + timeout: number + headers: Array<{ name: string; value: string }> + } + ) { + const request = new XMLHttpRequest() + + request.open('POST', this.host, true) + request.setRequestHeader('Content-Type', 'application/json') + request.timeout = this.timeout && this.timeout !== 1 ? this.timeout : 0 + + if (this.headers) { + this.headers.forEach(function (header) { + request.setRequestHeader(header.name, header.value) + }) + } + return request + } + }) + return httpProvider + } + // End vendored code +} + +/** Browser and testing-compatible signTypedData */ +const ethSignTypedData = async ( + web3: Web3Type, + wallet: EthereumWallet, + signatureData: EIP712TypedData +) => { + return await new Promise((resolve, reject) => { + let processedSignatureData: EIP712TypedData | string = signatureData + let method + // @ts-expect-error isMetaMask not captured by web3Provider + if (web3.currentProvider.isMetaMask === true) { + method = 'eth_signTypedData_v3' + processedSignatureData = JSON.stringify(signatureData) + } else { + method = 'eth_signTypedData' + // fix per https://github.com/ethereum/web3.js/issues/1119 + } + + ;(web3.currentProvider as HttpProvider).send( + { + method: method, + params: [wallet, processedSignatureData], + // @ts-expect-error from not in JsonRpcPayload + from: wallet + }, + (err, result) => { + if (err) { + reject(err) + } else if (result?.error) { + reject(result?.error) + } else { + resolve(result?.result) + } + } + ) + }) +} + +function override( + object: Class, + methodName: K, + callback: T +) { + object[methodName] = callback(object[methodName]) +} diff --git a/libs/src/utils/estimateGas.ts b/libs/src/utils/estimateGas.ts index 802acaa633d..7a7c24ac00d 100644 --- a/libs/src/utils/estimateGas.ts +++ b/libs/src/utils/estimateGas.ts @@ -1,17 +1,25 @@ +import type Wallet from 'ethereumjs-wallet' + // Default multiplier on top of gas estimate to be extra safe that txns // will go through const GAS_LIMIT_MULTIPLIER = 1.05 export interface ContractMethod { + arguments: string[] estimateGas: (config: { from: string | undefined gas: number | undefined }) => Promise _method: { name: string + inputs: Array<{ type: string }> } encodeABI: () => string - send: (config: { from: string; gas: number; gasPrice: number }) => unknown + send: (config: { + from: Wallet | undefined + gas: number + gasPrice?: number + }) => Tx } interface EstimateGasConfig { diff --git a/libs/src/utils/utils.ts b/libs/src/utils/utils.ts index bc0b10621ac..23ca873a224 100644 --- a/libs/src/utils/utils.ts +++ b/libs/src/utils/utils.ts @@ -51,7 +51,7 @@ export class Utils { return Web3.utils.isBN(number) } - static toBN(number: number, base: number) { + static toBN(number: number, base?: number) { return new Web3.utils.BN(number, base) } From f974bfe2d7d4d118c8aa2bc11d7a446fadb49dc3 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Sat, 23 Apr 2022 17:22:29 -0700 Subject: [PATCH 02/14] Refactor EthContracts to typescript --- .../contracts/GovernedContractClient.ts | 4 +- .../src/services/ethContracts/EthContracts.ts | 631 ++++++++++-------- .../ethContracts/audiusTokenClient.ts | 4 +- .../ethContracts/delegateManagerClient.ts | 4 +- .../services/ethContracts/governanceClient.ts | 4 +- libs/src/services/ethContracts/index.d.ts | 13 - libs/src/services/ethContracts/index.ts | 1 + .../services/ethContracts/registryClient.ts | 12 +- .../serviceProviderFactoryClient.ts | 4 +- .../ethContracts/stakingProxyClient.ts | 4 +- libs/src/utils/importContractABI.d.ts | 2 +- 11 files changed, 367 insertions(+), 316 deletions(-) delete mode 100644 libs/src/services/ethContracts/index.d.ts create mode 100644 libs/src/services/ethContracts/index.ts diff --git a/libs/src/services/contracts/GovernedContractClient.ts b/libs/src/services/contracts/GovernedContractClient.ts index 60ce4df130b..6ae95b7aecf 100644 --- a/libs/src/services/contracts/GovernedContractClient.ts +++ b/libs/src/services/contracts/GovernedContractClient.ts @@ -1,4 +1,4 @@ -import type { ContractABI } from '../../utils' +import type { ContractABI, Logger } from '../../utils' import type { GovernanceClient } from '../ethContracts/governanceClient' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { Web3Manager } from '../web3Manager' @@ -19,7 +19,7 @@ export class GovernedContractClient extends ContractClient { contractRegistryKey: string, getRegistryAddress: GetRegistryAddress, governanceClient: GovernanceClient, - logger = console + logger: Logger = console ) { super( web3Manager, diff --git a/libs/src/services/ethContracts/EthContracts.ts b/libs/src/services/ethContracts/EthContracts.ts index ba88b84d851..952246f2c56 100644 --- a/libs/src/services/ethContracts/EthContracts.ts +++ b/libs/src/services/ethContracts/EthContracts.ts @@ -1,284 +1,347 @@ -export {} -// import semver from 'semver' -// import { AudiusTokenClient } from './audiusTokenClient' -// import { RegistryClient } from './registryClient' -// import { GovernanceClient } from './governanceClient' -// import { ServiceTypeManagerClient } from './serviceTypeManagerClient' -// import { ServiceProviderFactoryClient } from './serviceProviderFactoryClient' -// import { StakingProxyClient } from './stakingProxyClient' -// import { DelegateManagerClient } from './delegateManagerClient' -// import { ClaimsManagerClient } from './claimsManagerClient' -// import { ClaimDistributionClient } from './claimDistributionClient' -// import { WormholeClient } from './wormholeClient' -// import { EthRewardsManagerClient } from './ethRewardsManagerClient' -// import { TrustedNotifierManagerClient } from './trustedNotifierManagerClient' -// import { Utils } from '../../utils' -// -// const AudiusTokenABI = Utils.importEthContractABI('AudiusToken.json').abi -// const RegistryABI = Utils.importEthContractABI('Registry.json').abi -// const GovernanceABI = Utils.importEthContractABI('Governance.json').abi -// const ServiceTypeManagerABI = Utils.importEthContractABI('ServiceTypeManager.json').abi -// const ServiceProviderFactoryABI = Utils.importEthContractABI('ServiceProviderFactory.json').abi -// const StakingABI = Utils.importEthContractABI('Staking.json').abi -// const DelegateManagerABI = Utils.importEthContractABI('DelegateManager.json').abi -// const ClaimsManagerABI = Utils.importEthContractABI('ClaimsManager.json').abi -// const ClaimDistributionABI = Utils.importEthContractABI('AudiusClaimDistributor.json').abi -// const WormholeClientABI = Utils.importEthContractABI('WormholeClient.json').abi -// const EthRewardsManagerABI = Utils.importEthContractABI('EthRewardsManager.json').abi -// const TrustedNotifierManagerABI = Utils.importEthContractABI('TrustedNotifierManager.json').abi -// -// const GovernanceRegistryKey = 'Governance' -// const ServiceTypeManagerProxyKey = 'ServiceTypeManagerProxy' -// const ServiceProviderFactoryRegistryKey = 'ServiceProviderFactory' -// const StakingProxyKey = 'StakingProxy' -// const DelegateManagerRegistryKey = 'DelegateManager' -// const ClaimsManagerProxyKey = 'ClaimsManagerProxy' -// const ClaimDistributionRegistryKey = 'ClaimDistribution' -// const EthRewardsManagerProxyKey = 'EthRewardsManagerProxy' -// const TrustedNotifierManagerProxyKey = 'TrustedNotifierManagerProxy' -// -// const TWO_MINUTES = 2 * 60 * 1000 -// -// const serviceType = Object.freeze({ -// DISCOVERY_PROVIDER: 'discovery-node', -// CREATOR_NODE: 'content-node' -// }) -// const serviceTypeList = Object.values(serviceType) -// -// class EthContracts { -// constructor ( -// ethWeb3Manager, -// tokenContractAddress, -// registryAddress, -// claimDistributionContractAddress, -// wormholeContractAddress, -// isServer, -// isDebug = false -// ) { -// this.ethWeb3Manager = ethWeb3Manager -// this.tokenContractAddress = tokenContractAddress -// this.claimDistributionContractAddress = claimDistributionContractAddress -// this.wormholeContractAddress = wormholeContractAddress -// this.registryAddress = registryAddress -// this.isServer = isServer -// this.isDebug = isDebug -// this.expectedServiceVersions = null -// -// this.AudiusTokenClient = new AudiusTokenClient( -// this.ethWeb3Manager, -// AudiusTokenABI, -// this.tokenContractAddress -// ) -// this.RegistryClient = new RegistryClient( -// this.ethWeb3Manager, -// RegistryABI, -// this.registryAddress -// ) -// this.getRegistryAddressForContract = this.getRegistryAddressForContract.bind(this) -// -// this.StakingProxyClient = new StakingProxyClient( -// this.ethWeb3Manager, -// StakingABI, -// StakingProxyKey, -// this.getRegistryAddressForContract, -// this.AudiusTokenClient -// ) -// -// this.GovernanceClient = new GovernanceClient( -// this.ethWeb3Manager, -// GovernanceABI, -// GovernanceRegistryKey, -// this.getRegistryAddressForContract, -// this.AudiusTokenClient, -// this.StakingProxyClient -// ) -// -// this.ClaimsManagerClient = new ClaimsManagerClient( -// this.ethWeb3Manager, -// ClaimsManagerABI, -// ClaimsManagerProxyKey, -// this.getRegistryAddressForContract -// ) -// -// this.EthRewardsManagerClient = new EthRewardsManagerClient( -// this.ethWeb3Manager, -// EthRewardsManagerABI, -// EthRewardsManagerProxyKey, -// this.getRegistryAddressForContract -// ) -// -// this.ServiceTypeManagerClient = new ServiceTypeManagerClient( -// this.ethWeb3Manager, -// ServiceTypeManagerABI, -// ServiceTypeManagerProxyKey, -// this.getRegistryAddressForContract, -// this.GovernanceClient -// ) -// -// this.ServiceProviderFactoryClient = new ServiceProviderFactoryClient( -// this.ethWeb3Manager, -// ServiceProviderFactoryABI, -// ServiceProviderFactoryRegistryKey, -// this.getRegistryAddressForContract, -// this.AudiusTokenClient, -// this.StakingProxyClient, -// this.GovernanceClient, -// this.isDebug -// ) -// -// this.DelegateManagerClient = new DelegateManagerClient( -// this.ethWeb3Manager, -// DelegateManagerABI, -// DelegateManagerRegistryKey, -// this.getRegistryAddressForContract, -// this.AudiusTokenClient, -// this.StakingProxyClient, -// this.GovernanceClient -// ) -// -// if (this.claimDistributionContractAddress) { -// this.ClaimDistributionClient = new ClaimDistributionClient( -// this.ethWeb3Manager, -// ClaimDistributionABI, -// ClaimDistributionRegistryKey, -// this.getRegistryAddressForContract, -// this.claimDistributionContractAddress -// ) -// } -// -// this.WormholeClient = new WormholeClient( -// this.ethWeb3Manager, -// WormholeClientABI, -// this.wormholeContractAddress, -// this.AudiusTokenClient -// ) -// -// this.TrustedNotifierManagerClient = new TrustedNotifierManagerClient( -// this.ethWeb3Manager, -// TrustedNotifierManagerABI, -// TrustedNotifierManagerProxyKey, -// this.getRegistryAddressForContract, -// this.GovernanceClient -// ) -// -// this.contractClients = [ -// this.ServiceTypeManagerClient, -// this.StakingProxyClient, -// this.ServiceProviderFactoryClient -// ] -// -// // Whether or not we are running in `regressed` mode, meaning we were -// // unable to select a discovery provider that was up-to-date. Clients may -// // want to consider blocking writes. -// this._regressedMode = false -// } -// -// async init () { -// if (!this.ethWeb3Manager || !this.tokenContractAddress || !this.registryAddress) throw new Error('Failed to initialize EthContracts') -// -// if (this.isServer) { -// await Promise.all(this.contractClients.map(client => client.init())) -// } -// } -// -// /** -// * Estabilishes that connection to discovery providers has regressed -// */ -// enterRegressedMode () { -// console.info('Entering regressed mode') -// this._regressedMode = true -// setTimeout(() => { -// console.info('Leaving regressed mode') -// this._regressedMode = false -// }, TWO_MINUTES) -// } -// -// isInRegressedMode () { -// return this._regressedMode -// } -// -// async getRegistryAddressForContract (contractName) { -// // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names -// this.contracts = this.contracts || { [this.registryAddress]: 'registry' } -// this.contractAddresses = this.contractAddresses || { registry: this.registryAddress } -// if (!this.contractAddresses[contractName]) { -// const address = await this.RegistryClient.getContract(contractName) -// this.contracts[address] = contractName -// this.contractAddresses[contractName] = address -// } -// return this.contractAddresses[contractName] -// } -// -// async getCurrentVersion (serviceType) { -// try { -// const version = await this.ServiceTypeManagerClient.getCurrentVersion(serviceType) -// return version -// } catch (e) { -// console.log(`Error retrieving version for ${serviceType}`) -// return null -// } -// } -// -// /* -// * Determine the latest version for deployed services such as discovery provider and cache -// */ -// async getExpectedServiceVersions () { -// const versions = await Promise.all( -// serviceTypeList.map(serviceType => this.getCurrentVersion(serviceType)) -// ) -// const expectedVersions = serviceTypeList.reduce((map, serviceType, i) => { -// if (versions[i]) { -// map[serviceType] = versions[i] -// } -// return map -// }, {}) -// return expectedVersions -// } -// -// /* -// * Determine whether major and minor versions match for two version strings -// * Version string 2 must have equivalent major/minor versions and a patch >= version1 -// * @param {string} version string 1 -// * @param {string} version string 2 -// */ -// isValidSPVersion (version1, version2) { -// return ( -// semver.major(version1) === semver.major(version2) && -// semver.minor(version1) === semver.minor(version2) && -// semver.patch(version2) >= semver.patch(version1) -// ) -// } -// -// /** -// * Determines whether the major and minor versions are equal -// * @param {string} version string 1 -// * @param {string} version string 2 -// */ -// hasSameMajorAndMinorVersion (version1, version2) { -// return ( -// semver.major(version1) === semver.major(version2) && -// semver.minor(version1) === semver.minor(version2) -// ) -// } -// -// async getServiceProviderList (spType) { -// return this.ServiceProviderFactoryClient.getServiceProviderList(spType) -// } -// -// async getNumberOfVersions (spType) { -// return this.ServiceTypeManagerClient.getNumberOfVersions(spType) -// } -// -// async getVersion (spType, queryIndex) { -// return this.ServiceTypeManagerClient.getVersion(spType, queryIndex) -// } -// -// async getServiceTypeInfo (spType) { -// return this.ServiceTypeManagerClient.getServiceTypeInfo(spType) -// } -// } -// -// module.exports = EthContracts -// module.exports.serviceType = serviceType -// -// +import semver from 'semver' +import { AudiusTokenClient } from './audiusTokenClient' +import { RegistryClient } from './registryClient' +import { GovernanceClient } from './governanceClient' +import { ServiceTypeManagerClient } from './serviceTypeManagerClient' +import { ServiceProviderFactoryClient } from './serviceProviderFactoryClient' +import { StakingProxyClient } from './stakingProxyClient' +import { DelegateManagerClient } from './delegateManagerClient' +import { ClaimsManagerClient } from './claimsManagerClient' +import { ClaimDistributionClient } from './claimDistributionClient' +import { WormholeClient } from './wormholeClient' +import { EthRewardsManagerClient } from './ethRewardsManagerClient' +import { TrustedNotifierManagerClient } from './trustedNotifierManagerClient' +import { Logger, Utils } from '../../utils' +import type { EthWeb3Manager } from '../ethWeb3Manager' +import type { ContractClient } from '../contracts/ContractClient' + +const AudiusTokenABI = Utils.importEthContractABI('AudiusToken.json').abi +const RegistryABI = Utils.importEthContractABI('Registry.json').abi +const GovernanceABI = Utils.importEthContractABI('Governance.json').abi +const ServiceTypeManagerABI = Utils.importEthContractABI( + 'ServiceTypeManager.json' +).abi +const ServiceProviderFactoryABI = Utils.importEthContractABI( + 'ServiceProviderFactory.json' +).abi +const StakingABI = Utils.importEthContractABI('Staking.json').abi +const DelegateManagerABI = Utils.importEthContractABI( + 'DelegateManager.json' +).abi +const ClaimsManagerABI = Utils.importEthContractABI('ClaimsManager.json').abi +const ClaimDistributionABI = Utils.importEthContractABI( + 'AudiusClaimDistributor.json' +).abi +const WormholeClientABI = Utils.importEthContractABI('WormholeClient.json').abi +const EthRewardsManagerABI = Utils.importEthContractABI( + 'EthRewardsManager.json' +).abi +const TrustedNotifierManagerABI = Utils.importEthContractABI( + 'TrustedNotifierManager.json' +).abi + +const GovernanceRegistryKey = 'Governance' +const ServiceTypeManagerProxyKey = 'ServiceTypeManagerProxy' +const ServiceProviderFactoryRegistryKey = 'ServiceProviderFactory' +const StakingProxyKey = 'StakingProxy' +const DelegateManagerRegistryKey = 'DelegateManager' +const ClaimsManagerProxyKey = 'ClaimsManagerProxy' +const ClaimDistributionRegistryKey = 'ClaimDistribution' +const EthRewardsManagerProxyKey = 'EthRewardsManagerProxy' +const TrustedNotifierManagerProxyKey = 'TrustedNotifierManagerProxy' + +const TWO_MINUTES = 2 * 60 * 1000 + +const serviceType = Object.freeze({ + DISCOVERY_PROVIDER: 'discovery-node', + CREATOR_NODE: 'content-node' +}) +const serviceTypeList = Object.values(serviceType) + +class EthContracts { + ethWeb3Manager: EthWeb3Manager + tokenContractAddress: string + claimDistributionContractAddress: string + wormholeContractAddress: string + registryAddress: string + isServer: boolean + logger: Logger + isDebug: boolean + expectedServiceVersions: null | string[] + AudiusTokenClient: AudiusTokenClient + RegistryClient: RegistryClient + StakingProxyClient: StakingProxyClient + GovernanceClient: GovernanceClient + ClaimsManagerClient: ClaimsManagerClient + EthRewardsManagerClient: EthRewardsManagerClient + ServiceTypeManagerClient: ServiceTypeManagerClient + ServiceProviderFactoryClient: ServiceProviderFactoryClient + DelegateManagerClient: DelegateManagerClient + ClaimDistributionClient: ClaimDistributionClient | undefined + WormholeClient: WormholeClient + TrustedNotifierManagerClient: TrustedNotifierManagerClient + contractClients: ContractClient[] + _regressedMode: boolean + contracts: Record | undefined + contractAddresses: Record | undefined + + constructor( + ethWeb3Manager: EthWeb3Manager, + tokenContractAddress: string, + registryAddress: string, + claimDistributionContractAddress: string, + wormholeContractAddress: string, + isServer: boolean, + logger = console, + isDebug = false + ) { + this.ethWeb3Manager = ethWeb3Manager + this.tokenContractAddress = tokenContractAddress + this.claimDistributionContractAddress = claimDistributionContractAddress + this.wormholeContractAddress = wormholeContractAddress + this.registryAddress = registryAddress + this.isServer = isServer + this.logger = logger + this.isDebug = isDebug + this.expectedServiceVersions = null + + this.AudiusTokenClient = new AudiusTokenClient( + this.ethWeb3Manager, + AudiusTokenABI, + this.tokenContractAddress + ) + this.RegistryClient = new RegistryClient( + this.ethWeb3Manager, + RegistryABI, + this.registryAddress + ) + this.getRegistryAddressForContract = + this.getRegistryAddressForContract.bind(this) + + this.StakingProxyClient = new StakingProxyClient( + this.ethWeb3Manager, + StakingABI, + StakingProxyKey, + this.getRegistryAddressForContract, + this.AudiusTokenClient, + this.logger + ) + + this.GovernanceClient = new GovernanceClient( + this.ethWeb3Manager, + GovernanceABI, + GovernanceRegistryKey, + this.getRegistryAddressForContract, + this.AudiusTokenClient, + this.StakingProxyClient, + this.logger + ) + + this.ClaimsManagerClient = new ClaimsManagerClient( + this.ethWeb3Manager, + ClaimsManagerABI, + ClaimsManagerProxyKey, + this.getRegistryAddressForContract, + this.logger + ) + + this.EthRewardsManagerClient = new EthRewardsManagerClient( + this.ethWeb3Manager, + EthRewardsManagerABI, + EthRewardsManagerProxyKey, + this.getRegistryAddressForContract, + this.logger + ) + + this.ServiceTypeManagerClient = new ServiceTypeManagerClient( + this.ethWeb3Manager, + ServiceTypeManagerABI, + ServiceTypeManagerProxyKey, + this.getRegistryAddressForContract, + this.GovernanceClient, + this.logger + ) + + this.ServiceProviderFactoryClient = new ServiceProviderFactoryClient( + this.ethWeb3Manager, + ServiceProviderFactoryABI, + ServiceProviderFactoryRegistryKey, + this.getRegistryAddressForContract, + this.AudiusTokenClient, + this.StakingProxyClient, + this.GovernanceClient, + this.logger, + this.isDebug + ) + + this.DelegateManagerClient = new DelegateManagerClient( + this.ethWeb3Manager, + DelegateManagerABI, + DelegateManagerRegistryKey, + this.getRegistryAddressForContract, + this.AudiusTokenClient, + this.StakingProxyClient, + this.GovernanceClient, + this.logger + ) + + if (this.claimDistributionContractAddress) { + this.ClaimDistributionClient = new ClaimDistributionClient( + this.ethWeb3Manager, + ClaimDistributionABI, + ClaimDistributionRegistryKey, + this.getRegistryAddressForContract, + this.logger, + this.claimDistributionContractAddress + ) + } + + this.WormholeClient = new WormholeClient( + this.ethWeb3Manager, + WormholeClientABI, + this.wormholeContractAddress, + this.AudiusTokenClient + ) + + this.TrustedNotifierManagerClient = new TrustedNotifierManagerClient( + this.ethWeb3Manager, + TrustedNotifierManagerABI, + TrustedNotifierManagerProxyKey, + this.getRegistryAddressForContract, + this.GovernanceClient, + this.logger + ) + + this.contractClients = [ + this.ServiceTypeManagerClient, + this.StakingProxyClient, + this.ServiceProviderFactoryClient + ] + + // Whether or not we are running in `regressed` mode, meaning we were + // unable to select a discovery provider that was up-to-date. Clients may + // want to consider blocking writes. + this._regressedMode = false + } + + async init() { + if ( + !this.ethWeb3Manager || + !this.tokenContractAddress || + !this.registryAddress + ) + throw new Error('Failed to initialize EthContracts') + + if (this.isServer) { + await Promise.all(this.contractClients.map((client) => client.init())) + } + } + + /** + * Estabilishes that connection to discovery providers has regressed + */ + enterRegressedMode() { + console.info('Entering regressed mode') + this._regressedMode = true + setTimeout(() => { + console.info('Leaving regressed mode') + this._regressedMode = false + }, TWO_MINUTES) + } + + isInRegressedMode() { + return this._regressedMode + } + + async getRegistryAddressForContract(contractName: string) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names + this.contracts = this.contracts || { [this.registryAddress]: 'registry' } + this.contractAddresses = this.contractAddresses || { + registry: this.registryAddress + } + if (!this.contractAddresses[contractName]) { + const address = await this.RegistryClient.getContract(contractName) + this.contracts[address] = contractName + this.contractAddresses[contractName] = address + } + + return this.contractAddresses[contractName] as string + } + + async getCurrentVersion(serviceType: string) { + try { + const version = await this.ServiceTypeManagerClient.getCurrentVersion( + serviceType + ) + return version + } catch (e) { + console.log(`Error retrieving version for ${serviceType}`) + return null + } + } + + /* + * Determine the latest version for deployed services such as discovery provider and cache + */ + async getExpectedServiceVersions() { + const versions = await Promise.all( + serviceTypeList.map( + async (serviceType) => await this.getCurrentVersion(serviceType) + ) + ) + const expectedVersions = serviceTypeList.reduce< + Record + >((map, serviceType, i) => { + if (versions[i]) { + map[serviceType] = versions[i] + } + return map + }, {}) + return expectedVersions + } + + /** + * Determine whether major and minor versions match for two version strings + * Version string 2 must have equivalent major/minor versions and a patch >= version1 + * @param version1 string 1 + * @param version2 string 2 + */ + isValidSPVersion(version1: string, version2: string) { + return ( + semver.major(version1) === semver.major(version2) && + semver.minor(version1) === semver.minor(version2) && + semver.patch(version2) >= semver.patch(version1) + ) + } + + /** + * Determines whether the major and minor versions are equal + * @param version1 string 1 + * @param version2 string 2 + */ + hasSameMajorAndMinorVersion(version1: string, version2: string) { + return ( + semver.major(version1) === semver.major(version2) && + semver.minor(version1) === semver.minor(version2) + ) + } + + async getServiceProviderList(spType: string) { + return this.ServiceProviderFactoryClient.getServiceProviderList(spType) + } + + async getNumberOfVersions(spType: string) { + return this.ServiceTypeManagerClient.getNumberOfVersions(spType) + } + + async getVersion(spType: string, queryIndex: number) { + return this.ServiceTypeManagerClient.getVersion(spType, queryIndex) + } + + async getServiceTypeInfo(spType: string) { + return this.ServiceTypeManagerClient.getServiceTypeInfo(spType) + } +} + +module.exports = EthContracts +module.exports.serviceType = serviceType diff --git a/libs/src/services/ethContracts/audiusTokenClient.ts b/libs/src/services/ethContracts/audiusTokenClient.ts index c575710ff7a..c7e5af9ff0d 100644 --- a/libs/src/services/ethContracts/audiusTokenClient.ts +++ b/libs/src/services/ethContracts/audiusTokenClient.ts @@ -6,7 +6,7 @@ import type Wallet from 'ethereumjs-wallet' export class AudiusTokenClient { ethWeb3Manager: EthWeb3Manager - contractABI: AbiItem + contractABI: AbiItem[] contractAddress: string web3: Web3 AudiusTokenContract: Contract @@ -14,7 +14,7 @@ export class AudiusTokenClient { constructor( ethWeb3Manager: EthWeb3Manager, - contractABI: AbiItem, + contractABI: AbiItem[], contractAddress: string ) { this.ethWeb3Manager = ethWeb3Manager diff --git a/libs/src/services/ethContracts/delegateManagerClient.ts b/libs/src/services/ethContracts/delegateManagerClient.ts index 91e76583c38..a3b535752a7 100644 --- a/libs/src/services/ethContracts/delegateManagerClient.ts +++ b/libs/src/services/ethContracts/delegateManagerClient.ts @@ -1,4 +1,4 @@ -import { ContractABI, Utils } from '../../utils' +import { ContractABI, Logger, Utils } from '../../utils' import type { GetRegistryAddress } from '../contracts/ContractClient' import { GovernedContractClient } from '../contracts/GovernedContractClient' import type { EthWeb3Manager } from '../ethWeb3Manager' @@ -24,7 +24,7 @@ export class DelegateManagerClient extends GovernedContractClient { audiusTokenClient: AudiusTokenClient, stakingProxyClient: StakingProxyClient, governanceClient: GovernanceClient, - logger = console + logger: Logger = console ) { super( ethWeb3Manager, diff --git a/libs/src/services/ethContracts/governanceClient.ts b/libs/src/services/ethContracts/governanceClient.ts index 2e60f14e831..b1d2232294c 100644 --- a/libs/src/services/ethContracts/governanceClient.ts +++ b/libs/src/services/ethContracts/governanceClient.ts @@ -1,5 +1,5 @@ import { ContractClient, GetRegistryAddress } from '../contracts/ContractClient' -import { ContractABI, ContractMethod, Utils } from '../../utils' +import { ContractABI, ContractMethod, Logger, Utils } from '../../utils' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { AudiusTokenClient } from './audiusTokenClient' import type { StakingProxyClient } from './stakingProxyClient' @@ -50,7 +50,7 @@ export class GovernanceClient extends ContractClient { getRegistryAddress: GetRegistryAddress, audiusTokenClient: AudiusTokenClient, stakingProxyClient: StakingProxyClient, - logger = console, + logger: Logger = console, isDebug = false ) { super( diff --git a/libs/src/services/ethContracts/index.d.ts b/libs/src/services/ethContracts/index.d.ts deleted file mode 100644 index 93f72a5f3f5..00000000000 --- a/libs/src/services/ethContracts/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ServiceWithEndpoint } from '../../utils' - -export default class EthContracts { - getCurrentVersion: (serviceName: string) => Promise - getNumberOfVersions: (spType: string) => Promise - getVersion: (spType: string, queryIndex: number) => Promise - getServiceProviderList: ( - serviceName: string - ) => Promise - - hasSameMajorAndMinorVersion: (version1: string, version2: string) => boolean - isInRegressedMode: () => boolean -} diff --git a/libs/src/services/ethContracts/index.ts b/libs/src/services/ethContracts/index.ts new file mode 100644 index 00000000000..e4d15f51150 --- /dev/null +++ b/libs/src/services/ethContracts/index.ts @@ -0,0 +1 @@ +export * from './EthContracts' \ No newline at end of file diff --git a/libs/src/services/ethContracts/registryClient.ts b/libs/src/services/ethContracts/registryClient.ts index a9f18ad3ed2..d2406636e62 100644 --- a/libs/src/services/ethContracts/registryClient.ts +++ b/libs/src/services/ethContracts/registryClient.ts @@ -1,19 +1,19 @@ import { Utils } from '../../utils' -import type { Web3Manager } from '../web3Manager' import type { AbiItem } from 'web3-utils' import type Web3 from 'web3' import type { Contract } from 'web3-eth-contract' +import type { EthWeb3Manager } from '../ethWeb3Manager' export class RegistryClient { - web3Manager: Web3Manager - contractABI: AbiItem + web3Manager: EthWeb3Manager + contractABI: AbiItem[] contractAddress: string web3: Web3 Registry: Contract constructor( - web3Manager: Web3Manager, - contractABI: AbiItem, + web3Manager: EthWeb3Manager, + contractABI: AbiItem[], contractAddress: string ) { this.web3Manager = web3Manager @@ -24,7 +24,7 @@ export class RegistryClient { this.Registry = new this.web3.eth.Contract(contractABI, contractAddress) } - async getContract(contractRegistryKey: string) { + async getContract(contractRegistryKey: string): Promise { Utils.checkStrLen(contractRegistryKey, 32) return this.Registry.methods .getContract(Utils.utf8ToHex(contractRegistryKey)) diff --git a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts index 7017d7ce461..cddea39a939 100644 --- a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts +++ b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts @@ -1,6 +1,6 @@ // TODO: a lot of extra parseInt's that result in incorrect (as unknown as string) typecasting -import { ContractABI, Utils } from '../../utils' +import { ContractABI, Logger, Utils } from '../../utils' import { GovernedContractClient } from '../contracts/GovernedContractClient' import axios, { AxiosRequestConfig } from 'axios' import { range } from 'lodash' @@ -32,7 +32,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { audiusTokenClient: AudiusTokenClient, stakingProxyClient: StakingProxyClient, governanceClient: GovernanceClient, - logger = console, + logger: Logger = console, isDebug = false ) { super( diff --git a/libs/src/services/ethContracts/stakingProxyClient.ts b/libs/src/services/ethContracts/stakingProxyClient.ts index 9c4314c152d..a22145ccf62 100644 --- a/libs/src/services/ethContracts/stakingProxyClient.ts +++ b/libs/src/services/ethContracts/stakingProxyClient.ts @@ -1,4 +1,4 @@ -import type { ContractABI } from '../../utils' +import type { ContractABI, Logger } from '../../utils' import { ContractClient, GetRegistryAddress } from '../contracts/ContractClient' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { AudiusTokenClient } from './audiusTokenClient' @@ -14,7 +14,7 @@ export class StakingProxyClient extends ContractClient { contractRegistryKey: string, getRegistryAddress: GetRegistryAddress, audiusTokenClient: AudiusTokenClient, - logger = console + logger: Logger = console ) { super( ethWeb3Manager, diff --git a/libs/src/utils/importContractABI.d.ts b/libs/src/utils/importContractABI.d.ts index 0ae50f75455..4df30ecad81 100644 --- a/libs/src/utils/importContractABI.d.ts +++ b/libs/src/utils/importContractABI.d.ts @@ -6,4 +6,4 @@ export type ContractABI = { } export const importDataContractABIs: (pathStr: string) => ContractABI -export const importEthContractABIs: (pathStr: string) => Record +export const importEthContractABIs: (pathStr: string) => ContractABI From 26b732f697a6a3c056d8e230f9d666c5aa493fa9 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Sat, 23 Apr 2022 17:39:25 -0700 Subject: [PATCH 03/14] Fix lint --- libs/package-lock.json | 81 ++++++++++++++++--- libs/package.json | 2 + libs/src/index.js | 73 ++++++++++++----- .../creatorNode/CreatorNodeSelection.ts | 2 +- .../discoveryProvider/DiscoveryProvider.ts | 4 +- .../DiscoveryProviderSelection.test.ts | 2 +- .../DiscoveryProviderSelection.ts | 2 +- .../src/services/ethContracts/EthContracts.ts | 25 +++--- .../ethContracts/claimsManagerClient.ts | 3 + .../ethContracts/delegateManagerClient.ts | 6 +- .../services/ethContracts/governanceClient.ts | 2 +- libs/src/services/ethContracts/index.ts | 2 +- .../serviceProviderFactoryClient.ts | 5 +- libs/src/services/web3Manager/Web3Manager.ts | 2 +- libs/types/@audius-hedgehog/index.d.ts | 6 +- 15 files changed, 159 insertions(+), 58 deletions(-) diff --git a/libs/package-lock.json b/libs/package-lock.json index 46a85aa4923..0d1ea788c6a 100644 --- a/libs/package-lock.json +++ b/libs/package-lock.json @@ -28,6 +28,24 @@ "node-localstorage": "^1.3.1", "randombytes": "^2.0.6", "safe-buffer": "^5.2.0" + }, + "dependencies": { + "ethereumjs-wallet": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz", + "integrity": "sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA==", + "requires": { + "aes-js": "^3.1.1", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^6.0.0", + "randombytes": "^2.0.6", + "safe-buffer": "^5.1.2", + "scryptsy": "^1.2.1", + "utf8": "^3.0.0", + "uuid": "^3.3.2" + } + } } }, "@babel/code-frame": { @@ -3732,6 +3750,15 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, + "@types/eth-sig-util": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/eth-sig-util/-/eth-sig-util-2.1.1.tgz", + "integrity": "sha512-vYP1UD19i0532+eQY7vXHfAKCzAQiI6aoLvomqu+ZewzUX/jvDSr41JGl8zQ9dMbfiHPDhMFY26bScCyZ7vzlg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -6670,19 +6697,55 @@ } }, "ethereumjs-wallet": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz", - "integrity": "sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz", + "integrity": "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA==", + "dev": true, "requires": { - "aes-js": "^3.1.1", + "aes-js": "^3.1.2", "bs58check": "^2.1.2", "ethereum-cryptography": "^0.1.3", - "ethereumjs-util": "^6.0.0", - "randombytes": "^2.0.6", - "safe-buffer": "^5.1.2", - "scryptsy": "^1.2.1", + "ethereumjs-util": "^7.1.2", + "randombytes": "^2.1.0", + "scrypt-js": "^3.0.1", "utf8": "^3.0.0", - "uuid": "^3.3.2" + "uuid": "^8.3.2" + }, + "dependencies": { + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + }, + "ethereumjs-util": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz", + "integrity": "sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A==", + "dev": true, + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } } }, "ethers": { diff --git a/libs/package.json b/libs/package.json index 587ce370559..a2d6f8f98fa 100644 --- a/libs/package.json +++ b/libs/package.json @@ -77,6 +77,7 @@ "@tsconfig/node16-strictest": "1.0.0", "@types/async-retry": "1.4.3", "@types/bs58": "4.0.1", + "@types/eth-sig-util": "^2.1.1", "@types/expect": "24.3.0", "@types/form-data": "^2.5.0", "@types/hashids": "2.0.1", @@ -93,6 +94,7 @@ "eslint-plugin-node": "11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "5.2.0", + "ethereumjs-wallet": "^1.0.2", "mocha": "9.2.2", "nock": "13.1.2", "nyc": "15.1.0", diff --git a/libs/src/index.js b/libs/src/index.js index 50500959114..115f90c041b 100644 --- a/libs/src/index.js +++ b/libs/src/index.js @@ -2,8 +2,8 @@ const packageJSON = require('../package.json') const { EthWeb3Manager } = require('./services/ethWeb3Manager') const { SolanaAudiusData } = require('./services/solanaAudiusData/index') -const Web3Manager = require('./services/web3Manager/index') -const EthContracts = require('./services/ethContracts/index') +const { Web3Manager } = require('./services/web3Manager') +const { EthContracts } = require('./services/ethContracts') const SolanaWeb3Manager = require('./services/solanaWeb3Manager/index') const AudiusContracts = require('./services/dataContracts/index') const { IdentityService } = require('./services/identity') @@ -30,7 +30,9 @@ const SolanaUtils = require('./services/solanaWeb3Manager/utils') const { Keypair } = require('@solana/web3.js') const { PublicKey } = require('@solana/web3.js') -const { RewardsAttester } = require('./services/solanaWeb3Manager/rewardsAttester') +const { + RewardsAttester +} = require('./services/solanaWeb3Manager/rewardsAttester') class AudiusLibs { /** * Configures a discovery provider wrapper @@ -105,7 +107,13 @@ class AudiusLibs { blockList = null, monitoringCallbacks = {} ) { - return { fallbackUrl, lazyConnect, passList, blockList, monitoringCallbacks } + return { + fallbackUrl, + lazyConnect, + passList, + blockList, + monitoringCallbacks + } } /** @@ -116,7 +124,12 @@ class AudiusLibs { * @param {?string} walletOverride wallet address to force use instead of the first wallet on the provided web3 * @param {?number} walletIndex if using a wallet returned from web3, pick the wallet at this index */ - static async configExternalWeb3 (registryAddress, web3Provider, networkId, walletOverride = null) { + static async configExternalWeb3 ( + registryAddress, + web3Provider, + networkId, + walletOverride = null + ) { const web3Instance = await Utils.configureWeb3(web3Provider, networkId) if (!web3Instance) { throw new Error('External web3 incorrectly configured') @@ -146,7 +159,9 @@ class AudiusLibs { } else if (Array.isArray(providers)) { providerList = providers } else { - throw new Error('Providers must be of type string, Array, or Web3 instance') + throw new Error( + 'Providers must be of type string, Array, or Web3 instance' + ) } return { @@ -184,7 +199,9 @@ class AudiusLibs { } else if (Array.isArray(providers)) { providerList = providers } else { - throw new Error('Providers must be of type string, Array, or Web3 instance') + throw new Error( + 'Providers must be of type string, Array, or Web3 instance' + ) } return { @@ -268,7 +285,9 @@ class AudiusLibs { audiusDataIdl }) { if (audiusDataAdminStorageKeypairPublicKey instanceof String) { - audiusDataAdminStorageKeypairPublicKey = new PublicKey(audiusDataAdminStorageKeypairPublicKey) + audiusDataAdminStorageKeypairPublicKey = new PublicKey( + audiusDataAdminStorageKeypairPublicKey + ) } if (audiusDataProgramId instanceof String) { audiusDataProgramId = new PublicKey(audiusDataProgramId) @@ -284,7 +303,9 @@ class AudiusLibs { rewardsManagerProgramPDA, rewardsManagerTokenPDA, useRelay, - feePayerKeypairs: feePayerSecretKeys ? feePayerSecretKeys.map(key => Keypair.fromSecretKey(key)) : null, + feePayerKeypairs: feePayerSecretKeys + ? feePayerSecretKeys.map((key) => Keypair.fromSecretKey(key)) + : null, confirmationTimeout, audiusDataAdminStorageKeypairPublicKey, audiusDataProgramId, @@ -298,13 +319,10 @@ class AudiusLibs { * @param {string} config.programId Program ID of the audius data program * @param {string} config.adminAccount Public Key of admin account */ - static configSolanaAudiusData ({ - programId, - adminAccount, - }) { + static configSolanaAudiusData ({ programId, adminAccount }) { return { programId, - adminAccount, + adminAccount } } @@ -399,8 +417,14 @@ class AudiusLibs { /** Identity Service */ if (this.identityServiceConfig) { - this.identityService = new IdentityService(this.identityServiceConfig.url, this.captcha) - this.hedgehog = new Hedgehog(this.identityService, this.identityServiceConfig.useHedgehogLocalStorage) + this.identityService = new IdentityService( + this.identityServiceConfig.url, + this.captcha + ) + this.hedgehog = new Hedgehog( + this.identityService, + this.identityServiceConfig.useHedgehogLocalStorage + ) } else if (this.web3Config && !this.web3Config.useExternalWeb3) { throw new Error('Identity Service required for internal Web3') } @@ -449,8 +473,11 @@ class AudiusLibs { this.ethWeb3Manager, this.ethWeb3Config ? this.ethWeb3Config.tokenAddress : null, this.ethWeb3Config ? this.ethWeb3Config.registryAddress : null, - (this.ethWeb3Config && this.ethWeb3Config.claimDistributionContractAddress) || null, - (this.ethWeb3Config && this.ethWeb3Config.wormholeContractAddress) || null, + (this.ethWeb3Config && + this.ethWeb3Config.claimDistributionContractAddress) || + null, + (this.ethWeb3Config && this.ethWeb3Config.wormholeContractAddress) || + null, this.isServer, this.logger, this.isDebug @@ -467,7 +494,12 @@ class AudiusLibs { contractsToInit.push(this.contracts.init()) } await Promise.all(contractsToInit) - if (this.wormholeConfig && this.ethWeb3Manager && this.ethContracts && this.solanaWeb3Manager) { + if ( + this.wormholeConfig && + this.ethWeb3Manager && + this.ethContracts && + this.solanaWeb3Manager + ) { this.wormholeClient = new Wormhole( this.hedgehog, this.ethWeb3Manager, @@ -506,7 +538,8 @@ class AudiusLibs { if (this.creatorNodeConfig) { const currentUser = this.userStateManager.getCurrentUser() const creatorNodeEndpoint = currentUser - ? CreatorNode.getPrimary(currentUser.creator_node_endpoint) || this.creatorNodeConfig.fallbackUrl + ? CreatorNode.getPrimary(currentUser.creator_node_endpoint) || + this.creatorNodeConfig.fallbackUrl : this.creatorNodeConfig.fallbackUrl this.creatorNode = new CreatorNode( diff --git a/libs/src/services/creatorNode/CreatorNodeSelection.ts b/libs/src/services/creatorNode/CreatorNodeSelection.ts index 08af8191b04..519c516c167 100644 --- a/libs/src/services/creatorNode/CreatorNodeSelection.ts +++ b/libs/src/services/creatorNode/CreatorNodeSelection.ts @@ -1,6 +1,6 @@ import type { AxiosResponse } from 'axios' import _ from 'lodash' -import type EthContracts from '../ethContracts' +import type { EthContracts } from '../ethContracts' import { ServiceSelection, diff --git a/libs/src/services/discoveryProvider/DiscoveryProvider.ts b/libs/src/services/discoveryProvider/DiscoveryProvider.ts index 09ef639fabd..a919f8d3950 100644 --- a/libs/src/services/discoveryProvider/DiscoveryProvider.ts +++ b/libs/src/services/discoveryProvider/DiscoveryProvider.ts @@ -13,8 +13,8 @@ import { DiscoveryProviderSelectionConfig } from './DiscoveryProviderSelection' import type { CurrentUser, UserStateManager } from '../../userStateManager' -import type EthContracts from '../ethContracts' -import type Web3Manager from '../web3Manager' +import type { EthContracts } from '../ethContracts' +import type { Web3Manager } from '../web3Manager' const MAX_MAKE_REQUEST_RETRY_COUNT = 5 const MAX_MAKE_REQUEST_RETRIES_WITH_404 = 2 diff --git a/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts b/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts index e390ed4e9f8..b4bbd386ff0 100644 --- a/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +++ b/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts @@ -3,7 +3,7 @@ import assert from 'assert' import semver from 'semver' import { DiscoveryProviderSelection } from './DiscoveryProviderSelection' import { DISCOVERY_PROVIDER_TIMESTAMP } from './constants' -import type EthContracts from '../ethContracts' +import type { EthContracts } from '../ethContracts' import { LocalStorage } from 'node-localstorage' const mockEthContracts = ( diff --git a/libs/src/services/discoveryProvider/DiscoveryProviderSelection.ts b/libs/src/services/discoveryProvider/DiscoveryProviderSelection.ts index 30bcc3d9617..8eb5cedc3df 100644 --- a/libs/src/services/discoveryProvider/DiscoveryProviderSelection.ts +++ b/libs/src/services/discoveryProvider/DiscoveryProviderSelection.ts @@ -12,7 +12,7 @@ import { REGRESSED_MODE_TIMEOUT } from './constants' import semver from 'semver' -import type EthContracts from '../ethContracts' +import type { EthContracts } from '../ethContracts' import type { AxiosResponse } from 'axios' import type { Maybe, Nullable } from '../../utils' diff --git a/libs/src/services/ethContracts/EthContracts.ts b/libs/src/services/ethContracts/EthContracts.ts index 952246f2c56..4558af3f595 100644 --- a/libs/src/services/ethContracts/EthContracts.ts +++ b/libs/src/services/ethContracts/EthContracts.ts @@ -52,13 +52,13 @@ const TrustedNotifierManagerProxyKey = 'TrustedNotifierManagerProxy' const TWO_MINUTES = 2 * 60 * 1000 -const serviceType = Object.freeze({ +export const serviceType = Object.freeze({ DISCOVERY_PROVIDER: 'discovery-node', CREATOR_NODE: 'content-node' }) const serviceTypeList = Object.values(serviceType) -class EthContracts { +export class EthContracts { ethWeb3Manager: EthWeb3Manager tokenContractAddress: string claimDistributionContractAddress: string @@ -233,7 +233,9 @@ class EthContracts { throw new Error('Failed to initialize EthContracts') if (this.isServer) { - await Promise.all(this.contractClients.map((client) => client.init())) + await Promise.all( + this.contractClients.map(async (client) => await client.init()) + ) } } @@ -255,8 +257,8 @@ class EthContracts { async getRegistryAddressForContract(contractName: string) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names - this.contracts = this.contracts || { [this.registryAddress]: 'registry' } - this.contractAddresses = this.contractAddresses || { + this.contracts = this.contracts ?? { [this.registryAddress]: 'registry' } + this.contractAddresses = this.contractAddresses ?? { registry: this.registryAddress } if (!this.contractAddresses[contractName]) { @@ -327,21 +329,20 @@ class EthContracts { } async getServiceProviderList(spType: string) { - return this.ServiceProviderFactoryClient.getServiceProviderList(spType) + return await this.ServiceProviderFactoryClient.getServiceProviderList( + spType + ) } async getNumberOfVersions(spType: string) { - return this.ServiceTypeManagerClient.getNumberOfVersions(spType) + return await this.ServiceTypeManagerClient.getNumberOfVersions(spType) } async getVersion(spType: string, queryIndex: number) { - return this.ServiceTypeManagerClient.getVersion(spType, queryIndex) + return await this.ServiceTypeManagerClient.getVersion(spType, queryIndex) } async getServiceTypeInfo(spType: string) { - return this.ServiceTypeManagerClient.getServiceTypeInfo(spType) + return await this.ServiceTypeManagerClient.getServiceTypeInfo(spType) } } - -module.exports = EthContracts -module.exports.serviceType = serviceType diff --git a/libs/src/services/ethContracts/claimsManagerClient.ts b/libs/src/services/ethContracts/claimsManagerClient.ts index 2cb939f4fd1..9b991d42082 100644 --- a/libs/src/services/ethContracts/claimsManagerClient.ts +++ b/libs/src/services/ethContracts/claimsManagerClient.ts @@ -1,7 +1,10 @@ import { Utils } from '../../utils' import { ContractClient } from '../contracts/ContractClient' +import type { EthWeb3Manager } from '../ethWeb3Manager' export class ClaimsManagerClient extends ContractClient { + // @ts-expect-error defined in ContractClient + override web3Manager: EthWeb3Manager /* ------- GETTERS ------- */ // Get the duration of a funding round in blocks diff --git a/libs/src/services/ethContracts/delegateManagerClient.ts b/libs/src/services/ethContracts/delegateManagerClient.ts index a3b535752a7..e476c03b849 100644 --- a/libs/src/services/ethContracts/delegateManagerClient.ts +++ b/libs/src/services/ethContracts/delegateManagerClient.ts @@ -45,7 +45,7 @@ export class DelegateManagerClient extends GovernedContractClient { const method = await this.getMethod('delegateStake', targetSP, amount) const tx = await this.web3Manager.sendTransaction(method) - const returnValues = tx.events?.['IncreaseDelegatedStake']?.returnValues + const returnValues = tx.events?.IncreaseDelegatedStake?.returnValues return { txReceipt: tx, @@ -255,7 +255,7 @@ export class DelegateManagerClient extends GovernedContractClient { const tx = await this.web3Manager.sendTransaction(method) const returnValues = - tx.events?.['UndelegateStakeRequestEvaluated']?.returnValues + tx.events?.UndelegateStakeRequestEvaluated?.returnValues return { txReceipt: tx, @@ -299,7 +299,7 @@ export class DelegateManagerClient extends GovernedContractClient { ) const tx = await this.web3Manager.sendTransaction(method) const returnValues = - tx.events?.['RemoveDelegatorRequestEvaluated']?.returnValues + tx.events?.RemoveDelegatorRequestEvaluated?.returnValues return { txReceipt: tx, delegator: returnValues._delegator, diff --git a/libs/src/services/ethContracts/governanceClient.ts b/libs/src/services/ethContracts/governanceClient.ts index b1d2232294c..247d1c4ba66 100644 --- a/libs/src/services/ethContracts/governanceClient.ts +++ b/libs/src/services/ethContracts/governanceClient.ts @@ -236,7 +236,7 @@ export class GovernanceClient extends ContractClient { description ) const tx = await this.web3Manager.sendTransaction(method) - const id = tx.events?.['ProposalSubmitted']?.returnValues?._proposalId + const id = tx.events?.ProposalSubmitted?.returnValues?._proposalId if (id) { return id } diff --git a/libs/src/services/ethContracts/index.ts b/libs/src/services/ethContracts/index.ts index e4d15f51150..81b8bbfdbca 100644 --- a/libs/src/services/ethContracts/index.ts +++ b/libs/src/services/ethContracts/index.ts @@ -1 +1 @@ -export * from './EthContracts' \ No newline at end of file +export * from './EthContracts' diff --git a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts index cddea39a939..f51f9ac8baf 100644 --- a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts +++ b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts @@ -96,7 +96,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { ) // @ts-expect-error TODO: this seems incorrect const tx = await this.web3Manager.sendTransaction(method, 1000000) - const returnValues = tx.events?.['RegisteredServiceProvider']?.returnValues + const returnValues = tx.events?.RegisteredServiceProvider?.returnValues return { txReceipt: tx, spID: parseInt(returnValues._spID), @@ -457,8 +457,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { endpoint ) const tx = await this.web3Manager.sendTransaction(method) - const returnValues = - tx.events?.['DeregisteredServiceProvider']?.returnValues + const returnValues = tx.events?.DeregisteredServiceProvider?.returnValues return { txReceipt: tx, diff --git a/libs/src/services/web3Manager/Web3Manager.ts b/libs/src/services/web3Manager/Web3Manager.ts index 04691a34de2..a236599355e 100644 --- a/libs/src/services/web3Manager/Web3Manager.ts +++ b/libs/src/services/web3Manager/Web3Manager.ts @@ -250,7 +250,7 @@ export class Web3Manager { const events: TransactionReceipt['events'] = {} // TODO: decodeLogs appears to return DecodedLog, not DecodedLog[] so maybe a type/version issue const decoded = this.AudiusABIDecoder.decodeLogs( - contractRegistryKey, + contractRegistryKey as string, receipt.logs ) as unknown as DecodedLog[] decoded.forEach((evt) => { diff --git a/libs/types/@audius-hedgehog/index.d.ts b/libs/types/@audius-hedgehog/index.d.ts index 6de70993d5f..4477085ad62 100644 --- a/libs/types/@audius-hedgehog/index.d.ts +++ b/libs/types/@audius-hedgehog/index.d.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/no-extraneous-class */ declare module '@audius/hedgehog' { import { IdentityService } from '../../src/services/identity' + import type Wallet from 'ethereumjs-wallet' type RecoveryInfo = { login: string host: string } - type Wallet = Record - export class Hedgehog { wallet: Wallet getFn: IdentityService['getFn'] @@ -23,7 +22,8 @@ declare module '@audius/hedgehog' { ): void async login(email: string, password: string): Promise async generateRecoveryInfo(): Promise - getWallet(): string + getWallet(): Wallet + async createWalletObj(passwordEntropy: string): Promise } export class WalletManager { From 694ff92e00051ba8ff74fdf7afa7121a08a3c164 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Sun, 17 Apr 2022 12:35:46 -0700 Subject: [PATCH 04/14] Refactor Web3Manager to typescript --- .../services/ABIDecoder/AudiusABIDecoder.ts | 2 +- libs/src/services/ABIDecoder/index.ts | 72 +---- libs/src/services/contracts/ContractClient.ts | 2 +- libs/src/services/creatorNode/CreatorNode.ts | 2 +- .../creatorNode/CreatorNodeSelection.test.ts | 40 +-- .../creatorNode/CreatorNodeSelection.ts | 4 +- .../DiscoveryProviderSelection.test.ts | 39 +-- .../src/services/ethContracts/EthContracts.ts | 2 +- .../ethContracts/delegateManagerClient.ts | 6 +- .../services/ethContracts/governanceClient.ts | 2 +- .../serviceProviderFactoryClient.ts | 5 +- .../services/ethWeb3Manager/EthWeb3Manager.ts | 15 +- libs/src/services/identity/IdentityService.ts | 2 +- libs/src/services/web3Manager/Web3Config.ts | 17 + libs/src/services/web3Manager/Web3Manager.ts | 4 +- .../services/web3Manager/XMLHttpRequest.ts | 9 + libs/src/services/web3Manager/index.d.ts | 22 -- libs/src/services/web3Manager/index.js | 290 ------------------ libs/src/services/web3Manager/index.ts | 2 + libs/src/utils/estimateGas.ts | 4 +- libs/types/@audius-hedgehog/index.d.ts | 2 +- 21 files changed, 94 insertions(+), 449 deletions(-) create mode 100644 libs/src/services/web3Manager/Web3Config.ts create mode 100644 libs/src/services/web3Manager/XMLHttpRequest.ts delete mode 100644 libs/src/services/web3Manager/index.d.ts delete mode 100644 libs/src/services/web3Manager/index.js create mode 100644 libs/src/services/web3Manager/index.ts diff --git a/libs/src/services/ABIDecoder/AudiusABIDecoder.ts b/libs/src/services/ABIDecoder/AudiusABIDecoder.ts index 3f561861609..7e9b95d1543 100644 --- a/libs/src/services/ABIDecoder/AudiusABIDecoder.ts +++ b/libs/src/services/ABIDecoder/AudiusABIDecoder.ts @@ -65,7 +65,7 @@ export class AudiusABIDecoder { return decoded } - static decodeLogs(_: string | unknown, logs: Log[]) { + static decodeLogs(_: string, logs: Log[]) { return abiDecoder.decodeLogs(logs) } } diff --git a/libs/src/services/ABIDecoder/index.ts b/libs/src/services/ABIDecoder/index.ts index 7e9b95d1543..5e75922f858 100644 --- a/libs/src/services/ABIDecoder/index.ts +++ b/libs/src/services/ABIDecoder/index.ts @@ -1,71 +1 @@ -import abiDecoder from 'abi-decoder' -import { Utils } from '../../utils' -import type { AbiItem, AbiInput } from 'web3-utils' -import type { Log } from 'web3-core' - -const abiMap: Record = {} - -function loadABI(abiFile: string) { - const contract = Utils.importDataContractABI(abiFile) - abiDecoder.addABI(contract.abi) - abiMap[contract.contractName] = contract.abi -} - -loadABI('Registry.json') -loadABI('UserFactory.json') -loadABI('TrackFactory.json') -loadABI('DiscoveryProviderFactory.json') -loadABI('SocialFeatureFactory.json') -loadABI('PlaylistFactory.json') -loadABI('UserLibraryFactory.json') -loadABI('UserReplicaSetManager.json') - -// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- should just use esm -export class AudiusABIDecoder { - static decodeMethod(contractName: string, encodedABI: string) { - const decoded = abiDecoder.decodeMethod(encodedABI) - if (!decoded) { - throw new Error('No Audius ABI matches given data') - } - - // hack around abi-decoder's lack of contract-specific support (only one global - // namespace of functions) - const abi = abiMap[contractName] - if (!abi) { - throw new Error('Unrecognized contract name') - } - - let foundFunction: AbiItem | undefined - abi.forEach((item) => { - if (item.type === 'function' && item.name === decoded.name) { - foundFunction = item - } - }) - - if (!foundFunction) { - throw new Error( - `Unrecognized function ${decoded.name} for contract ${contractName}` - ) - } - - const paramSpecs = foundFunction.inputs as AbiInput[] - decoded.params.forEach((param, idx) => { - if (idx >= paramSpecs.length) { - throw new Error('Extra parameter') - } - - const paramSpec = paramSpecs[idx] - if (paramSpec?.name !== param.name || paramSpec.type !== param.type) { - throw new Error( - `Invalid name or value for param ${paramSpec?.name}: ${paramSpec?.type}` - ) - } - }) - - return decoded - } - - static decodeLogs(_: string, logs: Log[]) { - return abiDecoder.decodeLogs(logs) - } -} +export * from './AudiusABIDecoder' diff --git a/libs/src/services/contracts/ContractClient.ts b/libs/src/services/contracts/ContractClient.ts index 772e1b856e3..13f18aed94f 100644 --- a/libs/src/services/contracts/ContractClient.ts +++ b/libs/src/services/contracts/ContractClient.ts @@ -1,5 +1,5 @@ import { ProviderSelection } from './ProviderSelection' -import Web3Manager from '../web3Manager' +import { Web3Manager } from '../web3Manager' import retry from 'async-retry' import type { ContractABI, Nullable, Logger } from '../../utils' import type { Contract } from 'web3-eth-contract' diff --git a/libs/src/services/creatorNode/CreatorNode.ts b/libs/src/services/creatorNode/CreatorNode.ts index d0ba6ba5b31..0f83fdf55d7 100644 --- a/libs/src/services/creatorNode/CreatorNode.ts +++ b/libs/src/services/creatorNode/CreatorNode.ts @@ -7,7 +7,7 @@ import { trackSchemaType, Schemas } from '../schemaValidator/SchemaValidator' -import type Web3Manager from '../web3Manager' +import type { Web3Manager }from '../web3Manager' import type { CurrentUser, UserStateManager } from '../../userStateManager' const { wait } = Utils diff --git a/libs/src/services/creatorNode/CreatorNodeSelection.test.ts b/libs/src/services/creatorNode/CreatorNodeSelection.test.ts index f9cee355673..c3cf95329fe 100644 --- a/libs/src/services/creatorNode/CreatorNodeSelection.test.ts +++ b/libs/src/services/creatorNode/CreatorNodeSelection.test.ts @@ -4,31 +4,33 @@ import semver from 'semver' import { CREATOR_NODE_SERVICE_NAME } from './constants' import { CreatorNodeSelection } from './CreatorNodeSelection' +import type { EthContracts } from '../ethContracts' const mockEthContracts = ( urls: string[], currrentVersion: string, previousVersions: string[] | null = null -) => ({ - getCurrentVersion: async () => currrentVersion, - getNumberOfVersions: async () => 2, - getVersion: async (_: string, queryIndex: number) => { - if (previousVersions) { - return previousVersions[queryIndex] as string +): EthContracts => + ({ + getCurrentVersion: async () => currrentVersion, + getNumberOfVersions: async () => 2, + getVersion: async (_: string, queryIndex: number) => { + if (previousVersions) { + return previousVersions[queryIndex] as string + } + return ['1.2.2', '1.2.3'][queryIndex] as string + }, + getServiceProviderList: async () => urls.map((u) => ({ endpoint: u })), + hasSameMajorAndMinorVersion: (version1: string, version2: string) => { + return ( + semver.major(version1) === semver.major(version2) && + semver.minor(version1) === semver.minor(version2) + ) + }, + isInRegressedMode: () => { + return false } - return ['1.2.2', '1.2.3'][queryIndex] as string - }, - getServiceProviderList: async () => urls.map((u) => ({ endpoint: u })), - hasSameMajorAndMinorVersion: (version1: string, version2: string) => { - return ( - semver.major(version1) === semver.major(version2) && - semver.minor(version1) === semver.minor(version2) - ) - }, - isInRegressedMode: () => { - return false - } -}) + } as unknown as EthContracts) const mockCreatorNode = { getSyncStatus: async () => { diff --git a/libs/src/services/creatorNode/CreatorNodeSelection.ts b/libs/src/services/creatorNode/CreatorNodeSelection.ts index 519c516c167..dc394039a33 100644 --- a/libs/src/services/creatorNode/CreatorNodeSelection.ts +++ b/libs/src/services/creatorNode/CreatorNodeSelection.ts @@ -22,13 +22,13 @@ type Timeout = number | null * In memory dictionary used to query spID from endpoint * Eliminates duplicate web3 calls within same session */ -const contentNodeEndpointToSpID: Record = {} +const contentNodeEndpointToSpID: Record = {} export function getSpIDForEndpoint(endpoint: string) { return contentNodeEndpointToSpID[endpoint] } -export function setSpIDForEndpoint(endpoint: string, spID?: string) { +export function setSpIDForEndpoint(endpoint: string, spID?: number) { contentNodeEndpointToSpID[endpoint] = spID } diff --git a/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts b/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts index b4bbd386ff0..b7a1073bc9b 100644 --- a/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +++ b/libs/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts @@ -10,26 +10,27 @@ const mockEthContracts = ( urls: string[], currrentVersion: string, previousVersions: string[] | null = null -): EthContracts => ({ - getCurrentVersion: async () => currrentVersion, - getNumberOfVersions: async (_: string) => 2, - getVersion: async (_: string, queryIndex: number): Promise => { - if (previousVersions) { - return previousVersions[queryIndex] as string +): EthContracts => + ({ + getCurrentVersion: async () => currrentVersion, + getNumberOfVersions: async (_: string) => 2, + getVersion: async (_: string, queryIndex: number): Promise => { + if (previousVersions) { + return previousVersions[queryIndex] as string + } + return ['1.2.2', '1.2.3'][queryIndex] as string + }, + getServiceProviderList: async () => urls.map((u) => ({ endpoint: u })), + hasSameMajorAndMinorVersion: (version1: string, version2: string) => { + return ( + semver.major(version1) === semver.major(version2) && + semver.minor(version1) === semver.minor(version2) + ) + }, + isInRegressedMode: () => { + return false } - return ['1.2.2', '1.2.3'][queryIndex] as string - }, - getServiceProviderList: async () => urls.map((u) => ({ endpoint: u })), - hasSameMajorAndMinorVersion: (version1: string, version2: string) => { - return ( - semver.major(version1) === semver.major(version2) && - semver.minor(version1) === semver.minor(version2) - ) - }, - isInRegressedMode: () => { - return false - } -}) + } as unknown as EthContracts) describe('DiscoveryProviderSelection', () => { beforeEach(() => { diff --git a/libs/src/services/ethContracts/EthContracts.ts b/libs/src/services/ethContracts/EthContracts.ts index 4558af3f595..675a2362624 100644 --- a/libs/src/services/ethContracts/EthContracts.ts +++ b/libs/src/services/ethContracts/EthContracts.ts @@ -278,7 +278,7 @@ export class EthContracts { return version } catch (e) { console.log(`Error retrieving version for ${serviceType}`) - return null + return '' } } diff --git a/libs/src/services/ethContracts/delegateManagerClient.ts b/libs/src/services/ethContracts/delegateManagerClient.ts index e476c03b849..a3b535752a7 100644 --- a/libs/src/services/ethContracts/delegateManagerClient.ts +++ b/libs/src/services/ethContracts/delegateManagerClient.ts @@ -45,7 +45,7 @@ export class DelegateManagerClient extends GovernedContractClient { const method = await this.getMethod('delegateStake', targetSP, amount) const tx = await this.web3Manager.sendTransaction(method) - const returnValues = tx.events?.IncreaseDelegatedStake?.returnValues + const returnValues = tx.events?.['IncreaseDelegatedStake']?.returnValues return { txReceipt: tx, @@ -255,7 +255,7 @@ export class DelegateManagerClient extends GovernedContractClient { const tx = await this.web3Manager.sendTransaction(method) const returnValues = - tx.events?.UndelegateStakeRequestEvaluated?.returnValues + tx.events?.['UndelegateStakeRequestEvaluated']?.returnValues return { txReceipt: tx, @@ -299,7 +299,7 @@ export class DelegateManagerClient extends GovernedContractClient { ) const tx = await this.web3Manager.sendTransaction(method) const returnValues = - tx.events?.RemoveDelegatorRequestEvaluated?.returnValues + tx.events?.['RemoveDelegatorRequestEvaluated']?.returnValues return { txReceipt: tx, delegator: returnValues._delegator, diff --git a/libs/src/services/ethContracts/governanceClient.ts b/libs/src/services/ethContracts/governanceClient.ts index 247d1c4ba66..b1d2232294c 100644 --- a/libs/src/services/ethContracts/governanceClient.ts +++ b/libs/src/services/ethContracts/governanceClient.ts @@ -236,7 +236,7 @@ export class GovernanceClient extends ContractClient { description ) const tx = await this.web3Manager.sendTransaction(method) - const id = tx.events?.ProposalSubmitted?.returnValues?._proposalId + const id = tx.events?.['ProposalSubmitted']?.returnValues?._proposalId if (id) { return id } diff --git a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts index f51f9ac8baf..cddea39a939 100644 --- a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts +++ b/libs/src/services/ethContracts/serviceProviderFactoryClient.ts @@ -96,7 +96,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { ) // @ts-expect-error TODO: this seems incorrect const tx = await this.web3Manager.sendTransaction(method, 1000000) - const returnValues = tx.events?.RegisteredServiceProvider?.returnValues + const returnValues = tx.events?.['RegisteredServiceProvider']?.returnValues return { txReceipt: tx, spID: parseInt(returnValues._spID), @@ -457,7 +457,8 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { endpoint ) const tx = await this.web3Manager.sendTransaction(method) - const returnValues = tx.events?.DeregisteredServiceProvider?.returnValues + const returnValues = + tx.events?.['DeregisteredServiceProvider']?.returnValues return { txReceipt: tx, diff --git a/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts b/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts index 0a8feac16d6..854c19f033d 100644 --- a/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts +++ b/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts @@ -1,12 +1,6 @@ import Web3 from '../../web3' import type Web3Type from 'web3' -import { - MultiProvider, - estimateGas, - Providers, - ContractMethod, - Maybe -} from '../../utils' +import { MultiProvider, estimateGas, ContractMethod, Maybe } from '../../utils' import { Transaction as EthereumTx } from 'ethereumjs-tx' import retry from 'async-retry' import type { IdentityService, RelayTransaction } from '../identity' @@ -27,7 +21,7 @@ export class EthWeb3Manager { web3: Web3Type identityService: IdentityService hedgehog: Hedgehog - ownerWallet: Maybe + ownerWallet: Maybe constructor( web3Config: Web3Config, @@ -62,6 +56,7 @@ export class EthWeb3Manager { getWalletAddress() { if (this.ownerWallet) { + // @ts-expect-error TODO extend ethereum-js-wallet to include toLowerCase return this.ownerWallet.toLowerCase() } throw new Error('Owner wallet not set') @@ -89,7 +84,7 @@ export class EthWeb3Manager { txGasLimit ?? (await estimateGas({ method: contractMethod, - from: this.ownerWallet as string, + from: this.ownerWallet, gasLimitMaximum: MAX_GAS_LIMIT })) if (contractAddress && privateKey) { @@ -218,7 +213,7 @@ export class EthWeb3Manager { async getRelayMethodParams( contractAddress: string, contractMethod: ContractMethod, - relayerWallet: string + relayerWallet: Wallet ) { const encodedABI = contractMethod.encodeABI() const gasLimit = await estimateGas({ diff --git a/libs/src/services/identity/IdentityService.ts b/libs/src/services/identity/IdentityService.ts index ecdb57938e8..1081d8ad7e3 100644 --- a/libs/src/services/identity/IdentityService.ts +++ b/libs/src/services/identity/IdentityService.ts @@ -5,7 +5,7 @@ import type { Captcha } from '../../utils' import { getTrackListens, TimeFrame } from './requests' import type { Web3Manager } from '../web3Manager' -import type { Log, TransactionReceipt } from 'web3-core' +import type { TransactionReceipt } from 'web3-core' import type Wallet from 'ethereumjs-wallet' type Data = Record diff --git a/libs/src/services/web3Manager/Web3Config.ts b/libs/src/services/web3Manager/Web3Config.ts new file mode 100644 index 00000000000..7b5f0ed4754 --- /dev/null +++ b/libs/src/services/web3Manager/Web3Config.ts @@ -0,0 +1,17 @@ +import type Web3 from 'web3' +import type { Providers } from '../../utils' +import type Wallet from 'ethereumjs-wallet' + +export type Web3Config = { + ownerWallet: Wallet + providers: Providers + useExternalWeb3: boolean + internalWeb3Config: { + web3ProviderEndpoints: string[] + privateKey: string + } + externalWeb3Config: { + web3: Web3 + ownerWallet: Wallet + } +} diff --git a/libs/src/services/web3Manager/Web3Manager.ts b/libs/src/services/web3Manager/Web3Manager.ts index a236599355e..9a65049d567 100644 --- a/libs/src/services/web3Manager/Web3Manager.ts +++ b/libs/src/services/web3Manager/Web3Manager.ts @@ -258,8 +258,8 @@ export class Web3Manager { evt.events.forEach((arg) => { returnValues[arg.name] = arg.value }) - const eventLog = { returnValues } - events[evt.name] = eventLog as EventLog + const eventLog = { returnValues } as EventLog + events[evt.name] = eventLog }) receipt.events = events } diff --git a/libs/src/services/web3Manager/XMLHttpRequest.ts b/libs/src/services/web3Manager/XMLHttpRequest.ts new file mode 100644 index 00000000000..ff85d081d9a --- /dev/null +++ b/libs/src/services/web3Manager/XMLHttpRequest.ts @@ -0,0 +1,9 @@ +let XMLHttpRequestRef: typeof window.XMLHttpRequest + +if (typeof window === 'undefined' || window === null) { + XMLHttpRequestRef = require('xmlhttprequest').XMLHttpRequest +} else { + XMLHttpRequestRef = window.XMLHttpRequest +} + +export { XMLHttpRequestRef as XMLHttpRequest } diff --git a/libs/src/services/web3Manager/index.d.ts b/libs/src/services/web3Manager/index.d.ts deleted file mode 100644 index 84462311566..00000000000 --- a/libs/src/services/web3Manager/index.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type Web3 from 'web3' -import type { provider as Provider } from 'web3-core' - -export type Web3Config = { - useExternalWeb3: boolean - internalWeb3Config: { - web3ProviderEndpoints: string[] - } -} - -class Web3Manager { - getWalletAddress(): string - sign(clientChallengeKey: string): Promise - provider(service: string, timeout: number): Provider - getWeb3(): Web3 - setWeb3(web3: Web3) - web3Config: Web3Config - web3: Web3 - ownerWallet: any -} - -export default Web3Manager diff --git a/libs/src/services/web3Manager/index.js b/libs/src/services/web3Manager/index.js deleted file mode 100644 index d64a926fe74..00000000000 --- a/libs/src/services/web3Manager/index.js +++ /dev/null @@ -1,290 +0,0 @@ -const Web3 = require('../../web3') -const sigUtil = require('eth-sig-util') -const retry = require('async-retry') -const { estimateGas } = require('../../utils/estimateGas') -const { AudiusABIDecoder } = require('../ABIDecoder/index') -const EthereumWallet = require('ethereumjs-wallet') -let XMLHttpRequestRef -if (typeof window === 'undefined' || window === null) { - XMLHttpRequestRef = require('xmlhttprequest').XMLHttpRequest -} else { - XMLHttpRequestRef = window.XMLHttpRequest -} - -const DEFAULT_GAS_LIMIT = 2000000 - -/** singleton class to be instantiated and persisted with every AudiusLibs */ -class Web3Manager { - constructor (web3Config, identityService, hedgehog, isServer) { - this.web3Config = web3Config - this.isServer = isServer - - // Unset if externalWeb3 = true - this.identityService = identityService - this.hedgehog = hedgehog - this.AudiusABIDecoder = AudiusABIDecoder - } - - async init () { - const web3Config = this.web3Config - if (!web3Config) throw new Error('Failed to initialize Web3Manager') - - if ( - // External Web3 - web3Config && - web3Config.useExternalWeb3 && - web3Config.externalWeb3Config && - web3Config.externalWeb3Config.web3 && - web3Config.externalWeb3Config.ownerWallet - ) { - this.web3 = web3Config.externalWeb3Config.web3 - this.useExternalWeb3 = true - this.ownerWallet = web3Config.externalWeb3Config.ownerWallet - } else if ( - // Internal Web3 - web3Config && - !web3Config.useExternalWeb3 && - web3Config.internalWeb3Config && - web3Config.internalWeb3Config.web3ProviderEndpoints - ) { - // either user has external web3 but it's not configured, or doesn't have web3 - this.web3 = new Web3(this.provider(web3Config.internalWeb3Config.web3ProviderEndpoints[0], 10000)) - this.useExternalWeb3 = false - - if (web3Config.internalWeb3Config.privateKey) { - const pkeyBuffer = Buffer.from(web3Config.internalWeb3Config.privateKey, 'hex') - this.ownerWallet = EthereumWallet.fromPrivateKey(pkeyBuffer) - return - } - - // create private key pair here if it doesn't already exist - const storedWallet = this.hedgehog.getWallet() - if (storedWallet) { - this.ownerWallet = storedWallet - } else { - const passwordEntropy = `audius-dummy-pkey-${Math.floor(Math.random() * 1000000)}` - this.ownerWallet = await this.hedgehog.createWalletObj(passwordEntropy) - } - } else { - throw new Error("web3ProviderEndpoint isn't passed into constructor") - } - } - - getWeb3 () { - return this.web3 - } - - setWeb3 (web3) { - this.web3 = web3 - } - - getWalletAddress () { - if (this.useExternalWeb3) { - // Lowercase the owner wallet. Consider using the checksum address. - // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md. - return this.ownerWallet.toLowerCase() - } else { - return this.ownerWallet.getAddressString() - } - } - - setOwnerWallet (ownerWallet) { - this.ownerWallet = ownerWallet - } - - web3IsExternal () { - return this.useExternalWeb3 - } - - getOwnerWalletPrivateKey () { - if (this.useExternalWeb3) { - throw new Error("Can't get owner wallet private key for external web3") - } else { - return this.ownerWallet.getPrivateKey() - } - } - - /** - * Signs provided string data (should be timestamped). - * @param {string} data - */ - async sign (data) { - if (this.useExternalWeb3) { - const account = this.getWalletAddress() - if (this.isServer) { - return this.web3.eth.sign(this.web3.utils.fromUtf8(data), account) - } else { - return this.web3.eth.personal.sign(this.web3.utils.fromUtf8(data), account) - } - } - - return sigUtil.personalSign(this.getOwnerWalletPrivateKey(), { data }) - } - - /** - * Given a data payload and signature, verifies that signature is valid, and returns - * Ethereum wallet address used to sign data. - * @param {string} data information that was signed - * @param {string} signature hex-formatted signature of data generated by web3 personalSign method - */ - async verifySignature (data, signature) { - return sigUtil.recoverPersonalSignature({ data: data, sig: signature }) - } - - async signTypedData (signatureData) { - if (this.useExternalWeb3) { - return ethSignTypedData( - this.getWeb3(), - this.getWalletAddress(), - signatureData - ) - } else { - // Due to changes in ethereumjs-util's toBuffer method as of v6.2.0 - // non hex-prefixed string values are not permitted and need to be - // provided directly as a buffer. - // https://github.com/ethereumjs/ethereumjs-util/releases/tag/v6.2.0 - Object.keys(signatureData.message).forEach(key => { - if ( - typeof signatureData.message[key] === 'string' && - !signatureData.message[key].startsWith('0x') - ) { - signatureData.message[key] = Buffer.from(signatureData.message[key]) - } - }) - return sigUtil.signTypedData( - this.ownerWallet.getPrivateKey(), - { data: signatureData } - ) - } - } - - async sendTransaction ( - contractMethod, - contractRegistryKey, - contractAddress, - txRetries = 5, - txGasLimit = null - ) { - const gasLimit = txGasLimit || await estimateGas({ - method: contractMethod, - gasLimitMaximum: DEFAULT_GAS_LIMIT - }) - if (this.useExternalWeb3) { - return contractMethod.send( - { from: this.ownerWallet, gas: gasLimit } - ) - } else { - const encodedABI = contractMethod.encodeABI() - - const response = await retry(async () => { - return this.identityService.relay( - contractRegistryKey, - contractAddress, - this.ownerWallet.getAddressString(), - encodedABI, - gasLimit - ) - }, { - // Retry function 5x by default - // 1st retry delay = 500ms, 2nd = 1500ms, 3rd...nth retry = 4000 ms (capped) - minTimeout: 500, - maxTimeout: 4000, - factor: 3, - retries: txRetries, - onRetry: (err, i) => { - if (err) { - console.log(`libs web3Manager transaction send retry error : ${err}`) - } - } - }) - - const receipt = response.receipt - - // interestingly, using contractMethod.send from Metamask's web3 (eg. like in the if - // above) parses the event log into an 'events' key on the transaction receipt and - // blows away the 'logs' key. However, using sendRawTransaction as our - // relayer does, returns only the logs. Here, we replicate the part of the 'events' - // key that our code consumes, but we may want to change our functions to consume - // this data in a different way in future (this parsing is messy). - // More on Metamask's / Web3.js' behavior here: - // https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-send - if (receipt.logs) { - const events = {} - const decoded = this.AudiusABIDecoder.decodeLogs(contractRegistryKey, receipt.logs) - decoded.forEach((evt) => { - const returnValues = {} - evt.events.forEach((arg) => { - returnValues[arg.name] = arg.value - }) - events[evt.name] = { returnValues } - }) - receipt.events = events - } - return response.receipt - } - } - - // TODO - Remove this. Adapted from https://github.com/raiden-network/webui/pull/51/files - // Vendored code below - provider (url, timeout) { - return this.monkeyPatchProvider(new Web3.providers.HttpProvider(url, timeout)) - } - - // TODO: Workaround for https://github.com/ethereum/web3.js/issues/1803 it should be immediately removed - // as soon as the issue is fixed upstream. - // Issue is also documented here https://github.com/ethereum/web3.js/issues/1802 - monkeyPatchProvider (httpProvider) { - override(httpProvider, '_prepareRequest', function () { - return function () { - const request = new XMLHttpRequestRef() - - request.open('POST', this.host, true) - request.setRequestHeader('Content-Type', 'application/json') - request.timeout = this.timeout && this.timeout !== 1 ? this.timeout : 0 - - if (this.headers) { - this.headers.forEach(function (header) { - request.setRequestHeader(header.name, header.value) - }) - } - return request - } - }) - return httpProvider - } - // End vendored code -} - -module.exports = Web3Manager - -/** Browser and testing-compatible signTypedData */ -const ethSignTypedData = (web3, wallet, signatureData) => { - return new Promise((resolve, reject) => { - let method - if (web3.currentProvider.isMetaMask === true) { - method = 'eth_signTypedData_v3' - signatureData = JSON.stringify(signatureData) - } else { - method = 'eth_signTypedData' - // fix per https://github.com/ethereum/web3.js/issues/1119 - } - - web3.currentProvider.send({ - method: method, - params: [wallet, signatureData], - from: wallet - }, (err, result) => { - if (err) { - reject(err) - } else if (result.error) { - reject(result.error) - } else { - resolve(result.result) - } - }) - }) -} - -function override (object, methodName, callback) { - object[methodName] = callback(object[methodName]) -} diff --git a/libs/src/services/web3Manager/index.ts b/libs/src/services/web3Manager/index.ts new file mode 100644 index 00000000000..1a0aa6d970a --- /dev/null +++ b/libs/src/services/web3Manager/index.ts @@ -0,0 +1,2 @@ +export * from './Web3Config' +export * from './Web3Manager' diff --git a/libs/src/utils/estimateGas.ts b/libs/src/utils/estimateGas.ts index 7a7c24ac00d..017a573b687 100644 --- a/libs/src/utils/estimateGas.ts +++ b/libs/src/utils/estimateGas.ts @@ -7,7 +7,7 @@ const GAS_LIMIT_MULTIPLIER = 1.05 export interface ContractMethod { arguments: string[] estimateGas: (config: { - from: string | undefined + from: Wallet | undefined gas: number | undefined }) => Promise _method: { @@ -24,7 +24,7 @@ export interface ContractMethod { interface EstimateGasConfig { method: ContractMethod - from?: string + from?: Wallet gasLimitMaximum: number multiplier?: number } diff --git a/libs/types/@audius-hedgehog/index.d.ts b/libs/types/@audius-hedgehog/index.d.ts index 4477085ad62..a836a24fbdf 100644 --- a/libs/types/@audius-hedgehog/index.d.ts +++ b/libs/types/@audius-hedgehog/index.d.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-extraneous-class */ declare module '@audius/hedgehog' { - import { IdentityService } from '../../src/services/identity' + import type { IdentityService } from '../../src/services/identity' import type Wallet from 'ethereumjs-wallet' type RecoveryInfo = { From 512271a2ee3efbbee56a9b5234181399566a1d3f Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 16:48:48 -0700 Subject: [PATCH 05/14] Rename contracts, remove dupes --- .../contracts/GovernedContractClient.ts | 2 +- ...iusTokenClient.ts => AudiusTokenClient.ts} | 0 ...onClient.ts => ClaimDistributionClient.ts} | 0 ...anagerClient.ts => ClaimsManagerClient.ts} | 0 ...agerClient.ts => DelegateManagerClient.ts} | 35 +- .../src/services/ethContracts/EthContracts.ts | 24 +- ...erClient.ts => EthRewardsManagerClient.ts} | 0 ...overnanceClient.ts => GovernanceClient.ts} | 4 +- .../{registryClient.ts => RegistryClient.ts} | 0 ...ent.ts => ServiceProviderFactoryClient.ts} | 6 +- ...rClient.ts => ServiceTypeManagerClient.ts} | 0 ...ngProxyClient.ts => StakingProxyClient.ts} | 2 +- ...ent.ts => TrustedNotifierManagerClient.ts} | 0 .../ethContracts/delegateManagerClient.js | 480 --------------- libs/src/services/ethContracts/index.js | 295 --------- .../serviceProviderFactoryClient.js | 569 ------------------ .../ethContracts/stakingProxyClient.js | 72 --- .../services/ethContracts/wormholeClient.ts | 2 +- 18 files changed, 52 insertions(+), 1439 deletions(-) rename libs/src/services/ethContracts/{audiusTokenClient.ts => AudiusTokenClient.ts} (100%) rename libs/src/services/ethContracts/{claimDistributionClient.ts => ClaimDistributionClient.ts} (100%) rename libs/src/services/ethContracts/{claimsManagerClient.ts => ClaimsManagerClient.ts} (100%) rename libs/src/services/ethContracts/{delegateManagerClient.ts => DelegateManagerClient.ts} (94%) rename libs/src/services/ethContracts/{ethRewardsManagerClient.ts => EthRewardsManagerClient.ts} (100%) rename libs/src/services/ethContracts/{governanceClient.ts => GovernanceClient.ts} (99%) rename libs/src/services/ethContracts/{registryClient.ts => RegistryClient.ts} (100%) rename libs/src/services/ethContracts/{serviceProviderFactoryClient.ts => ServiceProviderFactoryClient.ts} (99%) rename libs/src/services/ethContracts/{serviceTypeManagerClient.ts => ServiceTypeManagerClient.ts} (100%) rename libs/src/services/ethContracts/{stakingProxyClient.ts => StakingProxyClient.ts} (97%) rename libs/src/services/ethContracts/{trustedNotifierManagerClient.ts => TrustedNotifierManagerClient.ts} (100%) delete mode 100644 libs/src/services/ethContracts/delegateManagerClient.js delete mode 100644 libs/src/services/ethContracts/index.js delete mode 100644 libs/src/services/ethContracts/serviceProviderFactoryClient.js delete mode 100644 libs/src/services/ethContracts/stakingProxyClient.js diff --git a/libs/src/services/contracts/GovernedContractClient.ts b/libs/src/services/contracts/GovernedContractClient.ts index 6ae95b7aecf..9ec57c7d75e 100644 --- a/libs/src/services/contracts/GovernedContractClient.ts +++ b/libs/src/services/contracts/GovernedContractClient.ts @@ -1,5 +1,5 @@ import type { ContractABI, Logger } from '../../utils' -import type { GovernanceClient } from '../ethContracts/governanceClient' +import type { GovernanceClient } from '../ethContracts/GovernanceClient' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { Web3Manager } from '../web3Manager' import type { GetRegistryAddress } from './ContractClient' diff --git a/libs/src/services/ethContracts/audiusTokenClient.ts b/libs/src/services/ethContracts/AudiusTokenClient.ts similarity index 100% rename from libs/src/services/ethContracts/audiusTokenClient.ts rename to libs/src/services/ethContracts/AudiusTokenClient.ts diff --git a/libs/src/services/ethContracts/claimDistributionClient.ts b/libs/src/services/ethContracts/ClaimDistributionClient.ts similarity index 100% rename from libs/src/services/ethContracts/claimDistributionClient.ts rename to libs/src/services/ethContracts/ClaimDistributionClient.ts diff --git a/libs/src/services/ethContracts/claimsManagerClient.ts b/libs/src/services/ethContracts/ClaimsManagerClient.ts similarity index 100% rename from libs/src/services/ethContracts/claimsManagerClient.ts rename to libs/src/services/ethContracts/ClaimsManagerClient.ts diff --git a/libs/src/services/ethContracts/delegateManagerClient.ts b/libs/src/services/ethContracts/DelegateManagerClient.ts similarity index 94% rename from libs/src/services/ethContracts/delegateManagerClient.ts rename to libs/src/services/ethContracts/DelegateManagerClient.ts index a3b535752a7..e014215503b 100644 --- a/libs/src/services/ethContracts/delegateManagerClient.ts +++ b/libs/src/services/ethContracts/DelegateManagerClient.ts @@ -1,10 +1,11 @@ +import type BN from 'bn.js' import { ContractABI, Logger, Utils } from '../../utils' import type { GetRegistryAddress } from '../contracts/ContractClient' import { GovernedContractClient } from '../contracts/GovernedContractClient' import type { EthWeb3Manager } from '../ethWeb3Manager' -import type { AudiusTokenClient } from './audiusTokenClient' -import type { GovernanceClient } from './governanceClient' -import type { StakingProxyClient } from './stakingProxyClient' +import type { AudiusTokenClient } from './AudiusTokenClient' +import type { GovernanceClient } from './GovernanceClient' +import type { StakingProxyClient } from './StakingProxyClient' type GetEvent = { delegator: string @@ -433,6 +434,34 @@ export class DelegateManagerClient extends GovernedContractClient { return info } + async getSPMinDelegationAmount({ + serviceProvider + }: { + serviceProvider: string + }) { + const method = await this.getMethod( + 'getSPMinDelegationAmount', + serviceProvider + ) + const info = await method.call() + return Utils.toBN(info) + } + + async updateSPMinDelegationAmount({ + serviceProvider, + amount + }: { + serviceProvider: string + amount: BN + }) { + const method = await this.getMethod( + 'updateSPMinDelegationAmount', + serviceProvider, + amount + ) + return this.web3Manager.sendTransaction(method) + } + async updateRemoveDelegatorLockupDuration(duration: string) { const method = await this.getGovernedMethod( 'updateRemoveDelegatorLockupDuration', diff --git a/libs/src/services/ethContracts/EthContracts.ts b/libs/src/services/ethContracts/EthContracts.ts index 675a2362624..986cb25fc55 100644 --- a/libs/src/services/ethContracts/EthContracts.ts +++ b/libs/src/services/ethContracts/EthContracts.ts @@ -1,16 +1,16 @@ import semver from 'semver' -import { AudiusTokenClient } from './audiusTokenClient' -import { RegistryClient } from './registryClient' -import { GovernanceClient } from './governanceClient' -import { ServiceTypeManagerClient } from './serviceTypeManagerClient' -import { ServiceProviderFactoryClient } from './serviceProviderFactoryClient' -import { StakingProxyClient } from './stakingProxyClient' -import { DelegateManagerClient } from './delegateManagerClient' -import { ClaimsManagerClient } from './claimsManagerClient' -import { ClaimDistributionClient } from './claimDistributionClient' +import { AudiusTokenClient } from './AudiusTokenClient' +import { RegistryClient } from './RegistryClient' +import { GovernanceClient } from './GovernanceClient' +import { ServiceTypeManagerClient } from './ServiceTypeManagerClient' +import { ServiceProviderFactoryClient } from './ServiceProviderFactoryClient' +import { StakingProxyClient } from './StakingProxyClient' +import { DelegateManagerClient } from './DelegateManagerClient' +import { ClaimsManagerClient } from './ClaimsManagerClient' +import { ClaimDistributionClient } from './ClaimDistributionClient' import { WormholeClient } from './wormholeClient' -import { EthRewardsManagerClient } from './ethRewardsManagerClient' -import { TrustedNotifierManagerClient } from './trustedNotifierManagerClient' +import { EthRewardsManagerClient } from './EthRewardsManagerClient' +import { TrustedNotifierManagerClient } from './TrustedNotifierManagerClient' import { Logger, Utils } from '../../utils' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { ContractClient } from '../contracts/ContractClient' @@ -26,7 +26,7 @@ const ServiceProviderFactoryABI = Utils.importEthContractABI( ).abi const StakingABI = Utils.importEthContractABI('Staking.json').abi const DelegateManagerABI = Utils.importEthContractABI( - 'DelegateManager.json' + 'DelegateManagerV2.json' ).abi const ClaimsManagerABI = Utils.importEthContractABI('ClaimsManager.json').abi const ClaimDistributionABI = Utils.importEthContractABI( diff --git a/libs/src/services/ethContracts/ethRewardsManagerClient.ts b/libs/src/services/ethContracts/EthRewardsManagerClient.ts similarity index 100% rename from libs/src/services/ethContracts/ethRewardsManagerClient.ts rename to libs/src/services/ethContracts/EthRewardsManagerClient.ts diff --git a/libs/src/services/ethContracts/governanceClient.ts b/libs/src/services/ethContracts/GovernanceClient.ts similarity index 99% rename from libs/src/services/ethContracts/governanceClient.ts rename to libs/src/services/ethContracts/GovernanceClient.ts index b1d2232294c..56c3c65c6b2 100644 --- a/libs/src/services/ethContracts/governanceClient.ts +++ b/libs/src/services/ethContracts/GovernanceClient.ts @@ -1,8 +1,8 @@ import { ContractClient, GetRegistryAddress } from '../contracts/ContractClient' import { ContractABI, ContractMethod, Logger, Utils } from '../../utils' import type { EthWeb3Manager } from '../ethWeb3Manager' -import type { AudiusTokenClient } from './audiusTokenClient' -import type { StakingProxyClient } from './stakingProxyClient' +import type { AudiusTokenClient } from './AudiusTokenClient' +import type { StakingProxyClient } from './StakingProxyClient' import type { EventLog } from 'web3-core' type ProposalTxn = { diff --git a/libs/src/services/ethContracts/registryClient.ts b/libs/src/services/ethContracts/RegistryClient.ts similarity index 100% rename from libs/src/services/ethContracts/registryClient.ts rename to libs/src/services/ethContracts/RegistryClient.ts diff --git a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts b/libs/src/services/ethContracts/ServiceProviderFactoryClient.ts similarity index 99% rename from libs/src/services/ethContracts/serviceProviderFactoryClient.ts rename to libs/src/services/ethContracts/ServiceProviderFactoryClient.ts index cddea39a939..194346ee7d1 100644 --- a/libs/src/services/ethContracts/serviceProviderFactoryClient.ts +++ b/libs/src/services/ethContracts/ServiceProviderFactoryClient.ts @@ -6,9 +6,9 @@ import axios, { AxiosRequestConfig } from 'axios' import { range } from 'lodash' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { GetRegistryAddress } from '../contracts/ContractClient' -import type { AudiusTokenClient } from './audiusTokenClient' -import type { StakingProxyClient } from './stakingProxyClient' -import type { GovernanceClient } from './governanceClient' +import type { AudiusTokenClient } from './AudiusTokenClient' +import type { StakingProxyClient } from './StakingProxyClient' +import type { GovernanceClient } from './GovernanceClient' import urlJoin from 'proper-url-join' type GetEvent = { diff --git a/libs/src/services/ethContracts/serviceTypeManagerClient.ts b/libs/src/services/ethContracts/ServiceTypeManagerClient.ts similarity index 100% rename from libs/src/services/ethContracts/serviceTypeManagerClient.ts rename to libs/src/services/ethContracts/ServiceTypeManagerClient.ts diff --git a/libs/src/services/ethContracts/stakingProxyClient.ts b/libs/src/services/ethContracts/StakingProxyClient.ts similarity index 97% rename from libs/src/services/ethContracts/stakingProxyClient.ts rename to libs/src/services/ethContracts/StakingProxyClient.ts index a22145ccf62..5d2ae07bfbb 100644 --- a/libs/src/services/ethContracts/stakingProxyClient.ts +++ b/libs/src/services/ethContracts/StakingProxyClient.ts @@ -1,7 +1,7 @@ import type { ContractABI, Logger } from '../../utils' import { ContractClient, GetRegistryAddress } from '../contracts/ContractClient' import type { EthWeb3Manager } from '../ethWeb3Manager' -import type { AudiusTokenClient } from './audiusTokenClient' +import type { AudiusTokenClient } from './AudiusTokenClient' import type BN from 'bn.js' export class StakingProxyClient extends ContractClient { diff --git a/libs/src/services/ethContracts/trustedNotifierManagerClient.ts b/libs/src/services/ethContracts/TrustedNotifierManagerClient.ts similarity index 100% rename from libs/src/services/ethContracts/trustedNotifierManagerClient.ts rename to libs/src/services/ethContracts/TrustedNotifierManagerClient.ts diff --git a/libs/src/services/ethContracts/delegateManagerClient.js b/libs/src/services/ethContracts/delegateManagerClient.js deleted file mode 100644 index 56d6745e1cc..00000000000 --- a/libs/src/services/ethContracts/delegateManagerClient.js +++ /dev/null @@ -1,480 +0,0 @@ -const { Utils } = require('../../utils') -const GovernedContractClient = require('../contracts/GovernedContractClient') - -class DelegateManagerClient extends GovernedContractClient { - constructor ( - ethWeb3Manager, - contractABI, - contractRegistryKey, - getRegistryAddress, - audiusTokenClient, - stakingProxyClient, - governanceClient, - logger = console - ) { - super(ethWeb3Manager, contractABI, contractRegistryKey, getRegistryAddress, governanceClient, logger) - this.audiusTokenClient = audiusTokenClient - this.stakingProxyClient = stakingProxyClient - } - - async delegateStake (targetSP, amount) { - // Approve token transfer operation - const contractAddress = await this.stakingProxyClient.getAddress() - const tx0 = await this.audiusTokenClient.approve( - contractAddress, - amount) - const method = await this.getMethod( - 'delegateStake', - targetSP, - amount - ) - const tx = await this.web3Manager.sendTransaction( - method - ) - return { - txReceipt: tx, - tokenApproveReceipt: tx0, - delegator: tx.events.IncreaseDelegatedStake.returnValues._delegator, - serviceProvider: tx.events.IncreaseDelegatedStake.returnValues._serviceProvider, - increaseAmount: Utils.toBN(tx.events.IncreaseDelegatedStake.returnValues._increaseAmount) - } - } - - /* Pass either delegator or serviceProvider filters */ - async getIncreaseDelegateStakeEvents ({ - delegator, - serviceProvider, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const filter = {} - if (delegator) { - filter._delegator = delegator - } else { - filter._serviceProvider = serviceProvider - } - const events = await contract.getPastEvents('IncreaseDelegatedStake', { - fromBlock: queryStartBlock, - filter - }) - - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - delegator: event.returnValues._delegator, - increaseAmount: Utils.toBN(event.returnValues._increaseAmount), - serviceProvider: event.returnValues._serviceProvider - })) - } - - async getDecreaseDelegateStakeEvents ({ - delegator, - serviceProvider, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const filter = {} - if (delegator) { - filter._delegator = delegator - } - if (serviceProvider) { - filter._serviceProvider = serviceProvider - } - - const events = await contract.getPastEvents('UndelegateStakeRequestEvaluated', { - fromBlock: queryStartBlock, - filter - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - delegator: event.returnValues._delegator, - amount: Utils.toBN(event.returnValues._amount), - serviceProvider: event.returnValues._serviceProvider - })) - } - - async getUndelegateStakeRequestedEvents ({ - delegator, - serviceProvider, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const filter = {} - if (delegator) { - filter._delegator = delegator - } - if (serviceProvider) { - filter._serviceProvider = serviceProvider - } - - const events = await contract.getPastEvents('UndelegateStakeRequested', { - fromBlock: queryStartBlock, - filter - }) - - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - lockupExpiryBlock: parseInt(event.returnValues._lockupExpiryBlock), - delegator: event.returnValues._delegator, - amount: Utils.toBN(event.returnValues._amount), - serviceProvider: event.returnValues._serviceProvider - })) - } - - async getUndelegateStakeCancelledEvents ({ - delegator, - serviceProvider, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const filter = {} - if (delegator) { - filter._delegator = delegator - } - if (serviceProvider) { - filter._serviceProvider = serviceProvider - } - - const events = await contract.getPastEvents('UndelegateStakeRequestCancelled', { - fromBlock: queryStartBlock, - filter - }) - - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - delegator: event.returnValues._delegator, - amount: Utils.toBN(event.returnValues._amount), - serviceProvider: event.returnValues._serviceProvider - })) - } - - async getClaimEvents ({ - claimer, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('Claim', { - fromBlock: queryStartBlock, - filter: { - _claimer: claimer - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - claimer: event.returnValues._claimer, - rewards: Utils.toBN(event.returnValues._rewards), - newTotal: Utils.toBN(event.returnValues._newTotal) - })) - } - - async getSlashEvents ({ - target, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('Slash', { - fromBlock: queryStartBlock, - filter: { - _target: target - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - target: event.returnValues._target, - amount: Utils.toBN(event.returnValues._amount), - newTotal: Utils.toBN(event.returnValues._newTotal) - })) - } - - async getDelegatorRemovedEvents ({ - target, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('DelegatorRemoved', { - fromBlock: queryStartBlock, - filter: { - _target: target - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - serviceProvider: event.returnValues._serviceProvider, - delegator: event.returnValues._delegator, - unstakedAmount: Utils.toBN(event.returnValues._unstakedAmount) - })) - } - - async requestUndelegateStake (targetSP, amount) { - const method = await this.getMethod( - 'requestUndelegateStake', - targetSP, - amount - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async cancelUndelegateStakeRequest () { - const method = await this.getMethod( - 'cancelUndelegateStakeRequest' - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async undelegateStake () { - const method = await this.getMethod( - 'undelegateStake' - ) - - const tx = await this.web3Manager.sendTransaction( - method - ) - - return { - txReceipt: tx, - delegator: tx.events.UndelegateStakeRequestEvaluated.returnValues._delegator, - serviceProvider: tx.events.UndelegateStakeRequestEvaluated.returnValues._serviceProvider, - decreaseAmount: Utils.toBN(tx.events.UndelegateStakeRequestEvaluated.returnValues._amount) - } - } - - async claimRewards (serviceProvider, txRetries = 5) { - const method = await this.getMethod( - 'claimRewards', - serviceProvider - ) - return this.web3Manager.sendTransaction( - method, - null, - null, - txRetries - ) - } - - async requestRemoveDelegator (serviceProvider, delegator) { - const method = await this.getMethod( - 'requestRemoveDelegator', - serviceProvider, - delegator - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async cancelRemoveDelegatorRequest (serviceProvider, delegator) { - const method = await this.getMethod( - 'cancelRemoveDelegatorRequest', - serviceProvider, - delegator - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async removeDelegator (serviceProvider, delegator) { - const method = await this.getMethod( - 'removeDelegator', - serviceProvider, - delegator - ) - const tx = await this.web3Manager.sendTransaction( - method - ) - return { - txReceipt: tx, - delegator: tx.events.RemoveDelegatorRequestEvaluated.returnValues._delegator, - serviceProvider: tx.events.RemoveDelegatorRequestEvaluated.returnValues._serviceProvider, - unstakedAmount: Utils.toBN(tx.events.RemoveDelegatorRequestEvaluated.returnValues._unstakedAmount) - } - } - - // ========================================= View Functions ========================================= - - async getDelegatorsList (serviceProvider) { - const method = await this.getMethod( - 'getDelegatorsList', - serviceProvider - ) - const info = await method.call() - return info - } - - async getTotalDelegatedToServiceProvider (serviceProvider) { - const method = await this.getMethod( - 'getTotalDelegatedToServiceProvider', - serviceProvider - ) - const info = await method.call() - return Utils.toBN(info) - } - - async getTotalDelegatorStake (delegator) { - const method = await this.getMethod( - 'getTotalDelegatorStake', - delegator - ) - const info = await method.call() - return Utils.toBN(info) - } - - async getTotalLockedDelegationForServiceProvider (serviceProvider) { - const method = await this.getMethod( - 'getTotalLockedDelegationForServiceProvider', - serviceProvider - ) - const info = await method.call() - return Utils.toBN(info) - } - - async getDelegatorStakeForServiceProvider (delegator, serviceProvider) { - const method = await this.getMethod( - 'getDelegatorStakeForServiceProvider', - delegator, - serviceProvider - ) - const info = await method.call() - return Utils.toBN(info) - } - - async getPendingUndelegateRequest (delegator) { - const method = await this.getMethod( - 'getPendingUndelegateRequest', - delegator - ) - const info = await method.call() - return { - amount: Utils.toBN(info.amount), - lockupExpiryBlock: parseInt(info.lockupExpiryBlock), - target: info.target - } - } - - async getPendingRemoveDelegatorRequest (serviceProvider, delegator) { - const method = await this.getMethod( - 'getPendingRemoveDelegatorRequest', - serviceProvider, - delegator - ) - const info = await method.call() - return { lockupExpiryBlock: parseInt(info) } - } - - async getUndelegateLockupDuration () { - const method = await this.getMethod( - 'getUndelegateLockupDuration' - ) - const info = await method.call() - return parseInt(info) - } - - async getMaxDelegators () { - const method = await this.getMethod( - 'getMaxDelegators' - ) - const info = await method.call() - return parseInt(info) - } - - async getMinDelegationAmount () { - const method = await this.getMethod( - 'getMinDelegationAmount' - ) - const info = await method.call() - return Utils.toBN(info) - } - - async getRemoveDelegatorLockupDuration () { - const method = await this.getMethod( - 'getRemoveDelegatorLockupDuration' - ) - const info = await method.call() - return parseInt(info) - } - - async getRemoveDelegatorEvalDuration () { - const method = await this.getMethod( - 'getRemoveDelegatorEvalDuration' - ) - const info = await method.call() - return parseInt(info) - } - - async getGovernanceAddress () { - const method = await this.getMethod( - 'getGovernanceAddress' - ) - const info = await method.call() - return info - } - - async getServiceProviderFactoryAddress () { - const method = await this.getMethod( - 'getServiceProviderFactoryAddress' - ) - const info = await method.call() - return info - } - - async getClaimsManagerAddress () { - const method = await this.getMethod( - 'getClaimsManagerAddress' - ) - const info = await method.call() - return info - } - - async getStakingAddress () { - const method = await this.getMethod( - 'getStakingAddress' - ) - const info = await method.call() - return info - } - - async getSPMinDelegationAmount ({ serviceProvider }) { - const method = await this.getMethod( - 'getSPMinDelegationAmount', - serviceProvider - ) - const info = await method.call() - return Utils.toBN(info) - } - - async updateSPMinDelegationAmount ({ serviceProvider, amount }) { - const method = await this.getMethod( - 'updateSPMinDelegationAmount', - serviceProvider, - amount - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async updateRemoveDelegatorLockupDuration (duration) { - const method = await this.getGovernedMethod( - 'updateRemoveDelegatorLockupDuration', - duration - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async updateUndelegateLockupDuration (duration) { - const method = await this.getGovernedMethod( - 'updateUndelegateLockupDuration', - duration - ) - return this.web3Manager.sendTransaction( - method - ) - } -} - -module.exports = DelegateManagerClient diff --git a/libs/src/services/ethContracts/index.js b/libs/src/services/ethContracts/index.js deleted file mode 100644 index 0e4797dde28..00000000000 --- a/libs/src/services/ethContracts/index.js +++ /dev/null @@ -1,295 +0,0 @@ -const semver = require('semver') -let urlJoin = require('proper-url-join') - -const { AudiusTokenClient } = require('./audiusTokenClient') -const { RegistryClient } = require('./registryClient') -const { GovernanceClient } = require('./governanceClient') -const { ServiceTypeManagerClient } = require('./serviceTypeManagerClient') -const { ServiceProviderFactoryClient } = require('./serviceProviderFactoryClient') -const { StakingProxyClient } = require('./stakingProxyClient') -const { DelegateManagerClient } = require('./delegateManagerClient') -const { ClaimsManagerClient } = require('./claimsManagerClient') -const { ClaimDistributionClient } = require('./claimDistributionClient') -const { WormholeClient } = require('./wormholeClient') -const { EthRewardsManagerClient } = require('./ethRewardsManagerClient') -const { TrustedNotifierManagerClient } = require('./trustedNotifierManagerClient') -const { Utils } = require('../../utils') - -const AudiusTokenABI = Utils.importEthContractABI('AudiusToken.json').abi -const RegistryABI = Utils.importEthContractABI('Registry.json').abi -const GovernanceABI = Utils.importEthContractABI('Governance.json').abi -const ServiceTypeManagerABI = Utils.importEthContractABI('ServiceTypeManager.json').abi -const ServiceProviderFactoryABI = Utils.importEthContractABI('ServiceProviderFactory.json').abi -const StakingABI = Utils.importEthContractABI('Staking.json').abi -const DelegateManagerABI = Utils.importEthContractABI('DelegateManagerV2.json').abi -const ClaimsManagerABI = Utils.importEthContractABI('ClaimsManager.json').abi -const ClaimDistributionABI = Utils.importEthContractABI('AudiusClaimDistributor.json').abi -const WormholeClientABI = Utils.importEthContractABI('WormholeClient.json').abi -const EthRewardsManagerABI = Utils.importEthContractABI('EthRewardsManager.json').abi -const TrustedNotifierManagerABI = Utils.importEthContractABI('TrustedNotifierManager.json').abi - -const GovernanceRegistryKey = 'Governance' -const ServiceTypeManagerProxyKey = 'ServiceTypeManagerProxy' -const ServiceProviderFactoryRegistryKey = 'ServiceProviderFactory' -const StakingProxyKey = 'StakingProxy' -const DelegateManagerRegistryKey = 'DelegateManager' -const ClaimsManagerProxyKey = 'ClaimsManagerProxy' -const ClaimDistributionRegistryKey = 'ClaimDistribution' -const EthRewardsManagerProxyKey = 'EthRewardsManagerProxy' -const TrustedNotifierManagerProxyKey = 'TrustedNotifierManagerProxy' - -const TWO_MINUTES = 2 * 60 * 1000 - -const serviceType = Object.freeze({ - DISCOVERY_PROVIDER: 'discovery-node', - CREATOR_NODE: 'content-node' -}) -const serviceTypeList = Object.values(serviceType) -if (urlJoin && urlJoin.default) urlJoin = urlJoin.default - -class EthContracts { - constructor ( - ethWeb3Manager, - tokenContractAddress, - registryAddress, - claimDistributionContractAddress, - wormholeContractAddress, - isServer, - logger = console, - isDebug = false - ) { - this.ethWeb3Manager = ethWeb3Manager - this.tokenContractAddress = tokenContractAddress - this.claimDistributionContractAddress = claimDistributionContractAddress - this.wormholeContractAddress = wormholeContractAddress - this.registryAddress = registryAddress - this.isServer = isServer - this.logger = logger - this.isDebug = isDebug - this.expectedServiceVersions = null - - this.AudiusTokenClient = new AudiusTokenClient( - this.ethWeb3Manager, - AudiusTokenABI, - this.tokenContractAddress - ) - this.RegistryClient = new RegistryClient( - this.ethWeb3Manager, - RegistryABI, - this.registryAddress - ) - this.getRegistryAddressForContract = this.getRegistryAddressForContract.bind(this) - - this.StakingProxyClient = new StakingProxyClient( - this.ethWeb3Manager, - StakingABI, - StakingProxyKey, - this.getRegistryAddressForContract, - this.AudiusTokenClient, - this.logger - ) - - this.GovernanceClient = new GovernanceClient( - this.ethWeb3Manager, - GovernanceABI, - GovernanceRegistryKey, - this.getRegistryAddressForContract, - this.AudiusTokenClient, - this.StakingProxyClient, - this.logger - ) - - this.ClaimsManagerClient = new ClaimsManagerClient( - this.ethWeb3Manager, - ClaimsManagerABI, - ClaimsManagerProxyKey, - this.getRegistryAddressForContract, - this.logger - ) - - this.EthRewardsManagerClient = new EthRewardsManagerClient( - this.ethWeb3Manager, - EthRewardsManagerABI, - EthRewardsManagerProxyKey, - this.getRegistryAddressForContract, - this.logger - ) - - this.ServiceTypeManagerClient = new ServiceTypeManagerClient( - this.ethWeb3Manager, - ServiceTypeManagerABI, - ServiceTypeManagerProxyKey, - this.getRegistryAddressForContract, - this.GovernanceClient, - this.logger - ) - - this.ServiceProviderFactoryClient = new ServiceProviderFactoryClient( - this.ethWeb3Manager, - ServiceProviderFactoryABI, - ServiceProviderFactoryRegistryKey, - this.getRegistryAddressForContract, - this.AudiusTokenClient, - this.StakingProxyClient, - this.GovernanceClient, - this.logger, - this.isDebug - ) - - this.DelegateManagerClient = new DelegateManagerClient( - this.ethWeb3Manager, - DelegateManagerABI, - DelegateManagerRegistryKey, - this.getRegistryAddressForContract, - this.AudiusTokenClient, - this.StakingProxyClient, - this.GovernanceClient, - this.logger - ) - - if (this.claimDistributionContractAddress) { - this.ClaimDistributionClient = new ClaimDistributionClient( - this.ethWeb3Manager, - ClaimDistributionABI, - ClaimDistributionRegistryKey, - this.getRegistryAddressForContract, - this.logger, - this.claimDistributionContractAddress - ) - } - - this.WormholeClient = new WormholeClient( - this.ethWeb3Manager, - WormholeClientABI, - this.wormholeContractAddress, - this.AudiusTokenClient - ) - - this.TrustedNotifierManagerClient = new TrustedNotifierManagerClient( - this.ethWeb3Manager, - TrustedNotifierManagerABI, - TrustedNotifierManagerProxyKey, - this.getRegistryAddressForContract, - this.GovernanceClient, - this.logger - ) - - this.contractClients = [ - this.ServiceTypeManagerClient, - this.StakingProxyClient, - this.ServiceProviderFactoryClient - ] - - // Whether or not we are running in `regressed` mode, meaning we were - // unable to select a discovery provider that was up-to-date. Clients may - // want to consider blocking writes. - this._regressedMode = false - } - - async init () { - if (!this.ethWeb3Manager || !this.tokenContractAddress || !this.registryAddress) throw new Error('Failed to initialize EthContracts') - - if (this.isServer) { - await Promise.all(this.contractClients.map(client => client.init())) - } - } - - /** - * Estabilishes that connection to discovery providers has regressed - */ - enterRegressedMode () { - console.info('Entering regressed mode') - this._regressedMode = true - setTimeout(() => { - console.info('Leaving regressed mode') - this._regressedMode = false - }, TWO_MINUTES) - } - - isInRegressedMode () { - return this._regressedMode - } - - async getRegistryAddressForContract (contractName) { - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names - this.contracts = this.contracts || { [this.registryAddress]: 'registry' } - this.contractAddresses = this.contractAddresses || { registry: this.registryAddress } - if (!this.contractAddresses[contractName]) { - const address = await this.RegistryClient.getContract(contractName) - this.contracts[address] = contractName - this.contractAddresses[contractName] = address - } - return this.contractAddresses[contractName] - } - - async getCurrentVersion (serviceType) { - try { - const version = await this.ServiceTypeManagerClient.getCurrentVersion(serviceType) - return version - } catch (e) { - console.log(`Error retrieving version for ${serviceType}`) - return null - } - } - - /* - * Determine the latest version for deployed services such as discovery provider and cache - */ - async getExpectedServiceVersions () { - const versions = await Promise.all( - serviceTypeList.map(serviceType => this.getCurrentVersion(serviceType)) - ) - const expectedVersions = serviceTypeList.reduce((map, serviceType, i) => { - if (versions[i]) { - map[serviceType] = versions[i] - } - return map - }, {}) - return expectedVersions - } - - /* - * Determine whether major and minor versions match for two version strings - * Version string 2 must have equivalent major/minor versions and a patch >= version1 - * @param {string} version string 1 - * @param {string} version string 2 - */ - isValidSPVersion (version1, version2) { - return ( - semver.major(version1) === semver.major(version2) && - semver.minor(version1) === semver.minor(version2) && - semver.patch(version2) >= semver.patch(version1) - ) - } - - /** - * Determines whether the major and minor versions are equal - * @param {string} version string 1 - * @param {string} version string 2 - */ - hasSameMajorAndMinorVersion (version1, version2) { - return ( - semver.major(version1) === semver.major(version2) && - semver.minor(version1) === semver.minor(version2) - ) - } - - async getServiceProviderList (spType) { - return this.ServiceProviderFactoryClient.getServiceProviderList(spType) - } - - async getNumberOfVersions (spType) { - return this.ServiceTypeManagerClient.getNumberOfVersions(spType) - } - - async getVersion (spType, queryIndex) { - return this.ServiceTypeManagerClient.getVersion(spType, queryIndex) - } - - async getServiceTypeInfo (spType) { - return this.ServiceTypeManagerClient.getServiceTypeInfo(spType) - } -} - -module.exports = EthContracts -module.exports.serviceType = serviceType diff --git a/libs/src/services/ethContracts/serviceProviderFactoryClient.js b/libs/src/services/ethContracts/serviceProviderFactoryClient.js deleted file mode 100644 index 4198246225a..00000000000 --- a/libs/src/services/ethContracts/serviceProviderFactoryClient.js +++ /dev/null @@ -1,569 +0,0 @@ -const { Utils } = require('../../utils') -const GovernedContractClient = require('../contracts/GovernedContractClient') -const axios = require('axios') -const { range } = require('lodash') - -let urlJoin = require('proper-url-join') -if (urlJoin && urlJoin.default) urlJoin = urlJoin.default - -class ServiceProviderFactoryClient extends GovernedContractClient { - constructor ( - ethWeb3Manager, - contractABI, - contractRegistryKey, - getRegistryAddress, - audiusTokenClient, - stakingProxyClient, - governanceClient, - logger = console, - isDebug = false - ) { - super(ethWeb3Manager, contractABI, contractRegistryKey, getRegistryAddress, governanceClient, logger) - this.audiusTokenClient = audiusTokenClient - this.stakingProxyClient = stakingProxyClient - this.isDebug = isDebug - } - - async registerWithDelegate (serviceType, endpoint, amount, delegateOwnerWallet) { - const sanitizedEndpoint = endpoint.replace(/\/$/, '') - - if (!this.isDebug && !Utils.isHttps(sanitizedEndpoint)) { - throw new Error('Domain name not using https protocol!') - } - - if (!this.isDebug && !Utils.isFQDN(sanitizedEndpoint)) { - throw new Error('Not a fully qualified domain name!') - } - if (!Number.isInteger(amount) && !Utils.isBN(amount)) { - throw new Error('Invalid amount') - } - - const requestUrl = urlJoin(sanitizedEndpoint, 'health_check') - const axiosRequestObj = { - url: requestUrl, - method: 'get', - timeout: 1000 - } - const resp = await axios(axiosRequestObj) - const endpointServiceType = resp.data.data.service - - if (serviceType !== endpointServiceType) { - throw new Error('Attempting to register endpoint with mismatched service type') - } - - // Approve token transfer operation - const contractAddress = await this.stakingProxyClient.getAddress() - const tx0 = await this.audiusTokenClient.approve( - contractAddress, - amount - ) - - // Register and stake - const method = await this.getMethod('register', - Utils.utf8ToHex(serviceType), - sanitizedEndpoint, - amount, - delegateOwnerWallet) - const tx = await this.web3Manager.sendTransaction(method, 1000000) - return { - txReceipt: tx, - spID: parseInt(tx.events.RegisteredServiceProvider.returnValues._spID), - serviceType: Utils.hexToUtf8(tx.events.RegisteredServiceProvider.returnValues._serviceType), - owner: tx.events.RegisteredServiceProvider.returnValues._owner, - endpoint: tx.events.RegisteredServiceProvider.returnValues._endpoint, - tokenApproveReceipt: tx0 - } - } - - async register (serviceType, endpoint, amount) { - return this.registerWithDelegate( - serviceType, - endpoint, - amount, - this.web3Manager.getWalletAddress()) - } - - async getRegisteredServiceProviderEvents ({ - serviceType, - owner, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const filter = {} - if (owner) { - filter._owner = owner - } - if (serviceType) { - filter._serviceType = serviceType - } - const events = await contract.getPastEvents('RegisteredServiceProvider', { - fromBlock: queryStartBlock, - filter - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - spID: parseInt(event.returnValues._spID), - serviceType: Utils.hexToUtf8(event.returnValues._serviceType), - owner: event.returnValues._owner, - endpoint: event.returnValues._endpoint, - stakeAmount: Utils.toBN(event.returnValues._stakeAmount) - })) - } - - async getDeregisteredServiceProviderEvents ({ - serviceType, - owner, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const filter = {} - if (owner) { - filter._owner = owner - } - if (serviceType) { - filter._serviceType = serviceType - } - const events = await contract.getPastEvents('DeregisteredServiceProvider', { - fromBlock: queryStartBlock, - filter - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - spID: parseInt(event.returnValues._spID), - serviceType: Utils.hexToUtf8(event.returnValues._serviceType), - owner: event.returnValues._owner, - endpoint: event.returnValues._endpoint, - stakeAmount: Utils.toBN(event.returnValues._stakeAmount) - })) - } - - async getIncreasedStakeEvents ({ - owner, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('IncreasedStake', { - fromBlock: queryStartBlock, - filter: { - _owner: owner - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - owner: event.returnValues._owner, - increaseAmount: Utils.toBN(event.returnValues._increaseAmount), - newStakeAmount: Utils.toBN(event.returnValues._newStakeAmount) - })) - } - - async getDecreasedStakeEvaluatedEvents ({ - owner, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('DecreaseStakeRequestEvaluated', { - fromBlock: queryStartBlock, - filter: { - _owner: owner - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - owner: event.returnValues._owner, - decreaseAmount: Utils.toBN(event.returnValues._decreaseAmount), - newStakeAmount: Utils.toBN(event.returnValues._newStakeAmount) - })) - } - - async getDecreasedStakeRequestedEvents ({ - owner, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('DecreaseStakeRequested', { - fromBlock: queryStartBlock, - filter: { - _owner: owner - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - owner: event.returnValues._owner, - decreaseAmount: Utils.toBN(event.returnValues._decreaseAmount), - lockupExpiryBlock: parseInt(event.returnValues._lockupExpiryBlock) - })) - } - - async getDecreasedStakeCancelledEvents ({ - owner, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const events = await contract.getPastEvents('DecreaseStakeRequestCancelled', { - fromBlock: queryStartBlock, - filter: { - _owner: owner - } - }) - return events.map(event => ({ - blockNumber: parseInt(event.blockNumber), - owner: event.returnValues._owner, - decreaseAmount: Utils.toBN(event.returnValues._decreaseAmount), - lockupExpiryBlock: parseInt(event.returnValues._lockupExpiryBlock) - })) - } - - // Get the deregistered service's most recent endpoint and delegate owner wallet - async getDeregisteredService ({ - serviceType, - spID, - queryStartBlock = 0 - }) { - const contract = await this.getContract() - const service = { endpoint: '', delegateOwnerWallet: '' } - const registerEvents = await contract.getPastEvents('RegisteredServiceProvider', { - fromBlock: queryStartBlock, - filter: { - _spID: spID, - _serviceType: Utils.utf8ToHex(serviceType) - } - }) - - if (registerEvents.length > 0) { - const { _endpoint, _owner } = registerEvents[registerEvents.length - 1].returnValues - service.endpoint = _endpoint - service.owner = _owner - } - - const endpointUpdateEvents = await contract.getPastEvents('EndpointUpdated', { - fromBlock: queryStartBlock, - filter: { - _spID: spID, - _serviceType: Utils.utf8ToHex(serviceType) - } - }) - - if (endpointUpdateEvents.length > 0) { - const { _newEndpoint } = endpointUpdateEvents[endpointUpdateEvents.length - 1].returnValues - service.endpoint = _newEndpoint - } - - const walletEvents = await contract.getPastEvents('DelegateOwnerWalletUpdated', { - fromBlock: queryStartBlock, - filter: { - _spID: spID, - _serviceType: Utils.utf8ToHex(serviceType) - } - }) - - if (walletEvents.length > 0) { - const { _updatedWallet } = walletEvents[walletEvents.length - 1].returnValues - service.delegateOwnerWallet = _updatedWallet - } - - return service - } - - async increaseStake (amount) { - const contractAddress = await this.stakingProxyClient.getAddress() - const tx0 = await this.audiusTokenClient.approve( - contractAddress, - amount - ) - const method = await this.getMethod('increaseStake', amount) - const tx = await this.web3Manager.sendTransaction(method, 1000000) - return { - txReceipt: tx, - tokenApproveReceipt: tx0 - } - } - - /** - * Makes a request to decrease stake - * @param {BN} amount - * @returns decrease stake lockup expiry block - */ - async requestDecreaseStake (amount) { - const requestDecreaseMethod = await this.getMethod('requestDecreaseStake', amount) - await this.web3Manager.sendTransaction( - requestDecreaseMethod, - 1000000 - ) - - const account = this.web3Manager.getWalletAddress() - const lockupExpiryBlock = await this.getLockupExpiry(account) - return parseInt(lockupExpiryBlock) - } - - /** - * Gets the pending decrease stake request for a given account - * @param {string} account wallet address to fetch for - */ - async getPendingDecreaseStakeRequest (account) { - const requestInfoMethod = await this.getMethod('getPendingDecreaseStakeRequest', account) - const { - amount, - lockupExpiryBlock - } = await requestInfoMethod.call() - return { - amount: Utils.toBN(amount), - lockupExpiryBlock: parseInt(lockupExpiryBlock) - } - } - - /** - * Gets the pending decrease stake lockup duration - */ - async getDecreaseStakeLockupDuration () { - const requestInfoMethod = await this.getMethod('getDecreaseStakeLockupDuration') - const info = await requestInfoMethod.call() - return parseInt(info) - } - - /** - * Gets the deployer cut lockup duration - */ - async getDeployerCutLockupDuration () { - const requestInfoMethod = await this.getMethod('getDeployerCutLockupDuration') - const info = await requestInfoMethod.call() - return parseInt(info) - } - - /** - * Cancels the pending decrease stake request - * @param {string} account wallet address to cancel request for - */ - async cancelDecreaseStakeRequest (account) { - const requestCancelDecreaseMethod = await this.getMethod('cancelDecreaseStakeRequest', account) - await this.web3Manager.sendTransaction( - requestCancelDecreaseMethod, - 1000000 - ) - } - - /** - * Fetches the pending decrease stake lockup expiry block for a user - * @param {string} account wallet address to fetch for - */ - async getLockupExpiry (account) { - const { lockupExpiryBlock } = await this.getPendingDecreaseStakeRequest(account) - return parseInt(lockupExpiryBlock) - } - - async decreaseStake () { - const method = await this.getMethod('decreaseStake') - const tx = await this.web3Manager.sendTransaction(method, 1000000) - - return { - txReceipt: tx - } - } - - /** - * Deregisters a service - * @param {string} serviceType - * @param {string} endpoint - */ - async deregister (serviceType, endpoint) { - const method = await this.getMethod('deregister', - Utils.utf8ToHex(serviceType), - endpoint) - const tx = await this.web3Manager.sendTransaction(method) - return { - txReceipt: tx, - spID: parseInt(tx.events.DeregisteredServiceProvider.returnValues._spID), - serviceType: Utils.hexToUtf8(tx.events.DeregisteredServiceProvider.returnValues._serviceType), - owner: tx.events.DeregisteredServiceProvider.returnValues._owner, - endpoint: tx.events.DeregisteredServiceProvider.returnValues._endpoint - } - } - - async getTotalServiceTypeProviders (serviceType) { - const method = await this.getMethod('getTotalServiceTypeProviders', - Utils.utf8ToHex(serviceType) - ) - const count = await method.call() - return parseInt(count) - } - - async getServiceProviderIdFromEndpoint (endpoint) { - const method = await this.getMethod('getServiceProviderIdFromEndpoint', - (endpoint) - ) - const info = await method.call() - return parseInt(info) - } - - // TODO: Remove this method after all consumers are using - // `getServiceEndpointInfo` directly - async getServiceProviderInfo (serviceType, serviceId) { - return this.getServiceEndpointInfo(serviceType, serviceId) - } - - async getServiceEndpointInfo (serviceType, serviceId) { - const method = await this.getMethod('getServiceEndpointInfo', - Utils.utf8ToHex(serviceType), - serviceId - ) - const info = await method.call() - return { - owner: info.owner, - endpoint: info.endpoint.replace(/\/$/, ''), - spID: parseInt(serviceId), - type: serviceType, - blockNumber: parseInt(info.blockNumber), - delegateOwnerWallet: info.delegateOwnerWallet - } - } - - async getServiceProviderInfoFromEndpoint (endpoint) { - const requestUrl = urlJoin(endpoint, 'health_check') - const axiosRequestObj = { - url: requestUrl, - method: 'get', - timeout: 1000 - } - - const resp = await axios(axiosRequestObj) - const serviceType = resp.data.data.service - - const serviceProviderId = await this.getServiceProviderIdFromEndpoint(endpoint) - const info = await this.getServiceEndpointInfo(serviceType, serviceProviderId) - return info - } - - async getServiceProviderIdsFromAddress (ownerAddress, serviceType) { - const method = await this.getMethod('getServiceProviderIdsFromAddress', - ownerAddress, - Utils.utf8ToHex(serviceType) - ) - const info = await method.call() - return info.map(id => parseInt(id)) - } - - async getServiceProviderIdFromAddress (ownerAddress, serviceType) { - const infos = await this.getServiceProviderIdsFromAddress(ownerAddress, serviceType) - return infos[0] - } - - async getServiceEndpointInfoFromAddress (ownerAddress, serviceType) { - const spId = await this.getServiceProviderIdFromAddress(ownerAddress, serviceType) - - // cast this as an array for backwards compatibility because everything expects an array - const spInfo = [await this.getServiceEndpointInfo(serviceType, spId)] - return spInfo - } - - /** - * Returns all service providers of requested `serviceType` - * Returns array of objects with schema { blockNumber, delegateOwnerWallet, endpoint, owner, spID, type } - */ - async getServiceProviderList (serviceType) { - const numberOfProviders = await this.getTotalServiceTypeProviders(serviceType) - - const providerList = await Promise.all( - range(1, numberOfProviders + 1).map(i => - this.getServiceEndpointInfo(serviceType, i) - ) - ) - return providerList.filter(provider => provider.endpoint !== '') - } - - async updateDecreaseStakeLockupDuration (duration) { - const method = await this.getGovernedMethod( - 'updateDecreaseStakeLockupDuration', - duration - ) - return this.web3Manager.sendTransaction( - method - ) - } - - async getServiceProviderDetails (serviceProviderAddress) { - const method = await this.getMethod( - 'getServiceProviderDetails', - serviceProviderAddress - ) - const info = await method.call() - return { - deployerCut: parseInt(info.deployerCut), - deployerStake: Utils.toBN(info.deployerStake), - maxAccountStake: Utils.toBN(info.maxAccountStake), - minAccountStake: Utils.toBN(info.minAccountStake), - numberOfEndpoints: parseInt(info.numberOfEndpoints), - validBounds: info.validBounds - } - } - - async updateDelegateOwnerWallet (serviceType, endpoint, updatedDelegateOwnerWallet) { - const method = await this.getMethod( - 'updateDelegateOwnerWallet', - Utils.utf8ToHex(serviceType), - endpoint, - updatedDelegateOwnerWallet - ) - - const tx = await this.web3Manager.sendTransaction(method) - return tx - } - - async updateEndpoint (serviceType, oldEndpoint, newEndpoint) { - const method = await this.getMethod( - 'updateEndpoint', - Utils.utf8ToHex(serviceType), - oldEndpoint, - newEndpoint - ) - const tx = await this.web3Manager.sendTransaction(method) - return tx - } - - async requestUpdateDeployerCut (ownerAddress, deployerCut) { - const method = await this.getMethod( - 'requestUpdateDeployerCut', - ownerAddress, - deployerCut - ) - const tx = await this.web3Manager.sendTransaction(method) - return tx - } - - async getPendingUpdateDeployerCutRequest (ownerAddress) { - const method = await this.getMethod( - 'getPendingUpdateDeployerCutRequest', - ownerAddress - ) - const { lockupExpiryBlock, newDeployerCut } = await method.call() - return { lockupExpiryBlock: parseInt(lockupExpiryBlock), newDeployerCut: parseInt(newDeployerCut) } - } - - async cancelUpdateDeployerCut (ownerAddress) { - const method = await this.getMethod( - 'cancelUpdateDeployerCut', - ownerAddress - ) - const tx = await this.web3Manager.sendTransaction(method) - return tx - } - - async updateDeployerCut (ownerAddress) { - const method = await this.getMethod( - 'updateDeployerCut', - ownerAddress - ) - const tx = await this.web3Manager.sendTransaction(method) - return tx - } - - async updateServiceProviderStake (ownerAddress, newAmount) { - const method = await this.getMethod( - 'updateServiceProviderStake', - ownerAddress, - newAmount - ) - const tx = await this.web3Manager.sendTransaction(method) - return tx - } -} - -module.exports = ServiceProviderFactoryClient diff --git a/libs/src/services/ethContracts/stakingProxyClient.js b/libs/src/services/ethContracts/stakingProxyClient.js deleted file mode 100644 index 829b1c9a88b..00000000000 --- a/libs/src/services/ethContracts/stakingProxyClient.js +++ /dev/null @@ -1,72 +0,0 @@ -const { ContractClient } = require('../contracts/ContractClient') - -class StakingProxyClient extends ContractClient { - constructor (ethWeb3Manager, contractABI, contractRegistryKey, getRegistryAddress, audiusTokenClient, logger = console) { - super(ethWeb3Manager, contractABI, contractRegistryKey, getRegistryAddress, logger) - this.audiusTokenClient = audiusTokenClient - this.toBN = ethWeb3Manager.getWeb3().utils.toBN - } - - async token () { - const method = await this.getMethod('token') - return method.call() - } - - async totalStaked () { - const method = await this.getMethod('totalStaked') - return this.toBN(await method.call()) - } - - async supportsHistory () { - const method = await this.getMethod('supportsHistory') - return method.call() - } - - async totalStakedFor (account) { - const method = await this.getMethod('totalStakedFor', account) - return this.toBN(await method.call()) - } - - async totalStakedForAt (account, blockNumber) { - const method = await this.getMethod('totalStakedForAt', account, blockNumber) - return this.toBN(await method.call()) - } - - async totalStakedAt (blockNumber) { - const method = await this.getMethod('totalStakedAt', blockNumber) - return this.toBN(await method.call()) - } - - async isStaker (account) { - const method = await this.getMethod('isStaker', account) - return method.call() - } - - async getDelegateManagerAddress () { - const method = await this.getMethod('getDelegateManagerAddress') - return method.call() - } - - async getClaimsManagerAddress () { - const method = await this.getMethod('getClaimsManagerAddress') - return method.call() - } - - async getServiceProviderFactoryAddress () { - const method = await this.getMethod('getServiceProviderFactoryAddress') - return method.call() - } - - async getGovernanceAddress () { - const method = await this.getMethod('getGovernanceAddress') - return method.call() - } - - async getLastClaimedBlockForUser () { - const method = await this.getMethod('lastClaimedFor', this.web3Manager.getWalletAddress()) - const tx = await method.call() - return tx - } -} - -module.exports = StakingProxyClient diff --git a/libs/src/services/ethContracts/wormholeClient.ts b/libs/src/services/ethContracts/wormholeClient.ts index 9bdf105c385..4e50be7f9cd 100644 --- a/libs/src/services/ethContracts/wormholeClient.ts +++ b/libs/src/services/ethContracts/wormholeClient.ts @@ -1,7 +1,7 @@ import type Web3 from 'web3' import type { ContractABI } from '../../utils' import type { EthWeb3Manager } from '../ethWeb3Manager' -import type { AudiusTokenClient } from './audiusTokenClient' +import type { AudiusTokenClient } from './AudiusTokenClient' import type { Contract } from 'web3-eth-contract' import type Wallet from 'ethereumjs-wallet' import type BN from 'bn.js' From 9b785d12418df95fa48c3868a03f58dc70818973 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 16:52:10 -0700 Subject: [PATCH 06/14] Fix wallet usage --- .../services/ethContracts/AudiusTokenClient.ts | 15 +++++++-------- .../ethContracts/ClaimDistributionClient.ts | 3 +-- libs/src/services/ethContracts/wormholeClient.ts | 7 +++---- .../src/services/ethWeb3Manager/EthWeb3Manager.ts | 4 ++-- libs/src/services/identity/IdentityService.ts | 2 +- libs/src/utils/estimateGas.ts | 4 ++-- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/libs/src/services/ethContracts/AudiusTokenClient.ts b/libs/src/services/ethContracts/AudiusTokenClient.ts index c7e5af9ff0d..180962044e6 100644 --- a/libs/src/services/ethContracts/AudiusTokenClient.ts +++ b/libs/src/services/ethContracts/AudiusTokenClient.ts @@ -2,7 +2,6 @@ import type Web3 from 'web3' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { Contract } from 'web3-eth-contract' import type { AbiItem } from 'web3-utils' -import type Wallet from 'ethereumjs-wallet' export class AudiusTokenClient { ethWeb3Manager: EthWeb3Manager @@ -54,7 +53,7 @@ export class AudiusTokenClient { } // Get the name of the contract - async nonces(wallet: Wallet) { + async nonces(wallet: string) { // Pass along a unique param so the nonce value is always not cached const nonce = await this.AudiusTokenContract.methods.nonces(wallet).call({ _audiusBustCache: Date.now() @@ -75,9 +74,9 @@ export class AudiusTokenClient { } async transferFrom( - owner: Wallet, + owner: string, recipient: string, - relayer: Wallet, + relayer: string, amount: number ) { const method = this.AudiusTokenContract.methods.transferFrom( @@ -97,8 +96,8 @@ export class AudiusTokenClient { // Permit meta transaction of balance transfer async permit( - owner: Wallet, // address - spender: Wallet, // address + owner: string, // address + spender: string, // address value: number, // uint deadline: number, // uint v: number, // uint8 @@ -145,10 +144,10 @@ export class AudiusTokenClient { } async approveProxyTokens( - owner: Wallet, + owner: string, spender: string, value: number, - relayer: Wallet + relayer: string ) { const method = this.AudiusTokenContract.methods.approve(spender, value) const tx = await this.ethWeb3Manager.relayTransaction( diff --git a/libs/src/services/ethContracts/ClaimDistributionClient.ts b/libs/src/services/ethContracts/ClaimDistributionClient.ts index 12703235aa5..2b0f6ddc74c 100644 --- a/libs/src/services/ethContracts/ClaimDistributionClient.ts +++ b/libs/src/services/ethContracts/ClaimDistributionClient.ts @@ -1,4 +1,3 @@ -import type Wallet from 'ethereumjs-wallet' import { ContractClient } from '../contracts/ContractClient' import type { EthWeb3Manager } from '../ethWeb3Manager' @@ -23,7 +22,7 @@ export class ClaimDistributionClient extends ContractClient { */ async claim( index: number, - account: Wallet, + account: string, amount: string, merkleProof: string[] ) { diff --git a/libs/src/services/ethContracts/wormholeClient.ts b/libs/src/services/ethContracts/wormholeClient.ts index 4e50be7f9cd..822cc9e1f1b 100644 --- a/libs/src/services/ethContracts/wormholeClient.ts +++ b/libs/src/services/ethContracts/wormholeClient.ts @@ -3,7 +3,6 @@ import type { ContractABI } from '../../utils' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { AudiusTokenClient } from './AudiusTokenClient' import type { Contract } from 'web3-eth-contract' -import type Wallet from 'ethereumjs-wallet' import type BN from 'bn.js' export class WormholeClient { @@ -44,7 +43,7 @@ export class WormholeClient { /* ------- SETTERS ------- */ - async initialize(fromAcct: Wallet, wormholeAddress: string, relayer: Wallet) { + async initialize(fromAcct: string, wormholeAddress: string, relayer: string) { const method = this.WormholeContract.methods.initialize( this.audiusTokenClient.contractAddress, wormholeAddress @@ -65,14 +64,14 @@ export class WormholeClient { * specifies a solana wallet to realized the tokens in SOL */ async transferTokens( - fromAcct: Wallet, + fromAcct: string, amount: BN, chainId: number, solanaAccount: string, arbiterFee: string, deadline: string, signedDigest: { v: string; r: string; s: string }, - relayer: Wallet + relayer: string ) { const method = this.WormholeContract.methods.transferTokens( fromAcct, diff --git a/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts b/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts index 854c19f033d..38a6f81b627 100644 --- a/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts +++ b/libs/src/services/ethWeb3Manager/EthWeb3Manager.ts @@ -155,8 +155,8 @@ export class EthWeb3Manager { async relayTransaction( contractMethod: ContractMethod, contractAddress: string, - ownerWallet: Wallet, - relayerWallet?: Wallet, + ownerWallet: Wallet | string, + relayerWallet?: Wallet | string, txRetries = 5, txGasLimit: number | null = null ): Promise> { diff --git a/libs/src/services/identity/IdentityService.ts b/libs/src/services/identity/IdentityService.ts index 1081d8ad7e3..b9f9fdb2373 100644 --- a/libs/src/services/identity/IdentityService.ts +++ b/libs/src/services/identity/IdentityService.ts @@ -385,7 +385,7 @@ export class IdentityService { async ethRelay( contractAddress: string, - senderAddress: Wallet, + senderAddress: Wallet | string, encodedABI: string, gasLimit: string ): Promise { diff --git a/libs/src/utils/estimateGas.ts b/libs/src/utils/estimateGas.ts index 017a573b687..33bb372230a 100644 --- a/libs/src/utils/estimateGas.ts +++ b/libs/src/utils/estimateGas.ts @@ -7,7 +7,7 @@ const GAS_LIMIT_MULTIPLIER = 1.05 export interface ContractMethod { arguments: string[] estimateGas: (config: { - from: Wallet | undefined + from: Wallet | string | undefined gas: number | undefined }) => Promise _method: { @@ -24,7 +24,7 @@ export interface ContractMethod { interface EstimateGasConfig { method: ContractMethod - from?: Wallet + from?: Wallet | string gasLimitMaximum: number multiplier?: number } From 099db0250cd56fb001c2b422f86417599319d0ec Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 16:54:56 -0700 Subject: [PATCH 07/14] Rename wormholeClient --- libs/src/services/ethContracts/EthContracts.ts | 2 +- .../ethContracts/{wormholeClient.ts => WormholeClient.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename libs/src/services/ethContracts/{wormholeClient.ts => WormholeClient.ts} (100%) diff --git a/libs/src/services/ethContracts/EthContracts.ts b/libs/src/services/ethContracts/EthContracts.ts index 986cb25fc55..226fe0dbf09 100644 --- a/libs/src/services/ethContracts/EthContracts.ts +++ b/libs/src/services/ethContracts/EthContracts.ts @@ -8,7 +8,7 @@ import { StakingProxyClient } from './StakingProxyClient' import { DelegateManagerClient } from './DelegateManagerClient' import { ClaimsManagerClient } from './ClaimsManagerClient' import { ClaimDistributionClient } from './ClaimDistributionClient' -import { WormholeClient } from './wormholeClient' +import { WormholeClient } from './WormholeClient' import { EthRewardsManagerClient } from './EthRewardsManagerClient' import { TrustedNotifierManagerClient } from './TrustedNotifierManagerClient' import { Logger, Utils } from '../../utils' diff --git a/libs/src/services/ethContracts/wormholeClient.ts b/libs/src/services/ethContracts/WormholeClient.ts similarity index 100% rename from libs/src/services/ethContracts/wormholeClient.ts rename to libs/src/services/ethContracts/WormholeClient.ts From fc47ee01c39b84df5a2c5e2a832e992e0e9e8253 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 17:08:29 -0700 Subject: [PATCH 08/14] Replace number with BN --- .../services/ethContracts/AudiusTokenClient.ts | 15 ++++++++------- .../ethContracts/ClaimDistributionClient.ts | 3 ++- .../ethContracts/DelegateManagerClient.ts | 4 ++-- .../ethContracts/ServiceProviderFactoryClient.ts | 16 ++++++++++------ libs/src/utils/utils.ts | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/libs/src/services/ethContracts/AudiusTokenClient.ts b/libs/src/services/ethContracts/AudiusTokenClient.ts index 180962044e6..877e91ed6bc 100644 --- a/libs/src/services/ethContracts/AudiusTokenClient.ts +++ b/libs/src/services/ethContracts/AudiusTokenClient.ts @@ -2,6 +2,7 @@ import type Web3 from 'web3' import type { EthWeb3Manager } from '../ethWeb3Manager' import type { Contract } from 'web3-eth-contract' import type { AbiItem } from 'web3-utils' +import type BN from 'bn.js' export class AudiusTokenClient { ethWeb3Manager: EthWeb3Manager @@ -64,7 +65,7 @@ export class AudiusTokenClient { /* ------- SETTERS ------- */ - async transfer(recipient: string, amount: number) { + async transfer(recipient: string, amount: BN) { const contractMethod = this.AudiusTokenContract.methods.transfer( recipient, amount @@ -77,7 +78,7 @@ export class AudiusTokenClient { owner: string, recipient: string, relayer: string, - amount: number + amount: BN ) { const method = this.AudiusTokenContract.methods.transferFrom( owner, @@ -98,11 +99,11 @@ export class AudiusTokenClient { async permit( owner: string, // address spender: string, // address - value: number, // uint + value: BN, // uint deadline: number, // uint v: number, // uint8 - r: BinaryData, // bytes32 - s: BinaryData // bytes32 + r: Uint8Array | Buffer, // bytes32 + s: Uint8Array | Buffer // bytes32 ) { const contractMethod = this.AudiusTokenContract.methods.permit( owner, @@ -125,7 +126,7 @@ export class AudiusTokenClient { // Allow spender to withdraw from calling account up to value amount // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md - async approve(spender: string, value: number, privateKey = null) { + async approve(spender: string, value: BN, privateKey = null) { const contractMethod = this.AudiusTokenContract.methods.approve( spender, value @@ -146,7 +147,7 @@ export class AudiusTokenClient { async approveProxyTokens( owner: string, spender: string, - value: number, + value: BN, relayer: string ) { const method = this.AudiusTokenContract.methods.approve(spender, value) diff --git a/libs/src/services/ethContracts/ClaimDistributionClient.ts b/libs/src/services/ethContracts/ClaimDistributionClient.ts index 2b0f6ddc74c..a92b6bb1187 100644 --- a/libs/src/services/ethContracts/ClaimDistributionClient.ts +++ b/libs/src/services/ethContracts/ClaimDistributionClient.ts @@ -1,3 +1,4 @@ +import type BN from 'bn.js' import { ContractClient } from '../contracts/ContractClient' import type { EthWeb3Manager } from '../ethWeb3Manager' @@ -23,7 +24,7 @@ export class ClaimDistributionClient extends ContractClient { async claim( index: number, account: string, - amount: string, + amount: BN, merkleProof: string[] ) { const method = await this.getMethod( diff --git a/libs/src/services/ethContracts/DelegateManagerClient.ts b/libs/src/services/ethContracts/DelegateManagerClient.ts index e014215503b..3256f090aeb 100644 --- a/libs/src/services/ethContracts/DelegateManagerClient.ts +++ b/libs/src/services/ethContracts/DelegateManagerClient.ts @@ -39,7 +39,7 @@ export class DelegateManagerClient extends GovernedContractClient { this.stakingProxyClient = stakingProxyClient } - async delegateStake(targetSP: string, amount: number) { + async delegateStake(targetSP: string, amount: BN) { // Approve token transfer operation const contractAddress = await this.stakingProxyClient.getAddress() const tx0 = await this.audiusTokenClient.approve(contractAddress, amount) @@ -236,7 +236,7 @@ export class DelegateManagerClient extends GovernedContractClient { })) } - async requestUndelegateStake(targetSP: string, amount: number) { + async requestUndelegateStake(targetSP: string, amount: BN) { const method = await this.getMethod( 'requestUndelegateStake', targetSP, diff --git a/libs/src/services/ethContracts/ServiceProviderFactoryClient.ts b/libs/src/services/ethContracts/ServiceProviderFactoryClient.ts index 194346ee7d1..5d2330bbe65 100644 --- a/libs/src/services/ethContracts/ServiceProviderFactoryClient.ts +++ b/libs/src/services/ethContracts/ServiceProviderFactoryClient.ts @@ -10,6 +10,7 @@ import type { AudiusTokenClient } from './AudiusTokenClient' import type { StakingProxyClient } from './StakingProxyClient' import type { GovernanceClient } from './GovernanceClient' import urlJoin from 'proper-url-join' +import type BN from 'bn.js' type GetEvent = { serviceType: string @@ -51,7 +52,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { async registerWithDelegate( serviceType: string, endpoint: string, - amount: number, + amount: number | string | BN, delegateOwnerWallet: string ) { const sanitizedEndpoint = endpoint.replace(/\/$/, '') @@ -63,7 +64,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { if (!this.isDebug && !Utils.isFQDN(sanitizedEndpoint)) { throw new Error('Not a fully qualified domain name!') } - if (!Number.isInteger(amount) && !Utils.isBN(amount)) { + if (!Number.isInteger(amount) && !Utils.isBN(amount as string)) { throw new Error('Invalid amount') } @@ -84,7 +85,10 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { // Approve token transfer operation const contractAddress = await this.stakingProxyClient.getAddress() - const tx0 = await this.audiusTokenClient.approve(contractAddress, amount) + const tx0 = await this.audiusTokenClient.approve( + contractAddress, + amount as BN + ) // Register and stake const method = await this.getMethod( @@ -107,7 +111,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { } } - async register(serviceType: string, endpoint: string, amount: number) { + async register(serviceType: string, endpoint: string, amount: BN) { return await this.registerWithDelegate( serviceType, endpoint, @@ -337,7 +341,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { return service } - async increaseStake(amount: number) { + async increaseStake(amount: BN) { const contractAddress = await this.stakingProxyClient.getAddress() const tx0 = await this.audiusTokenClient.approve(contractAddress, amount) const method = await this.getMethod('increaseStake', amount) @@ -354,7 +358,7 @@ export class ServiceProviderFactoryClient extends GovernedContractClient { * @param amount * @returns decrease stake lockup expiry block */ - async requestDecreaseStake(amount: number) { + async requestDecreaseStake(amount: BN) { const requestDecreaseMethod = await this.getMethod( 'requestDecreaseStake', amount diff --git a/libs/src/utils/utils.ts b/libs/src/utils/utils.ts index 23ca873a224..da0fd052319 100644 --- a/libs/src/utils/utils.ts +++ b/libs/src/utils/utils.ts @@ -47,7 +47,7 @@ export class Utils { return Web3.utils.keccak256(utf8Str) } - static isBN(number: number) { + static isBN(number: number | string) { return Web3.utils.isBN(number) } From 349819b28be691b508b01dc33435c975f8b1f9db Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 17:13:40 -0700 Subject: [PATCH 09/14] Fix lint --- libs/src/services/creatorNode/CreatorNode.ts | 2 +- .../DiscoveryProvider.test.ts | 74 ------------------- .../ethContracts/DelegateManagerClient.ts | 2 +- libs/src/services/web3Manager/Web3Manager.ts | 4 +- .../services/web3Manager/XMLHttpRequest.ts | 2 + 5 files changed, 6 insertions(+), 78 deletions(-) delete mode 100644 libs/src/services/discoveryProvider/DiscoveryProvider.test.ts diff --git a/libs/src/services/creatorNode/CreatorNode.ts b/libs/src/services/creatorNode/CreatorNode.ts index 0f83fdf55d7..cd814f02cc8 100644 --- a/libs/src/services/creatorNode/CreatorNode.ts +++ b/libs/src/services/creatorNode/CreatorNode.ts @@ -7,7 +7,7 @@ import { trackSchemaType, Schemas } from '../schemaValidator/SchemaValidator' -import type { Web3Manager }from '../web3Manager' +import type { Web3Manager } from '../web3Manager' import type { CurrentUser, UserStateManager } from '../../userStateManager' const { wait } = Utils diff --git a/libs/src/services/discoveryProvider/DiscoveryProvider.test.ts b/libs/src/services/discoveryProvider/DiscoveryProvider.test.ts deleted file mode 100644 index 5fac623c667..00000000000 --- a/libs/src/services/discoveryProvider/DiscoveryProvider.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -export {} -// const DiscoveryProvider = require('.') -// const helpers = require('../../../tests/helpers') -// const { DISCOVERY_PROVIDER_TIMESTAMP } = require('./constants') -// -// let audiusInstance = helpers.audiusInstance -// -// // eslint-disable-next-line mocha/no-skipped-tests -- probably should be an integration test -// xit('will reselect a healthy disc prov if initialized disc prov becomes unhealthy', async () => { -// const LocalStorage = require('node-localstorage').LocalStorage -// const localStorage = new LocalStorage('./local-storage') -// -// // Make healthyThenUnhealthy respond with 200 initially -// const healthyThenUnhealthy = 'https://healthyThenUnhealthy.audius.co' -// const healthyThenUnhealthyInterceptor = nock(healthyThenUnhealthy) -// .persist() -// .get(uri => true) // hitting any route will respond with 200 -// .reply(200, { -// data: { -// service: 'discovery-node', -// version: '1.2.3', -// block_difference: 0 -// } -// }) -// -// const healthy = 'https://healthy.audius.co' -// nock(healthy) -// .persist() -// .get(uri => true) -// .delay(100) -// .reply(200, { -// data: { -// service: 'discovery-node', -// version: '1.2.3', -// block_difference: 0 -// } -// }) -// -// // Initialize libs and then set disc prov instance with eth contracts mock -// await audiusInstance.init() -// localStorage.removeItem(DISCOVERY_PROVIDER_TIMESTAMP) -// const mockDP = new DiscoveryProvider( -// null, // whitelist -// null, // blacklist -// audiusInstance.userStateManager, -// mockEthContracts([healthyThenUnhealthy, healthy], '1.2.3'), -// audiusInstance.web3Manager, -// null, // reselectTimeout -// null // selectionCallback -// ) -// await mockDP.init() -// audiusInstance.discoveryProvider = mockDP -// -// // Check that healthyThenUnhealthy was chosen and set in local stoarge -// const discProvLocalStorageData1 = JSON.parse(localStorage.getItem(DISCOVERY_PROVIDER_TIMESTAMP)) -// assert.strictEqual(audiusInstance.discoveryProvider.discoveryProviderEndpoint, healthyThenUnhealthy) -// assert.strictEqual(discProvLocalStorageData1.endpoint, healthyThenUnhealthy) -// -// // Then make healthyThenUnhealthy fail on successive requests -// // To remove any persistent interceptors: https://stackoverflow.com/a/59885361 -// nock.removeInterceptor(healthyThenUnhealthyInterceptor.interceptors[0]) -// nock(healthyThenUnhealthy) -// .persist() -// .get(uri => true) -// .reply(500, { unhealthy: true }) -// -// // Make a libs request to disc prov; should use healthy -// await audiusInstance.discoveryProvider.getUsers(1) -// const discProvLocalStorageData2 = JSON.parse(localStorage.getItem(DISCOVERY_PROVIDER_TIMESTAMP)) -// -// // Check that healthy was chosen and set in local stoarge -// assert.strictEqual(audiusInstance.discoveryProvider.discoveryProviderEndpoint, healthy) -// assert.strictEqual(discProvLocalStorageData2.endpoint, healthy) -// }) diff --git a/libs/src/services/ethContracts/DelegateManagerClient.ts b/libs/src/services/ethContracts/DelegateManagerClient.ts index 3256f090aeb..1ab7850445e 100644 --- a/libs/src/services/ethContracts/DelegateManagerClient.ts +++ b/libs/src/services/ethContracts/DelegateManagerClient.ts @@ -459,7 +459,7 @@ export class DelegateManagerClient extends GovernedContractClient { serviceProvider, amount ) - return this.web3Manager.sendTransaction(method) + return await this.web3Manager.sendTransaction(method) } async updateRemoveDelegatorLockupDuration(duration: string) { diff --git a/libs/src/services/web3Manager/Web3Manager.ts b/libs/src/services/web3Manager/Web3Manager.ts index 9a65049d567..a236599355e 100644 --- a/libs/src/services/web3Manager/Web3Manager.ts +++ b/libs/src/services/web3Manager/Web3Manager.ts @@ -258,8 +258,8 @@ export class Web3Manager { evt.events.forEach((arg) => { returnValues[arg.name] = arg.value }) - const eventLog = { returnValues } as EventLog - events[evt.name] = eventLog + const eventLog = { returnValues } + events[evt.name] = eventLog as EventLog }) receipt.events = events } diff --git a/libs/src/services/web3Manager/XMLHttpRequest.ts b/libs/src/services/web3Manager/XMLHttpRequest.ts index ff85d081d9a..6cba066e0e4 100644 --- a/libs/src/services/web3Manager/XMLHttpRequest.ts +++ b/libs/src/services/web3Manager/XMLHttpRequest.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + let XMLHttpRequestRef: typeof window.XMLHttpRequest if (typeof window === 'undefined' || window === null) { From 96d44d230fcfd73de82b6f20aa2f916e7858c085 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 17:19:42 -0700 Subject: [PATCH 10/14] Remove extraneous js class --- .../contracts/GovernedContractClient.js | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 libs/src/services/contracts/GovernedContractClient.js diff --git a/libs/src/services/contracts/GovernedContractClient.js b/libs/src/services/contracts/GovernedContractClient.js deleted file mode 100644 index a5acc8e25af..00000000000 --- a/libs/src/services/contracts/GovernedContractClient.js +++ /dev/null @@ -1,38 +0,0 @@ -const { ContractClient } = require('./ContractClient') - -/** - * Contract class that extends a ContractClient and provides an interface - * to retrieve governed methods that cannot be executed directly. - */ -class GovernedContractClient extends ContractClient { - constructor ( - web3Manager, - contractABI, - contractRegistryKey, - getRegistryAddress, - governanceClient, - logger = console - ) { - super(web3Manager, contractABI, contractRegistryKey, getRegistryAddress, logger) - this.governanceClient = governanceClient - } - - /** - * Gets a governed version of a method and allows a single transaction - * to be sent to the governance client with the appropriate payload. - * Similar to `getMethod` - */ - async getGovernedMethod (methodName, ...args) { - const contractMethod = await this.getMethod(methodName, ...args) - const { signature, callData } = this.governanceClient.getSignatureAndCallData(methodName, contractMethod) - const contractRegistryKey = this.web3Manager.getWeb3().utils.utf8ToHex(this.contractRegistryKey) - const method = await this.governanceClient.guardianExecuteTransaction( - contractRegistryKey, - signature, - callData - ) - return method - } -} - -module.exports = GovernedContractClient From 5b201d309bcbb1e31326b8219f343027bee1f5c4 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 17:26:24 -0700 Subject: [PATCH 11/14] Fix web3Manager import --- libs/src/services/solanaAudiusData/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/src/services/solanaAudiusData/index.ts b/libs/src/services/solanaAudiusData/index.ts index ccb06a72411..3221b2fb497 100644 --- a/libs/src/services/solanaAudiusData/index.ts +++ b/libs/src/services/solanaAudiusData/index.ts @@ -2,7 +2,7 @@ import * as AudiusData from '@audius/anchor-audius-data' import type { AudiusDataProgram } from '@audius/anchor-audius-data' import anchor, { BN, Idl } from '@project-serum/anchor' import type SolanaWeb3Manager from '../solanaWeb3Manager' -import type Web3Manager from '../web3Manager' +import type { Web3Manager } from '../web3Manager' import { PublicKey, Keypair, SystemProgram, Transaction } from '@solana/web3.js' import SolanaUtils from '../solanaWeb3Manager/utils' import { audiusDataErrorMapping } from './errors' From 32ed7da5b9b19203b7d59a5ca6a7ab94c6e71622 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 18:02:56 -0700 Subject: [PATCH 12/14] Fix governanceTest --- libs/tests/governanceTest.js | 219 ++++++++++++++++++++++++----------- 1 file changed, 150 insertions(+), 69 deletions(-) diff --git a/libs/tests/governanceTest.js b/libs/tests/governanceTest.js index c4e59022c08..c208c225c65 100644 --- a/libs/tests/governanceTest.js +++ b/libs/tests/governanceTest.js @@ -2,9 +2,9 @@ const helpers = require('./helpers') const assert = require('assert') const { convertAudsToWeiBN } = require('../initScripts/helpers/utils') const nock = require('nock') -const {GovernanceClient, Vote} = require('../src/services/ethContracts/governanceClient') +const { Vote } = require('../src/services/ethContracts/GovernanceClient') const time = require('@openzeppelin/test-helpers/src/time') -const { initializeLibConfig, deregisterSPEndpoint } = require('./helpers') +const { initializeLibConfig } = require('./helpers') const AudiusLibs = require('../src') const { audiusInstance: audius0, getRandomLocalhost } = helpers @@ -39,15 +39,20 @@ const setupAccounts = async () => { // Register endpoints & stake const inittedLibs = [audius0, audius1, audius2] - const defaultStake = convertAudsToWeiBN(audius0.ethWeb3Manager.getWeb3(), DEFAULT_STAKE) + const defaultStake = convertAudsToWeiBN( + audius0.ethWeb3Manager.getWeb3(), + DEFAULT_STAKE + ) for (const lib of inittedLibs) { const testEndpoint = getRandomLocalhost() - nock(testEndpoint).get('/health_check').reply(200, { - data: { - service: testServiceType, - version : '0.0.1' - } - }) + nock(testEndpoint) + .get('/health_check') + .reply(200, { + data: { + service: testServiceType, + version: '0.0.1' + } + }) await lib.ethContracts.ServiceProviderFactoryClient.register( testServiceType, @@ -70,9 +75,9 @@ const cleanupAccounts = async () => { const createProposal = () => { const targetContractRegistryKey = web3.utils.utf8ToHex('DelegateManager') - const callValue = web3.utils.toWei("0", 'ether') + const callValue = web3.utils.toWei('0', 'ether') const functionSignature = 'slash(uint256,address)' - const slashAmount = web3.utils.toWei("500", 'ether') + const slashAmount = web3.utils.toWei('500', 'ether') const targetAddress = accounts[1] const callData = [slashAmount, targetAddress] const description = PROPOSAL_DESCRIPTION @@ -97,11 +102,14 @@ const evaluateProposal = async (id, shouldVote = true) => { // Vote if (shouldVote) { const voteYes = Vote.yes - await audius0.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteYes}) + await audius0.ethContracts.GovernanceClient.submitVote({ + proposalId: id, + vote: voteYes + }) } // Advance blocks - const currentBlock = (await web3.eth.getBlock("latest")).number + const currentBlock = (await web3.eth.getBlock('latest')).number const desired = currentBlock + 20 await time.advanceBlockTo(desired) @@ -109,7 +117,7 @@ const evaluateProposal = async (id, shouldVote = true) => { await audius0.ethContracts.GovernanceClient.evaluateProposalOutcome(id) } -describe('Governance tests', function() { +describe('Governance tests', function () { this.timeout(5000) before(async function () { @@ -118,134 +126,191 @@ describe('Governance tests', function() { await audius0.ethContracts.GovernanceClient.setExecutionDelay(10) }) - afterEach(async function() { + afterEach(async function () { // Cleanup outstanding proposals - const ids = await audius0.ethContracts.GovernanceClient.getInProgressProposals() + const ids = + await audius0.ethContracts.GovernanceClient.getInProgressProposals() for (let id of ids) { await evaluateProposal(id, false) } }) - after(async function() { + after(async function () { await cleanupAccounts() }) - it('Submits a proposal successfully', async function() { + it('Submits a proposal successfully', async function () { const id = await submitProposal() assert.ok(id >= 0) }) - it('Gets submitted proposal by ID', async function() { + it('Gets submitted proposal by ID', async function () { // Submit the proposal const id = await submitProposal() - const proposal = await audius0.ethContracts.GovernanceClient.getProposalById(id) + const proposal = + await audius0.ethContracts.GovernanceClient.getProposalById(id) // Sanity check proposal assert.equal(proposal.proposalId, id) }) - it('Votes on proposals', async function() { + it('Votes on proposals', async function () { const id = await submitProposal() // Submit a yes vote initially const voteYes = Vote.yes - await audius0.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteYes}) - const proposal = await audius0.ethContracts.GovernanceClient.getProposalById(id) + await audius0.ethContracts.GovernanceClient.submitVote({ + proposalId: id, + vote: voteYes + }) + const proposal = + await audius0.ethContracts.GovernanceClient.getProposalById(id) const bnZero = toBN(0) assert.ok(proposal.voteMagnitudeNo.eq(bnZero)) assert.ok(proposal.voteMagnitudeYes.gt(bnZero)) // Update the vote to be 'no' const voteNo = Vote.no - await audius0.ethContracts.GovernanceClient.updateVote({proposalId: id, vote: voteNo}) - const proposal2 = await audius0.ethContracts.GovernanceClient.getProposalById(id) + await audius0.ethContracts.GovernanceClient.updateVote({ + proposalId: id, + vote: voteNo + }) + const proposal2 = + await audius0.ethContracts.GovernanceClient.getProposalById(id) assert.ok(proposal2.voteMagnitudeYes.eq(bnZero)) - assert.ok((proposal2.voteMagnitudeNo.gt(bnZero))) + assert.ok(proposal2.voteMagnitudeNo.gt(bnZero)) }) - it('Queries for votes', async function() { + it('Queries for votes', async function () { const id = await submitProposal() - const proposal = await audius0.ethContracts.GovernanceClient.getProposalById(id) + const proposal = + await audius0.ethContracts.GovernanceClient.getProposalById(id) const queryStartBlock = proposal.submissionBlockNumber // Test for vote creation const voteYes = Vote.yes - await audius0.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteYes}) - const votes = await audius0.ethContracts.GovernanceClient.getVotes({ proposalId: id, queryStartBlock }) + await audius0.ethContracts.GovernanceClient.submitVote({ + proposalId: id, + vote: voteYes + }) + const votes = await audius0.ethContracts.GovernanceClient.getVotes({ + proposalId: id, + queryStartBlock + }) assert.equal(votes.length, 1) assert.equal(votes[0].vote, 2) // Test for vote update const voteNo = Vote.no - await audius0.ethContracts.GovernanceClient.updateVote({proposalId: id, vote: voteNo}) - const updateVotes = await audius0.ethContracts.GovernanceClient.getVoteUpdates({ proposalId: id, queryStartBlock }) + await audius0.ethContracts.GovernanceClient.updateVote({ + proposalId: id, + vote: voteNo + }) + const updateVotes = + await audius0.ethContracts.GovernanceClient.getVoteUpdates({ + proposalId: id, + queryStartBlock + }) assert.equal(updateVotes.length, 1) assert.equal(updateVotes[0].vote, 1) }) - it('Gets in progress proposals', async function() { + it('Gets in progress proposals', async function () { const id = await submitProposal() - const inProgressIds = await audius0.ethContracts.GovernanceClient.getInProgressProposals() + const inProgressIds = + await audius0.ethContracts.GovernanceClient.getInProgressProposals() assert.equal(inProgressIds.length, 1) assert.equal(inProgressIds[0], id) // evaluate await evaluateProposal(id) - const inProgressIds2 = await audius0.ethContracts.GovernanceClient.getInProgressProposals() + const inProgressIds2 = + await audius0.ethContracts.GovernanceClient.getInProgressProposals() assert.equal(inProgressIds2.length, 0) }) - it('Gets all proposals', async function() { - const blockNumber = await audius0.ethWeb3Manager.getWeb3().eth.getBlockNumber() + it('Gets all proposals', async function () { + const blockNumber = await audius0.ethWeb3Manager + .getWeb3() + .eth.getBlockNumber() const id = await submitProposal() - const inProgressIds = await audius0.ethContracts.GovernanceClient.getProposals(blockNumber) + const inProgressIds = + await audius0.ethContracts.GovernanceClient.getProposals(blockNumber) assert.equal(inProgressIds.length, 1) assert.equal(inProgressIds[0].proposalId, id) // evaluate await evaluateProposal(id) - const inProgressIds2 = await audius0.ethContracts.GovernanceClient.getProposals(blockNumber) + const inProgressIds2 = + await audius0.ethContracts.GovernanceClient.getProposals(blockNumber) assert.equal(inProgressIds.length, 1) assert.equal(inProgressIds[0].proposalId, id) }) - it('Gets a proposal evaluation', async function() { - const blockNumber = await audius0.ethWeb3Manager.getWeb3().eth.getBlockNumber() + it('Gets a proposal evaluation', async function () { + const blockNumber = await audius0.ethWeb3Manager + .getWeb3() + .eth.getBlockNumber() const id = await submitProposal() await evaluateProposal(id) - const evaluation = await audius0.ethContracts.GovernanceClient.getProposalEvaluation(id) + const evaluation = + await audius0.ethContracts.GovernanceClient.getProposalEvaluation(id) assert.equal(evaluation.length, 1) - assert.equal(evaluation[0].returnValues._numVotes, "1") + assert.equal(evaluation[0].returnValues._numVotes, '1') }) - it('Gets a proposal submisson', async function() { - const blockNumber = await audius0.ethWeb3Manager.getWeb3().eth.getBlockNumber() + it('Gets a proposal submisson', async function () { + const blockNumber = await audius0.ethWeb3Manager + .getWeb3() + .eth.getBlockNumber() const id = await submitProposal() - const submission = await audius0.ethContracts.GovernanceClient.getProposalSubmission(id) + const submission = + await audius0.ethContracts.GovernanceClient.getProposalSubmission(id) assert.equal(submission.description, PROPOSAL_DESCRIPTION) assert.equal(submission.name, PROPOSAL_NAME) }) - it('Gets votes by address', async function() { + it('Gets votes by address', async function () { const id = await submitProposal() - const proposal = await audius0.ethContracts.GovernanceClient.getProposalById(id) + const proposal = + await audius0.ethContracts.GovernanceClient.getProposalById(id) const queryStartBlock = proposal.submissionBlockNumber // submit votes const voteYes = Vote.yes const voteNo = Vote.no - await audius1.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteYes}) - await audius2.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteNo}) + await audius1.ethContracts.GovernanceClient.submitVote({ + proposalId: id, + vote: voteYes + }) + await audius2.ethContracts.GovernanceClient.submitVote({ + proposalId: id, + vote: voteNo + }) // update some votes - await audius1.ethContracts.GovernanceClient.updateVote({proposalId: id, vote: voteNo}) - await audius2.ethContracts.GovernanceClient.updateVote({proposalId: id, vote: voteYes}) + await audius1.ethContracts.GovernanceClient.updateVote({ + proposalId: id, + vote: voteNo + }) + await audius2.ethContracts.GovernanceClient.updateVote({ + proposalId: id, + vote: voteYes + }) // get submissions and updates - const account1Submissions = await audius0.ethContracts.GovernanceClient.getVoteSubmissionsByAddress({ addresses: [accounts[1]], queryStartBlock }) - const account2Updates = await audius0.ethContracts.GovernanceClient.getVoteUpdatesByAddress({ addresses: [accounts[2]], queryStartBlock }) + const account1Submissions = + await audius0.ethContracts.GovernanceClient.getVoteSubmissionsByAddress({ + addresses: [accounts[1]], + queryStartBlock + }) + const account2Updates = + await audius0.ethContracts.GovernanceClient.getVoteUpdatesByAddress({ + addresses: [accounts[2]], + queryStartBlock + }) // assert filters work correctly assert.equal(account1Submissions.length, 1) @@ -254,48 +319,64 @@ describe('Governance tests', function() { assert.equal(account2Updates[0].voter, accounts[2]) }) - it('Gets Quorum Percent', async function() { - const percent = await audius0.ethContracts.GovernanceClient.getVotingQuorumPercent() + it('Gets Quorum Percent', async function () { + const percent = + await audius0.ethContracts.GovernanceClient.getVotingQuorumPercent() assert.ok(typeof percent === 'number') }) - it('Gets voting period', async function() { + it('Gets voting period', async function () { const period = await audius0.ethContracts.GovernanceClient.getVotingPeriod() assert.ok(typeof period === 'number') }) - it('Gets execution delay', async function() { - const delay = await audius0.ethContracts.GovernanceClient.getExecutionDelay() + it('Gets execution delay', async function () { + const delay = + await audius0.ethContracts.GovernanceClient.getExecutionDelay() assert.ok(typeof delay === 'number') }) - it('Gets target contract hash', async function() { + it('Gets target contract hash', async function () { const id = await submitProposal() - const hash = await audius0.ethContracts.GovernanceClient.getProposalTargetContractHash(id) + const hash = + await audius0.ethContracts.GovernanceClient.getProposalTargetContractHash( + id + ) assert.ok(typeof hash === 'string') assert.ok(hash.length > 0) }) - it('Gets vote by proposal and voter', async function() { + it('Gets vote by proposal and voter', async function () { const id = await submitProposal() // submit yes votes const voteYes = Vote.yes const voteNo = Vote.no - await audius1.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteYes}) - const result = await audius0.ethContracts.GovernanceClient.getVoteByProposalAndVoter({ + await audius1.ethContracts.GovernanceClient.submitVote({ proposalId: id, - voterAddress: accounts[1] + vote: voteYes }) + const result = + await audius0.ethContracts.GovernanceClient.getVoteByProposalAndVoter({ + proposalId: id, + voterAddress: accounts[1] + }) assert.equal(result, 2) // submit yes vote, update to no vote - await audius2.ethContracts.GovernanceClient.submitVote({proposalId: id, vote: voteYes}) - await audius2.ethContracts.GovernanceClient.updateVote({proposalId: id, vote: voteNo}) - const result2 = await audius0.ethContracts.GovernanceClient.getVoteByProposalAndVoter({ + await audius2.ethContracts.GovernanceClient.submitVote({ + proposalId: id, + vote: voteYes + }) + await audius2.ethContracts.GovernanceClient.updateVote({ proposalId: id, - voterAddress: accounts[2] + vote: voteNo }) + const result2 = + await audius0.ethContracts.GovernanceClient.getVoteByProposalAndVoter({ + proposalId: id, + voterAddress: accounts[2] + }) assert.equal(result2, 1) }) }) From be181c56b77d08663ec049d8b909dfb3b7dd7453 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 18:58:16 -0700 Subject: [PATCH 13/14] Fix imports, building --- libs/package-lock.json | 33 +++++-- libs/package.json | 1 + libs/src/services/creatorNode/CreatorNode.ts | 3 +- .../services/dataContracts/registryClient.js | 49 +++++++--- libs/src/types.ts | 2 +- libs/tests/providerSelectionTest.js | 97 ++++++++++++++----- 6 files changed, 137 insertions(+), 48 deletions(-) diff --git a/libs/package-lock.json b/libs/package-lock.json index 0d1ea788c6a..05a2586e149 100644 --- a/libs/package-lock.json +++ b/libs/package-lock.json @@ -3406,6 +3406,17 @@ "web3-core-method": "1.3.6", "web3-core-requestmanager": "1.3.6", "web3-utils": "1.3.6" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "requires": { + "@types/node": "*" + } + } } }, "web3-core-helpers": { @@ -3547,6 +3558,17 @@ "web3-core-subscriptions": "1.3.6", "web3-eth-abi": "1.3.6", "web3-utils": "1.3.6" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "requires": { + "@types/node": "*" + } + } } }, "web3-eth-ens": { @@ -3720,9 +3742,10 @@ } }, "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, "requires": { "@types/node": "*" } @@ -4780,7 +4803,6 @@ "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz", "integrity": "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==", "requires": { - "@types/bn.js": "^4.11.5", "bn.js": "^5.0.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" @@ -6687,7 +6709,6 @@ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", "requires": { - "@types/bn.js": "^4.11.3", "bn.js": "^4.11.0", "create-hash": "^1.1.2", "elliptic": "^6.5.2", @@ -12402,7 +12423,6 @@ "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.7.1.tgz", "integrity": "sha512-HOyDPj+4cNyeNPwgSeUkhtS0F+Pxc2obcm4oRYPW5ku6jnTO34pjaij0us+zoY3QEusR8FfAKVK1kFPZnS7Dzw==", "requires": { - "@types/bn.js": "^4.11.5", "@types/node": "^12.12.6", "bignumber.js": "^9.0.0", "web3-core-helpers": "1.7.1", @@ -12825,7 +12845,6 @@ "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.7.1.tgz", "integrity": "sha512-HpnbkPYkVK3lOyos2SaUjCleKfbF0SP3yjw7l551rAAi5sIz/vwlEzdPWd0IHL7ouxXbO0tDn7jzWBRcD3sTbA==", "requires": { - "@types/bn.js": "^4.11.5", "web3-core": "1.7.1", "web3-core-helpers": "1.7.1", "web3-core-method": "1.7.1", diff --git a/libs/package.json b/libs/package.json index a2d6f8f98fa..01a1073facb 100644 --- a/libs/package.json +++ b/libs/package.json @@ -76,6 +76,7 @@ "@rollup/plugin-typescript": "8.3.1", "@tsconfig/node16-strictest": "1.0.0", "@types/async-retry": "1.4.3", + "@types/bn.js": "5.1.0", "@types/bs58": "4.0.1", "@types/eth-sig-util": "^2.1.1", "@types/expect": "24.3.0", diff --git a/libs/src/services/creatorNode/CreatorNode.ts b/libs/src/services/creatorNode/CreatorNode.ts index cd814f02cc8..e28ec6d343d 100644 --- a/libs/src/services/creatorNode/CreatorNode.ts +++ b/libs/src/services/creatorNode/CreatorNode.ts @@ -862,8 +862,7 @@ export class CreatorNode { /** * Create headers and formData for file upload - * @param {Object} file the file to upload - * @param {boolean} [isTrackUpload] flag to determine if uploading track. If true, add track upload headers + * @param file the file to upload * @returns headers and formData in an object */ createFormDataAndUploadHeaders( diff --git a/libs/src/services/dataContracts/registryClient.js b/libs/src/services/dataContracts/registryClient.js index b58e43f82c4..9f7aa348269 100644 --- a/libs/src/services/dataContracts/registryClient.js +++ b/libs/src/services/dataContracts/registryClient.js @@ -1,9 +1,9 @@ const { Utils } = require('../../utils') -const Web3Manager = require('../web3Manager/index') +const { Web3Manager } = require('../web3Manager') const { ProviderSelection } = require('../contracts/ProviderSelection') class RegistryClient { - constructor (web3Manager, contractABI, contractAddress) { + constructor(web3Manager, contractABI, contractAddress) { this.web3Manager = web3Manager this.contractABI = contractABI this.contractAddress = contractAddress @@ -11,25 +11,32 @@ class RegistryClient { const web3 = this.web3Manager.getWeb3() this.Registry = new web3.eth.Contract(contractABI, contractAddress) - if (this.web3Manager instanceof Web3Manager && !this.web3Manager.web3Config.useExternalWeb3) { - const providerEndpoints = this.web3Manager.web3Config.internalWeb3Config.web3ProviderEndpoints + if ( + this.web3Manager instanceof Web3Manager && + !this.web3Manager.web3Config.useExternalWeb3 + ) { + const providerEndpoints = + this.web3Manager.web3Config.internalWeb3Config.web3ProviderEndpoints this.providerSelector = new ProviderSelection(providerEndpoints) } else { this.providerSelector = null } } - async getContract (contractRegistryKey) { + async getContract(contractRegistryKey) { try { Utils.checkStrLen(contractRegistryKey, 32) - const contract = await this.Registry.methods.getContract( - Utils.utf8ToHex(contractRegistryKey) - ).call() + const contract = await this.Registry.methods + .getContract(Utils.utf8ToHex(contractRegistryKey)) + .call() return contract } catch (e) { // If using ethWeb3Manager or useExternalWeb3 is true, do not do reselect provider logic and fail if (!this.providerSelector) { - console.error(`Failed to initialize contract ${JSON.stringify(this.contractABI)}`, e) + console.error( + `Failed to initialize contract ${JSON.stringify(this.contractABI)}`, + e + ) return } @@ -37,22 +44,34 @@ class RegistryClient { } } - async retryInit (contractRegistryKey) { + async retryInit(contractRegistryKey) { try { await this.selectNewEndpoint() const web3 = this.web3Manager.getWeb3() - this.Registry = new web3.eth.Contract(this.contractABI, this.contractAddress) + this.Registry = new web3.eth.Contract( + this.contractABI, + this.contractAddress + ) return await this.getContract(contractRegistryKey) } catch (e) { console.error(e.message) } } - async selectNewEndpoint () { - this.providerSelector.addUnhealthy(this.web3Manager.getWeb3().currentProvider.host) + async selectNewEndpoint() { + this.providerSelector.addUnhealthy( + this.web3Manager.getWeb3().currentProvider.host + ) - if (this.providerSelector.getUnhealthySize() === this.providerSelector.getServicesSize()) { - throw new Error(`No available, healthy providers to get contract ${JSON.stringify(this.contractABI)}`) + if ( + this.providerSelector.getUnhealthySize() === + this.providerSelector.getServicesSize() + ) { + throw new Error( + `No available, healthy providers to get contract ${JSON.stringify( + this.contractABI + )}` + ) } await this.providerSelector.select(this) diff --git a/libs/src/types.ts b/libs/src/types.ts index 3c4c0d1dced..8be08b3cc2c 100644 --- a/libs/src/types.ts +++ b/libs/src/types.ts @@ -1,3 +1,3 @@ export { Utils } from './utils' -export * from './services/creatorNode' +export { CreatorNode } from './services/creatorNode' export * from './sanityChecks' diff --git a/libs/tests/providerSelectionTest.js b/libs/tests/providerSelectionTest.js index cc0be82f426..e22c1b14ba7 100644 --- a/libs/tests/providerSelectionTest.js +++ b/libs/tests/providerSelectionTest.js @@ -2,7 +2,7 @@ const assert = require('assert') const sinon = require('sinon') const { ContractClient } = require('../src/services/contracts/ContractClient') -const Web3Manager = require('../src/services/web3Manager') +const { Web3Manager } = require('../src/services/web3Manager') const { EthWeb3Manager } = require('../src/services/ethWeb3Manager') const CONTRACT_INIT_MAX_ATTEMPTS = 5 @@ -22,13 +22,20 @@ describe('Testing ContractClient class with ProviderSelection', () => { */ it('should use initial audius gateway if healthy', async () => { contractClient = createContractClientWithInternalWeb3() - sinon.stub(contractClient.web3Manager.web3.eth, 'Contract').callsFake((arg1, arg2) => { return arg1 }) + sinon + .stub(contractClient.web3Manager.web3.eth, 'Contract') + .callsFake((arg1, arg2) => { + return arg1 + }) const initWithProviderSelectionSpy = sinon.spy(contractClient, 'init') const consoleSpy = sinon.spy(console, 'error') await contractClient.init() - assert.strictEqual(contractClient.web3Manager.getWeb3().currentProvider.host, 'https://audius.poa.network') + assert.strictEqual( + contractClient.web3Manager.getWeb3().currentProvider.host, + 'https://audius.poa.network' + ) assert(initWithProviderSelectionSpy.calledOnce) assert(consoleSpy.notCalled) }) @@ -37,19 +44,25 @@ describe('Testing ContractClient class with ProviderSelection', () => { * Given: both gatetways are unhealthy * When: we do contract logic * Should: try both gateways and then log error - * + * * @notice when web3 is set to a new object, the second call will throw a different error * that is acceptable as we don't care about what the error is */ it('should log error if both audius gateway and public gateway are unhealthy', async () => { contractClient = createContractClientWithInternalWeb3() - sinon.stub(contractClient.web3Manager.web3.eth, 'Contract').callsFake((arg1, arg2) => { throw new Error('Bad provider') }) + sinon + .stub(contractClient.web3Manager.web3.eth, 'Contract') + .callsFake((arg1, arg2) => { + throw new Error('Bad provider') + }) const initWithProviderSelectionSpy = sinon.spy(contractClient, 'init') const consoleSpy = sinon.spy(console, 'error') await contractClient.init() - assert(initWithProviderSelectionSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS) + assert( + initWithProviderSelectionSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS + ) assert(consoleSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS) }) @@ -60,13 +73,20 @@ describe('Testing ContractClient class with ProviderSelection', () => { */ it('should use initial gateway url if web3Manager is instanceof ethWeb3Manager and contract logic passes', async () => { contractClient = createContractClientWithEthWeb3Manager() - sinon.stub(contractClient.web3Manager.web3.eth, 'Contract').callsFake((arg1, arg2) => { return arg1 }) + sinon + .stub(contractClient.web3Manager.web3.eth, 'Contract') + .callsFake((arg1, arg2) => { + return arg1 + }) const initWithProviderSelectionSpy = sinon.spy(contractClient, 'init') const consoleSpy = sinon.spy(console, 'error') await contractClient.init() - assert.strictEqual(contractClient.web3Manager.getWeb3().currentProvider.host, 'https://eth.network') + assert.strictEqual( + contractClient.web3Manager.getWeb3().currentProvider.host, + 'https://eth.network' + ) assert(initWithProviderSelectionSpy.calledOnce) assert(consoleSpy.notCalled) }) @@ -78,14 +98,23 @@ describe('Testing ContractClient class with ProviderSelection', () => { */ it('should log error if web3Manager is instanceof ethWeb3Manager and contract logic fails', async () => { contractClient = createContractClientWithEthWeb3Manager() - sinon.stub(contractClient.web3Manager.web3.eth, 'Contract').callsFake((arg1, arg2) => { throw new Error('Bad provider') }) + sinon + .stub(contractClient.web3Manager.web3.eth, 'Contract') + .callsFake((arg1, arg2) => { + throw new Error('Bad provider') + }) const initWithProviderSelectionSpy = sinon.spy(contractClient, 'init') const consoleSpy = sinon.spy(console, 'error') await contractClient.init() - assert.strictEqual(contractClient.web3Manager.getWeb3().currentProvider.host, 'https://eth.network') - assert(initWithProviderSelectionSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS) + assert.strictEqual( + contractClient.web3Manager.getWeb3().currentProvider.host, + 'https://eth.network' + ) + assert( + initWithProviderSelectionSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS + ) assert(consoleSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS) }) @@ -96,13 +125,20 @@ describe('Testing ContractClient class with ProviderSelection', () => { */ it('should use initial gateway url if useExternalWeb3 is true and contract logic passes', async () => { contractClient = createContractClientWithExternalWeb3() - sinon.stub(contractClient.web3Manager.web3.eth, 'Contract').callsFake((arg1, arg2) => { return arg1 }) + sinon + .stub(contractClient.web3Manager.web3.eth, 'Contract') + .callsFake((arg1, arg2) => { + return arg1 + }) const initWithProviderSelectionSpy = sinon.spy(contractClient, 'init') const consoleSpy = sinon.spy(console, 'error') await contractClient.init() - assert.strictEqual(contractClient.web3Manager.getWeb3().currentProvider.host, 'https://audius.poa.network') + assert.strictEqual( + contractClient.web3Manager.getWeb3().currentProvider.host, + 'https://audius.poa.network' + ) assert(initWithProviderSelectionSpy.calledOnce) assert(consoleSpy.notCalled) }) @@ -114,13 +150,19 @@ describe('Testing ContractClient class with ProviderSelection', () => { */ it('should log error if useExternalWeb3 is true and contract logic fails', async () => { contractClient = createContractClientWithExternalWeb3() - sinon.stub(contractClient.web3Manager.web3.eth, 'Contract').callsFake((arg1, arg2) => { throw new Error('Bad provider') }) + sinon + .stub(contractClient.web3Manager.web3.eth, 'Contract') + .callsFake((arg1, arg2) => { + throw new Error('Bad provider') + }) const initWithProviderSelectionSpy = sinon.spy(contractClient, 'init') const consoleSpy = sinon.spy(console, 'error') await contractClient.init() - assert(initWithProviderSelectionSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS) + assert( + initWithProviderSelectionSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS + ) assert(consoleSpy.callCount === CONTRACT_INIT_MAX_ATTEMPTS) }) }) @@ -130,7 +172,7 @@ describe('Testing ContractClient class with ProviderSelection', () => { const gateways = ['https://audius.poa.network', 'https://public.poa.network'] // Creates barebones web3 object -function createWeb3Obj (host) { +function createWeb3Obj(host) { return { currentProvider: { host @@ -143,17 +185,26 @@ function createWeb3Obj (host) { // Helper functions to create ContractClient instances with the appropriate properties -function createContractClient (web3Manager) { - const getRegistryAddressFn = () => { return '0xaaaaaaaaaaaaaaaaaaa' } +function createContractClient(web3Manager) { + const getRegistryAddressFn = () => { + return '0xaaaaaaaaaaaaaaaaaaa' + } if (web3Manager instanceof Web3Manager) { - sinon.stub(web3Manager, 'provider').callsFake((arg1, arg2) => { return arg1 }) + sinon.stub(web3Manager, 'provider').callsFake((arg1, arg2) => { + return arg1 + }) } - return new ContractClient(web3Manager, 'contractABI', 'contractRegistryKey', getRegistryAddressFn) + return new ContractClient( + web3Manager, + 'contractABI', + 'contractRegistryKey', + getRegistryAddressFn + ) } -function createContractClientWithInternalWeb3 () { +function createContractClientWithInternalWeb3() { const web3Config = { useExternalWeb3: false, internalWeb3Config: { @@ -166,7 +217,7 @@ function createContractClientWithInternalWeb3 () { return createContractClient(web3Manager) } -function createContractClientWithExternalWeb3 () { +function createContractClientWithExternalWeb3() { const web3Config = { useExternalWeb3: true, internalWeb3Config: { @@ -179,7 +230,7 @@ function createContractClientWithExternalWeb3 () { return createContractClient(web3Manager) } -function createContractClientWithEthWeb3Manager () { +function createContractClientWithEthWeb3Manager() { const web3Config = { providers: ['https://audius.eth.network'], ownerWallet: '0xwallet' From 09126ee190430f4c6adae82bb4bcccea686f287b Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 10 May 2022 22:52:49 -0700 Subject: [PATCH 14/14] Fix lint --- libs/src/services/dataContracts/registryClient.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/src/services/dataContracts/registryClient.js b/libs/src/services/dataContracts/registryClient.js index 9f7aa348269..186f0723e39 100644 --- a/libs/src/services/dataContracts/registryClient.js +++ b/libs/src/services/dataContracts/registryClient.js @@ -3,7 +3,7 @@ const { Web3Manager } = require('../web3Manager') const { ProviderSelection } = require('../contracts/ProviderSelection') class RegistryClient { - constructor(web3Manager, contractABI, contractAddress) { + constructor (web3Manager, contractABI, contractAddress) { this.web3Manager = web3Manager this.contractABI = contractABI this.contractAddress = contractAddress @@ -23,7 +23,7 @@ class RegistryClient { } } - async getContract(contractRegistryKey) { + async getContract (contractRegistryKey) { try { Utils.checkStrLen(contractRegistryKey, 32) const contract = await this.Registry.methods @@ -44,7 +44,7 @@ class RegistryClient { } } - async retryInit(contractRegistryKey) { + async retryInit (contractRegistryKey) { try { await this.selectNewEndpoint() const web3 = this.web3Manager.getWeb3() @@ -58,7 +58,7 @@ class RegistryClient { } } - async selectNewEndpoint() { + async selectNewEndpoint () { this.providerSelector.addUnhealthy( this.web3Manager.getWeb3().currentProvider.host )