Skip to content

Commit 02eec65

Browse files
committed
fix
1 parent 73ff1ac commit 02eec65

1 file changed

Lines changed: 83 additions & 32 deletions

File tree

examples/oft-solana/tasks/solana/updateMetadata.ts

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ import {
22
UpdateV1InstructionAccounts,
33
UpdateV1InstructionArgs,
44
fetchMetadataFromSeeds,
5+
safeFetchMetadataFromSeeds,
56
updateV1,
67
} from '@metaplex-foundation/mpl-token-metadata'
78
import { publicKey, transactionBuilder } from '@metaplex-foundation/umi'
89
import { toWeb3JsTransaction } from '@metaplex-foundation/umi-web3js-adapters'
9-
import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID, createUpdateFieldInstruction } from '@solana/spl-token'
10+
import {
11+
TOKEN_2022_PROGRAM_ID,
12+
TOKEN_PROGRAM_ID,
13+
createUpdateFieldInstruction,
14+
getTokenMetadata,
15+
} from '@solana/spl-token'
1016
import { Keypair, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction } from '@solana/web3.js'
1117
import bs58 from 'bs58'
1218
import { task } from 'hardhat/config'
@@ -26,9 +32,9 @@ interface UpdateMetadataTaskArgs {
2632
vaultPda: string
2733
}
2834

29-
// Auto-detects metadata type based on mint owner:
30-
// - TOKEN_PROGRAM_ID -> Metaplex metadata
31-
// - TOKEN_2022_PROGRAM_ID -> Token Extensions metadata
35+
// Auto-detects metadata type:
36+
// - SPL Token mints (TOKEN_PROGRAM_ID) -> always use Metaplex metadata
37+
// - Token-2022 mints -> probe for Metaplex metadata PDA first; if exists, use Metaplex; otherwise use Token Extensions
3238
//
3339
// Example:
3440
// pnpm hardhat lz:oft:solana:update-metadata --eid <EID> --mint <MINT_ADDRESS> --name <NEW_TOKEN_NAME>
@@ -49,7 +55,7 @@ task(
4955
async ({ eid, name, mint: mintStr, sellerFeeBasisPoints, symbol, uri, vaultPda }: UpdateMetadataTaskArgs) => {
5056
const isTestnet = eid == EndpointId.SOLANA_V2_TESTNET
5157

52-
const { connection } = await deriveConnection(eid)
58+
const { umi, connection } = await deriveConnection(eid)
5359
const mintPubkey = new PublicKey(mintStr)
5460
const mintAccountInfo = await connection.getAccountInfo(mintPubkey)
5561

@@ -59,18 +65,8 @@ task(
5965

6066
const mintOwner = mintAccountInfo.owner.toBase58()
6167

62-
if (mintOwner === TOKEN_2022_PROGRAM_ID.toBase58()) {
63-
console.log('Detected Token-2022 mint, using Token Extensions metadata')
64-
await updateTokenExtensionsMetadata({
65-
eid,
66-
mintStr,
67-
name,
68-
symbol,
69-
uri,
70-
vaultPda,
71-
isTestnet,
72-
})
73-
} else if (mintOwner === TOKEN_PROGRAM_ID.toBase58()) {
68+
if (mintOwner === TOKEN_PROGRAM_ID.toBase58()) {
69+
// SPL Token mints always use Metaplex metadata
7470
console.log('Detected SPL Token mint, using Metaplex metadata')
7571
await updateMetaplexMetadata({
7672
eid,
@@ -82,6 +78,35 @@ task(
8278
vaultPda,
8379
isTestnet,
8480
})
81+
} else if (mintOwner === TOKEN_2022_PROGRAM_ID.toBase58()) {
82+
// Token-2022 mints could use either Metaplex or Token Extensions metadata
83+
// Probe for Metaplex metadata PDA first
84+
const metaplexMetadata = await safeFetchMetadataFromSeeds(umi, { mint: publicKey(mintStr) })
85+
86+
if (metaplexMetadata) {
87+
console.log('Detected Token-2022 mint with Metaplex metadata')
88+
await updateMetaplexMetadata({
89+
eid,
90+
mintStr,
91+
name,
92+
symbol,
93+
uri,
94+
sellerFeeBasisPoints,
95+
vaultPda,
96+
isTestnet,
97+
})
98+
} else {
99+
console.log('Detected Token-2022 mint with Token Extensions metadata')
100+
await updateTokenExtensionsMetadata({
101+
eid,
102+
mintStr,
103+
name,
104+
symbol,
105+
uri,
106+
vaultPda,
107+
isTestnet,
108+
})
109+
}
85110
} else {
86111
throw new Error(`Unknown mint owner program: ${mintOwner}`)
87112
}
@@ -183,7 +208,23 @@ async function updateTokenExtensionsMetadata({
183208

184209
const keypair = privateKeyStr ? Keypair.fromSecretKey(bs58.decode(privateKeyStr)) : undefined
185210
const mintPubkey = new PublicKey(mintStr)
186-
const updateAuthority = vaultPda ? new PublicKey(vaultPda) : keypair!.publicKey
211+
const expectedAuthority = vaultPda ? new PublicKey(vaultPda) : keypair!.publicKey
212+
213+
// Fetch Token Extensions metadata to verify update authority
214+
const tokenMetadata = await getTokenMetadata(connection, mintPubkey, 'confirmed', TOKEN_2022_PROGRAM_ID)
215+
if (!tokenMetadata) {
216+
throw new Error(`Token Extensions metadata not found for mint: ${mintStr}`)
217+
}
218+
219+
// Verify update authority
220+
if (!tokenMetadata.updateAuthority) {
221+
throw new Error('Metadata has no update authority set (metadata is immutable)')
222+
}
223+
if (tokenMetadata.updateAuthority.toBase58() !== expectedAuthority.toBase58()) {
224+
throw new Error(
225+
`Update authority mismatch. Expected: ${expectedAuthority.toBase58()}, Actual: ${tokenMetadata.updateAuthority.toBase58()}`
226+
)
227+
}
187228

188229
// Get current mint account info to calculate rent difference
189230
const mintAccountInfo = await connection.getAccountInfo(mintPubkey)
@@ -196,16 +237,26 @@ async function updateTokenExtensionsMetadata({
196237

197238
// Calculate additional bytes needed for new values and add rent if needed
198239
// Token Extensions metadata may need more space when updating to longer values
199-
if (!vaultPda) {
200-
// Estimate new total size: current size + length increases for each field
201-
// Add a buffer of 100 bytes to be safe
202-
const estimatedNewSize =
203-
mintAccountInfo.data.length + (name?.length ?? 0) + (symbol?.length ?? 0) + (uri?.length ?? 0) + 100
204-
const rentExemptLamports = await connection.getMinimumBalanceForRentExemption(estimatedNewSize)
205-
const currentLamports = mintAccountInfo.lamports
206-
const additionalLamports = rentExemptLamports - currentLamports
207-
208-
if (additionalLamports > 0) {
240+
// Estimate new total size: current size + length increases for each field
241+
// Add a buffer of 100 bytes to be safe
242+
const estimatedNewSize =
243+
mintAccountInfo.data.length + (name?.length ?? 0) + (symbol?.length ?? 0) + (uri?.length ?? 0) + 100
244+
const rentExemptLamports = await connection.getMinimumBalanceForRentExemption(estimatedNewSize)
245+
const currentLamports = mintAccountInfo.lamports
246+
const additionalLamports = rentExemptLamports - currentLamports
247+
248+
if (additionalLamports > 0) {
249+
if (vaultPda) {
250+
// For multisig, include rent transfer from vault to mint
251+
console.log(`Adding ${additionalLamports} lamports rent transfer from vault to mint`)
252+
instructions.push(
253+
SystemProgram.transfer({
254+
fromPubkey: new PublicKey(vaultPda),
255+
toPubkey: mintPubkey,
256+
lamports: additionalLamports,
257+
})
258+
)
259+
} else {
209260
console.log(`Adding ${additionalLamports} lamports for additional rent`)
210261
instructions.push(
211262
SystemProgram.transfer({
@@ -222,7 +273,7 @@ async function updateTokenExtensionsMetadata({
222273
createUpdateFieldInstruction({
223274
programId: TOKEN_2022_PROGRAM_ID,
224275
metadata: mintPubkey,
225-
updateAuthority,
276+
updateAuthority: expectedAuthority,
226277
field: 'name',
227278
value: name,
228279
})
@@ -234,7 +285,7 @@ async function updateTokenExtensionsMetadata({
234285
createUpdateFieldInstruction({
235286
programId: TOKEN_2022_PROGRAM_ID,
236287
metadata: mintPubkey,
237-
updateAuthority,
288+
updateAuthority: expectedAuthority,
238289
field: 'symbol',
239290
value: symbol,
240291
})
@@ -246,7 +297,7 @@ async function updateTokenExtensionsMetadata({
246297
createUpdateFieldInstruction({
247298
programId: TOKEN_2022_PROGRAM_ID,
248299
metadata: mintPubkey,
249-
updateAuthority,
300+
updateAuthority: expectedAuthority,
250301
field: 'uri',
251302
value: uri,
252303
})
@@ -260,7 +311,7 @@ async function updateTokenExtensionsMetadata({
260311
const { blockhash } = await connection.getLatestBlockhash()
261312

262313
const message = new TransactionMessage({
263-
payerKey: vaultPda ? updateAuthority : keypair!.publicKey,
314+
payerKey: vaultPda ? expectedAuthority : keypair!.publicKey,
264315
recentBlockhash: blockhash,
265316
instructions,
266317
}).compileToV0Message()

0 commit comments

Comments
 (0)