@tribecahq/tribeca-sdk
Version:
The TypeScript SDK for Tribeca, an open standard and toolkit for launching DAOs on Solana.
284 lines • 11.6 kB
JavaScript
import { createMemoInstruction, TransactionEnvelope, } from "@saberhq/solana-contrib";
import { getOrCreateATA, TOKEN_PROGRAM_ID } from "@saberhq/token-utils";
import { PublicKey, SystemProgram, SYSVAR_INSTRUCTIONS_PUBKEY, } from "@solana/web3.js";
import invariant from "tiny-invariant";
import { TRIBECA_ADDRESSES } from "../../constants";
import { GovernorWrapper } from "../govern/governor";
import { findWhitelistAddress } from ".";
import { findEscrowAddress } from "./pda";
/**
* Helper methods around a Locked Voter electorate.
*/
export class LockerWrapper {
constructor(sdk, locker, governorKey) {
this.sdk = sdk;
this.locker = locker;
this.governorKey = governorKey;
this._lockerData = null;
this.program = sdk.programs.LockedVoter;
this.governor = new GovernorWrapper(sdk, governorKey);
}
static async load(sdk, lockerKey, governorKey) {
const wrapper = new LockerWrapper(sdk, lockerKey, governorKey);
await wrapper.data();
return wrapper;
}
/**
* Fetches the data of the locker.
* @returns
*/
async reload() {
return this.program.account.locker.fetch(this.locker);
}
async fetchProposalData(proposalKey) {
return await this.sdk.govern.program.account.proposal.fetch(proposalKey);
}
async fetchEscrow(escrowKey) {
return await this.program.account.escrow.fetch(escrowKey);
}
async fetchEscrowByAuthority(authority = this.sdk.provider.wallet.publicKey) {
const [escrowKey] = await findEscrowAddress(this.locker, authority);
return this.fetchEscrow(escrowKey);
}
/**
* Fetches the data of the locker.
* @returns
*/
async data() {
if (!this._lockerData) {
this._lockerData = await this.reload();
}
return this._lockerData;
}
async getOrCreateEscrow(authority = this.sdk.provider.wallet.publicKey) {
const [escrow] = await findEscrowAddress(this.locker, authority);
const escrowData = await this.program.account.escrow.fetchNullable(escrow);
if (escrowData) {
return { escrow: escrow, instruction: null };
}
else {
return {
escrow: escrow,
instruction: await this.newEscrowIX(authority),
};
}
}
/**
* Creates the instruction to build a new Escrow.
* @param authority
* @returns
*/
async newEscrowIX(authority = this.sdk.provider.wallet.publicKey) {
const [escrow, bump] = await findEscrowAddress(this.locker, authority);
return this.program.instruction.newEscrow(bump, {
accounts: {
locker: this.locker,
escrow,
escrowOwner: authority,
payer: this.sdk.provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
});
}
async activateProposal({ proposal, authority = this.sdk.provider.wallet.publicKey, }) {
const [escrow] = await findEscrowAddress(this.locker, authority);
const ix = this.program.instruction.activateProposal({
accounts: {
locker: this.locker,
governor: this.governorKey,
proposal,
escrow,
escrowOwner: authority,
governProgram: TRIBECA_ADDRESSES.Govern,
},
});
return new TransactionEnvelope(this.sdk.provider, [ix]);
}
async lockTokensV1({ amount, duration, authority = this.sdk.provider.wallet.publicKey, }) {
invariant(this.locker, "locker not set");
const { escrow, instruction: initEscrowIx } = await this.getOrCreateEscrow(authority);
const { govTokenAccount, govTokenVault, instructions } = await this._getOrCreateGovTokenATAsInternal(authority, escrow);
if (initEscrowIx) {
instructions.push(initEscrowIx);
}
const lockerData = await this.reload();
instructions.push(this.program.instruction.lock(amount, duration, {
accounts: {
locker: this.locker,
escrow: escrow,
escrowOwner: authority,
escrowTokens: govTokenVault,
sourceTokens: govTokenAccount,
tokenProgram: TOKEN_PROGRAM_ID,
},
remainingAccounts: lockerData.params.whitelistEnabled
? [
{
pubkey: SYSVAR_INSTRUCTIONS_PUBKEY,
isSigner: false,
isWritable: false,
},
{
pubkey: PublicKey.default,
isSigner: false,
isWritable: false,
},
]
: [],
}));
return new TransactionEnvelope(this.sdk.provider, instructions);
}
async lockTokens({ amount, duration, authority = this.sdk.provider.wallet.publicKey, }) {
invariant(this.locker, "locker not set");
const { escrow, instruction: initEscrowIx } = await this.getOrCreateEscrow(authority);
const { govTokenAccount, govTokenVault, instructions } = await this._getOrCreateGovTokenATAsInternal(authority, escrow);
if (initEscrowIx) {
instructions.push(initEscrowIx);
}
const lockerData = await this.reload();
const lockAccounts = {
locker: this.locker,
escrow: escrow,
escrowOwner: authority,
escrowTokens: govTokenVault,
sourceTokens: govTokenAccount,
tokenProgram: TOKEN_PROGRAM_ID,
};
if (lockerData.params.whitelistEnabled) {
instructions.push(this.program.instruction.lockWithWhitelist(amount, duration, {
accounts: {
lock: lockAccounts,
instructionsSysvar: SYSVAR_INSTRUCTIONS_PUBKEY,
},
}));
}
else {
instructions.push(this.program.instruction.lockPermissionless(amount, duration, {
accounts: lockAccounts,
}));
}
return new TransactionEnvelope(this.sdk.provider, instructions);
}
async exit({ authority = this.sdk.provider.wallet.publicKey, }) {
invariant(this.locker, "locker not set");
const [escrow] = await findEscrowAddress(this.locker, authority);
const escrowData = await this.fetchEscrow(escrow);
const { govTokenAccount, instructions } = await this._getOrCreateGovTokenATAsInternal(authority, escrow);
instructions.push(this.program.instruction.exit({
accounts: {
locker: this.locker,
escrow,
escrowOwner: authority,
escrowTokens: escrowData.tokens,
destinationTokens: govTokenAccount,
payer: this.sdk.provider.wallet.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
},
}));
return new TransactionEnvelope(this.sdk.provider, instructions);
}
async castVotes({ voteSide, proposal, authority = this.sdk.provider.wallet.publicKey, reason, }) {
const ixs = [];
const { escrow, instruction: escrowIx } = await this.getOrCreateEscrow(authority);
if (escrowIx) {
ixs.push(escrowIx);
}
const { voteKey, instruction: createVoteIX } = await this.governor.getOrCreateVote({
proposal,
voter: authority,
});
if (createVoteIX) {
ixs.push(createVoteIX);
}
ixs.push(this.program.instruction.castVote(voteSide, {
accounts: {
locker: this.locker,
escrow: escrow,
voteDelegate: authority !== null && authority !== void 0 ? authority : this.sdk.provider.wallet.publicKey,
proposal,
vote: voteKey,
governor: this.governorKey,
governProgram: TRIBECA_ADDRESSES.Govern,
},
}));
if (reason === null || reason === void 0 ? void 0 : reason.length) {
ixs.push(createMemoInstruction(reason, [authority]));
}
return new TransactionEnvelope(this.sdk.provider, ixs);
}
async setVoteDelegate(newDelegate, authority = this.sdk.provider.wallet.publicKey) {
const [escrow] = await findEscrowAddress(this.locker, authority);
return new TransactionEnvelope(this.sdk.provider, [
this.program.instruction.setVoteDelegate(newDelegate, {
accounts: {
escrow,
escrowOwner: authority,
},
}),
]);
}
async createApproveProgramLockPrivilegeIx(programId, owner) {
const [whitelistEntry, bump] = await findWhitelistAddress(this.locker, programId, owner);
const lockerData = await this.reload();
const governorData = await this.sdk.programs.Govern.account.governor.fetch(lockerData.governor);
return this.program.instruction.approveProgramLockPrivilege(bump, {
accounts: {
locker: this.locker,
whitelistEntry,
governor: lockerData.governor,
smartWallet: governorData.smartWallet,
executableId: programId,
whitelistedOwner: owner !== null && owner !== void 0 ? owner : SystemProgram.programId,
payer: this.sdk.provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
});
}
async createRevokeProgramLockPrivilegeIx(programId, owner) {
const [whitelistEntry] = await findWhitelistAddress(this.locker, programId, owner);
const lockerData = await this.reload();
const governorData = await this.sdk.programs.Govern.account.governor.fetch(lockerData.governor);
return this.program.instruction.revokeProgramLockPrivilege({
accounts: {
locker: this.locker,
whitelistEntry,
governor: lockerData.governor,
smartWallet: governorData.smartWallet,
payer: this.sdk.provider.wallet.publicKey,
},
});
}
async setLockerParamsIx(args) {
const lockerData = await this.reload();
const governorData = await this.sdk.programs.Govern.account.governor.fetch(lockerData.governor);
return this.program.instruction.setLockerParams(args, {
accounts: {
locker: this.locker,
governor: lockerData.governor,
smartWallet: governorData.smartWallet,
},
});
}
async _getOrCreateGovTokenATAsInternal(authority, escrow) {
const { provider } = this.sdk;
const lockerData = await this.data();
const { address: govTokenAccount, instruction: ix1 } = await getOrCreateATA({
provider,
mint: lockerData.tokenMint,
owner: authority,
payer: authority,
});
const { address: govTokenVault, instruction: ix2 } = await getOrCreateATA({
provider,
mint: lockerData.tokenMint,
owner: escrow,
payer: authority,
});
return {
govTokenAccount,
govTokenVault,
instructions: [ix1, ix2].filter((ix) => !!ix),
};
}
}
//# sourceMappingURL=locker.js.map