Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: supporting unlocked with withdrawal period #6

Merged
merged 19 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ js/lib

/node_modules

js/dist
js/dist
**/wallet.json
50 changes: 10 additions & 40 deletions js/src/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,7 @@ const wallet = Keypair.fromSecretKey(
);

/** There are better way to generate an array of dates but be careful as it's irreversible */
const DATES = [
new Date(2022, 12),
new Date(2023, 1),
new Date(2023, 2),
new Date(2023, 3),
new Date(2023, 4),
new Date(2023, 5),
new Date(2023, 6),
new Date(2023, 7),
new Date(2023, 8),
new Date(2023, 9),
new Date(2023, 10),
new Date(2023, 11),
new Date(2024, 12),
new Date(2024, 2),
new Date(2024, 3),
new Date(2024, 4),
new Date(2024, 5),
new Date(2024, 6),
new Date(2024, 7),
new Date(2024, 8),
new Date(2024, 9),
new Date(2024, 10),
new Date(2024, 11),
new Date(2024, 12),
];
const DATE = new Date(2022, 12);

/** Info about the desintation */
const DESTINATION_OWNER = new PublicKey('');
Expand Down Expand Up @@ -86,19 +61,14 @@ const checks = async () => {
/** Function that locks the tokens */
const lock = async () => {
await checks();
const schedules: Schedule[] = [];
for (let date of DATES) {
schedules.push(
new Schedule(
/** Has to be in seconds */
// @ts-ignore
new Numberu64(date.getTime() / 1_000),
/** Don't forget to add decimals */
// @ts-ignore
new Numberu64(AMOUNT_PER_SCHEDULE * Math.pow(10, DECIMALS)),
),
);
}
const schedule: Schedule = new Schedule(
/** Has to be in seconds */
// @ts-ignore
new Numberu64(DATE.getTime() / 1_000),
/** Don't forget to add decimals */
// @ts-ignore
new Numberu64(AMOUNT_PER_SCHEDULE * Math.pow(10, DECIMALS)),
);
const seed = generateRandomSeed();

console.log(`Seed: ${seed}`);
Expand All @@ -112,7 +82,7 @@ const lock = async () => {
SOURCE_TOKEN_ACCOUNT,
DESTINATION_TOKEN_ACCOUNT,
MINT,
schedules,
schedule,
);

const tx = await signAndSendInstructions(connection, [], wallet, instruction);
Expand Down
59 changes: 51 additions & 8 deletions js/src/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ export function createInitInstruction(
vestingProgramId: PublicKey,
payerKey: PublicKey,
vestingAccountKey: PublicKey,
seeds: Array<Buffer | Uint8Array>,
numberOfSchedules: number,
seeds: Array<Buffer | Uint8Array>
): TransactionInstruction {
let buffers = [
Buffer.from(Int8Array.from([0]).buffer),
Buffer.concat(seeds),
// @ts-ignore
new Numberu32(numberOfSchedules).toBuffer(),
];

const data = Buffer.concat(buffers);
Expand Down Expand Up @@ -62,7 +59,7 @@ export function createCreateInstruction(
sourceTokenAccountKey: PublicKey,
destinationTokenAccountKey: PublicKey,
mintAddress: PublicKey,
schedules: Array<Schedule>,
schedule: Schedule,
seeds: Array<Buffer | Uint8Array>,
): TransactionInstruction {
let buffers = [
Expand All @@ -72,9 +69,7 @@ export function createCreateInstruction(
destinationTokenAccountKey.toBuffer(),
];

schedules.forEach(s => {
buffers.push(s.toBuffer());
});
buffers.push(schedule.toBuffer());

const data = Buffer.concat(buffers);
const keys = [
Expand Down Expand Up @@ -158,3 +153,51 @@ export function createUnlockInstruction(
data,
});
}

export function createInitializeUnlockInstruction(
vestingProgramId: PublicKey,
tokenProgramId: PublicKey,
clockSysvarId: PublicKey,
vestingAccountKey: PublicKey,
vestingTokenAccountKey: PublicKey,
destinationTokenAccountKey: PublicKey,
seeds: Array<Buffer | Uint8Array>,
): TransactionInstruction {
const data = Buffer.concat([
Buffer.from(Int8Array.from([3]).buffer),
Buffer.concat(seeds),
]);

const keys = [
{
pubkey: tokenProgramId,
isSigner: false,
isWritable: false,
},
{
pubkey: clockSysvarId,
isSigner: false,
isWritable: false,
},
{
pubkey: vestingAccountKey,
isSigner: false,
isWritable: true,
},
{
pubkey: vestingTokenAccountKey,
isSigner: false,
isWritable: true,
},
{
pubkey: destinationTokenAccountKey,
isSigner: false,
isWritable: true,
},
];
return new TransactionInstruction({
keys,
programId: vestingProgramId,
data,
});
}
54 changes: 49 additions & 5 deletions js/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
createCreateInstruction,
createInitInstruction,
createUnlockInstruction,
createInitializeUnlockInstruction,
} from './instructions';
import { ContractInfo, Schedule } from './state';
import { assert } from 'console';
Expand All @@ -36,7 +37,7 @@ export const TOKEN_VESTING_PROGRAM_ID = new PublicKey(
* @param possibleSourceTokenPubkey The source token account (i.e where locked tokens are originating from), if null it defaults to the ATA
* @param destinationTokenPubkey The destination token account i.e where unlocked tokens will be transfered
* @param mintAddress The mint of the tokens being vested
* @param schedules The array of vesting schedules
* @param schedule The vesting schedule
* @returns An array of `TransactionInstruction`
*/
export async function create(
Expand All @@ -48,7 +49,7 @@ export async function create(
possibleSourceTokenPubkey: PublicKey | null,
destinationTokenPubkey: PublicKey,
mintAddress: PublicKey,
schedules: Array<Schedule>,
schedule: Schedule,
): Promise<Array<TransactionInstruction>> {
// If no source token account was given, use the associated source account
if (possibleSourceTokenPubkey == null) {
Expand Down Expand Up @@ -92,8 +93,7 @@ export async function create(
programId,
payer,
vestingAccountKey,
[seedWord],
schedules.length,
[seedWord]
),
createAssociatedTokenAccountInstruction(
payer,
Expand All @@ -110,7 +110,7 @@ export async function create(
possibleSourceTokenPubkey,
destinationTokenPubkey,
mintAddress,
schedules,
schedule,
[seedWord],
),
];
Expand Down Expand Up @@ -161,6 +161,50 @@ export async function unlock(
return instruction;
}

/**
* This function can be used to initialize the unlock of vested tokens
* @param connection The Solana RPC connection object
* @param programId The token vesting program ID
* @param seedWord Seed words used to derive the vesting account
* @param mintAddress The mint of the vested tokens
* @returns An array of `TransactionInstruction`
*/
export async function initializeUnlock(
connection: Connection,
programId: PublicKey,
seedWord: Buffer | Uint8Array,
mintAddress: PublicKey,
): Promise<Array<TransactionInstruction>> {
seedWord = seedWord.slice(0, 31);
const [vestingAccountKey, bump] = await PublicKey.findProgramAddress(
[seedWord],
programId,
);
seedWord = Buffer.from(seedWord.toString('hex') + bump.toString(16), 'hex');

const vestingTokenAccountKey = await getAssociatedTokenAddress(
mintAddress,
vestingAccountKey,
true,
);

const vestingInfo = await getContractInfo(connection, vestingAccountKey);

let instruction = [
createInitializeUnlockInstruction(
programId,
TOKEN_PROGRAM_ID,
SYSVAR_CLOCK_PUBKEY,
vestingAccountKey,
vestingTokenAccountKey,
vestingInfo.destinationAddress,
[seedWord],
),
];

return instruction;
}

/**
* This function can be used retrieve information about a vesting account
* @param connection The Solana RPC connection object
Expand Down
24 changes: 24 additions & 0 deletions program/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ arbitrary = { version = "0.4", features = ["derive"], optional = true }
honggfuzz = { version = "0.5", optional = true }

[dev-dependencies]
solana-sdk = "1.5.6"
solana-program-test = "1.5.6"
solana-sdk = "1.18.23"
solana-program-test = "1.18.23"
solana-test-framework = { git = "https://github.com/halbornteam/solana-test-framework", branch = "solana1.18" }
tokio = { version = "1.0", features = ["macros"]}

[lib]
Expand Down
Loading
Loading