diff --git a/packages/suite/src/actions/suite/constants/protocolConstants.ts b/packages/suite/src/actions/suite/constants/protocolConstants.ts index af32590d556..fa2fa35bf63 100644 --- a/packages/suite/src/actions/suite/constants/protocolConstants.ts +++ b/packages/suite/src/actions/suite/constants/protocolConstants.ts @@ -1,3 +1,5 @@ export const FILL_SEND_FORM = '@protocol/fill-send-form'; export const SAVE_COIN_PROTOCOL = '@protocol/save-coin-protocol'; +export const FILL_AOPP = '@protocol/fill-aopp'; +export const SAVE_AOPP_PROTOCOL = '@protocol/save-aopp-protocol'; export const RESET = '@protocol/reset'; diff --git a/packages/suite/src/actions/suite/protocolActions.ts b/packages/suite/src/actions/suite/protocolActions.ts index 6082042dfec..53e3196aeb0 100644 --- a/packages/suite/src/actions/suite/protocolActions.ts +++ b/packages/suite/src/actions/suite/protocolActions.ts @@ -1,16 +1,24 @@ import { PROTOCOL } from './constants'; import type { PROTOCOL_SCHEME } from '@suite-constants/protocol'; -import type { SendFormState } from '@suite-reducers/protocolReducer'; +import type { SendFormState, AoppState } from '@suite-reducers/protocolReducer'; export type ProtocolAction = | { type: typeof PROTOCOL.FILL_SEND_FORM; payload: boolean; } + | { + type: typeof PROTOCOL.FILL_AOPP; + payload: boolean; + } | { type: typeof PROTOCOL.SAVE_COIN_PROTOCOL; payload: SendFormState; } + | { + type: typeof PROTOCOL.SAVE_AOPP_PROTOCOL; + payload: AoppState; + } | { type: typeof PROTOCOL.RESET }; export const fillSendForm = (shouldFill: boolean): ProtocolAction => ({ @@ -27,6 +35,16 @@ export const saveCoinProtocol = ( payload: { scheme, address, amount }, }); +export const fillAopp = (shouldFill: boolean): ProtocolAction => ({ + type: PROTOCOL.FILL_AOPP, + payload: shouldFill, +}); + +export const saveAoppProtocol = (payload: AoppState): ProtocolAction => ({ + type: PROTOCOL.SAVE_AOPP_PROTOCOL, + payload, +}); + export const resetProtocol = (): ProtocolAction => ({ type: PROTOCOL.RESET, }); diff --git a/packages/suite/src/reducers/suite/protocolReducer.ts b/packages/suite/src/reducers/suite/protocolReducer.ts index 570573f2173..fc595198121 100644 --- a/packages/suite/src/reducers/suite/protocolReducer.ts +++ b/packages/suite/src/reducers/suite/protocolReducer.ts @@ -2,6 +2,7 @@ import produce from 'immer'; import { PROTOCOL } from '@suite-actions/constants'; import type { Action } from '@suite-types'; import type { PROTOCOL_SCHEME } from '@suite-constants/protocol'; +import type { Network } from '@wallet-types'; export interface SendFormState { scheme: PROTOCOL_SCHEME; @@ -9,16 +10,25 @@ export interface SendFormState { amount?: number; } +export interface AoppState { + message: string; + asset: Network['symbol']; + callback?: string; + format?: string; +} + type Autofill = Partial & { shouldFill?: boolean; }; export interface State { sendForm: Autofill; + aopp: Autofill; } export const initialState: State = { sendForm: {}, + aopp: {}, }; const protocolReducer = (state: State = initialState, action: Action): State => @@ -33,6 +43,16 @@ const protocolReducer = (state: State = initialState, action: Action): State => draft.sendForm.amount = action.payload.amount; draft.sendForm.shouldFill = false; break; + case PROTOCOL.FILL_AOPP: + draft.aopp.shouldFill = action.payload; + break; + case PROTOCOL.SAVE_AOPP_PROTOCOL: + draft.aopp.message = action.payload.message; + draft.aopp.callback = action.payload.callback; + draft.aopp.asset = action.payload.asset; + draft.aopp.format = action.payload.format; + draft.aopp.shouldFill = false; + break; case PROTOCOL.RESET: return initialState; // no default diff --git a/packages/suite/src/utils/suite/__fixtures__/parseUri.ts b/packages/suite/src/utils/suite/__fixtures__/parseUri.ts index baff265a90e..939a5b8cc64 100644 --- a/packages/suite/src/utils/suite/__fixtures__/parseUri.ts +++ b/packages/suite/src/utils/suite/__fixtures__/parseUri.ts @@ -130,6 +130,42 @@ export const getProtocolInfo = [ amount: undefined, }, }, + { + description: 'valid AOPP uri', + uri: 'aopp:?v=0&msg=MESSAGE&asset=btc&format=any&callback=https%3A%2F%2Ftesting.21analytics.ch%2Fproofs%2Fc220a28e-0e99-4be6-8578-a886f628ee20', + result: { + scheme: 'aopp', + v: '0', + msg: 'MESSAGE', + asset: 'btc', + format: 'any', + callback: 'https://testing.21analytics.ch/proofs/c220a28e-0e99-4be6-8578-a886f628ee20', + }, + }, + { + description: 'valid AOPP uri with invalid callback', + uri: 'aopp:?v=0&msg=MESSAGE&asset=btc&format=any&callback=a', + result: { + scheme: 'aopp', + v: '0', + msg: 'MESSAGE', + asset: 'btc', + format: 'any', + callback: undefined, + }, + }, + { + description: 'valid AOPP uri, callback with slashes', + uri: 'aopp:?v=0&msg=MESSAGE&asset=btc&format=any&callback=https://foo.bar', + result: { + scheme: 'aopp', + v: '0', + msg: 'MESSAGE', + asset: 'btc', + format: 'any', + callback: 'https://foo.bar', + }, + }, { description: 'invalid uri', uri: 'gibberish', diff --git a/packages/suite/src/utils/suite/parseUri.ts b/packages/suite/src/utils/suite/parseUri.ts index b04d3927d46..f493728a26e 100644 --- a/packages/suite/src/utils/suite/parseUri.ts +++ b/packages/suite/src/utils/suite/parseUri.ts @@ -1,4 +1,6 @@ import { PROTOCOL_SCHEME } from '@suite-constants/protocol'; +import { isNetworkSymbol } from '@wallet-utils/accountUtils'; +import type { Network } from '@wallet-types'; // Parse URL query string (like 'foo=bar&baz=1337) into an object export const parseQuery = (uri: string) => { @@ -28,7 +30,16 @@ export type CoinProtocolInfo = { amount?: number; }; -export const getProtocolInfo = (uri: string): CoinProtocolInfo | null => { +export type AoppProtocolInfo = { + scheme: PROTOCOL_SCHEME.AOPP; + msg: string; + asset: Network['symbol']; + v?: string; + format?: string; + callback?: string; +}; + +export const getProtocolInfo = (uri: string): CoinProtocolInfo | AoppProtocolInfo | null => { const url = parseUri(uri); if (!url) return null; @@ -48,5 +59,19 @@ export const getProtocolInfo = (uri: string): CoinProtocolInfo | null => { }; } + if (scheme === PROTOCOL_SCHEME.AOPP) { + if (!params.msg) return null; + if (!params.asset || !isNetworkSymbol(params.asset)) return null; + const validCallback = parseUri(params.callback ?? ''); + return { + scheme, + v: params.v, + asset: params.asset, + format: params.format, + msg: params.msg, + callback: validCallback ? params.callback : undefined, + }; + } + return null; }; diff --git a/packages/suite/src/utils/wallet/accountUtils.ts b/packages/suite/src/utils/wallet/accountUtils.ts index 350c840ed0d..3c0ff13e354 100644 --- a/packages/suite/src/utils/wallet/accountUtils.ts +++ b/packages/suite/src/utils/wallet/accountUtils.ts @@ -247,6 +247,9 @@ export const getSelectedAccount = ( export const getNetwork = (symbol: string) => NETWORKS.find(c => c.symbol === symbol) || null; +export const isNetworkSymbol = (symbol: string): symbol is Network['symbol'] => + !!getNetwork(symbol); + /** * Returns a string used as an index to separate txs for given account inside a transactions reducer *