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: deprecating changing vest ownership #3

Merged
merged 4 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 0 additions & 12 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,6 @@ info \
--seed $SEED " | bash
```

Change owner:
```bash
echo "RUST_BACKTRACE=1 ./target/debug/vesting-contract-cli \
--url https://api.devnet.solana.com \
--program_id $PROGRAM_ID \
change-destination \
--seed $SEED \
--current_destination_owner ~/.config/solana/id_dest.json \
--new_destination_token_address $ACCOUNT_TOKEN_NEW_DEST \
--payer ~/.config/solana/id_owner.json" | bash
```

And unlock tokens according schedule:
```bash
echo "RUST_BACKTRACE=1 ./target/debug/vesting-contract-cli \
Expand Down
126 changes: 1 addition & 125 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use spl_associated_token_account::{create_associated_token_account, get_associat
use spl_token;
use std::convert::TryInto;
use token_vesting::{
instruction::{change_destination, create, init, unlock, Schedule},
instruction::{create, init, unlock, Schedule},
state::{unpack_schedules, VestingScheduleHeader},
};

Expand Down Expand Up @@ -147,52 +147,6 @@ fn command_unlock_svc(
rpc_client.send_transaction(&transaction).unwrap();
}

fn command_change_destination(
rpc_client: RpcClient,
program_id: Pubkey,
destination_token_account_owner: Keypair,
opt_new_destination_account: Option<Pubkey>,
opt_new_destination_token_account: Option<Pubkey>,
vesting_seed: [u8; 32],
payer: Keypair,
) {
// Find the non reversible public key for the vesting contract via the seed
let (vesting_pubkey, _) = Pubkey::find_program_address(&[&vesting_seed[..31]], &program_id);

let packed_state = rpc_client.get_account_data(&vesting_pubkey).unwrap();
let state_header =
VestingScheduleHeader::unpack(&packed_state[..VestingScheduleHeader::LEN]).unwrap();
let destination_token_pubkey = state_header.destination_address;

let new_destination_token_account = match opt_new_destination_token_account {
None => get_associated_token_address(
&opt_new_destination_account.unwrap(),
&state_header.mint_address,
),
Some(new_destination_token_account) => new_destination_token_account,
};

let unlock_instruction = change_destination(
&program_id,
&vesting_pubkey,
&destination_token_account_owner.pubkey(),
&destination_token_pubkey,
&new_destination_token_account,
vesting_seed,
)
.unwrap();

let mut transaction = Transaction::new_with_payer(&[unlock_instruction], Some(&payer.pubkey()));

let recent_blockhash = rpc_client.get_recent_blockhash().unwrap().0;
transaction.sign(
&[&payer, &destination_token_account_owner],
recent_blockhash,
);

rpc_client.send_transaction(&transaction).unwrap();
}

fn command_info(
rpc_client: RpcClient,
rpc_url: String,
Expand Down Expand Up @@ -445,66 +399,6 @@ fn main() {
),
)
)
.subcommand(SubCommand::with_name("change-destination").about("Change the destination of a vesting contract")
.arg(
Arg::with_name("seed")
.long("seed")
.value_name("SEED")
.validator(is_parsable::<String>)
.takes_value(true)
.help(
"Specify the seed for the vesting contract.",
),
)
.arg(
Arg::with_name("current_destination_owner")
.long("current_destination_owner")
.value_name("KEYPAIR")
.validator(is_keypair)
.takes_value(true)
.help(
"Specify the current destination owner account keypair. \
This may be a keypair file, the ASK keyword. \
Defaults to the client keypair.",
),
)
.arg(
Arg::with_name("new_destination_address")
.long("new_destination_address")
.value_name("ADDRESS")
.validator(is_pubkey)
.takes_value(true)
.help(
"Specify the new destination (non-token) account address. \
If specified, the vesting destination will be the associated \
token account for the mint of the contract."
),
)
.arg(
Arg::with_name("new_destination_token_address")
.long("new_destination_token_address")
.value_name("ADDRESS")
.validator(is_pubkey)
.takes_value(true)
.help(
"Specify the new destination token account address. \
If specified, this address will be used as a destination, \
and overwrite the associated token account.",
),
)
.arg(
Arg::with_name("payer")
.long("payer")
.value_name("KEYPAIR")
.validator(is_keypair)
.takes_value(true)
.help(
"Specify the transaction fee payer account address. \
This may be a keypair file, the ASK keyword. \
Defaults to the client keypair.",
),
)
)
.subcommand(SubCommand::with_name("info").about("Print information about a vesting contract")
.arg(
Arg::with_name("seed")
Expand Down Expand Up @@ -629,24 +523,6 @@ fn main() {
let payer_keypair = keypair_of(arg_matches, "payer").unwrap();
command_unlock_svc(rpc_client, program_id, vesting_seed, payer_keypair)
}
("change-destination", Some(arg_matches)) => {
let vesting_seed = pubkey_of(arg_matches, "seed").unwrap().to_bytes();
let destination_account_owner =
keypair_of(arg_matches, "current_destination_owner").unwrap();
let opt_new_destination_account = pubkey_of(arg_matches, "new_destination_address");
let opt_new_destination_token_account =
pubkey_of(arg_matches, "new_destination_token_address");
let payer_keypair = keypair_of(arg_matches, "payer").unwrap();
command_change_destination(
rpc_client,
program_id,
destination_account_owner,
opt_new_destination_account,
opt_new_destination_token_account,
vesting_seed,
payer_keypair,
)
}
("info", Some(arg_matches)) => {
let vesting_seed = pubkey_of(arg_matches, "seed").unwrap().to_bytes();
let rpcurl = value_of(arg_matches, "rpc_url").unwrap();
Expand Down
4 changes: 0 additions & 4 deletions js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ The code allows you to

- Create vesting instructions for any SPL token: `createCreateInstruction`
- Create unlock instructions: `createUnlockInstruction`
- Change the destination of the vested tokens: `createChangeDestinationInstruction`

(To import Solana accounts created with [Sollet](https://sollet.io) you can use `getAccountFromSeed`)

Expand All @@ -37,7 +36,4 @@ Fetching contract r2p2mLJvyrTzetxxsttQ54CS1m18zMgYqKSRzxP9WpE
✅ Successfully created unlocking instructions
🚚 Transaction signature: 2Vg3W1w8WBdRAWBEwFTn2BtMkKPD3Xor7SRvzC193UnsUnhmneUChPHe7vLF9Lfw9BKxWH5JbbJmnda4XztHMVHz

Fetching contract r2p2mLJvyrTzetxxsttQ54CS1m18zMgYqKSRzxP9WpE
✅ Successfully changed destination
🚚 Transaction signature: 4tgPgCdM5ubaSKNLKD1WrfAJPZgRajxRSnmcPkHcN1TCeCRmq3cUCYVdCzsYwr63JRf4D2K1UZnt8rwu2pkGxeYe
```
42 changes: 0 additions & 42 deletions js/src/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,45 +158,3 @@ export function createUnlockInstruction(
data,
});
}

export function createChangeDestinationInstruction(
vestingProgramId: PublicKey,
vestingAccountKey: PublicKey,
currentDestinationTokenAccountOwner: PublicKey,
currentDestinationTokenAccount: PublicKey,
targetDestinationTokenAccount: PublicKey,
seeds: Array<Buffer | Uint8Array>,
): TransactionInstruction {
const data = Buffer.concat([
Buffer.from(Int8Array.from([3]).buffer),
Buffer.concat(seeds),
]);

const keys = [
{
pubkey: vestingAccountKey,
isSigner: false,
isWritable: true,
},
{
pubkey: currentDestinationTokenAccount,
isSigner: false,
isWritable: false,
},
{
pubkey: currentDestinationTokenAccountOwner,
isSigner: true,
isWritable: false,
},
{
pubkey: targetDestinationTokenAccount,
isSigner: false,
isWritable: false,
},
];
return new TransactionInstruction({
keys,
programId: vestingProgramId,
data,
});
}
54 changes: 1 addition & 53 deletions js/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
getAssociatedTokenAddress,
} from '@solana/spl-token';
import {
createChangeDestinationInstruction,
createCreateInstruction,
createInitInstruction,
createUnlockInstruction,
Expand Down Expand Up @@ -185,55 +184,4 @@ export async function getContractInfo(
throw new Error('Vesting contract account is not initialized');
}
return info!;
}

/**
* This function can be used to transfer a vesting account to a new wallet. It requires the current owner to sign.
* @param connection The Solana RPC connection object
* @param programId The token vesting program ID
* @param currentDestinationTokenAccountPublicKey The current token account to which the vested tokens are transfered to as they unlock
* @param newDestinationTokenAccountOwner The new owner of the vesting account
* @param newDestinationTokenAccount The new token account to which the vested tokens will be transfered to as they unlock
* @param vestingSeed Seed words used to derive the vesting account
* @returns An array of `TransactionInstruction`
*/
export async function changeDestination(
connection: Connection,
programId: PublicKey,
currentDestinationTokenAccountPublicKey: PublicKey,
newDestinationTokenAccountOwner: PublicKey | undefined,
newDestinationTokenAccount: PublicKey | undefined,
vestingSeed: Array<Buffer | Uint8Array>,
): Promise<Array<TransactionInstruction>> {
let seedWord = vestingSeed[0];
seedWord = seedWord.slice(0, 31);
const [vestingAccountKey, bump] = await PublicKey.findProgramAddress(
[seedWord],
programId,
);
seedWord = Buffer.from(seedWord.toString('hex') + bump.toString(16), 'hex');

const contractInfo = await getContractInfo(connection, vestingAccountKey);
if (!newDestinationTokenAccount) {
assert(
!!newDestinationTokenAccountOwner,
'At least one of newDestinationTokenAccount and newDestinationTokenAccountOwner must be provided!',
);
newDestinationTokenAccount = await getAssociatedTokenAddress(
contractInfo.mintAddress,
newDestinationTokenAccountOwner!,
true,
);
}

return [
createChangeDestinationInstruction(
programId,
vestingAccountKey,
currentDestinationTokenAccountPublicKey,
contractInfo.destinationAddress,
newDestinationTokenAccount,
[seedWord],
),
];
}
}
Loading