diff --git a/packages/restapi/src/lib/channels/signature.helpers.ts b/packages/restapi/src/lib/channels/signature.helpers.ts index fadb4e480..e6513767a 100644 --- a/packages/restapi/src/lib/channels/signature.helpers.ts +++ b/packages/restapi/src/lib/channels/signature.helpers.ts @@ -50,6 +50,32 @@ export const getSubscriptionMessageV2 = ( } }; +export const getSubscriptionMessageV3 = ( + channel: string, + userAddress: string, + action: channelActionType, + userSetting?: string | null +) => { + const actionTypeKey = + action === 'Unsubscribe' ? 'unsubscriber' : 'subscriber'; + if (action == 'Subscribe') { + return JSON.stringify({ + channel, + [actionTypeKey]: userAddress, + action: action, + userSetting: userSetting?? '', + timestamp: Math.floor(Date.now() / 1000), + }, null, 4); + } else { + return JSON.stringify({ + channel, + [actionTypeKey]: userAddress, + action: action, + timestamp: Math.floor(Date.now() / 1000), + }, null, 4); + } +}; + export const getTypeInformation = (action: string) => { if (action === 'Subscribe') { return { diff --git a/packages/restapi/src/lib/channels/subscribeV3.ts b/packages/restapi/src/lib/channels/subscribeV3.ts new file mode 100644 index 000000000..cb40cfa8f --- /dev/null +++ b/packages/restapi/src/lib/channels/subscribeV3.ts @@ -0,0 +1,106 @@ +import { getCAIPAddress, getConfig, getCAIPDetails, Signer } from '../helpers'; +import { + getDomainInformation, + getTypeInformationV2, + getSubscriptionMessageV3, +} from './signature.helpers'; +import Constants, { ENV } from '../constants'; +import { SignerType } from '../types'; +import { axiosPost } from '../utils/axiosUtil'; + +export type SubscribeOptionsV2Type = { + signer: SignerType; + channelAddress: string; + userAddress: string; + settings?: string | null; + verifyingContractAddress?: string; + env?: ENV; + origin?: string; + onSuccess?: () => void; + onError?: (err: Error) => void; +}; + +export const subscribeV2 = async (options: SubscribeOptionsV2Type) => { + const { + signer, + channelAddress, + userAddress, + settings = undefined, + verifyingContractAddress, + env = Constants.ENV.PROD, + origin, + 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: getSubscriptionMessageV3( + channelCAIPDetails.address, + userCAIPDetails.address, + 'Subscribe', + settings + ), + }; + // sign a message using EIP712 + const pushSigner = new Signer(signer); + const signature = await pushSigner.signTypedData( + domainInformation, + typeInformation, + messageInformation, + 'Data' + ); + + const verificationProof = signature; // might change + + const body = { + verificationProof: `eip712v3:${verificationProof}`, + message: messageInformation.data, + origin: origin + }; + + const res = await axiosPost(requestUrl, body); + + if (typeof onSuccess === 'function') onSuccess(); + + return { status: res.status, message: 'successfully opted into channel' }; + } catch (err: any) { + if (typeof onError === 'function') onError(err as Error); + + return { + status: err?.response?.status ?? '', + message: err instanceof Error ? err.message : JSON.stringify(err), + }; + } +}; diff --git a/packages/restapi/src/lib/channels/unsubscribeV3.ts b/packages/restapi/src/lib/channels/unsubscribeV3.ts new file mode 100644 index 000000000..caf12b8e2 --- /dev/null +++ b/packages/restapi/src/lib/channels/unsubscribeV3.ts @@ -0,0 +1,103 @@ +import { getCAIPAddress, getConfig, getCAIPDetails, Signer } from '../helpers'; +import { + getDomainInformation, + getTypeInformationV2, + getSubscriptionMessageV3, +} from './signature.helpers'; +import Constants, { ENV } from '../constants'; +import { SignerType } from '../types'; +import { axiosPost } from '../utils/axiosUtil'; + +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: getSubscriptionMessageV3( + channelCAIPDetails.address, + userCAIPDetails.address, + 'Unsubscribe' + ), + }; + + // sign a message using EIP712 + const pushSigner = new Signer(signer); + const signature = await pushSigner.signTypedData( + domainInformation, + typeInformation, + messageInformation, + 'Data' + ); + + const verificationProof = signature; // might change + + const body = { + verificationProof: `eip712v3:${verificationProof}`, + message: messageInformation.data, + }; + + const res = await axiosPost(requestUrl, body); + + if (typeof onSuccess === 'function') onSuccess(); + + return { status: res.status, message: 'successfully opted out channel' }; + } catch (err: any) { + if (typeof onError === 'function') onError(err as Error); + + return { + status: err?.response?.status ?? '', + message: err instanceof Error ? err.message : JSON.stringify(err), + }; + } +};