Skip to content

Commit

Permalink
Realm wizard creator (solana-labs#165)
Browse files Browse the repository at this point in the history
* feat: realm-wizard creator

* chore: removed unecessary code

* fix: mismatch arguments

* fix: removed unused variabled

* chore: removed .lock files

* feat: readded yarn.lock

* fix: yarn lock file

* updated gitignore

* chore: use default instance in examples

* fix: remove unnecessary signers from  councilMembersSignersChunks

* chore: remove unused var

* chore: remove unused mint functions

* chore: remove unused oyster artefacts

* chore: rename create-realm-validator

* fix: Update realm creation options description

* chore: update realm description text

Co-authored-by: Sebastian.Bor <[email protected]>
  • Loading branch information
Mury12 and SebastianBor authored Dec 22, 2021
1 parent 110f0c2 commit f870cc7
Show file tree
Hide file tree
Showing 31 changed files with 1,907 additions and 419 deletions.
4 changes: 3 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Run in context of one realm: starts with default realm and disables realms page navigation
# REALM=MNGO
MAINNET_RPC=https://mango.rpcpool.com
DEVNET_RPC=https://mango.devnet.rpcpool.com
DEVNET_RPC=https://mango.devnet.rpcpool.com

DEFAULT_GOVERNANCE_PROGRAM_ID=GTesTBiEWE32WHXXE2S4XbZvA5CrEc4xs6ZgRe895dP
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
# misc
.DS_Store
*.pem
.env

# debug
npm-debug.log*
Expand Down
231 changes: 231 additions & 0 deletions actions/createMultisigRealm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import {
GovernanceConfig,
MintMaxVoteWeightSource,
VoteThresholdPercentage,
VoteWeightSource,
} from '@models/accounts'
import { ProgramVersion } from '@models/registry/constants'
import { withCreateMintGovernance } from '@models/withCreateMintGovernance'
import { withCreateRealm } from '@models/withCreateRealm'
import { withDepositGoverningTokens } from '@models/withDepositGoverningTokens'
import { withSetRealmAuthority } from '@models/withSetRealmAuthority'
import { BN } from '@project-serum/anchor'
import {
Connection,
Keypair,
PublicKey,
TransactionInstruction,
} from '@solana/web3.js'

import { withCreateAssociatedTokenAccount } from '@tools/sdk/splToken/withCreateAssociatedTokenAccount'
import { withCreateMint } from '@tools/sdk/splToken/withCreateMint'
import { withMintTo } from '@tools/sdk/splToken/withMintTo'
import {
getMintNaturalAmountFromDecimal,
getTimestampFromDays,
} from '@tools/sdk/units'
import {
getWalletPublicKey,
sendTransactions,
SequenceType,
WalletSigner,
} from 'utils/sendTransactions'
import { chunks } from '@utils/helpers'

/// Creates multisig realm with community mint with 0 supply
/// and council mint used as multisig token
export const createMultisigRealm = async (
connection: Connection,
programId: PublicKey,
programVersion: ProgramVersion,

name: string,
yesVoteThreshold: number,
councilWalletPks: PublicKey[],

wallet: WalletSigner
) => {
const walletPk = getWalletPublicKey(wallet)

const mintsSetupInstructions: TransactionInstruction[] = []
const councilMembersInstructions: TransactionInstruction[] = []

const mintsSetupSigners: Keypair[] = []

// Default to 100% supply
const communityMintMaxVoteWeightSource =
MintMaxVoteWeightSource.FULL_SUPPLY_FRACTION

// The community mint is going to have 0 supply and we arbitrarily set it to 1m
const minCommunityTokensToCreate = 1000000

// Community mint decimals
const communityMintDecimals = 6

// Create community mint
const communityMintPk = await withCreateMint(
connection,
mintsSetupInstructions,
mintsSetupSigners,
walletPk,
null,
communityMintDecimals,
walletPk
)

// Create council mint
const councilMintPk = await withCreateMint(
connection,
mintsSetupInstructions,
mintsSetupSigners,
walletPk,
null,
0,
walletPk
)

let walletAtaPk: PublicKey | undefined

for (const teamWalletPk of councilWalletPks) {
const ataPk = await withCreateAssociatedTokenAccount(
councilMembersInstructions,
councilMintPk,
teamWalletPk,
walletPk
)
// Mint 1 council token to each team member
await withMintTo(
councilMembersInstructions,
councilMintPk,
ataPk,
walletPk,
1
)

if (teamWalletPk.equals(walletPk)) {
walletAtaPk = ataPk
}
}

// Create realm
const realmInstructions: TransactionInstruction[] = []
const realmSigners: Keypair[] = []

// Convert to mint natural amount
const minCommunityTokensToCreateAsMintValue = new BN(
getMintNaturalAmountFromDecimal(
minCommunityTokensToCreate,
communityMintDecimals
)
)

const realmPk = await withCreateRealm(
realmInstructions,
programId,
programVersion,
name,
walletPk,
communityMintPk,
walletPk,
councilMintPk,
communityMintMaxVoteWeightSource,
minCommunityTokensToCreateAsMintValue,
undefined
)

let tokenOwnerRecordPk: PublicKey

// If the current wallet is in the team then deposit the council token
if (walletAtaPk) {
tokenOwnerRecordPk = await withDepositGoverningTokens(
realmInstructions,
programId,
realmPk,
walletAtaPk,
councilMintPk,
walletPk,
walletPk,
walletPk
)
} else {
// Let's throw for now if the current wallet isn't in the team
// TODO: To fix it we would have to make it temp. as part of the team and then remove after the realm is created
throw new Error('Current wallet must be in the team')
}

// Put community and council mints under the realm governance with default config
const config = new GovernanceConfig({
voteThresholdPercentage: new VoteThresholdPercentage({
value: yesVoteThreshold,
}),
minCommunityTokensToCreateProposal: minCommunityTokensToCreateAsMintValue,
// Do not use instruction hold up time
minInstructionHoldUpTime: 0,
// max voting time 3 days
maxVotingTime: getTimestampFromDays(3),
voteWeightSource: VoteWeightSource.Deposit,
proposalCoolOffTime: 0,
minCouncilTokensToCreateProposal: new BN(1),
})

const {
governanceAddress: communityMintGovPk,
} = await withCreateMintGovernance(
realmInstructions,
programId,
realmPk,
communityMintPk,
config,
walletPk,
walletPk,
tokenOwnerRecordPk,
walletPk
)

await withCreateMintGovernance(
realmInstructions,
programId,
realmPk,
councilMintPk,
config,
walletPk,
walletPk,
tokenOwnerRecordPk,
walletPk
)

// Set the community governance as the realm authority
withSetRealmAuthority(
realmInstructions,
programId,
realmPk,
walletPk,
communityMintGovPk
)

try {
const councilMembersChunks = chunks(councilMembersInstructions, 10)
// only walletPk needs to sign the minting instructions and it's a signer by default and we don't have to include any more signers
const councilMembersSignersChunks = Array(councilMembersChunks.length).fill(
[]
)

const tx = await sendTransactions(
connection,
wallet,
[mintsSetupInstructions, ...councilMembersChunks, realmInstructions],
[mintsSetupSigners, ...councilMembersSignersChunks, realmSigners],
SequenceType.Sequential
)

return {
tx,
realmPk,
communityMintPk,
councilMintPk,
}
} catch (ex) {
console.error(ex)
throw ex
}
}
5 changes: 4 additions & 1 deletion actions/registerRealm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { MintMaxVoteWeightSource } from '../models/accounts'
import { withCreateRealm } from '../models/withCreateRealm'
import { RpcContext } from '../models/core/api'
import { sendTransaction } from '../utils/send'
import { ProgramVersion } from '@models/registry/constants'

export async function registerRealm(
{ connection, wallet, programId, programVersion, walletPubkey }: RpcContext,
{ connection, wallet, walletPubkey }: RpcContext,
programId: PublicKey,
programVersion: ProgramVersion,
name: string,
communityMint: PublicKey,
councilMint: PublicKey | undefined,
Expand Down
42 changes: 22 additions & 20 deletions components/Loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@ interface LoadingProps {

const Loading: FunctionComponent<LoadingProps> = ({ className }) => {
return (
<svg
className={`${className} animate-spin h-5 w-5`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className={`opacity-25`}
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className={`opacity-90`}
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
<div className="w-full flex justify-center">
<svg
className={`${className} animate-spin h-5 w-5`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className={`opacity-25`}
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className={`opacity-90`}
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
</div>
)
}

Expand Down
Loading

0 comments on commit f870cc7

Please sign in to comment.