Skip to content

Commit

Permalink
Merge pull request #52 from TalismanSociety/refactor/disable-tx-execu…
Browse files Browse the repository at this point in the history
…tion-vault-insufficient-funds

[FEAT] - Disable tx execution if multisig doesn't have enough funds
  • Loading branch information
UrbanWill authored Jun 26, 2024
2 parents fc69175 + c5701a0 commit f89d2af
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { selectedAccountState } from '@domains/auth'
import { multisigDepositTotalSelector, tokenPriceState } from '@domains/chains'
import { accountsState } from '@domains/extension'
import { balancesState } from '@domains/balances'
import { Balance, Transaction, TransactionType, usePendingTransactions, useSelectedMultisig } from '@domains/multisig'
import {
Balance,
Transaction,
TransactionType,
usePendingTransactions,
useSelectedMultisig,
calcSumOutgoing,
calcVoteSum,
} from '@domains/multisig'
import { Skeleton } from '@talismn/ui'
import { balanceToFloat, formatUsd } from '@util/numbers'
import { cn } from '@util/tailwindcss'
Expand Down Expand Up @@ -38,6 +46,9 @@ export const SignerCta: React.FC<{
const extensionAccounts = useRecoilValue(accountsState)
const balances = useRecoilValue(balancesState)
const [asDraft, setAsDraft] = useState(false)
const [sumOutgoing] = useMemo(() => calcSumOutgoing(t), [t])
const voteSum = useMemo(() => calcVoteSum(t), [t])
const [multisig] = useSelectedMultisig()
const { transactions: pendingTransactions, loading: pendingLoading } = usePendingTransactions()
const feeTokenPrice = useRecoilValueLoadable(tokenPriceState(fee?.token))
const existentialDepositLoadable = useRecoilValueLoadable(
Expand Down Expand Up @@ -68,6 +79,38 @@ export const SignerCta: React.FC<{
return !Object.values(t.approvals).find(v => v === true)
}, [t])

const multisigHasEnoughBalance = useMemo(() => {
// No need to check for multisig balance if not going to execute the transaction
if (asDraft || !readyToExecute || existentialDepositLoadable.state === 'loading') return true

const availableBalance =
balances?.find(({ address, chainId, source }) => {
const parsedAddress = Address.fromSs58(address)
return (
parsedAddress &&
parsedAddress.isEqual(multisig.proxyAddress) &&
source === 'substrate-native' &&
chainId === t.multisig.chain.squidIds.chainData
)
}).sum.planck.transferable ?? 0n

const txTokensAmount = BigInt(
t.decoded?.type === TransactionType.Vote ? voteSum?.amount.toString() ?? 0 : sumOutgoing?.amount.toString() ?? 0
)

return availableBalance >= txTokensAmount
}, [
asDraft,
balances,
existentialDepositLoadable.state,
multisig.proxyAddress,
readyToExecute,
sumOutgoing?.amount,
t.decoded?.type,
t.multisig.chain.squidIds.chainData,
voteSum?.amount,
])

const connectedAccountHasEnoughBalance: boolean = useMemo(() => {
if (asDraft || existentialDepositLoadable.state === 'loading') return true

Expand Down Expand Up @@ -186,13 +229,21 @@ export const SignerCta: React.FC<{
if (!connectedAccountHasEnoughBalance) return 'Insufficient Balance'
// Return 'Execute' or 'Approve & Execute' based on approvals count and multisig threshold
if (readyToExecute) {
if (!multisigHasEnoughBalance) return 'Insufficient Multisig Balance'
if (approvalsCount >= t.multisig.threshold) return 'Execute'
return 'Approve & Execute'
}

// Default to 'Approve' if no other conditions are met
return 'Approve'
}, [approvalsCount, asDraft, connectedAccountHasEnoughBalance, readyToExecute, t.multisig.threshold])
}, [
approvalsCount,
asDraft,
connectedAccountHasEnoughBalance,
multisigHasEnoughBalance,
readyToExecute,
t.multisig.threshold,
])

// get fee and signable extrinsic
return (
Expand Down Expand Up @@ -253,6 +304,7 @@ export const SignerCta: React.FC<{
disabled={
missingExecutionCallData ||
!connectedAccountHasEnoughBalance ||
!multisigHasEnoughBalance ||
loading.any ||
(!asDraft && (!connectedAccountCanApprove || !canApproveAsChangeConfig || !fee))
}
Expand Down
25 changes: 25 additions & 0 deletions apps/multisig/src/domains/multisig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,31 @@ export const calcSumOutgoing = (t: Transaction): Balance[] => {
}, [])
}

export const calcVoteSum = (t: Transaction): Balance | null => {
if (t.decoded?.type !== TransactionType.Vote || !t.decoded.voteDetails) return null

const { convictionVote, details, token } = t.decoded.voteDetails
const { Standard, SplitAbstain } = details

let amount: BN

// TODO: Add support to Split votes
switch (convictionVote) {
case 'SplitAbstain':
amount = Object.values(SplitAbstain!).reduce((acc, balance) => acc.add(balance), new BN(0))
break
case 'Standard':
amount = Standard?.balance!
break
default:
// Handle removeVote
amount = new BN(0)
break
}

return { amount, token }
}

interface ChangeConfigCall {
section: 'utility'
method: 'batchAll'
Expand Down

0 comments on commit f89d2af

Please sign in to comment.