-
Notifications
You must be signed in to change notification settings - Fork 1
/
transferOwnership.ts
162 lines (139 loc) · 6.72 KB
/
transferOwnership.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import * as anchor from "@project-serum/anchor";
import { Connection as solanaConnection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import idl from "./idl.json";
import * as multisig from "@sqds/multisig";
const fs = require('fs');
(async () => {
// TODO: needs to be token owner & creator of the Squads multisig
const tokenOwnerWalletPath = "ww4AoktpBksE1M4zk6vWxujbqtaqhGY59VMuAxV4yxq.json";
const walletJSON = JSON.parse(fs.readFileSync(tokenOwnerWalletPath, "utf-8"));
const walletKeypair = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(walletJSON));
// TODO: change to your NTT manager address
const nttManagerProgramId = "ntTPvQLdTaMUXNX1LBbAvbz9i2GyH3KwB8Q3NFDZhzK";
const nttManagerProgramIdKey = new PublicKey(nttManagerProgramId);
const solanaCon = new solanaConnection("https://api.devnet.solana.com");
const [configPublicKey, _configPublicKeyBump] = await PublicKey.findProgramAddress(
[Buffer.from("config")],
nttManagerProgramIdKey
);
// claiming ownership from temporary account with squads sdk!
// TODO: change to your squads pubkey
const squadsAddress = new PublicKey("squri3LHQffRXoy3ZFbNCWg2Ck38jzcNXNQJQQJzPEa");
// Derive the PDA of the Squads Vault
// this is going to be the Upgrade authority address, which is controlled by the Squad!
const [vaultPda] = multisig.getVaultPda({
multisigPda: squadsAddress,
index: 0,
});
console.log(vaultPda);
// temporary pda, needed before claim instruction
const [upgradeLockPublicKey, _upgradeLockPublicKey] = await PublicKey.findProgramAddress(
[Buffer.from("upgrade_lock")],
nttManagerProgramIdKey
);
// The programDataPublicKey is the PDA that stores the program's data
const bpfLoaderUpgradeableProgramPublicKey = new PublicKey("BPFLoaderUpgradeab1e11111111111111111111111");
const [programDataPublicKey, _programDataBump] = await PublicKey.findProgramAddress(
[nttManagerProgramIdKey.toBuffer()],
bpfLoaderUpgradeableProgramPublicKey
);
const anchorConnection = new anchor.web3.Connection(anchor.web3.clusterApiUrl("devnet"), "confirmed");
const wallet = new anchor.Wallet(walletKeypair);
const provider = new anchor.AnchorProvider(anchorConnection, wallet, { preflightCommitment: "confirmed" });
anchor.setProvider(provider);
const program = new anchor.Program(idl as anchor.Idl, nttManagerProgramId, provider);
// delegate ownership to a temporary account!
await program.methods
.transferOwnership()
.accounts({
config: configPublicKey,
owner: wallet.publicKey,
newOwner: vaultPda,
upgradeLock: upgradeLockPublicKey,
programData: programDataPublicKey,
bpfLoaderUpgradeableProgram: bpfLoaderUpgradeableProgramPublicKey,
})
.signers([wallet.payer]).rpc();
// this needs to be someone who has permissions to sign transactions for the squad!
const squadMember = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(walletJSON));
// Get deserialized multisig account info
const multisigInfo = await multisig.accounts.Multisig.fromAccountAddress(
solanaCon,
squadsAddress
);
// Get the updated transaction index
const currentTransactionIndex = Number(multisigInfo.transactionIndex);
const newTransactionIndex = BigInt(currentTransactionIndex + 1);
// this transaction gets wrapped and send to the vault of the squads to be signed there
const instructionClaim = await program.methods
.claimOwnership()
.accounts({
config: configPublicKey,
upgradeLock: upgradeLockPublicKey,
newOwner: vaultPda,
programData: programDataPublicKey,
bpfLoaderUpgradeableProgram: bpfLoaderUpgradeableProgramPublicKey,
}).instruction();
// Build a message with instructions we want to execute
const testClaimMessage = new TransactionMessage({
payerKey: vaultPda,
recentBlockhash: (await solanaCon.getLatestBlockhash()).blockhash,
instructions: [instructionClaim],
});
const uploadTransactionIx = await multisig.instructions.vaultTransactionCreate({
multisigPda: squadsAddress,
// every squad has a global counter for transactions
transactionIndex: newTransactionIndex,
creator: squadMember.publicKey,
vaultIndex: 0,
ephemeralSigners: 0,
transactionMessage: testClaimMessage,
});
// proposal is squad specific!
const createProposalIx = multisig.instructions.proposalCreate({
multisigPda: squadsAddress,
transactionIndex: newTransactionIndex,
creator: squadMember.publicKey
});
/*
ONLY DEVNET VERSION
transferOwnershipMainnet.ts can be used with Squads UI for Mainnet!!
*/
// proposalApprove method needs to be executed for every member of the squad!
// only needed for testing purposes, if on devnet.
// Squads UI is only available on mainnet, which can be used instead!
const createApproveIx = multisig.instructions.proposalApprove({
multisigPda: squadsAddress,
transactionIndex: newTransactionIndex,
member: squadMember.publicKey
});
const finalTxMessage = new TransactionMessage({
payerKey: squadMember.publicKey,
recentBlockhash: (await solanaCon.getLatestBlockhash()).blockhash,
instructions: [uploadTransactionIx, createProposalIx, createApproveIx],
}).compileToV0Message();
const transaction = new VersionedTransaction(finalTxMessage);
// needs to be signed by as many squads members to reach threshold,
// for that we also execute the proposalApprove method
transaction.sign([squadMember]);
const signature = await solanaCon.sendTransaction(transaction);
await solanaCon.confirmTransaction(signature);
const executeClaimIx = await multisig.instructions.vaultTransactionExecute({
connection: solanaCon,
multisigPda: squadsAddress,
transactionIndex: newTransactionIndex,
member: squadMember.publicKey
});
const executeFinalTx = new TransactionMessage({
payerKey: squadMember.publicKey,
recentBlockhash: (await solanaCon.getLatestBlockhash()).blockhash,
instructions: [executeClaimIx.instruction],
}).compileToV0Message(executeClaimIx.lookupTableAccounts);
const transactionFinal = new VersionedTransaction(executeFinalTx);
// needs to be signed by as many squads members to reach threshold,
// for that we also execute the proposalApprove method
transactionFinal.sign([squadMember]);
const signatureFinal = await solanaCon.sendTransaction(transactionFinal);
await solanaCon.confirmTransaction(signatureFinal);
console.log("Ownership transfer on devnet completed successfully.");
})();