Skip to content

Commit

Permalink
Merge pull request #278 from sunrise-stake/feature/add-locked-gsol
Browse files Browse the repository at this point in the history
Feature/add locked gsol
  • Loading branch information
dankelleher authored Oct 2, 2023
2 parents 8b17f52 + a7afc81 commit b845958
Show file tree
Hide file tree
Showing 8 changed files with 435 additions and 1 deletion.
32 changes: 32 additions & 0 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,38 @@ export class SunriseStakeClient {
return transactions;
}

/**
* Add some staked gSOL to locked gSOL
* @param lamports
*/
public async addLockedGSol(lamports: BN): Promise<Transaction[]> {
if (
!this.stakerGSolTokenAccount ||
!this.config ||
!this.marinade ||
!this.marinadeState ||
!this.lockClient
)
throw new Error("init not called");

const transactions: Transaction[] = [];

const recoverInstruction = await this.recoverTickets();

if (recoverInstruction) {
transactions.push(new Transaction().add(recoverInstruction));
}

const lockTx = await this.lockClient.addLockedGSol(
this.stakerGSolTokenAccount,
this.env.impactNFT,
lamports
);
transactions.push(lockTx);

return transactions;
}

/**
* Unlock a user's gSOL so that it can be unstaked
*/
Expand Down
42 changes: 42 additions & 0 deletions packages/client/src/lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,48 @@ export class LockClient {
.transaction();
}

public async addLockedGSol(
sourceGSolTokenAccount: PublicKey,
impactNFTConfig: EnvironmentConfig["impactNFT"],
lamports: BN
): Promise<Transaction> {
if (!this.impactNFTClient) throw new Error("LockClient not initialized");
const [epochReportAccount] = findEpochReportAccount(this.config);

type Accounts = Parameters<
ReturnType<typeof this.program.methods.lockGsol>["accounts"]
>[0];

const preInstructions: TransactionInstruction[] = [];

const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
units: 500000,
});
preInstructions.push(modifyComputeUnits);

const allImpactNFTAccounts = await this.getImpactNFTAccounts();
const accounts: Accounts = {
state: this.config.stateAddress,
gsolMint: this.config.gsolMint,
authority: this.authority,
sourceGsolAccount: sourceGSolTokenAccount,
lockGsolAccount: this.lockTokenAccountAddress,
lockAccount: this.lockAccountAddress,
epochReportAccount,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_PROGRAM_ID,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
clock: SYSVAR_CLOCK_PUBKEY,
...allImpactNFTAccounts,
};

return this.program.methods
.addLockedGsol(lamports)
.accounts(accounts)
.preInstructions(preInstructions)
.transaction();
}

public async unlockGSol(
targetGSolTokenAccount: PublicKey
): Promise<Transaction> {
Expand Down
142 changes: 142 additions & 0 deletions packages/client/src/types/sunrise_stake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1759,6 +1759,77 @@ export type SunriseStake = {
}
]
},
{
"name": "addLockedGsol",
"accounts": [
{
"name": "state",
"isMut": false,
"isSigner": false
},
{
"name": "gsolMint",
"isMut": false,
"isSigner": false
},
{
"name": "payer",
"isMut": true,
"isSigner": true
},
{
"name": "authority",
"isMut": true,
"isSigner": true
},
{
"name": "lockAccount",
"isMut": true,
"isSigner": false
},
{
"name": "sourceGsolAccount",
"isMut": true,
"isSigner": false
},
{
"name": "lockGsolAccount",
"isMut": true,
"isSigner": false
},
{
"name": "epochReportAccount",
"isMut": false,
"isSigner": false
},
{
"name": "clock",
"isMut": false,
"isSigner": false
},
{
"name": "systemProgram",
"isMut": false,
"isSigner": false
},
{
"name": "tokenProgram",
"isMut": false,
"isSigner": false
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "lamports",
"type": "u64"
}
]
},
{
"name": "unlockGsol",
"accounts": [
Expand Down Expand Up @@ -4383,6 +4454,77 @@ export const IDL: SunriseStake = {
}
]
},
{
"name": "addLockedGsol",
"accounts": [
{
"name": "state",
"isMut": false,
"isSigner": false
},
{
"name": "gsolMint",
"isMut": false,
"isSigner": false
},
{
"name": "payer",
"isMut": true,
"isSigner": true
},
{
"name": "authority",
"isMut": true,
"isSigner": true
},
{
"name": "lockAccount",
"isMut": true,
"isSigner": false
},
{
"name": "sourceGsolAccount",
"isMut": true,
"isSigner": false
},
{
"name": "lockGsolAccount",
"isMut": true,
"isSigner": false
},
{
"name": "epochReportAccount",
"isMut": false,
"isSigner": false
},
{
"name": "clock",
"isMut": false,
"isSigner": false
},
{
"name": "systemProgram",
"isMut": false,
"isSigner": false
},
{
"name": "tokenProgram",
"isMut": false,
"isSigner": false
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "lamports",
"type": "u64"
}
]
},
{
"name": "unlockGsol",
"accounts": [
Expand Down
53 changes: 53 additions & 0 deletions packages/tests/impact-nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ describe("Impact NFTs", () => {

const details = await client.details();
expect(details.impactNFTDetails?.tokenAccount).to.exist;
expect(details.lockDetails?.amountLocked.toNumber()).to.eq(
lockLamports.toNumber()
);
expect(details.lockDetails?.yield.toNumber()).to.eq(0);
});

it("cannot re-lock", async () => {
Expand Down Expand Up @@ -154,6 +158,55 @@ describe("Impact NFTs", () => {
expect(details.lockDetails?.yield?.toNumber()).to.equal(expectedYield);
});

it("can add gSOL to existing locked account", async () => {
const transactions = await client.addLockedGSol(lockLamports);
await client.sendAndConfirmTransactions(
transactions,
undefined,
undefined,
true
);

const details = await client.details();
expect(details.lockDetails?.amountLocked.toNumber()).to.eq(
lockLamports.add(lockLamports).toNumber()
);
expect(details.impactNFTDetails?.tokenAccount).to.exist;
});

it("registers add gSOL correctly", async () => {
await burnGSol(new BN(burnLamports), client);

// Switch to the next epoch so that the lock account can be updated and the yield applied
await waitForNextEpoch(client);

await client.sendAndConfirmTransactions(await client.updateLockAccount());

const currentEpoch = await client.provider.connection.getEpochInfo();
const details = await client.details();

expectedYield = details.lockDetails!.yield.toNumber();
expect(details.lockDetails?.updatedToEpoch.toNumber()).to.equal(
currentEpoch.epoch
);
// the exact number varies depending on other tests
// expected yield = 1 * 0.11 SOL + 2 * 0.125 SOL
expect(expectedYield).to.be.greaterThan(360000000);
});

it("cannot add gSOL to existing locked account if epoch is not updated", async () => {
await waitForNextEpoch(client);
const transactions = await client.addLockedGSol(lockLamports);
const shouldFail = client.sendAndConfirmTransactions(
transactions,
undefined,
undefined,
true
);

return expect(shouldFail).to.be.rejected;
});

it("updating again after the next epoch has no effect if no more yield is added", async () => {
await waitForNextEpoch(client);

Expand Down
Loading

0 comments on commit b845958

Please sign in to comment.