Skip to content

Commit f010c17

Browse files
authored
Merge pull request #38 from macalinao/igm/vsr
Adds voter stake registry client
2 parents 8b16e87 + 88f81b4 commit f010c17

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+7541
-1
lines changed

.changeset/proud-dragons-spend.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@macalinao/clients-voter-stake-registry": patch
3+
"@macalinao/coda-visitors": patch
4+
"@macalinao/coda": patch
5+
---
6+
7+
Adds coda-visitors package and clients-voter-stake-registry

bun.lock

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,21 @@
113113
"@solana/kit": "*",
114114
},
115115
},
116+
"clients/voter-stake-registry": {
117+
"name": "@macalinao/clients-voter-stake-registry",
118+
"version": "0.1.0",
119+
"devDependencies": {
120+
"@macalinao/coda": "workspace:*",
121+
"@macalinao/eslint-config": "catalog:",
122+
"@macalinao/tsconfig": "catalog:",
123+
"@solana/kit": "catalog:",
124+
"eslint": "catalog:",
125+
"typescript": "catalog:",
126+
},
127+
"peerDependencies": {
128+
"@solana/kit": "*",
129+
},
130+
},
116131
"packages/coda": {
117132
"name": "@macalinao/coda",
118133
"version": "0.4.0",
@@ -122,6 +137,7 @@
122137
"dependencies": {
123138
"@codama/nodes-from-anchor": "catalog:",
124139
"@codama/renderers-rust": "^1.2.7",
140+
"@macalinao/coda-visitors": "workspace:*",
125141
"@macalinao/codama-nodes-from-anchor-x": "workspace:*",
126142
"@macalinao/codama-rename-visitor": "workspace:*",
127143
"@macalinao/codama-renderers-js-esm": "workspace:*",
@@ -138,6 +154,19 @@
138154
"typescript": "catalog:",
139155
},
140156
},
157+
"packages/coda-visitors": {
158+
"name": "@macalinao/coda-visitors",
159+
"version": "0.1.0",
160+
"dependencies": {
161+
"codama": "catalog:",
162+
},
163+
"devDependencies": {
164+
"@macalinao/eslint-config": "catalog:",
165+
"@macalinao/tsconfig": "catalog:",
166+
"eslint": "catalog:",
167+
"typescript": "catalog:",
168+
},
169+
},
141170
"packages/codama-instruction-accounts-dedupe-visitor": {
142171
"name": "@macalinao/codama-instruction-accounts-dedupe-visitor",
143172
"version": "0.4.0",
@@ -495,8 +524,12 @@
495524

496525
"@macalinao/clients-token-metadata": ["@macalinao/clients-token-metadata@workspace:clients/token-metadata"],
497526

527+
"@macalinao/clients-voter-stake-registry": ["@macalinao/clients-voter-stake-registry@workspace:clients/voter-stake-registry"],
528+
498529
"@macalinao/coda": ["@macalinao/coda@workspace:packages/coda"],
499530

531+
"@macalinao/coda-visitors": ["@macalinao/coda-visitors@workspace:packages/coda-visitors"],
532+
500533
"@macalinao/codama-instruction-accounts-dedupe-visitor": ["@macalinao/codama-instruction-accounts-dedupe-visitor@workspace:packages/codama-instruction-accounts-dedupe-visitor"],
501534

502535
"@macalinao/codama-nodes-from-anchor-x": ["@macalinao/codama-nodes-from-anchor-x@workspace:packages/codama-nodes-from-anchor-x"],
@@ -1929,12 +1962,20 @@
19291962

19301963
"@isaacs/cliui/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
19311964

1965+
"@macalinao/clients-voter-stake-registry/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
1966+
1967+
"@macalinao/coda/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
1968+
1969+
"@macalinao/coda-visitors/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
1970+
19321971
"@macalinao/codama-instruction-accounts-dedupe-visitor/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
19331972

19341973
"@macalinao/codama-nodes-from-anchor-x/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
19351974

19361975
"@macalinao/codama-rename-visitor/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
19371976

1977+
"@macalinao/codama-renderers-js-esm/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
1978+
19381979
"@macalinao/codama-renderers-markdown/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
19391980

19401981
"@manypkg/find-root/@types/node": ["@types/[email protected]", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="],
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# @macalinao/clients-voter-stake-registry
2+
3+
[![npm version](https://img.shields.io/npm/v/@macalinao/clients-voter-stake-registry.svg)](https://www.npmjs.com/package/@macalinao/clients-voter-stake-registry)
4+
5+
TypeScript client for the SPL Governance Voter Stake Registry program, generated using Coda with full ESM support.
6+
7+
## Installation
8+
9+
```bash
10+
bun add @macalinao/clients-voter-stake-registry
11+
```
12+
13+
## Development
14+
15+
This client is generated from the Voter Stake Registry IDL using Coda CLI:
16+
17+
```bash
18+
# Generate the client from idls/voter_stake_registry.json
19+
bun run codegen
20+
21+
# Build the TypeScript
22+
bun run build
23+
```
24+
25+
### Configuration
26+
27+
The `coda.config.mjs` file defines custom PDAs for the Voter Stake Registry program, including:
28+
29+
- **Registrar**: The voting registrar account - one per governance realm and governing mint
30+
- **Voter**: Individual voter accounts tied to a registrar and voter authority
31+
- **Voter Weight Record**: The account shown to spl-governance to prove vote weight
32+
33+
## Usage
34+
35+
```typescript
36+
import {
37+
findRegistrarPda,
38+
findVoterPda,
39+
findVoterWeightRecordPda
40+
} from "@macalinao/clients-voter-stake-registry";
41+
42+
// Get the registrar PDA
43+
const registrarPda = await findRegistrarPda({
44+
realm: realmPublicKey,
45+
realmGoverningTokenMint: mintPublicKey,
46+
});
47+
48+
// Get a voter PDA
49+
const voterPda = await findVoterPda({
50+
registrar: registrarPublicKey,
51+
voterAuthority: authorityPublicKey,
52+
});
53+
54+
// Get a voter weight record PDA
55+
const voterWeightRecordPda = await findVoterWeightRecordPda({
56+
registrar: registrarPublicKey,
57+
voterAuthority: authorityPublicKey,
58+
});
59+
```
60+
61+
## License
62+
63+
Copyright © 2025 Ian Macalinao
64+
65+
Licensed under the Apache License, Version 2.0
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import {
2+
accountNode,
3+
accountValueNode,
4+
addNodesVisitor,
5+
addPdasVisitor,
6+
bytesTypeNode,
7+
bytesValueNode,
8+
constantPdaSeedNodeFromString,
9+
defineConfig,
10+
fieldDiscriminatorNode,
11+
fixedSizeTypeNode,
12+
numberTypeNode,
13+
optionTypeNode,
14+
payerValueNode,
15+
pdaLinkNode,
16+
pdaSeedValueNode,
17+
pdaValueNode,
18+
publicKeyTypeNode,
19+
publicKeyValueNode,
20+
setInstructionAccountDefaultValuesVisitor,
21+
structFieldTypeNode,
22+
structTypeNode,
23+
updateAccountsVisitor,
24+
variablePdaSeedNode,
25+
} from "@macalinao/coda";
26+
27+
const addCustomPDAsVisitor = addPdasVisitor({
28+
voterStakeRegistry: [
29+
{
30+
name: "registrar",
31+
docs: [
32+
"The voting registrar. There can only be a single registrar",
33+
"per governance realm and governing mint.",
34+
],
35+
seeds: [
36+
variablePdaSeedNode("realm", publicKeyTypeNode()),
37+
constantPdaSeedNodeFromString("utf8", "registrar"),
38+
variablePdaSeedNode("realmGoverningTokenMint", publicKeyTypeNode()),
39+
],
40+
},
41+
{
42+
name: "voter",
43+
docs: [
44+
"The voter account for a given voter authority.",
45+
"Each voter authority has a unique voter account per registrar.",
46+
],
47+
seeds: [
48+
variablePdaSeedNode("registrar", publicKeyTypeNode()),
49+
constantPdaSeedNodeFromString("utf8", "voter"),
50+
variablePdaSeedNode("voterAuthority", publicKeyTypeNode()),
51+
],
52+
},
53+
{
54+
name: "voterWeightRecord",
55+
docs: [
56+
"The voter weight record is the account that will be shown to spl-governance",
57+
"to prove how much vote weight the voter has. See update_voter_weight_record.",
58+
],
59+
seeds: [
60+
variablePdaSeedNode("registrar", publicKeyTypeNode()),
61+
constantPdaSeedNodeFromString("utf8", "voter-weight-record"),
62+
variablePdaSeedNode("voterAuthority", publicKeyTypeNode()),
63+
],
64+
},
65+
],
66+
});
67+
68+
export default defineConfig({
69+
outputDir: "./src/generated",
70+
docs: {
71+
npmPackageName: "@macalinao/clients-voter-stake-registry",
72+
},
73+
visitors: [
74+
addNodesVisitor({
75+
voterStakeRegistry: {
76+
accounts: [
77+
// See: https://github.com/Mythic-Project/oyster/blob/main/packages/governance-sdk/src/addins/serialisation.ts
78+
accountNode({
79+
name: "voterWeightRecord",
80+
discriminators: [fieldDiscriminatorNode("discriminator", 0)],
81+
data: structTypeNode([
82+
structFieldTypeNode({
83+
name: "discriminator",
84+
defaultValueStrategy: "omitted",
85+
type: fixedSizeTypeNode(bytesTypeNode(), 8),
86+
defaultValue: bytesValueNode("base16", "3265663939623462"),
87+
}),
88+
structFieldTypeNode({
89+
name: "realm",
90+
type: publicKeyTypeNode(),
91+
}),
92+
structFieldTypeNode({
93+
name: "governingTokenMint",
94+
type: publicKeyTypeNode(),
95+
}),
96+
structFieldTypeNode({
97+
name: "governingTokenOwner",
98+
type: publicKeyTypeNode(),
99+
}),
100+
structFieldTypeNode({
101+
name: "voterWeight",
102+
type: numberTypeNode("u64"),
103+
}),
104+
structFieldTypeNode({
105+
name: "voterWeightExpiry",
106+
type: optionTypeNode(numberTypeNode("u64")),
107+
}),
108+
structFieldTypeNode({
109+
name: "weightAction",
110+
type: optionTypeNode(numberTypeNode("u8")),
111+
}),
112+
structFieldTypeNode({
113+
name: "weightActionTarget",
114+
type: optionTypeNode(publicKeyTypeNode()),
115+
}),
116+
// ['realm', 'pubkey'],
117+
// ['governingTokenMint', 'pubkey'],
118+
// ['governingTokenOwner', 'pubkey'],
119+
// ['voterWeight', 'u64'],
120+
// ['voterWeightExpiry', { kind: 'option', type: 'u64' }],
121+
// ['weightAction', { kind: 'option', type: 'u8' }],
122+
// ['weightActionTarget', { kind: 'option', type: 'pubkey' }],
123+
]),
124+
pda: pdaLinkNode("voterWeightRecord"),
125+
}),
126+
],
127+
},
128+
}),
129+
updateAccountsVisitor({
130+
registrar: {
131+
pda: pdaLinkNode("registrar"),
132+
},
133+
voter: {
134+
pda: pdaLinkNode("voter"),
135+
},
136+
}),
137+
138+
setInstructionAccountDefaultValuesVisitor([
139+
{
140+
account: "tokenProgram",
141+
defaultValue: publicKeyValueNode(
142+
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
143+
),
144+
},
145+
{
146+
account: "associatedTokenProgram",
147+
defaultValue: publicKeyValueNode(
148+
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
149+
),
150+
},
151+
{
152+
account: "systemProgram",
153+
defaultValue: publicKeyValueNode("11111111111111111111111111111111"),
154+
},
155+
{
156+
account: "rent",
157+
defaultValue: publicKeyValueNode(
158+
"SysvarRent111111111111111111111111111111111",
159+
),
160+
},
161+
{
162+
account: "instructions",
163+
defaultValue: publicKeyValueNode(
164+
"Sysvar1nstructions1111111111111111111111111",
165+
),
166+
},
167+
{
168+
account: "payer",
169+
defaultValue: payerValueNode(),
170+
},
171+
172+
{
173+
account: "voter",
174+
instruction: "createVoter",
175+
defaultValue: pdaValueNode(pdaLinkNode("voter"), [
176+
pdaSeedValueNode("registrar", accountValueNode("registrar")),
177+
pdaSeedValueNode(
178+
"voterAuthority",
179+
accountValueNode("voterAuthority"),
180+
),
181+
]),
182+
},
183+
{
184+
account: "voterWeightRecord",
185+
instruction: "createVoter",
186+
defaultValue: pdaValueNode(pdaLinkNode("voterWeightRecord"), [
187+
pdaSeedValueNode("registrar", accountValueNode("registrar")),
188+
pdaSeedValueNode(
189+
"voterAuthority",
190+
accountValueNode("voterAuthority"),
191+
),
192+
]),
193+
},
194+
]),
195+
addCustomPDAsVisitor,
196+
],
197+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { configs } from "@macalinao/eslint-config";
2+
3+
export default [
4+
...configs.fast,
5+
{
6+
languageOptions: {
7+
parserOptions: {
8+
tsconfigRootDir: import.meta.dirname,
9+
},
10+
},
11+
},
12+
{
13+
files: ["src/generated/**/*.ts"],
14+
rules: {
15+
"@typescript-eslint/no-non-null-assertion": "off",
16+
"@typescript-eslint/prefer-nullish-coalescing": "off",
17+
},
18+
},
19+
{
20+
files: [
21+
"src/generated/instructions/*.ts",
22+
"src/generated/types/*.ts",
23+
"src/generated/errors/*.ts",
24+
],
25+
rules: {
26+
"@typescript-eslint/no-unnecessary-condition": "off",
27+
"no-constant-condition": "off",
28+
"@typescript-eslint/no-empty-object-type": "off",
29+
},
30+
},
31+
];

0 commit comments

Comments
 (0)