Skip to content

Commit 6109f49

Browse files
authored
Merge branch 'solana-labs:main' into main
2 parents afd34f5 + 658a927 commit 6109f49

File tree

12 files changed

+249
-179
lines changed

12 files changed

+249
-179
lines changed

components/Members/AddMember.tsx

Lines changed: 25 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,28 @@ import {
55
} from '@heroicons/react/outline'
66
import { ViewState } from './types'
77
import useMembersListStore from 'stores/useMembersListStore'
8-
import { PublicKey, TransactionInstruction } from '@solana/web3.js'
9-
import useRealm from '@hooks/useRealm'
10-
import Input from '@components/inputs/Input'
8+
import { PublicKey } from '@solana/web3.js'
9+
import useRealm from 'hooks/useRealm'
10+
import Input from 'components/inputs/Input'
1111
import Button, { SecondaryButton } from '@components/Button'
12-
import Textarea from '@components/inputs/Textarea'
12+
import Textarea from 'components/inputs/Textarea'
1313
import VoteBySwitch from 'pages/dao/[symbol]/proposal/components/VoteBySwitch'
14-
import {
15-
getMintMinAmountAsDecimal,
16-
parseMintNaturalAmountFromDecimal,
17-
} from '@tools/sdk/units'
18-
import { precision } from '@utils/formatting'
14+
import { getMintMinAmountAsDecimal } from '@tools/sdk/units'
15+
import { precision } from 'utils/formatting'
1916
import useWalletStore from 'stores/useWalletStore'
20-
import { getMintSchema } from '@utils/validations'
17+
import { getMintSchema } from 'utils/validations'
2118
import { useEffect, useState } from 'react'
22-
import { MintForm, UiInstruction } from '@utils/uiTypes/proposalCreationTypes'
23-
import { validateInstruction } from '@utils/instructionTools'
24-
import useGovernanceAssets from '@hooks/useGovernanceAssets'
25-
import {
26-
ASSOCIATED_TOKEN_PROGRAM_ID,
27-
Token,
28-
TOKEN_PROGRAM_ID,
29-
} from '@solana/spl-token'
30-
import {
31-
getInstructionDataFromBase64,
32-
serializeInstructionToBase64,
33-
} from '@models/serialisation'
34-
import { getATA } from '@utils/ataTools'
35-
import { RpcContext } from '@models/core/api'
36-
import { Governance } from '@models/accounts'
37-
import { ParsedAccount } from '@models/core/accounts'
19+
import { MintForm, UiInstruction } from 'utils/uiTypes/proposalCreationTypes'
20+
import useGovernanceAssets from 'hooks/useGovernanceAssets'
21+
import { getInstructionDataFromBase64 } from 'models/serialisation'
22+
import { RpcContext } from 'models/core/api'
23+
import { Governance } from 'models/accounts'
24+
import { ParsedAccount } from 'models/core/accounts'
3825
import { useRouter } from 'next/router'
3926
import { createProposal } from 'actions/createProposal'
40-
import { notify } from '@utils/notifications'
41-
import useQueryContext from '@hooks/useQueryContext'
27+
import { notify } from 'utils/notifications'
28+
import useQueryContext from 'hooks/useQueryContext'
29+
import { getMintInstruction } from 'utils/instructionTools'
4230

4331
interface AddMemberForm extends MintForm {
4432
description: string
@@ -110,57 +98,16 @@ const AddMember = () => {
11098
propertyName: 'amount',
11199
})
112100
}
113-
//TODO common getMintInstruction
114101
async function getInstruction(): Promise<UiInstruction> {
115-
const isValid = await validateInstruction({ schema, form, setFormErrors })
116-
let serializedInstruction = ''
117-
const prerequisiteInstructions: TransactionInstruction[] = []
118-
if (isValid && programId && form.mintAccount?.governance?.pubkey) {
119-
//this is the original owner
120-
const destinationAccount = new PublicKey(form.destinationAccount)
121-
const mintPK = form.mintAccount.governance.info.governedAccount
122-
const mintAmount = parseMintNaturalAmountFromDecimal(
123-
form.amount!,
124-
form.mintAccount.mintInfo?.decimals
125-
)
126-
//we find true receiver address if its wallet and we need to create ATA the ata address will be the receiver
127-
const { currentAddress: receiverAddress, needToCreateAta } = await getATA(
128-
connection,
129-
destinationAccount,
130-
mintPK,
131-
wallet!
132-
)
133-
//we push this createATA instruction to transactions to create right before creating proposal
134-
//we don't want to create ata only when instruction is serialized
135-
if (needToCreateAta) {
136-
prerequisiteInstructions.push(
137-
Token.createAssociatedTokenAccountInstruction(
138-
ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID
139-
TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID
140-
mintPK, // mint
141-
receiverAddress, // ata
142-
destinationAccount, // owner of token account
143-
wallet!.publicKey! // fee payer
144-
)
145-
)
146-
}
147-
const transferIx = Token.createMintToInstruction(
148-
TOKEN_PROGRAM_ID,
149-
form.mintAccount.governance.info.governedAccount,
150-
receiverAddress,
151-
form.mintAccount.governance!.pubkey,
152-
[],
153-
mintAmount
154-
)
155-
serializedInstruction = serializeInstructionToBase64(transferIx)
156-
}
157-
const obj: UiInstruction = {
158-
serializedInstruction,
159-
isValid,
160-
governance: form.mintAccount?.governance,
161-
prerequisiteInstructions: prerequisiteInstructions,
162-
}
163-
return obj
102+
return getMintInstruction({
103+
schema,
104+
form,
105+
programId,
106+
connection,
107+
wallet,
108+
governedMintInfoAccount: form.mintAccount,
109+
setFormErrors,
110+
})
164111
}
165112
//TODO common handle propose
166113
const handlePropose = async () => {

components/Members/MembersCompactWrapper.tsx

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ import MemberOverview from './MemberOverview'
88
import { PlusIcon } from '@heroicons/react/outline'
99
import AddMember from './AddMember'
1010
import useGovernanceAssets from '@hooks/useGovernanceAssets'
11+
import Tooltip from '@components/Tooltip'
1112

1213
const MembersCompactWrapper = () => {
13-
const { symbol, councilMint } = useRealm()
14+
const {
15+
symbol,
16+
councilMint,
17+
toManyCouncilOutstandingProposalsForUse,
18+
toManyCommunityOutstandingProposalsForUser,
19+
} = useRealm()
1420
const { members } = useMembers()
1521
const membersCount = members.length
1622
const { setCurrentCompactView, resetCompactViewState } = useMembersListStore()
17-
const { canUseMintInstruction } = useGovernanceAssets()
23+
const {
24+
canUseMintInstruction,
25+
canMintRealmCouncilToken,
26+
} = useGovernanceAssets()
1827
const currentView = useMembersListStore((s) => s.compact.currentView)
1928
const totalVotesCast = members.reduce((prev, current) => {
2029
const councilTotalVotes = current.council?.info.totalVotesCount || 0
@@ -31,23 +40,40 @@ const MembersCompactWrapper = () => {
3140
<>
3241
<h3 className="mb-4 flex items-center">
3342
Members ({membersCount})
34-
{councilMint && canUseMintInstruction && (
35-
<div
36-
onClick={goToAddMemberView}
37-
className={`bg-bkg-2 default-transition
43+
{councilMint &&
44+
canUseMintInstruction &&
45+
canMintRealmCouncilToken() && (
46+
<Tooltip
47+
contentClassName="ml-auto"
48+
content={
49+
toManyCouncilOutstandingProposalsForUse &&
50+
toManyCommunityOutstandingProposalsForUser
51+
? 'You have to many outstanding proposals'
52+
: ''
53+
}
54+
>
55+
<div
56+
onClick={goToAddMemberView}
57+
className={`bg-bkg-2 default-transition
3858
flex flex-col items-center justify-center
3959
rounded-lg hover:bg-bkg-3 ml-auto
40-
hover:cursor-pointer`}
41-
>
42-
<div
43-
className="bg-[rgba(255,255,255,0.06)] h-6 w-6 flex
60+
hover:cursor-pointer ${
61+
toManyCouncilOutstandingProposalsForUse &&
62+
toManyCommunityOutstandingProposalsForUser
63+
? 'opacity-60 pointer-events-none'
64+
: ''
65+
}`}
66+
>
67+
<div
68+
className="bg-[rgba(255,255,255,0.06)] h-6 w-6 flex
4469
font-bold items-center justify-center
4570
rounded-full text-fgd-3"
46-
>
47-
<PlusIcon />
48-
</div>
49-
</div>
50-
)}
71+
>
72+
<PlusIcon />
73+
</div>
74+
</div>
75+
</Tooltip>
76+
)}
5177
</h3>
5278
<div className="bg-bkg-1 mb-3 px-4 py-2 rounded-md w-full">
5379
<p className="text-fgd-3 text-xs">Total votes cast</p>

components/TokenBalanceCard.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ const TokenDeposit = ({
102102
proposals,
103103
governances,
104104
} = useRealm()
105-
106105
// Do not show deposits for mints with zero supply because nobody can deposit anyway
107106
if (!mint || mint.supply.isZero()) {
108107
return null

components/TreasuryAccount/AccountsCompactWrapper.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ const AccountsCompactWrapper = () => {
2020
const { resetCompactViewState } = useTreasuryAccountStore()
2121
const connected = useWalletStore((s) => s.connected)
2222
const { fmtUrlWithCluster } = useQueryContext()
23-
const { ownVoterWeight, symbol, realm } = useRealm()
23+
const {
24+
ownVoterWeight,
25+
symbol,
26+
realm,
27+
toManyCommunityOutstandingProposalsForUser,
28+
toManyCouncilOutstandingProposalsForUse,
29+
} = useRealm()
2430
const goToNewAccountForm = () => {
2531
router.push(fmtUrlWithCluster(`/dao/${symbol}${NEW_TREASURY_ROUTE}`))
2632
}
@@ -29,7 +35,10 @@ const AccountsCompactWrapper = () => {
2935
? ownVoterWeight.canCreateGovernance(realm)
3036
: null
3137
const isConnectedWithGovernanceCreationPermission =
32-
connected && canCreateGovernance
38+
connected &&
39+
canCreateGovernance &&
40+
!toManyCommunityOutstandingProposalsForUser &&
41+
!toManyCouncilOutstandingProposalsForUse
3342
const getCurrentView = () => {
3443
switch (currentView) {
3544
case ViewState.MainView:
@@ -45,6 +54,9 @@ const AccountsCompactWrapper = () => {
4554
? 'Connect your wallet to create new account'
4655
: !canCreateGovernance
4756
? "You don't have enough governance power to create a new treasury account"
57+
: toManyCommunityOutstandingProposalsForUser &&
58+
toManyCouncilOutstandingProposalsForUse
59+
? 'You have to many outstanding proposals'
4860
: ''
4961
}
5062
>

hooks/useGovernanceAssets.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ export default function useGovernanceAssets() {
3131
)
3232
)
3333
}
34+
const canMintRealmCommunityToken = () => {
35+
const governances = getGovernancesByAccountType(
36+
GovernanceAccountType.MintGovernance
37+
)
38+
return !!governances.find(
39+
(x) =>
40+
x.info.governedAccount.toBase58() ==
41+
realm?.info.communityMint.toBase58()
42+
)
43+
}
44+
const canMintRealmCouncilToken = () => {
45+
const governances = getGovernancesByAccountType(
46+
GovernanceAccountType.MintGovernance
47+
)
48+
49+
return !!governances.find(
50+
(x) =>
51+
x.info.governedAccount.toBase58() ==
52+
realm?.info.config.councilMint?.toBase58()
53+
)
54+
}
3455
// TODO: Check governedAccounts from all governances plus search for token accounts owned by governances
3556
const canUseTransferInstruction = canUseGovernanceForInstruction(
3657
GovernanceAccountType.TokenGovernance
@@ -176,5 +197,7 @@ export default function useGovernanceAssets() {
176197
getMintWithGovernances,
177198
canUseTransferInstruction,
178199
canUseMintInstruction,
200+
canMintRealmCommunityToken,
201+
canMintRealmCouncilToken,
179202
}
180203
}

hooks/useRealm.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ export default function useRealm() {
8383
realm.info.config.councilMint &&
8484
!councilMint?.supply.isZero()
8585

86+
//TODO take from realm config when available
87+
const realmCfgMaxOutstandingProposalCount = 10
88+
const toManyCommunityOutstandingProposalsForUser =
89+
ownTokenRecord &&
90+
ownTokenRecord?.info.outstandingProposalCount >=
91+
realmCfgMaxOutstandingProposalCount
92+
const toManyCouncilOutstandingProposalsForUse =
93+
ownCouncilTokenRecord &&
94+
ownCouncilTokenRecord?.info.outstandingProposalCount >=
95+
realmCfgMaxOutstandingProposalCount
96+
8697
return {
8798
realm,
8899
realmInfo,
@@ -103,5 +114,7 @@ export default function useRealm() {
103114
realmDisplayName: realmInfo?.displayName ?? realm?.info?.name,
104115
canChooseWhoVote,
105116
councilTokenOwnerRecords,
117+
toManyCouncilOutstandingProposalsForUse,
118+
toManyCommunityOutstandingProposalsForUser,
106119
}
107120
}

models/accounts.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,16 @@ export class TokenOwnerRecord {
342342

343343
governanceDelegate?: PublicKey
344344

345+
outstandingProposalCount: number
346+
345347
constructor(args: {
346348
realm: PublicKey
347349
governingTokenMint: PublicKey
348350
governingTokenOwner: PublicKey
349351
governingTokenDepositAmount: BN
350352
unrelinquishedVotesCount: number
351353
totalVotesCount: number
354+
outstandingProposalCount: number
352355
reserved: Uint8Array
353356
}) {
354357
this.realm = args.realm
@@ -358,6 +361,7 @@ export class TokenOwnerRecord {
358361
this.unrelinquishedVotesCount = args.unrelinquishedVotesCount
359362
this.totalVotesCount = args.totalVotesCount
360363
this.reserved = args.reserved
364+
this.outstandingProposalCount = args.outstandingProposalCount
361365
}
362366
}
363367

pages/dao/[symbol]/index.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import RealmHeader from 'components/RealmHeader'
1010
import { PublicKey } from '@solana/web3.js'
1111
import AccountsCompactWrapper from '@components/TreasuryAccount/AccountsCompactWrapper'
1212
import MembersCompactWrapper from '@components/Members/MembersCompactWrapper'
13+
import Tooltip from '@components/Tooltip'
1314

1415
const compareProposals = (p1: Proposal, p2: Proposal) => {
1516
const p1Rank = p1.getStateSortRank()
@@ -28,7 +29,13 @@ const compareProposals = (p1: Proposal, p2: Proposal) => {
2829
}
2930

3031
const REALM = () => {
31-
const { proposals, realmTokenAccount, ownTokenRecord } = useRealm()
32+
const {
33+
proposals,
34+
realmTokenAccount,
35+
ownTokenRecord,
36+
toManyCommunityOutstandingProposalsForUser,
37+
toManyCouncilOutstandingProposalsForUse,
38+
} = useRealm()
3239
const [filters, setFilters] = useState<ProposalState[]>([])
3340
const [displayedProposals, setDisplayedProposals] = useState(
3441
Object.entries(proposals)
@@ -84,7 +91,16 @@ const REALM = () => {
8491
<h4 className="text-fgd-2">{`${filteredProposals.length} proposals`}</h4>
8592
<div className="flex items-center">
8693
<div className="mr-4">
87-
<NewProposalBtn />
94+
<Tooltip
95+
content={
96+
toManyCommunityOutstandingProposalsForUser &&
97+
toManyCouncilOutstandingProposalsForUse
98+
? 'You have to many outstanding proposals'
99+
: ''
100+
}
101+
>
102+
<NewProposalBtn />
103+
</Tooltip>
88104
</div>
89105
<ProposalFilter filters={filters} setFilters={setFilters} />
90106
</div>

0 commit comments

Comments
 (0)