types.ts
1 /* eslint-disable @typescript-eslint/no-explicit-any */ 2 import type { SendTransactionsResponse } from '@safe-global/safe-apps-sdk'; 3 import type { TransactionLike, TypedDataDomain, TypedDataField } from 'ethers'; 4 import type { TransactionReceipt } from 'ethers'; 5 import type { Component, ComponentProps } from 'svelte'; 6 7 export type TransactionWrapper = { 8 title: string; 9 transaction: TransactionLike; 10 gasless?: { 11 nonceGetter: () => Promise<number>; 12 ERC2771Data: (nonce: number) => { 13 domain: TypedDataDomain; 14 types: Record<string, TypedDataField[]>; 15 payload: Record<string, unknown>; 16 }; 17 }; 18 applyGasBuffer: boolean; 19 }; 20 21 /** 22 * Use this to display transactions that are handled off-chain (e.g. through Gelato Relay) but have to be awaited 23 * as part of a multi-transaction interaction. 24 */ 25 export type ExternalTransaction = { 26 title: string; 27 external: true; 28 expectedDurationMs: number; 29 expectedDurationText: string; 30 /** When this resolves, moves on to next transaction */ 31 promise: () => Promise<void>; 32 }; 33 34 export type TransactionWrapperOrExternalTransaction = TransactionWrapper | ExternalTransaction; 35 36 export interface TransactPayload<T> { 37 before?: T; 38 transactions: ( 39 context: Context<T>, 40 ) => 41 | TransactionWrapperOrExternalTransaction[] 42 | Promise<TransactionWrapperOrExternalTransaction[]>; 43 after?: (receipts: TransactionReceipt[], context: Context<T>) => PromiseLike<void>; 44 afterSafe?: ( 45 sendTransactionsResponse: SendTransactionsResponse, 46 context: Context<T>, 47 ) => PromiseLike<void>; 48 headline: string; 49 description?: string; 50 icon?: { 51 component: Component<any>; 52 props?: Record<string, unknown>; 53 }; 54 messages?: { 55 duringBefore?: string; 56 duringAfter?: string; 57 }; 58 } 59 60 export type SomeTransactPayload = <R>( 61 payload: <T extends BeforeFunc | undefined>(transactPayload: TransactPayload<T>) => R, 62 ) => R; 63 64 export type BeforeFunc = () => PromiseLike<Record<string, unknown> | void>; 65 66 type Context<T> = T extends BeforeFunc ? Awaited<ReturnType<T>> : undefined; 67 68 export function makeTransactPayload<T extends BeforeFunc | undefined>( 69 i: TransactPayload<T>, 70 ): SomeTransactPayload { 71 return (cb) => cb(i); 72 } 73 74 export interface UpdateAwaitStepParams { 75 message?: string; 76 subtitle?: string; 77 link?: { 78 url: string; 79 label: string; 80 }; 81 icon?: { 82 component: Component; 83 props: Record<string, unknown>; 84 }; 85 } 86 87 export type UpdateAwaitStepFn = (params: UpdateAwaitStepParams) => void; 88 89 export interface AwaitPendingPayload extends UpdateAwaitStepParams { 90 message: string; 91 /** 92 * The promise to await. `updateFn` may be used to adjust the icon 93 * and text displayed on the await step before the promise resolves. 94 */ 95 promise: (updateFn: UpdateAwaitStepFn) => Promise<unknown>; 96 } 97 98 export interface MovePayload { 99 by: number; 100 } 101 102 export interface SidestepPayload { 103 steps: Steps; 104 onSidestepComplete?: () => void; 105 } 106 107 export type StepComponentEvents = { 108 /** Go forward one step (or a custom amount by setting `by`). */ 109 goForward: MovePayload | void; 110 /** Go backward one step (or a custom amount by setting `by`). */ 111 goBackward: MovePayload | void; 112 /** 113 * Await a promise while displaying a customizable spinner dialog. 114 * Once the passed promise is resolved, advances in the flow. If 115 * the promise rejects, displays a rich error message step, with 116 * the ability to jump back to the step that triggered the await. 117 */ 118 await: AwaitPendingPayload; 119 /** 120 * Temporarily append a secondary flow after the current step, and 121 * navigate to the first step in the sidestep. Once the sidestep flow 122 * triggers `conclude`, go back to the original step in the original flow. 123 */ 124 sidestep: SidestepPayload; 125 /** 126 * Conclude a flow. Either goes back to the original flow if a sidestep 127 * is currently active, or closes the stepper modal. 128 */ 129 conclude: void; 130 // TODO: add description. 131 transact: SomeTransactPayload; 132 }; 133 134 type OmitContext<T> = Omit<T, 'context'>; 135 136 type ComponentAndProps<T extends Component<any>> = { 137 component: T; 138 props: OmitContext<ComponentProps<T>>; 139 }; 140 141 export type Step<T extends Component<any>> = { 142 component: T; 143 props: OmitContext<ComponentProps<T>>; 144 condition?: () => boolean; 145 staticHeaderComponent?: ComponentAndProps<Component>; 146 }; 147 148 type SomeStep = <R>(step: <T extends Component<any>>(step: Step<T>) => R) => R; 149 150 export type Steps = SomeStep[]; 151 152 export function makeStep<T extends Component<any>>(i: Step<T>): SomeStep { 153 return (cb) => cb(i); 154 }