Skip to content

Commit

Permalink
error handling for transaction submission
Browse files Browse the repository at this point in the history
  • Loading branch information
Piefayth committed Mar 29, 2024
1 parent 9b33839 commit b780606
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 51 deletions.
23 changes: 20 additions & 3 deletions src/features/management/transactSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export type TransactState = {
addSpendError: string | undefined
} & Transaction

export type TransactionSubmissionState = "idle" | "building" | "submitting" | "submitted" | "completed" | "failed"

export type Transaction = {
spends: Spend[]
mints: Mint[]
Expand All @@ -39,6 +41,8 @@ export type Transaction = {
validity: ValidityInterval
metadataFilename: string
transactionHistory: Transaction[]
transactionSubmissionState: TransactionSubmissionState
transactionSubmissionError: string | null
}

const initialState: TransactState = {
Expand All @@ -52,7 +56,9 @@ const initialState: TransactState = {
to: ''
},
metadataFilename: 'None',
transactionHistory: []
transactionHistory: [],
transactionSubmissionState: 'idle',
transactionSubmissionError: null,
}


Expand Down Expand Up @@ -91,7 +97,7 @@ const transactSlice = createSlice({
setAddSpendError(state, action: PayloadAction<string>) {
state.addSpendError = action.payload
},
onTransactionSubmission(state, action: PayloadAction<Transaction>) {
onSuccessfulTransaction(state, action: PayloadAction<Transaction>) {
state.transactionHistory.push(action.payload)
state.mints = []
state.spends = []
Expand All @@ -100,6 +106,15 @@ const transactSlice = createSlice({
state.validity.from = ''
state.validity.to = ''
state.metadataFilename = 'None'
state.transactionSubmissionState = 'completed'
state.transactionSubmissionError = null
},
setTransactionSubmissionState(state, action: PayloadAction<TransactionSubmissionState>) {
state.transactionSubmissionState = action.payload
},
setTransactionSubmissionError(state, action: PayloadAction<string>) {
state.transactionSubmissionError = action.payload
state.transactionSubmissionState = 'failed'
},
clearAddSpendError(state) {
state.addSpendError = undefined
Expand All @@ -119,6 +134,8 @@ export const {
setExtraSigners,
setValidityInterval,
setMetadata,
onTransactionSubmission
onSuccessfulTransaction,
setTransactionSubmissionState,
setTransactionSubmissionError
} = transactSlice.actions
export default transactSlice.reducer
121 changes: 74 additions & 47 deletions src/panels/management/transact/Submit.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useDispatch, useSelector } from "react-redux"
import { RootState } from "../../../app/store"
import { Mint, Payment, Spend, TransactState, onTransactionSubmission } from "../../../features/management/transactSlice"
import { Mint, Payment, Spend, TransactState, onSuccessfulTransaction, setTransactionSubmissionError, setTransactionSubmissionState } from "../../../features/management/transactSlice"
import { File } from '../../../features/files/filesSlice'
import { SerializableAssets, deserializeAssets, deserializeUtxos, serializeAssets } from "../../../util/utxo"
import { Data, Emulator, Lucid } from "lucid-cardano"
import { Data, Emulator, Lucid, TxSigned } from "lucid-cardano"
import { useLucid } from "../../../components/LucidProvider"
import { Contract, Wallet } from "../../../features/management/managementSlice"
import { constructObject } from "../../../util/data"
Expand Down Expand Up @@ -48,13 +48,42 @@ function Submit() {
}

const wasThereAnyFeedback = feedbackComponents.length > 0
if (!wasThereAnyFeedback) {
if (!wasThereAnyFeedback && ['building', 'failed', 'idle', 'completed'].includes(transactState.transactionSubmissionState)) {
feedbackComponents.push(
<div key='readyToSubmit'>
✔️ Ready to submit.
</div>
)
}

if (transactState.transactionSubmissionState === 'submitting') {
feedbackComponents.push(
<div key='submitting'>
🔧 Building transaction...
</div>
)
} else if (transactState.transactionSubmissionState === 'submitted') {
feedbackComponents.push(
<div key='submitted'>
⟳ Awaiting confirmation...
</div>
)
} else if (transactState.transactionSubmissionState === 'completed') {
feedbackComponents.push(
<div key='completed'>
✔️ Last transaction was successful!
</div>
)
} else if (transactState.transactionSubmissionState === 'failed') {
feedbackComponents.push(
<div key='failed'>
❌ Transaction failed!
<p>
{ transactState.transactionSubmissionError }
</p>
</div>
)
}

return (
<div className='transact-submit'>
Expand All @@ -69,22 +98,25 @@ function Submit() {
disabled={wasThereAnyFeedback}
className={`button submit-transaction-button ${wasThereAnyFeedback ? 'disabled' : ''}`}
onClick={() => {
submit(lucid, transactState, files, contracts, wallets)
.then((txhash) => {
dispatch(setTransactionSubmissionState('building'))
buildTransaction(lucid, transactState, files, contracts, wallets)
.then((signedTransaction) => {
dispatch(setTransactionSubmissionState('submitting'))
return signedTransaction.submit()
})
.then((txHash) => {
dispatch(setTransactionSubmissionState('submitted'))
if (lucid.network === 'Custom') {
(lucid.provider as Emulator).awaitTx(txhash)
(lucid.provider as Emulator).awaitTx(txHash)
} else {
return Promise.resolve()
}
})
.then(() => {
dispatch(onTransactionSubmission(transactState))
console.log("tx success")
dispatch(onSuccessfulTransaction(transactState))
})
.catch((e) => {
// handle error
console.log(e)
console.log("tx failure")
dispatch(setTransactionSubmissionError(e.message))
})
}}
>Submit Transaction</button>
Expand All @@ -95,7 +127,7 @@ function Submit() {
}

// TODO: all the sad paths in here need to become user facing errors
async function submit(lucid: Lucid, transactState: TransactState, files: File[], contracts: Contract[], wallets: Wallet[]) {
async function buildTransaction(lucid: Lucid, transactState: TransactState, files: File[], contracts: Contract[], wallets: Wallet[]): Promise<TxSigned> {
const { mints, spends, payments, extraSigners, validity, metadataFilename } = transactState

const tx = lucid.newTx()
Expand All @@ -105,16 +137,14 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
const doesSpendHaveRedeemer = spend.redeemerFileName && spend.redeemerFileName !== 'None'

if (doesSpendHaveRedeemer && !redeemerFile) {
console.error(`Could not find ${spend.redeemerFileName} to build redeemer`)
throw Error("todo real error")
throw Error(`Could not find ${spend.redeemerFileName} to build redeemer`)
}

if (spend.source === 'contract') {
const spendFromAddress = spend.utxos[0].address
const contract = contracts.find(contract => contract.address === spendFromAddress)
if (!contract) {
console.error(`Could not find script address ${spendFromAddress} in contracts.`)
throw Error("todo real error")
throw Error(`Could not find script address ${spendFromAddress} in contracts.`)
}

tx.attachSpendingValidator(contract.script)
Expand All @@ -125,9 +155,12 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
try {
const redeemerJson = JSON.parse(redeemerFile.content)
redeemer = constructObject(redeemerJson)
} catch (e) {
console.error(e)
throw Error("todo real error")
} catch (e: any) {
if (e.message && e.message.includes('JSON.parse')) {
throw Error(`Invalid JSON in ${redeemerFile.name}`)
} else {
throw Error(`JSON in ${redeemerFile.name} cannot be converted to Data`)
}
}
}

Expand All @@ -143,14 +176,12 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
const doesMintHaveRedeemer = mint.redeemerFileName && mint.redeemerFileName !== 'None'

if (doesMintHaveRedeemer && !redeemerFile) {
console.error(`Could not find ${mint.redeemerFileName} to build redeemer`)
throw Error("todo real error")
throw Error(`Could not find ${mint.redeemerFileName} to build redeemer`)
}

const contract = contracts.find(contract => contract.scriptHash === mint.policyId)
if (!contract) {
console.error(`Could not find script address ${mint.policyId} in contracts`)
throw Error("todo real error")
throw Error(`Could not find script address ${mint.policyId} in contracts`)
}

tx.attachMintingPolicy(contract.script)
Expand All @@ -162,9 +193,12 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
redeemer = constructObject(redeemerJson)

console.log(redeemer)
} catch (e) {
console.error(e)
throw Error("todo real error")
} catch (e: any) {
if (e.message && e.message.includes('JSON.parse')) {
throw Error(`Invalid JSON in ${redeemerFile.name}`)
} else {
throw Error(`JSON in ${redeemerFile.name} cannot be converted to Data`)
}
}
}

Expand All @@ -180,18 +214,20 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
const doesPaymentHaveDatum = payment.datumFileName && payment.datumFileName !== 'None'

if (doesPaymentHaveDatum && !datumFile) {
console.error(`Could not find ${payment.datumFileName} to build datum`)
throw Error("todo real error")
throw Error(`Could not find ${payment.datumFileName} to build datum`)
}

let datum
if (doesPaymentHaveDatum && datumFile) {
try {
const datumJson = JSON.parse(datumFile.content)
datum = constructObject(datumJson)
} catch(e) {
console.error(e)
throw Error("todo real error")
} catch(e: any) {
if (e.message && e.message.includes('JSON.parse')) {
throw Error(`Invalid JSON in ${datumFile.name}`)
} else {
throw Error(`JSON in ${datumFile.name} cannot be converted to Data`)
}
}
}

Expand Down Expand Up @@ -219,25 +255,22 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
const metadataFile = files.find(file => file.name === metadataFilename)

if (!metadataFile) {
console.error(`Could not find metadata file ${metadataFilename}`)
throw Error("todo real error")
throw Error(`Could not find metadata file ${metadataFilename}`)
}

let metadata
try {
metadata = JSON.parse(metadataFile.content)
} catch (e) {
console.error(e)
throw Error("todo real error")
throw Error(`Invalid JSON in metadata file ${metadata}`)
}

for (const maybeLabel of Object.keys(metadata)) {
try {
const labelNumber = parseInt(maybeLabel)
tx.attachMetadata(labelNumber, metadata[maybeLabel])
} catch (e) {
console.error(`Expected numeric label key in metadata, instead received key ${maybeLabel}`)
throw Error("todo real error")
throw Error(`Expected numeric label key in metadata, instead received key ${maybeLabel}`)
}
}
}
Expand All @@ -250,14 +283,13 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],

for (const signerAddress of extraSigners) {
if (walletSpendAddresses.includes(signerAddress)) {
continue
continue // wallet will sign this in the next loop
}

const signerWallet = wallets.find(wallet => wallet.address === signerAddress)

if (!signerWallet) {
console.error(`Could not find wallet for address ${signerAddress}`)
throw Error("todo real error")
throw Error(`Could not find wallet for address ${signerAddress}`)
}

lucid.selectWalletFromSeed(signerWallet.seed)
Expand All @@ -268,8 +300,7 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],
const spendingWallet = wallets.find(wallet => wallet.address === walletSpendAddress)

if (!spendingWallet) {
console.error(`Could not find wallet for adress ${walletSpendAddress}`)
throw Error("todo real error")
throw Error(`Could not find wallet for adress ${walletSpendAddress}`)
}

lucid.selectWalletFromSeed(spendingWallet.seed)
Expand All @@ -278,11 +309,7 @@ async function submit(lucid: Lucid, transactState: TransactState, files: File[],

const signedTx = await unsignedTx.complete()

const submitted = signedTx.submit()

await submitted

return signedTx.toHash()
return signedTx
}

function canAffordTotal(mints: Mint[], spends: Spend[], payments: Payment[]) {
Expand Down
2 changes: 1 addition & 1 deletion src/panels/management/transact/UtxoSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ function UtxoSelector() {
>
<option value='wallet'>Wallet</option>
<option value='contract'>Contract</option>
<option value='custom'>Custom</option>
{/* <option value='custom'>Custom</option> */}
</select>
</div>

Expand Down

0 comments on commit b780606

Please sign in to comment.