Skip to content

Commit

Permalink
CentrifugeJS: Fix transactions (#1622)
Browse files Browse the repository at this point in the history
  • Loading branch information
onnovisser authored Oct 6, 2023
1 parent 5c05021 commit e80d1e6
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 71 deletions.
24 changes: 4 additions & 20 deletions centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const useSignRemark = (
{
txHash: string
blockNumber: string
isEvmOnSubstrate?: boolean
chainId: Network
},
unknown
Expand All @@ -52,29 +51,13 @@ export const useSignRemark = (
const substrateMutation = useCentrifugeTransaction('Sign remark', (cent) => cent.remark.signRemark, {
onSuccess: async (_, result) => {
try {
let txHash: string
let blockNumber: string
// @ts-expect-error
if (isEvmOnSubstrate && result?.[0]?.wait) {
// @ts-expect-error
const evmResult = await result[0].wait()
txHash = evmResult?.transactionHash
blockNumber = evmResult?.blockNumber.toString()
} else {
txHash = result.txHash.toHex()
// @ts-expect-error
blockNumber = result.blockNumber.toString()
}
const chainId = connectedNetwork === 'centrifuge' ? await centrifuge.getChainId() : connectedNetwork

await sendDocumentsToIssuer({
txHash,
blockNumber,
isEvmOnSubstrate,
txHash: result.txHash,
blockNumber: result.blockNumber.toString(),
chainId: chainId || 136,
})
setIsSubstrateTxLoading(false)
} catch (e) {
} finally {
setIsSubstrateTxLoading(false)
}
},
Expand Down Expand Up @@ -151,6 +134,7 @@ export const useSignRemark = (
}
}
executePaymentInfo()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [centrifuge, selectedAccount])

const signEvmRemark = async (args: [message: string]) => {
Expand Down
3 changes: 2 additions & 1 deletion centrifuge-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@centrifuge/centrifuge-js",
"version": "0.5.0",
"version": "0.6.0",
"description": "",
"homepage": "https://github.com/centrifuge/apps/tree/main/centrifuge-js#readme",
"author": "",
Expand All @@ -18,6 +18,7 @@
"start": "run-p -l start:esbuild start:types",
"start:esbuild": "node ./esbuild watch",
"start:types": "tsc --watch --emitDeclarationOnly --outDir dist",
"prepare": "yarn build",
"build": "yarn clean && yarn build:esbuild && yarn build:types",
"build:esbuild": "node ./esbuild",
"build:types": "tsc --emitDeclarationOnly --outDir dist",
Expand Down
71 changes: 42 additions & 29 deletions centrifuge-js/src/CentrifugeBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
throwError,
} from 'rxjs'
import { fromFetch } from 'rxjs/fetch'
import { TransactionOptions } from './types'
import { TransactionErrorResult, TransactionOptions, TransactionResult } from './types'
import { computeMultisig, evmToSubstrateAddress, isSameAddress } from './utils'
import { CurrencyBalance } from './utils/BN'
import { getPolkadotApi } from './utils/web3'
Expand Down Expand Up @@ -293,7 +293,7 @@ export class CentrifugeBase {

try {
const $paymentInfo = submittable.paymentInfo(signingAddress)
const $balances = api.query.system.account(signingAddress)
const $balances = api.query.system.account(this.getSignerAddress())

if (options?.paymentInfo) {
return $paymentInfo.pipe(
Expand Down Expand Up @@ -334,11 +334,7 @@ export class CentrifugeBase {
switchMap(() => actualSubmittable.signAndSend(signingAddress, { signer, era: options?.era }))
)
).pipe(
tap((result) => {
options?.onStatusChange?.(result)
if (result.status.isInBlock) this.getTxCompletedEvents().next(result.events)
}),
takeWhile((result) => {
map((result) => {
const errors = result.events.filter(({ event }) => {
const possibleProxyErr = event.data[0]?.toHuman()
return (
Expand All @@ -352,9 +348,21 @@ export class CentrifugeBase {
if (errors.length && this.config.debug) {
console.log('🚨 error', JSON.stringify(errors))
}
const hasError = !!(result.dispatchError || errors.length)

return !result.status.isInBlock && !hasError
return {
data: result,
events: result.events,
status: result.status.type,
error: result.dispatchError || errors[0],
txHash: result.txHash.toHuman() as string,
blockNumber: (result as any).blockNumber ? Number((result as any).blockNumber?.toString()) : undefined,
}
}),
tap((result) => {
options?.onStatusChange?.(result)
if (result.status === 'InBlock') this.getTxCompletedEvents().next(result.events)
}),
takeWhile((result) => {
return result.status !== 'InBlock' && !result.error
}, true)
)
} catch (e) {
Expand Down Expand Up @@ -387,26 +395,31 @@ export class CentrifugeBase {
return from(response.wait()).pipe(
map((receipt) => [response, receipt] as const),
startWith([response, null] as const),
catchError(() => of([{ ...response, error: new Error('failed') }] as const)),
tap(([result, receipt]) => {
if ('error' in result || receipt?.status === 0) {
options?.onStatusChange?.({
events: [],
dispatchError: {},
status: {
hash: { toHex: () => result.hash as any },
},
} as any)
} else {
options?.onStatusChange?.({
events: [],
status: {
isInBlock: receipt?.status === 1,
isFinalized: receipt?.status === 1,
hash: { toHex: () => result.hash as any },
},
} as any)
map(([response, receipt]) => {
const result: TransactionResult = {
data: { response, receipt: receipt ?? undefined },
// TODO: Events
events: [],
status: receipt ? 'InBlock' : 'Broadcast',
error: receipt?.status === 0 ? new Error('failed') : undefined,
txHash: response.hash,
blockNumber: receipt?.blockNumber,
}
return result
}),
catchError(() => {
const result: TransactionErrorResult = {
data: { response, receipt: undefined },
events: [],
status: 'Invalid',
error: new Error('failed'),
txHash: response.hash,
blockNumber: undefined,
}
return of(result)
}),
tap((result) => {
options?.onStatusChange?.(result)
})
)
})
Expand Down
25 changes: 24 additions & 1 deletion centrifuge-js/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
import {
TransactionReceipt as EvmTransactionReceipt,
TransactionResponse as EvmTransactionResponse,
} from '@ethersproject/providers'
import { AddressOrPair } from '@polkadot/api/types'
import { ISubmittableResult } from '@polkadot/types/types'
import { HexString } from '@polkadot/util/types'
import BN from 'bn.js'
import { Config } from '../CentrifugeBase'

type EvmTransactionResult = {
response: EvmTransactionResponse
receipt?: EvmTransactionReceipt
}

type TransactionResultBase = {
data: ISubmittableResult | EvmTransactionResult
events: ISubmittableResult['events']
status: ISubmittableResult['status']['type']
txHash: string
blockNumber?: number
error: any
}

export type TransactionPendingResult = TransactionResultBase & { error: null; blockNumber: undefined }
export type TransactionSuccessResult = TransactionResultBase & { error: null; blockNumber: number }
export type TransactionErrorResult = TransactionResultBase & { error: any; blockNumber: undefined }
export type TransactionResult = TransactionPendingResult | TransactionSuccessResult | TransactionErrorResult

export type TransactionOptions = {
batch?: boolean
signOnly?: boolean
sendOnly?: boolean
era?: number
paymentInfo?: AddressOrPair
onStatusChange?: (result: ISubmittableResult) => void
onStatusChange?: (result: TransactionResult) => void
createType?: 'immediate' | 'propose' | 'notePreimage'
dryRun?: boolean
proxies?: Config['proxies']
Expand Down
18 changes: 18 additions & 0 deletions centrifuge-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## 0.2.0 (2023-10-03)


### ⚠ BREAKING CHANGES

* **centrifuge-js:** Centrifuge->getChainId() now returns a Promise

### Features

* implement global onboarding status endpoint ([#1364](https://github.com/centrifuge/apps/issues/1364)) ([b64db29](https://github.com/centrifuge/apps/commit/b64db2904ad670aa012bb9a69396bf28ab31d5d7))


### Bug Fixes

* autoconnect wallet when visiting through gnosis safe app ([#1375](https://github.com/centrifuge/apps/issues/1375)) ([0991b30](https://github.com/centrifuge/apps/commit/0991b30ee0ffead4b1799496f49a7401c6371a14))
* **centrifuge-js:** Get chain ID from chain ([#1535](https://github.com/centrifuge/apps/issues/1535)) ([e36462f](https://github.com/centrifuge/apps/commit/e36462fcc58589830f9fb9a68e0ba11302b1eb6e))
* **centrifuge-react:** Add useBalances hook ([#1092](https://github.com/centrifuge/apps/issues/1092)) ([2eef4e1](https://github.com/centrifuge/apps/commit/2eef4e133cac560f62b6db0023b23875563e03cf))

### [0.1.1](https://github.com/centrifuge/apps/compare/centrifuge-react/v0.1.0...centrifuge-react/v0.1.1) (2023-09-28)


Expand Down
5 changes: 3 additions & 2 deletions centrifuge-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@centrifuge/centrifuge-react",
"version": "0.1.1",
"version": "0.2.0",
"description": "Centrifuge React Library",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
Expand All @@ -9,7 +9,8 @@
],
"scripts": {
"build": "tsc",
"prepare": "yarn build",
"build:deps": "cd ../centrifuge-js && yarn build && cd ../fabric && yarn build && cd ../centrifuge-react",
"prepare": "yarn build:deps && yarn build",
"bump": "standard-version --sign",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
Expand Down
46 changes: 28 additions & 18 deletions centrifuge-react/src/hooks/useCentrifugeTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Centrifuge, { TransactionOptions } from '@centrifuge/centrifuge-js'
import Centrifuge, { TransactionOptions, TransactionSuccessResult } from '@centrifuge/centrifuge-js'
import { ISubmittableResult } from '@polkadot/types/types'
import * as React from 'react'
import { lastValueFrom, Observable } from 'rxjs'
Expand All @@ -15,7 +15,7 @@ export type CentrifugeTransactionOptions = Pick<TransactionOptions, 'createType'
export function useCentrifugeTransaction<T extends Array<any>>(
title: string,
transactionCallback: (centrifuge: Centrifuge) => (args: T, options?: TransactionOptions) => Observable<any>,
options: { onSuccess?: (args: T, result: ISubmittableResult) => void; onError?: (error: any) => void } = {}
options: { onSuccess?: (args: T, result: TransactionSuccessResult) => void; onError?: (error: any) => void } = {}
) {
const { addOrUpdateTransaction, updateTransaction } = useTransactions()
const { showWallets, substrate, walletDialog, evm, isEvmOnSubstrate } = useWallet()
Expand Down Expand Up @@ -78,22 +78,28 @@ export function useCentrifugeTransaction<T extends Array<any>>(
})
let errorObject: any

if (result.dispatchError || errors.length) {
const { data } = result

if (result.error) {
txError = result.error
let errorMessage = 'Transaction failed'
txError = result.dispatchError || errors[0]
if (errors.length) {
const error = errors[0].event.data[0] as any
if (error.isModule) {
// for module errors, we have the section indexed, lookup
const decoded = api.registry.findMetaError(error.asModule)
const { section, method, docs } = decoded
errorObject = new PalletError(section, method)
errorMessage = errorObject.message || errorMessage
console.error(`${section}.${method}: ${docs.join(' ')}`)
} else {
// Other, CannotLookup, BadOrigin, no extra info
console.error(error.toString())
if (isSubstrateResult(data)) {
if (errors.length) {
const error = errors[0].event.data[0] as any
if (error.isModule) {
// for module errors, we have the section indexed, lookup
const decoded = api.registry.findMetaError(error.asModule)
const { section, method, docs } = decoded
errorObject = new PalletError(section, method)
errorMessage = errorObject.message || errorMessage
console.error(`${section}.${method}: ${docs.join(' ')}`)
} else {
// Other, CannotLookup, BadOrigin, no extra info
console.error(error.toString())
}
}
} else {
console.error(result.error)
}

updateTransaction(id, (prev) => ({
Expand All @@ -102,14 +108,14 @@ export function useCentrifugeTransaction<T extends Array<any>>(
error: errorObject,
dismissed: prev.status === 'failed' && prev.dismissed,
}))
} else if (result.status.isInBlock || result.status.isFinalized) {
} else if (['InBlock', 'Finalized'].includes(result.status)) {
updateTransaction(id, (prev) =>
prev.status === 'failed'
? {}
: { status: 'succeeded', dismissed: prev.status === 'succeeded' && prev.dismissed }
)
} else {
updateTransaction(id, { status: 'pending', hash: result.status.hash.toHex() })
updateTransaction(id, { status: 'pending', hash: result.txHash })
}
},
})
Expand Down Expand Up @@ -173,3 +179,7 @@ export function useCentrifugeTransaction<T extends Array<any>>(
: false,
}
}

function isSubstrateResult(data: any): data is ISubmittableResult {
return 'toHuman' in data
}
25 changes: 25 additions & 0 deletions simulation-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"ts-node": {
"compilerOptions": {
"module": "commonjs"
}
},
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src", "**/*.d.ts", ".eslintrc.js", "functions"]
}

0 comments on commit e80d1e6

Please sign in to comment.