Skip to content

Commit

Permalink
Revert "user anchor for serialization (#6)" (#7)
Browse files Browse the repository at this point in the history
This reverts commit 2aa04d3.
  • Loading branch information
arielsegura authored Oct 3, 2022
1 parent 504bb88 commit f324232
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 50 deletions.
5 changes: 2 additions & 3 deletions packages/solana-dao-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
},
"homepage": "https://github.com/nation-io/sdk#readme",
"dependencies": {
"@project-serum/anchor": "^0.25.0",
"@project-serum/spl-governance": "^3.0.0",
"@solana/web3.js": "^1.62.0"
"@solana/web3.js": "^1.62.0",
"borsh": "^0.7.0"
},
"devDependencies": {
"@open-wc/building-rollup": "^2.0.2",
Expand Down
3 changes: 2 additions & 1 deletion packages/solana-dao-sdk/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SolanaDao } from "./index";
import { PublicKey } from "@solana/web3.js";
import { BN } from "bn.js";

describe("SolanaDao", () => {
test("calling getDao return a value", async () => {
Expand All @@ -11,7 +12,7 @@ describe("SolanaDao", () => {
authority: new PublicKey("5We3g4zpinMcxSFrkvPWRzcSM5oRYxfWc9c8idefAQdi"),
communityMint: new PublicKey("ukrn3bJz9dzSN2VpF76ytpNcugXHEy4VuRou7bpizgF"),
councilMint: new PublicKey("cUAxX8fEXdisGSgRnfL4aH3WeomK5b277XqHHS4U9z7"),
minCommunityWeightToCreateGovernance: "1000000000000",
minCommunityTokensToCreateGovernance: "1000000000000",
votingProposalCount: 0
};
expect(actualDao).toEqual(expectedDao);
Expand Down
50 changes: 12 additions & 38 deletions packages/solana-dao-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Connection, clusterApiUrl, PublicKey, ConfirmOptions, SendOptions, Signer, Transaction } from '@solana/web3.js';
import { Connection, clusterApiUrl, PublicKey } from '@solana/web3.js';
import { AccountType } from './internal/governance/model';
import { RealmV2Serializer } from './internal/serialization';
import { splGovernanceProgram, SplGovernance } from "@project-serum/spl-governance";
import { Keypair } from "@solana/web3.js";
import { Program, AnchorProvider, Wallet } from "@project-serum/anchor";
import BN from 'bn.js';

/**
* Note: This interface is an abstraction introduced by the SDK so that consumers don't care about having to (de)serialize deprecated or unused fields
Expand All @@ -19,55 +18,30 @@ export type Dao = {
// Optional mint address of the token to be used to represent voting power in the council
councilMint?: PublicKey;
// Min number of community tokens required to create a governance
minCommunityWeightToCreateGovernance: String; // TODO should be BN, but the test fails
minCommunityTokensToCreateGovernance: String; // TODO should be BN, but the test fails
// The number of proposals in voting state
votingProposalCount: number;
};

class NoOpWallet extends Wallet {
constructor(){
// a key pair we don't use but it's necessary in the constructor
super(Keypair.generate());
}
signTransaction(tx: Transaction): Promise<Transaction> {
throw new Error('Method not implemented.');
}
signAllTransactions(txs: Transaction[]): Promise<Transaction[]> {
throw new Error('Method not implemented.');
}
get publicKey(): PublicKey {
return PublicKey.default;
}

}

/**
* By default, Anchor expects a provider or default to Local which expects a valid wallet configured (i.e. node)
*/
class NoOpProvider extends AnchorProvider {
constructor(connection: Connection){
super(connection, new NoOpWallet(), AnchorProvider.defaultOptions())
}
}

export class SolanaDao {
connection: Connection;
program: Program<SplGovernance>;
serializer: RealmV2Serializer;
serializer = new RealmV2Serializer();

constructor(connection?: Connection) {
this.connection = connection ? connection : new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
this.program = splGovernanceProgram({provider: new NoOpProvider(this.connection)});
this.serializer = new RealmV2Serializer(this.program.coder);
}

async getDao(publicKey: PublicKey): Promise<Dao | null> {

const accountInfo = await this.connection.getAccountInfo(publicKey);
if (!accountInfo) {
return null;
}
const buffer: Buffer = accountInfo.data;
const buffer: Buffer = accountInfo.data;
// first field must be the account type
const accountType = buffer[0];
if (accountType !== AccountType.RealmV2.valueOf()) {
return null;
}
const realm = this.serializer.deserialize(buffer);

return {
Expand All @@ -76,7 +50,7 @@ export class SolanaDao {
authority: realm.authority,
communityMint: realm.communityMint,
councilMint: realm.config.councilMint,
minCommunityWeightToCreateGovernance: realm.config.minCommunityWeightToCreateGovernance.toString(10),
minCommunityTokensToCreateGovernance: realm.config.minCommunityTokensToCreateGovernance.toString(10),
votingProposalCount: realm.votingProposalCount
};
}
Expand Down
29 changes: 29 additions & 0 deletions packages/solana-dao-sdk/src/internal/governance/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* These types are literally a from the ones in the smart contract
* https://github.com/solana-labs/solana-program-library/blob/6dfc68db1370c65076e8a860db74fd6483161d8a/governance/program/src/state/enums.rs#L7
*/
export enum AccountType {
Uninitialized = 0,
RealmV1 = 1,
TokenOwnerRecordV1 = 2,
GovernanceV1 = 3,
ProgramGovernanceV1 = 4,
ProposalV1 = 5,
SignatoryRecordV1 = 6,
VoteRecordV1 = 7,
ProposalInstructionV1 = 8,
MintGovernanceV1 = 9,
TokenGovernanceV1 = 10,
RealmConfig = 11,
VoteRecordV2 = 12,
ProposalTransactionV2 = 13,
ProposalV2 = 14,
ProgramMetadata = 15,
RealmV2 = 16,
TokenOwnerRecordV2 = 17,
GovernanceV2 = 18,
ProgramGovernanceV2 = 19,
MintGovernanceV2 = 20,
TokenGovernanceV2 = 21,
SignatoryRecordV2 = 22,
}
97 changes: 89 additions & 8 deletions packages/solana-dao-sdk/src/internal/serialization.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
import { PublicKey } from '@solana/web3.js';
import { BinaryReader, BinaryWriter, deserialize, Schema } from 'borsh';
import { AccountType } from './governance/model';
import BN from 'bn.js';
import { Coder } from '@project-serum/anchor';

export const extendBorsh = () => {
(BinaryReader.prototype as any).readPubkey = function () {
const reader = this as unknown as BinaryReader;
const array = reader.readFixedArray(32);
const pk = new PublicKey(array);
return pk;
};

(BinaryWriter.prototype as any).writePubkey = function (value: PublicKey) {
const writer = this as unknown as BinaryWriter;
writer.writeFixedArray(value.toBuffer());
};
};

extendBorsh();

// TODO move to some constants.ts
export const SYSTEM_PROGRAM_ID = new PublicKey(
'11111111111111111111111111111111',
);

class RealmConfig {
accountType = AccountType.RealmConfig;
realm: PublicKey;
minCommunityWeightToCreateGovernance: BN;
minCommunityTokensToCreateGovernance: BN;
councilMint?: PublicKey;

constructor(args: {
realm: PublicKey;
minCommunityWeightToCreateGovernance: BN;
minCommunityTokensToCreateGovernance: BN;
councilMint?: PublicKey;
}) {
this.realm = args.realm;
this.minCommunityWeightToCreateGovernance = args.minCommunityWeightToCreateGovernance;
this.minCommunityTokensToCreateGovernance = args.minCommunityTokensToCreateGovernance;
this.councilMint = args.councilMint;
}
}

// types doesn't seem to be exported for spl-governance on Anchor :(
class Realm {
accountType = AccountType.RealmV2;
communityMint: PublicKey;
config: RealmConfig;
reserved: Uint8Array;
Expand All @@ -44,11 +67,69 @@ class Realm {
}
}

enum MintMaxVoteWeightSourceType {
SupplyFraction = 0,
Absolute = 1,
}

class MintMaxVoteWeightSource {
type = MintMaxVoteWeightSourceType.SupplyFraction;
value: BN;

constructor(args: { value: BN }) {
this.value = args.value;
}
}

const realmV2BorshSchema: Schema = new Map<Function, any>(
[
[
RealmConfig,
{
kind: 'struct',
fields: [
['legacy1', 'u8'],
['legacy2', 'u8'],
['reserved', [6]],
['minCommunityTokensToCreateGovernance', 'u64'],
['communityMintMaxVoteWeightSource', MintMaxVoteWeightSource],
['councilMint', { kind: 'option', type: 'pubkey' }],
],
},
],
[
Realm,
{
kind: 'struct',
fields: [
['accountType', 'u8'],
['communityMint', 'pubkey'],
['config', RealmConfig],
['reserved', [6]],
['votingProposalCount', 'u16'],
['authority', { kind: 'option', type: 'pubkey' }],
['name', 'string'],
['reserved_v2', [128]],
],
},
],
[
MintMaxVoteWeightSource,
{
kind: 'struct',
fields: [
['type', 'u8'],
['value', 'u64'],
],
},
]
]
)

export class RealmV2Serializer {
constructor(readonly coder: Coder){}
deserialize(buffer: Buffer): Realm {
deserialize(buffer: Buffer) {
try {
return this.coder.accounts.decode<Realm>("realmV2", buffer);
return deserialize(realmV2BorshSchema, Realm, buffer);
} catch (e) {
throw e;
}
Expand Down

0 comments on commit f324232

Please sign in to comment.