diff --git a/packages/examples/sdk-frontend-react/src/app/components/Connect.tsx b/packages/examples/sdk-frontend-react/src/app/components/Connect.tsx index 1041ea25b..cc5b89378 100644 --- a/packages/examples/sdk-frontend-react/src/app/components/Connect.tsx +++ b/packages/examples/sdk-frontend-react/src/app/components/Connect.tsx @@ -19,10 +19,12 @@ const NETWORK_MAPPING: NwMappingType = { 10: 'OPTIMISM_MAINNET', 1442: 'POLYGON_ZK_EVM_TESTNET', 1101: 'POLYGON_ZK_EVM_MAINNET', + 421613: "ARBITRUM_TESTNET", + 42161: "ARBITRUMONE_MAINNET" }; const injected = new InjectedConnector({ - supportedChainIds: [1, 3, 4, 5, 42, 137, 80001, 56, 97, 10, 420, 1442, 1101], + supportedChainIds: [1, 3, 4, 5, 42, 137, 80001, 56, 97, 10, 420, 1442, 1101, 421613, 42161], }); const ConnectWrapper = styled.div` diff --git a/packages/examples/sdk-frontend-react/src/app/helpers.ts b/packages/examples/sdk-frontend-react/src/app/helpers.ts index d5af6bc15..bad12f49d 100644 --- a/packages/examples/sdk-frontend-react/src/app/helpers.ts +++ b/packages/examples/sdk-frontend-react/src/app/helpers.ts @@ -18,7 +18,7 @@ const Constants = { }, DEFAULT_CHAIN_ID: 5, DEV_CHAIN_ID: 99999, - NON_ETH_CHAINS: [137, 80001, 56, 97, 10, 420, 1442, 1101], + NON_ETH_CHAINS: [137, 80001, 56, 97, 10, 420, 1442, 1101, 421613, 42161], ETH_CHAINS: [1, 5], }; diff --git a/packages/reactnative/README.md b/packages/reactnative/README.md index 4eae3bfa0..d3e665c64 100644 --- a/packages/reactnative/README.md +++ b/packages/reactnative/README.md @@ -208,5 +208,5 @@ where | cta | string | Call To Action Link (given during notification creation) | | image | string | Any media link (given during notification creation) | | appbot | string | is the notification is from EPNS bot the value is "1" else "0" | -| chainName | string | Can be anyone of the following blockchain networks on which the notification was sent - "ETH_MAINNET", "ETH_TEST_GOERLI", "POLYGON_MAINNET", "POLYGON_TEST_MUMBAI", "BSC_MAINNET, "BSC_TESTNET", "OPTIMISM_MAINNET", "OPTIMISM_TESTNET", "POLYGON_ZK_EVM_TESTNET", "POLYGON_ZK_EVM_MAINNET", "THE_GRAPH" | +| chainName | string | Can be anyone of the following blockchain networks on which the notification was sent - "ETH_MAINNET", "ETH_TEST_GOERLI", "POLYGON_MAINNET", "POLYGON_TEST_MUMBAI", "BSC_MAINNET, "BSC_TESTNET", "OPTIMISM_MAINNET", "OPTIMISM_TESTNET", "POLYGON_ZK_EVM_TESTNET", "POLYGON_ZK_EVM_MAINNET", "ARBITRUM_TESTNET", "ARBITRUMONE_MAINNET", "THE_GRAPH" | | youTubeAPIKey | string | Your generated Youtube API key | \ No newline at end of file diff --git a/packages/reactnative/src/lib/components/chainDetails/arbitrumSVG.tsx b/packages/reactnative/src/lib/components/chainDetails/arbitrumSVG.tsx new file mode 100644 index 000000000..7106c3552 --- /dev/null +++ b/packages/reactnative/src/lib/components/chainDetails/arbitrumSVG.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { SVGProps } from 'react'; + +const ArbitrumSvgComponent = (props: SVGProps) => ( + + + + + + + +); + +export default ArbitrumSvgComponent; diff --git a/packages/reactnative/src/lib/components/chainDetails/index.tsx b/packages/reactnative/src/lib/components/chainDetails/index.tsx index 08c8cb55b..dbd6c8e19 100644 --- a/packages/reactnative/src/lib/components/chainDetails/index.tsx +++ b/packages/reactnative/src/lib/components/chainDetails/index.tsx @@ -3,9 +3,15 @@ import PolygonSvg from "./polygonSVG"; import GraphSvg from "./thegraphSVG"; import BscSvg from "./bscSVG"; import OptimismSvg from "./optimismSVG" -import PolygonZKEVMSvg from "./polygonZKEVMSVG"; +import PolygonZKEVMSvg from "./polygonZkEVMSVG"; +import ArbitrumSvgComponent from "./arbitrumSVG"; -export default { +type Network = { + label: string; + Icon: any; +}; + +const networks: Record = { ETH_TEST_GOERLI: { label: "ETHEREUM GOERLI", Icon: EthereumSvg }, ETH_MAINNET: { label: "ETHEREUM MAINNET", Icon: EthereumSvg }, POLYGON_TEST_MUMBAI: { label: "POLYGON MUMBAI", Icon: PolygonSvg }, @@ -16,5 +22,9 @@ export default { OPTIMISM_MAINNET: { label: "OPTIMISM MAINNET", Icon: OptimismSvg }, POLYGON_ZK_EVM_TESTNET: {label:"POLYGON_ZK_EVM_TESTNET",Icon: PolygonZKEVMSvg}, POLYGON_ZK_EVM_MAINNET: {label:"POLYGON_ZK_EVM_MAINNET",Icon: PolygonZKEVMSvg}, + ARBITRUM_TESTNET: {label:"ARBITRUM_TESTNET",Icon: ArbitrumSvgComponent}, + ARBITRUMONE_MAINNET: {label: "ARBITRUMONE_MAINNET", Icon: ArbitrumSvgComponent}, THE_GRAPH: { label: "THE GRAPH", Icon: GraphSvg }, -}; \ No newline at end of file +}; + +export default networks \ No newline at end of file diff --git a/packages/reactnative/src/lib/components/notifications/notification.tsx b/packages/reactnative/src/lib/components/notifications/notification.tsx index 30869655e..bbc064056 100644 --- a/packages/reactnative/src/lib/components/notifications/notification.tsx +++ b/packages/reactnative/src/lib/components/notifications/notification.tsx @@ -18,7 +18,7 @@ import { ImageDownloadWithIndicator, VideoDownloadWithIndicator } from '../load // ================= Define types -export type chainNameType = "ETH_TEST_GOERLI" | "POLYGON_TEST_MUMBAI" | "ETH_MAINNET" | "POLYGON_MAINNET" | "BSC_MAINNET" | "BSC_TESTNET" | "OPTIMISM_MAINNET" | "OPTIMISM_TESTNET" | "POLYGON_ZK_EVM_TESTNET" | "POLYGON_ZK_EVM_MAINNET" | "THE_GRAPH" | undefined; +export type chainNameType = "ETH_TEST_GOERLI" | "POLYGON_TEST_MUMBAI" | "ETH_MAINNET" | "POLYGON_MAINNET" | "BSC_MAINNET" | "BSC_TESTNET" | "OPTIMISM_MAINNET" | "OPTIMISM_TESTNET" | "POLYGON_ZK_EVM_TESTNET" | "POLYGON_ZK_EVM_MAINNET" | "ARBITRUMONE_MAINNET" | "ARBITRUM_TESTNET" | "THE_GRAPH" | undefined; const botImageLocalPath = '../../assets/epnsbot.png'; diff --git a/packages/restapi/README.md b/packages/restapi/README.md index 1aa9dffde..7cf523dd4 100644 --- a/packages/restapi/README.md +++ b/packages/restapi/README.md @@ -181,6 +181,8 @@ Binance Mainnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa Binance Testnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa Optimism Mainnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa Optimism Testnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa +Arbitrum Mainnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa +Arbitrum One Testnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa ``` # SDK Features diff --git a/packages/restapi/src/lib/channels/index.ts b/packages/restapi/src/lib/channels/index.ts index 51f12c24e..6e584d9d3 100644 --- a/packages/restapi/src/lib/channels/index.ts +++ b/packages/restapi/src/lib/channels/index.ts @@ -4,4 +4,6 @@ export * from './search'; export * from './subscribe'; export * from './unsubscribe'; export * from './_getSubscribers'; -export * from './getSubscribers'; \ No newline at end of file +export * from './getSubscribers'; +export * from './subscribeV2'; +export * from './unsubscribeV2'; diff --git a/packages/restapi/src/lib/channels/signature.helpers.ts b/packages/restapi/src/lib/channels/signature.helpers.ts index 33f977f4e..c67e456e4 100644 --- a/packages/restapi/src/lib/channels/signature.helpers.ts +++ b/packages/restapi/src/lib/channels/signature.helpers.ts @@ -1,47 +1,77 @@ - -type channelActionType = "Unsubscribe" | "Subscribe"; +type channelActionType = 'Unsubscribe' | 'Subscribe'; export const getDomainInformation = ( chainId: number, verifyingContract: string ) => { return { - name: "EPNS COMM V1", + name: 'EPNS COMM V1', chainId, verifyingContract, }; -} - +}; + export const getSubscriptionMessage = ( channel: string, userAddress: string, action: channelActionType ) => { - const actionTypeKey = (action === "Unsubscribe") ? "unsubscriber" : "subscriber"; - + const actionTypeKey = + action === 'Unsubscribe' ? 'unsubscriber' : 'subscriber'; + return { channel, [actionTypeKey]: userAddress, action: action, }; -} +}; + +export const getSubscriptionMessageV2 = ( + channel: string, + userAddress: string, + action: channelActionType, + userSetting?: string +) => { + const actionTypeKey = + action === 'Unsubscribe' ? 'unsubscriber' : 'subscriber'; + if (action == 'Subscribe') { + return JSON.stringify({ + channel, + [actionTypeKey]: userAddress, + action: action, + userSetting: userSetting?? '', + }, null, 4); + } else { + return JSON.stringify({ + channel, + [actionTypeKey]: userAddress, + action: action, + }, null, 4); + } +}; export const getTypeInformation = (action: string) => { - if (action === "Subscribe") { + if (action === 'Subscribe') { return { Subscribe: [ - { name: "channel", type: "address" }, - { name: "subscriber", type: "address" }, - { name: "action", type: "string" }, + { name: 'channel', type: 'address' }, + { name: 'subscriber', type: 'address' }, + { name: 'action', type: 'string' }, ], }; } return { Unsubscribe: [ - { name: "channel", type: "address" }, - { name: "unsubscriber", type: "address" }, - { name: "action", type: "string" }, + { name: 'channel', type: 'address' }, + { name: 'unsubscriber', type: 'address' }, + { name: 'action', type: 'string' }, ], }; -}; \ No newline at end of file +}; + +export const getTypeInformationV2 = () => { + return { + Data: [{ name: 'data', type: 'string' }], + }; +}; diff --git a/packages/restapi/src/lib/channels/subscribeV2.ts b/packages/restapi/src/lib/channels/subscribeV2.ts new file mode 100644 index 000000000..4802f6c18 --- /dev/null +++ b/packages/restapi/src/lib/channels/subscribeV2.ts @@ -0,0 +1,110 @@ +import axios from 'axios'; +import { + getCAIPAddress, + getConfig, + getCAIPDetails, + signTypedData, +} from '../helpers'; +import { + getDomainInformation, + getTypeInformationV2, + getSubscriptionMessageV2, +} from './signature.helpers'; +import Constants, { ENV } from '../constants'; +import { SignerType } from '../types'; +export type SubscribeOptionsV2Type = { + signer: SignerType; + channelAddress: string; + userAddress: string; + userSetting?: string; + verifyingContractAddress?: string; + env?: ENV; + onSuccess?: () => void; + onError?: (err: Error) => void; +}; + +export const subscribeV2 = async (options: SubscribeOptionsV2Type) => { + const { + signer, + channelAddress, + userAddress, + userSetting = undefined, + verifyingContractAddress, + env = Constants.ENV.PROD, + onSuccess, + onError, + } = options || {}; + try { + const _channelAddress = await getCAIPAddress( + env, + channelAddress, + 'Channel' + ); + + const channelCAIPDetails = getCAIPDetails(_channelAddress); + if (!channelCAIPDetails) throw Error('Invalid Channel CAIP!'); + + const chainId = parseInt(channelCAIPDetails.networkId, 10); + + const _userAddress = await getCAIPAddress(env, userAddress, 'User'); + + const userCAIPDetails = getCAIPDetails(_userAddress); + if (!userCAIPDetails) throw Error('Invalid User CAIP!'); + + const { API_BASE_URL, EPNS_COMMUNICATOR_CONTRACT } = getConfig( + env, + channelCAIPDetails + ); + + const requestUrl = `${API_BASE_URL}/v1/channels/${_channelAddress}/subscribe`; + // get domain information + const domainInformation = getDomainInformation( + chainId, + verifyingContractAddress || EPNS_COMMUNICATOR_CONTRACT + ); + + // get type information + const typeInformation = getTypeInformationV2(); + + // get message + const messageInformation = { + data: getSubscriptionMessageV2( + channelCAIPDetails.address, + userCAIPDetails.address, + 'Subscribe', + userSetting + ), + }; + // sign a message using EIP712 + const signature = await signTypedData( + signer, + domainInformation, + typeInformation, + messageInformation, + 'Data' + ); + + const verificationProof = signature; // might change + + const body = { + verificationProof: `eip712v2:${verificationProof}`, + message: + messageInformation.data, + + }; + + await axios.post(requestUrl, body); + + if (typeof onSuccess === 'function') onSuccess(); + + return { status: 'success', message: 'successfully opted into channel' }; + } catch (err) { + console.log(err); + if (typeof onError === 'function') onError(err as Error); + + return { + status: 'error', + message: err instanceof Error ? err.message : JSON.stringify(err), + }; + } +}; diff --git a/packages/restapi/src/lib/channels/unsubscribeV2.ts b/packages/restapi/src/lib/channels/unsubscribeV2.ts new file mode 100644 index 000000000..e62cc2519 --- /dev/null +++ b/packages/restapi/src/lib/channels/unsubscribeV2.ts @@ -0,0 +1,110 @@ +import axios from 'axios'; +import { + getCAIPAddress, + getConfig, + getCAIPDetails, + signTypedData, +} from '../helpers'; +import { + getTypeInformation, + getDomainInformation, + getSubscriptionMessage, + getTypeInformationV2, + getSubscriptionMessageV2, +} from './signature.helpers'; +import Constants, { ENV } from '../constants'; +import { SignerType } from '../types'; + +export type UnSubscribeOptionsV2Type = { + signer: SignerType; + channelAddress: string; + userAddress: string; + verifyingContractAddress?: string; + env?: ENV; + onSuccess?: () => void; + onError?: (err: Error) => void; +}; + +export const unsubscribeV2 = async (options: UnSubscribeOptionsV2Type) => { + const { + signer, + channelAddress, + userAddress, + verifyingContractAddress, + env = Constants.ENV.PROD, + onSuccess, + onError, + } = options || {}; + + try { + const _channelAddress = await getCAIPAddress( + env, + channelAddress, + 'Channel' + ); + + const channelCAIPDetails = getCAIPDetails(_channelAddress); + if (!channelCAIPDetails) throw Error('Invalid Channel CAIP!'); + + const chainId = parseInt(channelCAIPDetails.networkId, 10); + + const _userAddress = await getCAIPAddress(env, userAddress, 'User'); + + const userCAIPDetails = getCAIPDetails(_userAddress); + if (!userCAIPDetails) throw Error('Invalid User CAIP!'); + + const { API_BASE_URL, EPNS_COMMUNICATOR_CONTRACT } = getConfig( + env, + channelCAIPDetails + ); + + const requestUrl = `${API_BASE_URL}/v1/channels/${_channelAddress}/unsubscribe`; + + // get domain information + const domainInformation = getDomainInformation( + chainId, + verifyingContractAddress || EPNS_COMMUNICATOR_CONTRACT + ); + + // get type information + const typeInformation = getTypeInformationV2(); + + // get message + const messageInformation = { + data: getSubscriptionMessageV2( + channelCAIPDetails.address, + userCAIPDetails.address, + 'Unsubscribe' + ), + }; + + // sign a message using EIP712 + const signature = await signTypedData( + signer, + domainInformation, + typeInformation, + messageInformation, + 'Unsubscribe' + ); + + const verificationProof = signature; // might change + + const body = { + verificationProof: `eip712v2:${verificationProof}`, + message: messageInformation.data, + }; + + await axios.post(requestUrl, body); + + if (typeof onSuccess === 'function') onSuccess(); + + return { status: 'success', message: 'successfully opted out channel' }; + } catch (err) { + if (typeof onError === 'function') onError(err as Error); + + return { + status: 'error', + message: err instanceof Error ? err.message : JSON.stringify(err), + }; + } +}; diff --git a/packages/restapi/src/lib/config.ts b/packages/restapi/src/lib/config.ts index e597c22bc..20d902c0a 100644 --- a/packages/restapi/src/lib/config.ts +++ b/packages/restapi/src/lib/config.ts @@ -1,19 +1,5 @@ import Constants from './constants'; -import { coreABI } from './abis/core'; -import { commABI } from './abis/comm'; -import { tokenABI } from './abis/token'; -import { - mainnet, - goerli, - polygon, - polygonMumbai, - bsc, - bscTestnet, - optimism, - optimismGoerli, - polygonZkEvm, - polygonZkEvmTestnet, -} from 'viem/chains'; + const { ENV } = Constants; // for methods not needing the entire config @@ -39,16 +25,12 @@ const BLOCKCHAIN_NETWORK = { OPTIMISM_MAINNET: 'eip155:10', POLYGON_ZK_EVM_TESTNET: 'eip155:1442', POLYGON_ZK_EVM_MAINNET: 'eip155:1101', + ARBITRUM_TESTNET: 'eip155:421613', + ARBITRUMONE_MAINNET: "eip155:42161" }; -export type ALIAS_CHAIN = 'POLYGON' | 'BSC' | 'OPTIMISM' | 'POLYGONZKEVM'; +export type ALIAS_CHAIN = 'POLYGON' | 'BSC' | 'OPTIMISM' | 'POLYGONZKEVM' | "ARBITRUMONE"; -export const ETH_CHAIN_ID = { - [ENV.PROD]: 1, - [ENV.STAGING]: 5, - [ENV.DEV]: 5, - [ENV.LOCAL]: 5, -}; export const ALIAS_CHAIN_ID = { POLYGON: { [ENV.PROD]: 137, @@ -72,80 +54,21 @@ export const ALIAS_CHAIN_ID = { [ENV.PROD]: 1101, [ENV.STAGING]: 1442, [ENV.DEV]: 1442, - [ENV.LOCAL]: 1442, + [ENV.LOCAL]: 420, }, + ARBITRUMONE: { + [ENV.PROD]: 42161, + [ENV.STAGING]: 421613, + [ENV.DEV]: 421613, + [ENV.LOCAL]: 421613, + } }; -export const CHAIN_ID = { - ETHEREUM: ETH_CHAIN_ID, - ...ALIAS_CHAIN_ID, -}; - -export const CHAIN_NAME: { [key: number]: string } = { - // eth - 1: 'ETHEREUM', - 5: 'ETHEREUM', - // polygon - 137: 'POLYGON', - 80001: 'POLYGON', - // bsc - 56: 'BSC', - 97: 'BSC', - // optimism - 10: 'OPTIMISM', - 420: 'OPTIMISM', - // plygonzkevm - 1101: 'POLYGONZKEVM', - 1442: 'POLYGONZKEVM', -}; export interface ConfigType { API_BASE_URL: string; EPNS_COMMUNICATOR_CONTRACT: string; } - -export const VIEM_CORE_CONFIG = { - [ENV.PROD]: { - NETWORK: mainnet, - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_CORE_CONTRACT: '0x66329Fdd4042928BfCAB60b179e1538D56eeeeeE', - }, - [ENV.STAGING]: { - NETWORK: goerli, - API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_CORE_CONTRACT: '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C', - }, - [ENV.DEV]: { - NETWORK: goerli, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_CORE_CONTRACT: '0x23346B732d56d34EC4e890419fBFB8548216a799', - }, - [ENV.LOCAL]: { - NETWORK: goerli, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_CORE_CONTRACT: '0x23346B732d56d34EC4e890419fBFB8548216a799', - }, -}; - -export const CORE_CONFIG = { - [ENV.PROD]: { - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_CORE_CONTRACT: '0x66329Fdd4042928BfCAB60b179e1538D56eeeeeE', - }, - [ENV.STAGING]: { - API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_CORE_CONTRACT: '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C', - }, - [ENV.DEV]: { - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_CORE_CONTRACT: '0x23346B732d56d34EC4e890419fBFB8548216a799', - }, - [ENV.LOCAL]: { - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_CORE_CONTRACT: '0x23346B732d56d34EC4e890419fBFB8548216a799', - }, -}; - const CONFIG = { [ENV.PROD]: { [BLOCKCHAIN_NETWORK.ETH_MAINNET]: { @@ -168,6 +91,10 @@ const CONFIG = { API_BASE_URL: API_BASE_URL[ENV.PROD], EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', }, + [BLOCKCHAIN_NETWORK.ARBITRUMONE_MAINNET]: { + API_BASE_URL: API_BASE_URL[ENV.PROD], + EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', + }, }, [ENV.STAGING]: { [BLOCKCHAIN_NETWORK.ETH_GOERLI]: { @@ -190,6 +117,10 @@ const CONFIG = { API_BASE_URL: API_BASE_URL[ENV.STAGING], EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', }, + [BLOCKCHAIN_NETWORK.ARBITRUM_TESTNET]: { + API_BASE_URL: API_BASE_URL[ENV.STAGING], + EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', + }, }, [ENV.DEV]: { [BLOCKCHAIN_NETWORK.ETH_GOERLI]: { @@ -212,6 +143,10 @@ const CONFIG = { API_BASE_URL: API_BASE_URL[ENV.DEV], EPNS_COMMUNICATOR_CONTRACT: '0x630b152e4185c63D7177c656b56b26f878C61572', }, + [BLOCKCHAIN_NETWORK.ARBITRUM_TESTNET]: { + API_BASE_URL: API_BASE_URL[ENV.STAGING], + EPNS_COMMUNICATOR_CONTRACT: '0x2f6aE0907116A2c50D712e78b48D874fadeB6850', + }, }, [ENV.LOCAL]: { [BLOCKCHAIN_NETWORK.ETH_GOERLI]: { @@ -234,149 +169,11 @@ const CONFIG = { API_BASE_URL: API_BASE_URL[ENV.DEV], EPNS_COMMUNICATOR_CONTRACT: '0x630b152e4185c63D7177c656b56b26f878C61572', }, - }, -}; - -export default CONFIG; -export const TOKEN = { - [ENV.PROD]: '0xf418588522d5dd018b425E472991E52EBBeEEEEE', - [ENV.STAGING]: '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', - [ENV.DEV]: '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', - [ENV.LOCAL]: '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', -}; - -export const TOKEN_VIEM_NETWORK_MAP = { - [ENV.PROD]: mainnet, - [ENV.STAGING]: goerli, - [ENV.DEV]: goerli, - [ENV.LOCAL]: goerli, -} - -export const MIN_TOKEN_BALANCE = { - [ENV.PROD]: 50, - [ENV.STAGING]: 50, - [ENV.DEV]: 50, - [ENV.LOCAL]: 50, -}; -export const ABIS = { - CORE: coreABI, - COMM: commABI, - TOKEN: tokenABI, -}; - -export const CHANNEL_TYPE = { - TIMEBOUND: 4, - GENERAL: 2, -}; - - -export const VIEM_CONFIG = { - [ENV.PROD]: { - [BLOCKCHAIN_NETWORK.ETH_MAINNET]: { - NETWORK: mainnet, - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.POLYGON_MAINNET]: { - NETWORK: polygon, - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.BSC_MAINNET]: { - NETWORK: bsc, - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.OPTIMISM_MAINNET]: { - NETWORK: optimism, - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.POLYGON_ZK_EVM_MAINNET]: { - NETWORK: polygonZkEvm, - API_BASE_URL: API_BASE_URL[ENV.PROD], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - }, - [ENV.STAGING]: { - [BLOCKCHAIN_NETWORK.ETH_GOERLI]: { - NETWORK: goerli, - API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.POLYGON_MUMBAI]: { - NETWORK: polygonMumbai, - API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.BSC_TESTNET]: { - NETWORK: bscTestnet, - API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.OPTIMISM_TESTNET]: { - NETWORK: optimismGoerli, - API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - [BLOCKCHAIN_NETWORK.POLYGON_ZK_EVM_TESTNET]: { - NETWORK: polygonZkEvmTestnet, + [BLOCKCHAIN_NETWORK.ARBITRUM_TESTNET]: { API_BASE_URL: API_BASE_URL[ENV.STAGING], - EPNS_COMMUNICATOR_CONTRACT: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', - }, - }, - [ENV.DEV]: { - [BLOCKCHAIN_NETWORK.ETH_GOERLI]: { - NETWORK: goerli, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0xc064F30bac07e84500c97A04D21a9d1bfFC72Ec0', - }, - [BLOCKCHAIN_NETWORK.POLYGON_MUMBAI]: { - NETWORK: polygonMumbai, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0xAf55BE8e6b0d6107891bA76eADeEa032ef8A4504', - }, - [BLOCKCHAIN_NETWORK.BSC_TESTNET]: { - NETWORK: bscTestnet, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0x4132061E3349ff36cFfCadA460E10Bd4f31F7ea8', - }, - [BLOCKCHAIN_NETWORK.OPTIMISM_TESTNET]: { - NETWORK: optimismGoerli, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0x4305D572F2bf38Fc2AE8D0172055b1EFd18F57a6', - }, - [BLOCKCHAIN_NETWORK.POLYGON_ZK_EVM_TESTNET]: { - NETWORK: polygonZkEvmTestnet, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0x630b152e4185c63D7177c656b56b26f878C61572', - }, - }, - [ENV.LOCAL]: { - [BLOCKCHAIN_NETWORK.ETH_GOERLI]: { - NETWORK: goerli, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0xc064F30bac07e84500c97A04D21a9d1bfFC72Ec0', - }, - [BLOCKCHAIN_NETWORK.POLYGON_MUMBAI]: { - NETWORK: polygonMumbai, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0xAf55BE8e6b0d6107891bA76eADeEa032ef8A4504', - }, - [BLOCKCHAIN_NETWORK.BSC_TESTNET]: { - NETWORK: bscTestnet, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0x4132061E3349ff36cFfCadA460E10Bd4f31F7ea8', - }, - [BLOCKCHAIN_NETWORK.OPTIMISM_TESTNET]: { - NETWORK: optimismGoerli, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0x4305D572F2bf38Fc2AE8D0172055b1EFd18F57a6', - }, - [BLOCKCHAIN_NETWORK.POLYGON_ZK_EVM_TESTNET]: { - NETWORK: polygonZkEvmTestnet, - API_BASE_URL: API_BASE_URL[ENV.DEV], - EPNS_COMMUNICATOR_CONTRACT: '0x630b152e4185c63D7177c656b56b26f878C61572', + EPNS_COMMUNICATOR_CONTRACT: '0x2f6aE0907116A2c50D712e78b48D874fadeB6850', }, }, }; + +export default CONFIG; diff --git a/packages/restapi/src/lib/constants.ts b/packages/restapi/src/lib/constants.ts index 4735b547a..595f52077 100644 --- a/packages/restapi/src/lib/constants.ts +++ b/packages/restapi/src/lib/constants.ts @@ -55,7 +55,7 @@ const Constants = { }, DEFAULT_CHAIN_ID: 5, DEV_CHAIN_ID: 99999, - NON_ETH_CHAINS: [137, 80001, 56, 97, 10, 420, 1442, 1101], + NON_ETH_CHAINS: [137, 80001, 56, 97, 10, 420, 1442, 1101, 421613, 42161], ETH_CHAINS: [1, 5], ENC_TYPE_V1: 'x25519-xsalsa20-poly1305', ENC_TYPE_V2: 'aes256GcmHkdfSha256', diff --git a/packages/restapi/src/lib/payloads/constants.ts b/packages/restapi/src/lib/payloads/constants.ts index a3c5d9a41..da01fafc7 100644 --- a/packages/restapi/src/lib/payloads/constants.ts +++ b/packages/restapi/src/lib/payloads/constants.ts @@ -13,6 +13,8 @@ export const CHAIN_ID_TO_SOURCE: ChainIdToSourceType = { 420: 'OPTIMISM_TESTNET', 1442: 'POLYGON_ZK_EVM_TESTNET', 1101: 'POLYGON_ZK_EVM_MAINNET', + 421613: "ARBITRUM_TESTNET", + 42161: "ARBITRUMONE_MAINNET" }; export const SOURCE_TYPES = { @@ -26,6 +28,8 @@ export const SOURCE_TYPES = { OPTIMISM_TESTNET: 'OPTIMISM_TESTNET', POLYGON_ZK_EVM_TESTNET: 'POLYGON_ZK_EVM_TESTNET', POLYGON_ZK_EVM_MAINNET: 'POLYGON_ZK_EVM_MAINNET', + ARBITRUM_TESTNET: "ARBITRUM_TESTNET", + ARBITRUMONE_MAINNET: "ARBITRUMONE_MAINNET", THE_GRAPH: 'THE_GRAPH', PUSH_VIDEO: 'PUSH_VIDEO', }; diff --git a/packages/restapi/src/lib/payloads/helpers.ts b/packages/restapi/src/lib/payloads/helpers.ts index 6b7016feb..0ef67d4f2 100644 --- a/packages/restapi/src/lib/payloads/helpers.ts +++ b/packages/restapi/src/lib/payloads/helpers.ts @@ -67,6 +67,9 @@ export function getPayloadForAPIInput( ...(inputOptions?.payload?.additionalMeta && { additionalMeta: inputOptions?.payload?.additionalMeta, }), + ...(inputOptions?.payload?.index && { + index: inputOptions?.payload?.index, + }), }, recipients: recipients, }; @@ -321,7 +324,7 @@ export function getSource( export function getCAIPFormat(chainId: number, address: string) { // EVM based chains - if ([1, 5, 42, 137, 80001, 56, 97, 10, 420, 1442, 1101].includes(chainId)) { + if ([1, 5, 42, 137, 80001, 56, 97, 10, 420, 1442, 1101, 421613, 42161].includes(chainId)) { return `eip155:${chainId}:${address}`; } diff --git a/packages/restapi/src/lib/types/index.ts b/packages/restapi/src/lib/types/index.ts index e75a057a5..336175364 100644 --- a/packages/restapi/src/lib/types/index.ts +++ b/packages/restapi/src/lib/types/index.ts @@ -116,6 +116,7 @@ export interface ISendNotificationInputOptions { * use additionalMeta instead */ metadata?: any; + index?: string; }; recipients?: string | string[]; // CAIP or plain ETH channel: string; // CAIP or plain ETH diff --git a/packages/restapi/tests/lib/channel/sendNotification.test.ts b/packages/restapi/tests/lib/channel/sendNotification.test.ts new file mode 100644 index 000000000..692e8e740 --- /dev/null +++ b/packages/restapi/tests/lib/channel/sendNotification.test.ts @@ -0,0 +1,70 @@ +import * as path from 'path'; +import * as dotenv from 'dotenv'; +dotenv.config({ path: path.resolve(__dirname, '../../.env') }); +import * as PUSH_PAYLOAD from '../../../src/lib/payloads'; +import { expect } from 'chai'; +import Constants from '../../../src/lib/constants'; +import { ethers } from 'ethers'; + +describe('PUSH_PAYLOAD.sendNotification functionality', () => { + let signer1: any; + let account1: string; + let signer2: any; + let account2: string; + + beforeEach(async () => { + signer1 = new ethers.Wallet( + '0xb9d00f786e1d024cfed08f696a775217ff75501f4aacef5ec0795fc4a2eb9df1' + ); + account1 = signer1.address; + + const WALLET2 = ethers.Wallet.createRandom(); + signer2 = new ethers.Wallet(WALLET2.privateKey); + account2 = WALLET2.address; + }); + + it('Should send notification with setting index', async () => { + enum IDENTITY_TYPE { + MINIMAL = 0, + IPFS = 1, + DIRECT_PAYLOAD = 2, + SUBGRAPH = 3, + } + + enum NOTIFICATION_TYPE { + BROADCAST = 1, + TARGETTED = 3, + SUBSET = 4, + } + + enum ENV { + PROD = 'prod', + STAGING = 'staging', + DEV = 'dev', + /** + * **This is for local development only** + */ + LOCAL = 'local', + } + const res = await PUSH_PAYLOAD.sendNotification({ + env: ENV.DEV, + signer: signer1, + type: NOTIFICATION_TYPE.BROADCAST, + identityType: IDENTITY_TYPE.DIRECT_PAYLOAD, + + notification: { + title: 'hey', + body: 'hey', + }, + payload: { + title: 'hey', + body: 'hey', + cta: '', + img: '', + index: '1-2-10', + }, + channel: `eip155:5:${account1}`, + }); + expect(res.status).to.be.equal(204); + }); +}); diff --git a/packages/restapi/tests/lib/channel/subscribeV2.test.ts b/packages/restapi/tests/lib/channel/subscribeV2.test.ts new file mode 100644 index 000000000..b1bd3f0b4 --- /dev/null +++ b/packages/restapi/tests/lib/channel/subscribeV2.test.ts @@ -0,0 +1,56 @@ +import * as path from 'path'; +import * as dotenv from 'dotenv'; +dotenv.config({ path: path.resolve(__dirname, '../../.env') }); +import * as PUSH_CHANNEL from '../../../src/lib/channels/'; +import { expect } from 'chai'; +import Constants from '../../../src/lib/constants'; +import { ethers } from 'ethers'; + +describe('PUSH_CHANNEL.subscribeV2 functionality', () => { + let signer1: any; + let account1: string; + let signer2: any; + let account2: string; + + beforeEach(async () => { + const WALLET1 = ethers.Wallet.createRandom(); + signer1 = new ethers.Wallet(WALLET1.privateKey); + account1 = WALLET1.address; + + const WALLET2 = ethers.Wallet.createRandom(); + signer2 = new ethers.Wallet(WALLET2.privateKey); + account2 = WALLET2.address; + }); + + it('Should subscribe to the channel via V2 without settings', async () => { + const res = await PUSH_CHANNEL.subscribeV2({ + signer: signer1, + channelAddress: 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681', + userAddress: `eip155:5:${account1}`, + env: Constants.ENV.STAGING, + }); + expect(res.status).to.be.equal('success'); + }); + + it('Should unsubscribe to the channel via V2 without settings', async () => { + const res = await PUSH_CHANNEL.unsubscribeV2({ + signer: signer1, + channelAddress: 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681', + userAddress: `eip155:5:${account1}`, + env: Constants.ENV.STAGING, + }); + console.log(res) + expect(res.status).to.be.equal('success'); + }); + + it('Should subscribe to the channel via V2 with settings', async () => { + const res = await PUSH_CHANNEL.subscribeV2({ + signer: signer1, + channelAddress: 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681', + userAddress: `eip155:5:${account1}`, + env: Constants.ENV.STAGING, + userSetting: '2-1-0+2-1', + }); + expect(res.status).to.be.equal('success'); + }); +}); diff --git a/packages/uiweb/README.md b/packages/uiweb/README.md index fe84d77d5..d5f2e96f0 100644 --- a/packages/uiweb/README.md +++ b/packages/uiweb/README.md @@ -149,7 +149,7 @@ where | cta | string | Call To Action Link (given during notification creation) | | image | string | Any media link (given during notification creation) | | url | string | Channel Link (given during channel setup) | -| chainName | string | Can be anyone of the following blockchain networks on which the notification was sent - "ETH_MAINNET", "ETH_TEST_GOERLI", "POLYGON_MAINNET", "POLYGON_TEST_MUMBAI", "BSC_MAINNET, "BSC_TESTNET", "OPTIMISM_MAINNET", "OPTIMISM_TESTNET", "POLYGON_ZK_EVM_TESTNET", "POLYGON_ZK_EVM_MAINNET", "THE_GRAPH" | +| chainName | string | Can be anyone of the following blockchain networks on which the notification was sent - "ETH_MAINNET", "ETH_TEST_GOERLI", "POLYGON_MAINNET", "POLYGON_TEST_MUMBAI", "BSC_MAINNET, "BSC_TESTNET", "OPTIMISM_MAINNET", "OPTIMISM_TESTNET", "POLYGON_ZK_EVM_TESTNET", "POLYGON_ZK_EVM_MAINNET", "ARBITRUM_TESTNET", "ARBITRUMONE_MAINNET", "THE_GRAPH" | | theme | string | 'light' or 'dark' (customization to be given by the dApp) | | customTheme | INotificationItemTheme | custom theme object for the component | | isSpam | boolean | whether a spam notification or not | diff --git a/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx b/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx index f739f6e0a..2ab7aaf7b 100644 --- a/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx +++ b/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx @@ -126,4 +126,4 @@ const ConnectButtonDiv = styled.div` @media ${device.mobileL} { font-size: 12px; } -`; +`; \ No newline at end of file diff --git a/packages/uiweb/src/lib/components/notification/chainDetails.tsx b/packages/uiweb/src/lib/components/notification/chainDetails.tsx index ad783c516..403f12c39 100644 --- a/packages/uiweb/src/lib/components/notification/chainDetails.tsx +++ b/packages/uiweb/src/lib/components/notification/chainDetails.tsx @@ -7,7 +7,7 @@ import { BSCSvg } from "../../icons/BSCSvg"; import { OptimismSvg } from "../../icons/OptimismSvg"; import { PolygonzkevmSvg } from "../../icons/PolygonzkevmSvg"; import { TheGraphSvg } from "../../icons/TheGraphSvg"; -import { ReactElement } from "react"; +import { ArbitrumSvg } from "../../icons/ArbitrumSvg" const createSVGIcon = (element:any, chainName: string) => { return ( @@ -58,5 +58,14 @@ export default { label: 'POLYGON ZK EVM MAINNET', icon: createSVGIcon(, 'Polygon ZK EVM Mainnet'), }, + + ARBITRUMONE_MAINNET: { + label: 'ARBITRUMONE MAINNET', + icon: createSVGIcon(, 'Arbitrum Mainnet'), + }, + ARBITRUM_TESTNET: { + label: 'ARBITRUM TESTNET', + icon: createSVGIcon(, 'Arbitrum Testnet'), + }, THE_GRAPH: { label: 'THE GRAPH', icon: createSVGIcon(, 'The Graph') }, }; diff --git a/packages/uiweb/src/lib/components/notification/index.tsx b/packages/uiweb/src/lib/components/notification/index.tsx index bcec3907b..8ce4dd911 100644 --- a/packages/uiweb/src/lib/components/notification/index.tsx +++ b/packages/uiweb/src/lib/components/notification/index.tsx @@ -34,6 +34,8 @@ export type chainNameType = | 'OPTIMISM_TESTNET' | 'POLYGON_ZK_EVM_TESTNET' | 'POLYGON_ZK_EVM_MAINNET' + | 'ARBITRUMONE_MAINNET' + | 'ARBITRUM_TESTNET' | 'THE_GRAPH' | undefined; diff --git a/packages/uiweb/src/lib/config/constants.ts b/packages/uiweb/src/lib/config/constants.ts index b91a48dad..edd9b7cf7 100644 --- a/packages/uiweb/src/lib/config/constants.ts +++ b/packages/uiweb/src/lib/config/constants.ts @@ -51,25 +51,30 @@ export const allowedNetworks = { 1, //for ethereum mainnet 137, //for polygon mainnet 56, // for bnb mainnet - // 10 // for optimism mainnet + 10, // for optimism mainnet + 42161 // for arbitrum mainnet ], 'dev' : [ 5, // for eth goerli 80001, //for mumbai polygon 97, // bnb testnet - 420 // optimism goerli testnet + 420, // optimism goerli testnet + 421613 // for arbitrum testnet ], 'staging' : [ // 42, //for kovan 5, // for goerli 80001, //for mumbai polygon - 97 // bnb testnet + 97, // bnb testnet + 420, // optimism goerli testnet + 421613 // for arbitrum testnet ], 'local' :[ 5, // for eth goerli 80001, //for mumbai polygon 97, // bnb testnet - 420 // optimism goerli testnet + 420, // optimism goerli testnet + 421613 // for arbitrum testnet ] } diff --git a/packages/uiweb/src/lib/icons/ArbitrumSvg.tsx b/packages/uiweb/src/lib/icons/ArbitrumSvg.tsx new file mode 100644 index 000000000..5703e7717 --- /dev/null +++ b/packages/uiweb/src/lib/icons/ArbitrumSvg.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; + +export const ArbitrumSvg = () => ( + + + + + +); +