Skip to content

Commit

Permalink
Grouping instructions into transactions and splitting a bit better th…
Browse files Browse the repository at this point in the history
…e codebase
  • Loading branch information
lucaslencinas committed Oct 9, 2022
1 parent 6828197 commit 01b6529
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 102 deletions.
2 changes: 0 additions & 2 deletions packages/react-app-example-daos/config-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// More info here https://github.com/facebook/create-react-app/pull/12021#issuecomment-1108426483
module.exports = {
webpack: function (config, env) {
console.log(config.module);
config.module.rules = config.module.rules.map((rule) => {
if (rule.oneOf instanceof Array) {
console.log(JSON.stringify(rule.oneOf));
rule.oneOf[rule.oneOf.length - 1].exclude = [
/\.(js|mjs|jsx|cjs|ts|tsx)$/,
/\.html$/,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { createContext, PropsWithChildren, useContext } from "react";
import { SolanaDao } from "solana-dao-sdk";
import { Connection, clusterApiUrl } from "@solana/web3.js";

type ContextType = SolanaDao;

Expand All @@ -18,8 +19,9 @@ export const useDaoClient = (): ContextType => {
export const DaoProvider: React.FunctionComponent<PropsWithChildren> = ({
children,
}) => {
const devnetConnection = new Connection(clusterApiUrl("devnet"), "finalized");
return (
<DaoContext.Provider value={new SolanaDao()}>
<DaoContext.Provider value={new SolanaDao(devnetConnection)}>
{children}
</DaoContext.Provider>
);
Expand Down
4 changes: 4 additions & 0 deletions packages/solana-dao-sdk/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,16 @@ describe("SolanaDao", () => {
client.setWallet(userWallet);

const daoName = `dao ${randomId()}`;

const createdDao = await client.createDao(
[userWallet.publicKey],
daoName,
60
);

// So we can check on the Explorer how it looks.
console.log(createdDao.signatures);

// Checking that we receive the publicKeys
expect(createdDao.communityMintPk).toBeTruthy();
expect(createdDao.councilMintPk).toBeTruthy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
} from "../units";
import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
import { Wallet } from "../../wallet";
import { TOKEN_PROGRAM_ID } from "../tokens";

// The community mint is going to have 0 supply and we arbitrarily set it to 1m
const minCommunityTokensToCreate = MIN_COMMUNITY_TOKENS_TO_CREATE_W_0_SUPPLY;
Expand Down
47 changes: 47 additions & 0 deletions packages/solana-dao-sdk/src/internal/sender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Wallet } from "../wallet";
import {
Connection,
sendAndConfirmRawTransaction,
Transaction,
BlockhashWithExpiryBlockHeight,
} from "@solana/web3.js";
import base58 from "bs58";

/**
* Utility function that sequentially sends and confirm already signed transactions.
*
* @param wallet
* @param connection
* @param transactions
* @param recentBlockhash
* @returns the signatures of each transaction
*/
export async function sendTransactions(
wallet: Wallet,
connection: Connection,
transactions: Transaction[],
recentBlockhash: BlockhashWithExpiryBlockHeight
): Promise<string[]> {
const signedTxs = await wallet.signAllTransactions(transactions);

const signatures: string[] = [];
for (const signed of signedTxs) {
const rawTransaction = signed.serialize();
const signature = await sendAndConfirmRawTransaction(
connection,
rawTransaction,
{
signature: base58.encode(signed.signature!),
blockhash: recentBlockhash.blockhash,
lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,
},
{
commitment: "confirmed",
}
);

signatures.push(signature);
}

return signatures;
}
167 changes: 71 additions & 96 deletions packages/solana-dao-sdk/src/internal/services/daoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {
sendAndConfirmRawTransaction,
Transaction,
TransactionInstruction,
BlockhashWithExpiryBlockHeight,
} from "@solana/web3.js";
import base58 from "bs58";

import { Wallet } from "../../wallet";
import { TokenRepository } from "../repositories/tokenRepository";
import { DaoRepository } from "../repositories/daoRepository";
import { sendTransactions } from "../sender";

const communityMintDecimals = 6;
const tokenAmount = 1;
Expand All @@ -18,6 +20,7 @@ export type MultiSigDaoResponse = {
daoPk: PublicKey;
communityMintPk: PublicKey;
councilMintPk: PublicKey;
signatures: string[];
};

export class DaoService {
Expand Down Expand Up @@ -50,30 +53,51 @@ export class DaoService {
}

const walletPk = this.wallet.publicKey;
const recentBlockhash = await this.connection.getLatestBlockhash();

const { communityMintPk, councilMintPk } = await this.createMintsForDao(
walletPk
);

const { walletAtaPk } = await this.mintCouncilTokensToMembers(
walletPk,
councilWalletsPks,
councilMintPk
);

const { daoPk } = await this.createConfiguredDao(
name,
yesVoteThreshold,
walletPk,
const {
communityMintPk,
councilMintPk,
walletAtaPk
transaction: mintTransaction,
} = await this.createMintsForDao(walletPk, recentBlockhash);

const { walletAtaPk, transaction: membersTransaction } =
await this.mintCouncilTokensToMembers(
walletPk,
councilWalletsPks,
councilMintPk,
recentBlockhash
);

const { daoPk, transaction: daoTransaction } =
await this.createConfiguredDao(
name,
yesVoteThreshold,
walletPk,
communityMintPk,
councilMintPk,
walletAtaPk,
recentBlockhash
);

const transactions = [
mintTransaction,
membersTransaction,
daoTransaction,
];

const signatures = await sendTransactions(
this.wallet,
this.connection,
transactions,
recentBlockhash
);

return {
daoPk,
communityMintPk: communityMintPk,
councilMintPk: councilMintPk,
signatures,
};
} catch (ex) {
console.error(ex);
Expand All @@ -84,8 +108,10 @@ export class DaoService {
private async mintCouncilTokensToMembers(
walletPk: PublicKey,
councilWalletsPks: PublicKey[],
councilMintPk: PublicKey
councilMintPk: PublicKey,
recentBlockhash: BlockhashWithExpiryBlockHeight
): Promise<{
transaction: Transaction;
walletAtaPk: PublicKey;
}> {
const instructions: TransactionInstruction[] = [];
Expand Down Expand Up @@ -122,39 +148,25 @@ export class DaoService {
throw new Error("Current wallet must be in the team");
}

const recentBlockhash2 = await this.connection.getLatestBlockhash();
const councilTokenRelatedTx = new Transaction({
blockhash: recentBlockhash2.blockhash,
lastValidBlockHeight: recentBlockhash2.lastValidBlockHeight,
const transaction = new Transaction({
blockhash: recentBlockhash.blockhash,
lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,
feePayer: this.wallet.publicKey,
});

instructions.forEach((instruction) =>
councilTokenRelatedTx.add(instruction)
);
instructions.forEach((instruction) => transaction.add(instruction));

const signedTxs2 = await this.wallet.signAllTransactions([
councilTokenRelatedTx,
]);
const recentBlockhash3 = await this.connection.getLatestBlockhash();

await Promise.all(
signedTxs2.map((signed) => {
const rawTransaction = signed.serialize();
return sendAndConfirmRawTransaction(this.connection, rawTransaction, {
signature: base58.encode(signed.signature!),
blockhash: recentBlockhash3.blockhash,
lastValidBlockHeight: recentBlockhash3.lastValidBlockHeight,
});
})
);

return { walletAtaPk };
return { transaction, walletAtaPk };
}

private async createMintsForDao(
walletPk: PublicKey
): Promise<{ communityMintPk: PublicKey; councilMintPk: PublicKey }> {
walletPk: PublicKey,
recentBlockhash: BlockhashWithExpiryBlockHeight
): Promise<{
transaction: Transaction;
communityMintPk: PublicKey;
councilMintPk: PublicKey;
}> {
if (!this.wallet) {
throw new Error("There is no wallet available");
}
Expand All @@ -173,53 +185,30 @@ export class DaoService {
walletPk
);

const recentBlockhash = await this.connection.getLatestBlockhash();
const communityMintTx = new Transaction({
blockhash: recentBlockhash.blockhash,
lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,
feePayer: this.wallet.publicKey,
});

const councilsMintTx = new Transaction({
const transaction = new Transaction({
blockhash: recentBlockhash.blockhash,
lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,
feePayer: this.wallet.publicKey,
});

communityMint.instructions.forEach((instruction) =>
communityMintTx.add(instruction)
transaction.add(instruction)
);

if (communityMint.signers.length > 0) {
communityMintTx.partialSign(...communityMint.signers);
}

councilMint.instructions.forEach((instruction) =>
councilsMintTx.add(instruction)
transaction.add(instruction)
);

if (communityMint.signers.length > 0) {
transaction.partialSign(...communityMint.signers);
}

if (councilMint.signers.length > 0) {
councilsMintTx.partialSign(...councilMint.signers);
transaction.partialSign(...councilMint.signers);
}

const signedTxs1 = await this.wallet.signAllTransactions([
communityMintTx,
councilsMintTx,
]);

const recentBlockhash1 = await this.connection.getLatestBlockhash();

await Promise.all(
signedTxs1.map((signed) => {
const rawTransaction = signed.serialize();
return sendAndConfirmRawTransaction(this.connection, rawTransaction, {
signature: base58.encode(signed.signature!),
blockhash: recentBlockhash1.blockhash,
lastValidBlockHeight: recentBlockhash1.lastValidBlockHeight,
});
})
);
return {
transaction,
communityMintPk: communityMint.publicKey,
councilMintPk: councilMint.publicKey,
};
Expand All @@ -231,8 +220,9 @@ export class DaoService {
walletPk: PublicKey,
communityMintPk: PublicKey,
councilMintPk: PublicKey,
walletAtaPk: PublicKey
): Promise<{ daoPk: PublicKey }> {
walletAtaPk: PublicKey,
recentBlockhash: BlockhashWithExpiryBlockHeight
): Promise<{ daoPk: PublicKey; transaction: Transaction }> {
if (!this.wallet) {
throw new Error("There is no wallet available");
}
Expand All @@ -246,29 +236,14 @@ export class DaoService {
walletAtaPk
);

const recentBlockhash4 = await this.connection.getLatestBlockhash();
const realmRelatedTx = new Transaction({
blockhash: recentBlockhash4.blockhash,
lastValidBlockHeight: recentBlockhash4.lastValidBlockHeight,
const transaction = new Transaction({
blockhash: recentBlockhash.blockhash,
lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,
feePayer: this.wallet.publicKey,
});

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

const signedTxs3 = await this.wallet.signAllTransactions([realmRelatedTx]);
const recentBlockhash5 = await this.connection.getLatestBlockhash();

await Promise.all(
signedTxs3.map((signed) => {
const rawTransaction = signed.serialize();
return sendAndConfirmRawTransaction(this.connection, rawTransaction, {
signature: base58.encode(signed.signature!),
blockhash: recentBlockhash5.blockhash,
lastValidBlockHeight: recentBlockhash5.lastValidBlockHeight,
});
})
);
instructions.forEach((instruction) => transaction.add(instruction));

return { daoPk };
return { daoPk, transaction };
}
}
4 changes: 2 additions & 2 deletions packages/solana-dao-sdk/src/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ export async function createWallet(
});

const balance = await connection.getBalance(keypair.publicKey);
console.log(`Wallet created with: ${balance} LAMPORTS`);
console.log(`Wallet ${keypair.publicKey} created with: ${balance} LAMPORTS`);

const wallet = new TestWallet(keypair);

return wallet;
}

export function randomId() {
return (Math.random() + 1).toString(36).substring(10);
return Math.random().toString(36).slice(2, 10);
}

0 comments on commit 01b6529

Please sign in to comment.