Skip to content

Commit 8adbfbf

Browse files
feat: sui transaction parsing (#11451)
1 parent af440d6 commit 8adbfbf

File tree

1 file changed

+103
-5
lines changed

1 file changed

+103
-5
lines changed

packages/chain-adapters/src/sui/SuiChainAdapter.ts

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import type { SuiTransactionBlockResponse } from '@mysten/sui/client'
12
import { SuiClient } from '@mysten/sui/client'
2-
import { Transaction } from '@mysten/sui/transactions'
3+
import { Transaction as SuiTransaction } from '@mysten/sui/transactions'
34
import type { AssetId, ChainId } from '@shapeshiftoss/caip'
45
import {
56
ASSET_NAMESPACE,
@@ -12,6 +13,7 @@ import type { HDWallet, SuiWallet } from '@shapeshiftoss/hdwallet-core'
1213
import { supportsSui } from '@shapeshiftoss/hdwallet-core'
1314
import type { Bip44Params, RootBip44Params } from '@shapeshiftoss/types'
1415
import { KnownChainIds } from '@shapeshiftoss/types'
16+
import { TransferType, TxStatus } from '@shapeshiftoss/unchained-client'
1517

1618
import type { ChainAdapter as IChainAdapter } from '../api'
1719
import { ChainAdapterError, ErrorHandler } from '../error/ErrorHandler'
@@ -27,6 +29,7 @@ import type {
2729
SignAndBroadcastTransactionInput,
2830
SignTx,
2931
SignTxInput,
32+
Transaction,
3033
ValidAddressResult,
3134
} from '../types'
3235
import { ChainAdapterDisplayName, ValidAddressResultType } from '../types'
@@ -244,7 +247,7 @@ export class ChainAdapter implements IChainAdapter<KnownChainIds.SuiMainnet> {
244247
const { from, accountNumber, to, value, chainSpecific } = input
245248
const { tokenId, gasBudget, gasPrice } = chainSpecific
246249

247-
const tx = new Transaction()
250+
const tx = new SuiTransaction()
248251

249252
tx.setSender(from)
250253

@@ -445,7 +448,7 @@ export class ChainAdapter implements IChainAdapter<KnownChainIds.SuiMainnet> {
445448

446449
const gasPrice = await this.client.getReferenceGasPrice()
447450

448-
const tx = new Transaction()
451+
const tx = new SuiTransaction()
449452

450453
tx.setSender(from)
451454

@@ -518,7 +521,102 @@ export class ChainAdapter implements IChainAdapter<KnownChainIds.SuiMainnet> {
518521
return
519522
}
520523

521-
parseTx(): Promise<never> {
522-
throw new Error('SUI transaction parsing not yet implemented')
524+
async parseTx(txHashOrTx: unknown, pubkey: string): Promise<Transaction> {
525+
try {
526+
// Fetch full transaction data if only txHash was provided
527+
const tx =
528+
typeof txHashOrTx === 'string'
529+
? await this.client.getTransactionBlock({
530+
digest: txHashOrTx,
531+
options: {
532+
showInput: true,
533+
showEffects: true,
534+
showBalanceChanges: true,
535+
},
536+
})
537+
: (txHashOrTx as SuiTransactionBlockResponse)
538+
539+
const sender = tx.transaction?.data.sender ?? ''
540+
541+
const txid = tx.digest
542+
const blockHeight = Number(tx.checkpoint ?? 0)
543+
const blockTime = tx.timestampMs ? Math.floor(Number(tx.timestampMs) / 1000) : 0
544+
545+
const latestCheckpoint = await this.client.getLatestCheckpointSequenceNumber()
546+
const confirmations = tx.checkpoint ? Number(latestCheckpoint) - Number(tx.checkpoint) + 1 : 0
547+
548+
const status =
549+
tx.effects?.status.status === 'success'
550+
? TxStatus.Confirmed
551+
: tx.effects?.status.status === 'failure'
552+
? TxStatus.Failed
553+
: TxStatus.Unknown
554+
555+
const gasUsed = tx.effects?.gasUsed
556+
const fee = gasUsed
557+
? {
558+
assetId: this.assetId,
559+
value: (
560+
BigInt(gasUsed.computationCost) +
561+
BigInt(gasUsed.storageCost) -
562+
BigInt(gasUsed.storageRebate)
563+
).toString(),
564+
}
565+
: undefined
566+
567+
const balanceChanges = tx.balanceChanges ?? []
568+
569+
const transfers = balanceChanges.map(change => {
570+
let ownerAddress: string | null = null
571+
if (typeof change.owner === 'object' && 'AddressOwner' in change.owner) {
572+
ownerAddress = change.owner.AddressOwner
573+
}
574+
575+
const assetId =
576+
change.coinType === '0x2::sui::SUI'
577+
? this.assetId
578+
: toAssetId({
579+
chainId: this.chainId,
580+
assetNamespace: ASSET_NAMESPACE.suiCoin,
581+
assetReference: change.coinType,
582+
})
583+
584+
const amount = BigInt(change.amount)
585+
const isReceive = amount > 0n
586+
const isSend = amount < 0n
587+
588+
const transferType =
589+
ownerAddress === pubkey
590+
? isReceive
591+
? TransferType.Receive
592+
: TransferType.Send
593+
: TransferType.Contract
594+
595+
return {
596+
assetId,
597+
from: isSend ? [sender] : ownerAddress ? [ownerAddress] : [sender],
598+
to: isReceive ? [ownerAddress ?? sender] : [sender],
599+
type: transferType,
600+
value: amount < 0n ? (-amount).toString() : amount.toString(),
601+
}
602+
})
603+
604+
return {
605+
txid,
606+
blockHeight,
607+
blockTime,
608+
blockHash: undefined,
609+
chainId: this.chainId,
610+
confirmations,
611+
status,
612+
fee,
613+
transfers,
614+
pubkey,
615+
}
616+
} catch (err) {
617+
return ErrorHandler(err, {
618+
translation: 'chainAdapters.errors.parseTx',
619+
})
620+
}
523621
}
524622
}

0 commit comments

Comments
 (0)