Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding createDao() method #8

Merged
merged 5 commits into from
Oct 18, 2022
Merged

Adding createDao() method #8

merged 5 commits into from
Oct 18, 2022

Conversation

lucaslencinas
Copy link
Contributor

@lucaslencinas lucaslencinas commented Oct 4, 2022

Description

Adding the createDao() method.

Basically, doing the same steps that are done across several repositories in one place.
In Nation, Oyster UI, Governance UI, Marinade and probably other places there are lots of duplicated code in order to create a simple MultiSig wallet.
With this PR, any project should be able to do just:

const dao = await client.createDao(members, name, yesVoteThreshold)

Also:

  • Created a daoService using a daoRepository and a tokenRepository in order to split the responsibility.
  • Created a TestWallet

Things still to do:

  • Unify all the transactions into one. I'd rather do that in another PR. Not a blocker for me for now.

const daoName = `dao ${randomName}`;

const someDao = await client.createDao(
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's draft but I don't want to forget this:

  1. It would be nice if the method createDao uses the connection in this (we pass it over in the constructor), so that we always use the same connection in every call.
  2. Program ID could be a default value, same as the program version
  3. We can set the user wallet with a setter after the user connects their wallet or pass it in the constructor if we have it
  4. If we pass userWallet, we can infer userWallet.publicKey in the method

@@ -23,12 +26,63 @@ export type Dao = {
votingProposalCount: number;
};

export class TestWallet {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely see this TestWallet being reused in other test cases, should we move it to the test directory?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, is it implementing some other wallet interface? it would be good to annotate with "extends" or "implements" in that case

const tokenAmount = 1;

export async function createMultisigRealm1(
connection: Connection,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid passing the connection, wallet, programId and program version everywhere, we could migrate the method createMultisigRealm as a public method in an internal object/class like DAORepository or DAOStorage, or something that indicates input/output, and pass the parameters I mentioned in the constructor. Then createMint, and all creations can be private methods in the same object.

);

if (communityMint.signers.length > 0) {
communityMintTx.partialSign(...communityMint.signers);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does communityMint.signers contain in this context? just wallet.publicKey or some new public keys?

feePayer: wallet.publicKey,
});

const councilsMintTx = new Transaction({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to improve readibility, lets move this down to line ~92 right before we need it

walletPk
);

// transaction signatures here for now
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to improve readability, let's move this comment to line 102, right where it's relevant

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, do you have any thoughts about what could be a better place for the signatures? If yes, I'd transform this comment either in a ticket or a TODO saying what to do. If not, I'd just remove the comment as we won't do anything with it in the near future and for now will be forever :D


// signature.toString('base64')

const recentBlockhash1 = await connection.getLatestBlockhash();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why another blockchash? could we not use just one?

} 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");
Copy link
Contributor

@arielsegura arielsegura Oct 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what happens if we reach this line? I'm guessing the DAO is partially created as we already signed and sent a few transactions, but others below this line were never executed.

Is it possible for us to fail fast? i.e. do this check before even signing a transaction


realmInstructions.forEach((instruction) => realmRelatedTx.add(instruction));

const signedTxs3 = await wallet.signAllTransactions([realmRelatedTx]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you tried signing and sending all transactions at the same time? i.e. generating all the instructions first and then packing them all (in the right order) in the same transaction

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason I'm still having issues with that. If I cannot fix it quite fast, I'll do it in a following PR

@arielsegura
Copy link
Contributor

arielsegura commented Oct 4, 2022

const dao = await client.createDao(connetion, wallet, members, name, yesVoteThreshold)

this could be simplified to

await client.createDao(wallet, members, name, yesVoteThreshold)

as the connection is in the client already (this.connection).

Even further:

// when we detect that the user connected their wallet
client.setWallet(wallet);
...
// yesVoteThreshold: default to 0.5? or 0.6? I think I read somewhere what a nice default value is. And we can change it anytime no? 
// members: default to single member, `wallet.publicKey`
// name mandatory with no default value. Sounds like the most important parameter
await client.createDao(name) 
// whenever we detect that the user disconnected their wallet
client.setWallet(undefined)

@lucaslencinas lucaslencinas changed the title [DRAFT] First version of createDao() First version of createDao() Oct 6, 2022
@lucaslencinas lucaslencinas changed the title First version of createDao() Adding createDao() method Oct 9, 2022
@lucaslencinas
Copy link
Contributor Author

This is ready for another review. @arielsegura

## Moving to react-app-rewired

Due to a couple of issues we started using create-app-rewired.
This is because CRA doesn't handle .cjs files and inside the spl-governance dependecy tree we have a few of them when compiled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is because CRA doesn't handle .cjs files and inside the spl-governance dependecy tree we have a few of them when compiled.
This is because CRA doesn't handle .cjs files and inside the spl-governance dependency tree we have a few of them when compiled.

Copy link
Contributor

@arielsegura arielsegura left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks really really good, good job!

I left some non blocking comments (nit) that we can discuss later or fix in other PRs, some other comments would be nice to address them now, but we can also discuss that :D

🎉

return (
<DaoContext.Provider value={new SolanaDao()}>
<DaoContext.Provider value={new SolanaDao(devnetConnection)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you are changing this because of the too many requests problem, devnet can have the same problem. I think it would be good to use mainnet by default, even on the example. Devnet is usually very unstable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For rendering DAOs yeah, we could use prod, yes. But now that we have the createDao() would you want to spend real SOL for trying out things?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we can have a query parameter like most products do to indicate which network tu use

Copy link
Contributor

@arielsegura arielsegura Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For rendering DAOs yeah, we could use prod, yes. But now that we have the createDao() would you want to spend real SOL for trying out things?

ideally, we should integrate with a wallet that would enable the user to make this decision. I'm suggesting mainnet-beta because the first interaction that "testers" will have is reading, not writing. If this first interaction fails (because devnet is unstable), I really doubt they will try writing


export const governanceProgramVersion = 2;
export const governancePk = new PublicKey(
"GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this governancePk is the same as the constant in index.ts. Let's have it in only one place

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only one now 🤔

);

// So we can check on the Explorer how it looks.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: what exactly do you look for in the explorer? I wonder if we can call getDao to make a few assertions instead?

packages/solana-dao-sdk/src/index.ts Outdated Show resolved Hide resolved
packages/solana-dao-sdk/src/index.ts Outdated Show resolved Hide resolved
packages/solana-dao-sdk/src/internal/tokens.ts Outdated Show resolved Hide resolved
packages/solana-dao-sdk/src/test/utils.ts Outdated Show resolved Hide resolved
packages/solana-dao-sdk/src/test/utils.ts Outdated Show resolved Hide resolved
packages/solana-dao-sdk/src/wallet.ts Outdated Show resolved Hide resolved
packages/solana-dao-sdk/src/wallet.ts Outdated Show resolved Hide resolved
@lucaslencinas lucaslencinas merged commit 127ae68 into main Oct 18, 2022
@lucaslencinas lucaslencinas deleted the createDao-first-version branch October 18, 2022 05:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants