Skip to content

Commit

Permalink
fix: Human-readable notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
usame-algan committed Aug 28, 2023
1 parent 9ececeb commit 0154d0b
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 112 deletions.
4 changes: 1 addition & 3 deletions src/components/tx/SignOrExecuteForm/ExecuteForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ const ExecuteForm = ({
origin,
onlyExecute,
isCreation,
humanDescription,
}: SignOrExecuteProps & {
safeTx?: SafeTransaction
humanDescription?: string
}): ReactElement => {
// Form state
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
Expand Down Expand Up @@ -87,7 +85,7 @@ const ExecuteForm = ({
const txOptions = getTxOptions(advancedParams, currentChain)

try {
const executedTxId = await executeTx({ txOptions, safeTx, txId, origin, willRelay, humanDescription })
const executedTxId = await executeTx(txOptions, safeTx, txId, origin, willRelay)
setTxFlow(<SuccessScreen txId={executedTxId} />, undefined, false)
} catch (_err) {
const err = asError(_err)
Expand Down
6 changes: 1 addition & 5 deletions src/components/tx/SignOrExecuteForm/SignForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ const SignForm = ({
isBatch,
isBatchable,
isCreation,
humanDescription,
}: SignOrExecuteProps & {
safeTx?: SafeTransaction
humanDescription?: string
}): ReactElement => {
// Form state
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
Expand Down Expand Up @@ -55,9 +53,7 @@ const SignForm = ({
setSubmitError(undefined)

try {
await (isAddingToBatch
? addToBatch(safeTx, origin, humanDescription)
: signTx(safeTx, txId, origin, humanDescription))
await (isAddingToBatch ? addToBatch(safeTx, origin) : signTx(safeTx, txId, origin))
} catch (_err) {
const err = asError(_err)
logError(Errors._804, err)
Expand Down
28 changes: 8 additions & 20 deletions src/components/tx/SignOrExecuteForm/hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ describe('SignOrExecute hooks', () => {
const { result } = renderHook(() => useTxActions())
const { executeTx } = result.current

const id = await executeTx({ txOptions: { gasPrice: 1 }, safeTx: createSafeTx() })
const id = await executeTx({ gasPrice: 1 }, createSafeTx())
expect(proposeSpy).toHaveBeenCalled()
expect(executeSpy).toHaveBeenCalled()
expect(id).toEqual('123')
Expand Down Expand Up @@ -373,7 +373,7 @@ describe('SignOrExecute hooks', () => {
const { result } = renderHook(() => useTxActions())
const { executeTx } = result.current

const id = await executeTx({ txOptions: { gasPrice: 1 }, safeTx: createSafeTx(), txId: '455' })
const id = await executeTx({ gasPrice: 1 }, createSafeTx(), '455')
expect(proposeSpy).not.toHaveBeenCalled()
expect(executeSpy).toHaveBeenCalled()
expect(id).toEqual('455')
Expand All @@ -400,7 +400,7 @@ describe('SignOrExecute hooks', () => {

// Expect signTx to throw an error
await expect(signTx()).rejects.toThrowError('Transaction not provided')
await expect(executeTx({ txOptions: { gasPrice: 1 } })).rejects.toThrowError('Transaction not provided')
await expect(executeTx({ gasPrice: 1 })).rejects.toThrowError('Transaction not provided')
})

it('should relay a tx execution', async () => {
Expand Down Expand Up @@ -435,13 +435,7 @@ describe('SignOrExecute hooks', () => {
dynamicPart: () => '',
})

const id = await executeTx({
txOptions: { gasPrice: 1 },
safeTx: tx,
txId: '123',
origin: 'origin.com',
willRelay: true,
})
const id = await executeTx({ gasPrice: 1 }, tx, '123', 'origin.com', true)
expect(proposeSpy).not.toHaveBeenCalled()
expect(relaySpy).toHaveBeenCalled()
expect(id).toEqual('123')
Expand Down Expand Up @@ -490,13 +484,7 @@ describe('SignOrExecute hooks', () => {
const { result } = renderHook(() => useTxActions())
const { executeTx } = result.current

const id = await executeTx({
txOptions: { gasPrice: 1 },
safeTx: tx,
txId: '123',
origin: 'origin.com',
willRelay: true,
})
const id = await executeTx({ gasPrice: 1 }, tx, '123', 'origin.com', true)
expect(proposeSpy).toHaveBeenCalled()
expect(signSpy).toHaveBeenCalled()
expect(relaySpy).toHaveBeenCalled()
Expand Down Expand Up @@ -546,9 +534,9 @@ describe('SignOrExecute hooks', () => {
const { result } = renderHook(() => useTxActions())
const { executeTx } = result.current

await expect(() =>
executeTx({ txOptions: { gasPrice: 1 }, safeTx: tx, txId: '123', origin: 'origin.com', willRelay: true }),
).rejects.toThrowError('Cannot relay an unsigned transaction from a smart contract wallet')
await expect(() => executeTx({ gasPrice: 1 }, tx, '123', 'origin.com', true)).rejects.toThrowError(
'Cannot relay an unsigned transaction from a smart contract wallet',
)
expect(proposeSpy).not.toHaveBeenCalled()
expect(signSpy).not.toHaveBeenCalled()
expect(relaySpy).not.toHaveBeenCalled()
Expand Down
62 changes: 21 additions & 41 deletions src/components/tx/SignOrExecuteForm/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,18 @@ import type { OnboardAPI } from '@web3-onboard/core'
import { getSafeTxGas, getRecommendedNonce } from '@/services/tx/tx-sender/recommendedNonce'
import useAsync from '@/hooks/useAsync'
import { useUpdateBatch } from '@/hooks/useDraftBatch'
import { type TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'

type TxActions = {
addToBatch: (safeTx?: SafeTransaction, origin?: string, humanDescription?: string) => Promise<string>
signTx: (safeTx?: SafeTransaction, txId?: string, origin?: string, humanDescription?: string) => Promise<string>
executeTx: ({
txOptions,
safeTx,
txId,
origin,
willRelay,
humanDescription,
}: {
txOptions: TransactionOptions
safeTx?: SafeTransaction
txId?: string
origin?: string
willRelay?: boolean
humanDescription?: string
}) => Promise<string>
executeTx: (
txOptions: TransactionOptions,
safeTx?: SafeTransaction,
txId?: string,
origin?: string,
isRelayed?: boolean,
) => Promise<string>
}

function assertTx(safeTx: SafeTransaction | undefined): asserts safeTx {
Expand All @@ -59,29 +52,22 @@ export const useTxActions = (): TxActions => {
const safeAddress = safe.address.value
const { chainId, version } = safe

const proposeTx = async (
sender: string,
safeTx: SafeTransaction,
txId?: string,
origin?: string,
humanDescription?: string,
) => {
const proposeTx = async (sender: string, safeTx: SafeTransaction, txId?: string, origin?: string) => {
return dispatchTxProposal({
chainId,
safeAddress,
sender,
safeTx,
txId,
origin,
humanDescription,
})
}

const addToBatch: TxActions['addToBatch'] = async (safeTx, origin, humanDescription) => {
const addToBatch: TxActions['addToBatch'] = async (safeTx, origin) => {
assertTx(safeTx)
assertWallet(wallet)

const tx = await proposeTx(wallet.address, safeTx, undefined, origin, humanDescription)
const tx = await proposeTx(wallet.address, safeTx, undefined, origin)
await addTxToBatch(tx)
return tx.txId
}
Expand Down Expand Up @@ -118,41 +104,35 @@ export const useTxActions = (): TxActions => {
}

// Otherwise, sign off-chain
const signedTx = await dispatchTxSigning(safeTx, version, onboard, chainId, txId, humanDescription)
const tx = await proposeTx(wallet.address, signedTx, txId, origin, humanDescription)
const signedTx = await dispatchTxSigning(safeTx, version, onboard, chainId, txId)
const tx = await proposeTx(wallet.address, signedTx, txId, origin)
return tx.txId
}

const executeTx: TxActions['executeTx'] = async ({
txOptions,
safeTx,
txId,
origin,
willRelay,
humanDescription,
}) => {
const executeTx: TxActions['executeTx'] = async (txOptions, safeTx, txId, origin, isRelayed) => {
assertTx(safeTx)
assertWallet(wallet)
assertOnboard(onboard)

let tx: TransactionDetails | undefined
// Relayed transactions must be fully signed, so request a final signature if needed
if (willRelay && safeTx.signatures.size < safe.threshold) {
if (isRelayed && safeTx.signatures.size < safe.threshold) {
safeTx = await signRelayedTx(safeTx)
const tx = await proposeTx(wallet.address, safeTx, txId, origin, humanDescription)
tx = await proposeTx(wallet.address, safeTx, txId, origin)
txId = tx.txId
}

// Propose the tx if there's no id yet ("immediate execution")
if (!txId) {
const tx = await proposeTx(wallet.address, safeTx, txId, origin, humanDescription)
tx = await proposeTx(wallet.address, safeTx, txId, origin)
txId = tx.txId
}

// Relay or execute the tx via connected wallet
if (willRelay) {
await dispatchTxRelay(safeTx, safe, txId, txOptions.gasLimit, humanDescription)
if (isRelayed) {
await dispatchTxRelay(safeTx, safe, txId, txOptions.gasLimit, tx?.txInfo.humanDescription)
} else {
await dispatchTxExecution(safeTx, txOptions, txId, onboard, chainId, safeAddress, humanDescription)
await dispatchTxExecution(safeTx, txOptions, txId, onboard, chainId, safeAddress, tx?.txInfo.humanDescription)
}

return txId
Expand Down
17 changes: 2 additions & 15 deletions src/components/tx/SignOrExecuteForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,9 @@ const SignOrExecuteForm = (props: SignOrExecuteProps): ReactElement => {
<RiskConfirmationError />

{willExecute ? (
<ExecuteForm
{...props}
safeTx={safeTx}
isCreation={isCreation}
// @ts-ignore TODO: Update gateway-typescript-sdk type
humanDescription={decodedData?.humanDescription}
/>
<ExecuteForm {...props} safeTx={safeTx} isCreation={isCreation} />
) : (
<SignForm
{...props}
safeTx={safeTx}
isBatchable={isBatchable}
isCreation={isCreation}
// @ts-ignore TODO: Update gateway-typescript-sdk type
humanDescription={decodedData?.humanDescription}
/>
<SignForm {...props} safeTx={safeTx} isBatchable={isBatchable} isCreation={isCreation} />
)}
</TxCard>
</>
Expand Down
5 changes: 2 additions & 3 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import chains from './chains'
export const IS_PRODUCTION = !!process.env.NEXT_PUBLIC_IS_PRODUCTION
export const IS_DEV = process.env.NODE_ENV === 'development'

export const GATEWAY_URL_PRODUCTION =
process.env.NEXT_PUBLIC_GATEWAY_URL_PRODUCTION || 'https://safe-client.safe.global'
export const GATEWAY_URL_STAGING = process.env.NEXT_PUBLIC_GATEWAY_URL_STAGING || 'https://safe-client.staging.5afe.dev'
export const GATEWAY_URL_PRODUCTION = process.env.NEXT_PUBLIC_GATEWAY_URL_PRODUCTION || 'http://localhost:3030'
export const GATEWAY_URL_STAGING = process.env.NEXT_PUBLIC_GATEWAY_URL_STAGING || 'http://localhost:3030'

// Magic numbers
export const POLLING_INTERVAL = 15_000
Expand Down
7 changes: 2 additions & 5 deletions src/services/tx/tx-sender/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,13 @@ export const dispatchTxProposal = async ({
safeTx,
txId,
origin,
humanDescription,
}: {
chainId: string
safeAddress: string
sender: string
safeTx: SafeTransaction
txId?: string
origin?: string
humanDescription?: string
}): Promise<TransactionDetails> => {
const safeSDK = getAndValidateSafeSDK()
const safeTxHash = await safeSDK.getTransactionHash(safeTx)
Expand All @@ -55,10 +53,9 @@ export const dispatchTxProposal = async ({
txDispatch(TxEvent.SIGNATURE_PROPOSE_FAILED, {
txId,
error: asError(error),
humanDescription,
})
} else {
txDispatch(TxEvent.PROPOSE_FAILED, { error: asError(error), humanDescription })
txDispatch(TxEvent.PROPOSE_FAILED, { error: asError(error) })
}
throw error
}
Expand All @@ -69,7 +66,7 @@ export const dispatchTxProposal = async ({
txDispatch(txId ? TxEvent.SIGNATURE_PROPOSED : TxEvent.PROPOSED, {
txId: proposedTx.txId,
signerAddress: txId ? sender : undefined,
humanDescription,
humanDescription: proposedTx.txInfo.humanDescription,
})
}

Expand Down
40 changes: 20 additions & 20 deletions src/services/tx/txEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,28 @@ export enum TxEvent {
}

type Id = { txId: string; groupKey?: string } | { txId?: string; groupKey: string }
type TxDescription = { humanDescription?: string }
type HumanDescription = { humanDescription?: string }

interface TxEvents {
[TxEvent.SIGNED]: TxDescription & { txId?: string }
[TxEvent.SIGN_FAILED]: TxDescription & { txId?: string; error: Error }
[TxEvent.PROPOSE_FAILED]: TxDescription & { error: Error }
[TxEvent.PROPOSED]: TxDescription & { txId: string }
[TxEvent.SIGNATURE_PROPOSE_FAILED]: TxDescription & { txId: string; error: Error }
[TxEvent.SIGNATURE_PROPOSED]: TxDescription & { txId: string; signerAddress: string }
[TxEvent.SIGNATURE_INDEXED]: TxDescription & { txId: string }
[TxEvent.ONCHAIN_SIGNATURE_REQUESTED]: Id & TxDescription
[TxEvent.ONCHAIN_SIGNATURE_SUCCESS]: Id & TxDescription
[TxEvent.EXECUTING]: Id & TxDescription
[TxEvent.PROCESSING]: Id & TxDescription & { txHash: string }
[TxEvent.PROCESSING_MODULE]: Id & TxDescription & { txHash: string }
[TxEvent.PROCESSED]: Id & TxDescription & { safeAddress: string }
[TxEvent.REVERTED]: Id & TxDescription & { error: Error }
[TxEvent.RELAYING]: Id & TxDescription & { taskId: string }
[TxEvent.FAILED]: Id & TxDescription & { error: Error }
[TxEvent.SUCCESS]: Id & TxDescription
[TxEvent.SAFE_APPS_REQUEST]: TxDescription & { safeAppRequestId: RequestId; safeTxHash: string }
[TxEvent.BATCH_ADD]: Id & TxDescription
[TxEvent.SIGNED]: HumanDescription & { txId?: string }
[TxEvent.SIGN_FAILED]: HumanDescription & { txId?: string; error: Error }
[TxEvent.PROPOSE_FAILED]: HumanDescription & { error: Error }
[TxEvent.PROPOSED]: HumanDescription & { txId: string }
[TxEvent.SIGNATURE_PROPOSE_FAILED]: HumanDescription & { txId: string; error: Error }
[TxEvent.SIGNATURE_PROPOSED]: HumanDescription & { txId: string; signerAddress: string }
[TxEvent.SIGNATURE_INDEXED]: HumanDescription & { txId: string }
[TxEvent.ONCHAIN_SIGNATURE_REQUESTED]: Id & HumanDescription
[TxEvent.ONCHAIN_SIGNATURE_SUCCESS]: Id & HumanDescription
[TxEvent.EXECUTING]: Id & HumanDescription
[TxEvent.PROCESSING]: Id & HumanDescription & { txHash: string }
[TxEvent.PROCESSING_MODULE]: Id & HumanDescription & { txHash: string }
[TxEvent.PROCESSED]: Id & HumanDescription & { safeAddress: string }
[TxEvent.REVERTED]: Id & HumanDescription & { error: Error }
[TxEvent.RELAYING]: Id & HumanDescription & { taskId: string }
[TxEvent.FAILED]: Id & HumanDescription & { error: Error }
[TxEvent.SUCCESS]: Id & HumanDescription
[TxEvent.SAFE_APPS_REQUEST]: HumanDescription & { safeAppRequestId: RequestId; safeTxHash: string }
[TxEvent.BATCH_ADD]: Id & HumanDescription
}

const txEventBus = new EventBus<TxEvents>()
Expand Down

0 comments on commit 0154d0b

Please sign in to comment.