diff --git a/src/baseIdrissCrypto.ts b/src/baseIdrissCrypto.ts index ee2ddc0..e22aa68 100644 --- a/src/baseIdrissCrypto.ts +++ b/src/baseIdrissCrypto.ts @@ -28,6 +28,8 @@ import type { import type { AssetLiability } from './types/assetLiability'; import { AssetType } from './types/assetType'; import type { VotingParams } from './types/votingParams'; +import {TwitterNameResolverArbitrary} from "./twitterNameResolverArbitrary"; +import {TwitterNameResolver} from "./twitterNameResolver"; const IDRISS_HOMEPAGE = 'https://idriss.xyz'; @@ -35,7 +37,9 @@ export abstract class BaseIdrissCrypto { protected web3Provider: Web3Provider; protected registryWeb3Provider: Web3Provider; protected contractsAddressess: ContractsAddresses; - + private twitterNameResolver: TwitterNameResolver; + private twitterNameResolverArbitrary: TwitterNameResolverArbitrary; + private TWITTER_BEARER_TOKEN: null | string = null; private idrissRegistryContract: Contract; private idrissMultipleRegistryContract: Contract; private idrissReverseMappingContract: Contract; @@ -71,6 +75,7 @@ export abstract class BaseIdrissCrypto { CONTRACTS_ADDRESSES.idrissTipping, }; + this.TWITTER_BEARER_TOKEN = connectionOptions.twitterApiKey ?? null; this.web3Provider = connectionOptions.web3Provider; this.registryWeb3Provider = Web3ProviderAdapter.fromWeb3( @@ -105,6 +110,8 @@ export abstract class BaseIdrissCrypto { ABIS.IDrissTippingAbi, this.contractsAddressess.idrissTipping, ); + this.twitterNameResolver = new TwitterNameResolver(); + this.twitterNameResolverArbitrary = new TwitterNameResolverArbitrary(this.TWITTER_BEARER_TOKEN); } public static matchInput(input: string) { @@ -320,18 +327,18 @@ export abstract class BaseIdrissCrypto { resolvedIDriss[resolveOptions.walletTag!] && resolvedIDriss[resolveOptions.walletTag!].length > 0 ? this.callWeb3Tipping( - resolvedIDriss[resolveOptions.walletTag!], - asset, - message, - transactionOptions, - ) + resolvedIDriss[resolveOptions.walletTag!], + asset, + message, + transactionOptions, + ) : this.callWeb3SendToAnyone( - hash, - beneficiary, - asset, - message, - transactionOptions, - )); + hash, + beneficiary, + asset, + message, + transactionOptions, + )); return result; } @@ -1245,4 +1252,12 @@ export abstract class BaseIdrissCrypto { ? ('@' + (await reverseTwitterID(result))).toLowerCase() : result; } + + public async reverseResolveArbitrary(username: string) { + return this.twitterNameResolverArbitrary.reverseTwitterID(username); + } + + public async transformIdentifierArbitrary (id: string) { + return this.twitterNameResolverArbitrary.getTwitterID(id); + } } diff --git a/src/browser.ts b/src/browser.ts index 6f1e7cd..2947976 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -9,16 +9,16 @@ import { BaseIdrissCrypto } from './baseIdrissCrypto'; type IdrissCryptoConnectionOptions = Omit & ( | { - providerType?: 'web3'; - web3Provider?: provider; - } + providerType?: 'web3'; + web3Provider?: provider; + } | { - providerType: 'ethersv5'; - web3Provider?: ConstructorParameters< - typeof ethers.providers.Web3Provider - >[0]; - } - ); + providerType: 'ethersv5'; + web3Provider?: ConstructorParameters< + typeof ethers.providers.Web3Provider + >[0]; + } + ); export class IdrissCrypto extends BaseIdrissCrypto { constructor( @@ -36,7 +36,7 @@ export class IdrissCrypto extends BaseIdrissCrypto { web3Provider = Web3ProviderAdapter.fromWeb3( new Web3( connectionOptions.web3Provider ?? - new Web3.providers.HttpProvider(url), + new Web3.providers.HttpProvider(url), ), ); } diff --git a/src/twitterNameResolver.ts b/src/twitterNameResolver.ts new file mode 100644 index 0000000..84ed5d4 --- /dev/null +++ b/src/twitterNameResolver.ts @@ -0,0 +1,17 @@ +import {fetchSafe} from "../lib/utils"; + +export class TwitterNameResolver { + async getTwitterID(inputCombination: string): Promise { + const response = await fetchSafe("https://www.idriss.xyz/v1/getTwitterID?identifier=" + encodeURIComponent(inputCombination)); + if (response.status != 200) throw new Error("IDriss API responded with code " + response.status + " " + response.statusText + "\r\n" + await response.text()) + const json = await response.json(); + return json.twitterID; + } + + async reverseTwitterID(id: string): Promise { + const response = await fetchSafe("https://www.idriss.xyz/v1/getTwitterNames?ids=" + encodeURIComponent(id)); + if (response.status != 200) throw new Error("IDriss API responded with code " + response.status + " " + response.statusText + "\r\n" + await response.text()) + const json = await response.json(); + return json.twitterNames[id]; + } +} diff --git a/src/twitterNameResolverArbitrary.ts b/src/twitterNameResolverArbitrary.ts new file mode 100644 index 0000000..1c34cad --- /dev/null +++ b/src/twitterNameResolverArbitrary.ts @@ -0,0 +1,45 @@ +import {fetchSafe} from "../lib/utils"; +import { object, string } from "zod"; + +const TwitterResponseSchema = object({ + data: object({ + id: string(), + name: string(), + username: string(), + }) +}); + +const callTwitterApi = async (url: string, BEARER_TOKEN: string) => { + if (!BEARER_TOKEN) throw new Error(`Please provide API Token (Bearer Token) for Twitter.`) + const response = await fetchSafe(url, { + method: 'GET', + headers: { + 'Authorization': `Bearer ${BEARER_TOKEN}`, + }, + }); + if (!response.ok) { + throw new Error(`Error: ${response.status}`); + } + return response.json(); +} + +export class TwitterNameResolverArbitrary { + protected TWITTER_BEARER_TOKEN: null | string = null; + + constructor(twitterApiKey: string | null) { + this.TWITTER_BEARER_TOKEN = twitterApiKey; + } + + async getTwitterID(inputCombination: string): Promise { + const json = await callTwitterApi(`https://api.twitter.com/2/users/by/username/${inputCombination}`, this.TWITTER_BEARER_TOKEN as string); + const validatedData = TwitterResponseSchema.parse(json); + return validatedData?.data?.id as string; + } + + async reverseTwitterID(id: string): Promise { + const json = await callTwitterApi(`https://api.twitter.com/2/users/${id}`, this.TWITTER_BEARER_TOKEN as string); + const validatedData = TwitterResponseSchema.parse(json); + return validatedData?.data?.username as string; + } + +} diff --git a/src/types/connectionOptions.ts b/src/types/connectionOptions.ts index 4def18d..ff4dc3d 100644 --- a/src/types/connectionOptions.ts +++ b/src/types/connectionOptions.ts @@ -1,16 +1,18 @@ -import type { Web3Provider } from '../web3Provider'; +import {provider} from 'web3-core' /** * Those are optional configuration items that can be passed to IDrissCrypto */ export type ConnectionOptions = { - web3Provider: Web3Provider; + //overrides default HttpWeb3Provider, which only supports calling blockchain, and not sending transactions to modify it + web3Provider?: provider, // overriding contract addresses is added as a helper for testing purposes. It should not be changed in other cases - sendToAnyoneContractAddress?: string; - tippingContractAddress?: string; - votingContractAddress?: string; - idrissRegistryContractAddress?: string; + sendToAnyoneContractAddress?: string, + tippingContractAddress?: string, + votingContractAddress?: string, + idrissRegistryContractAddress?: string, + reverseIDrissMappingContractAddress?: string, + priceOracleContractAddress?: string, idrissMultipleRegistryContractAddress?: string; - reverseIDrissMappingContractAddress?: string; - priceOracleContractAddress?: string; -}; + twitterApiKey?: string; +} diff --git a/yarn.lock b/yarn.lock index a07836c..f6faacf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9163,3 +9163,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.22.4: + version "3.22.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" + integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==