Skip to content

Commit 4c60e2c

Browse files
authored
Update IDL + js clients (#265)
* Update IDL + js clients * add changeset * Add examples * version changeset * review updates
1 parent 7c4bc45 commit 4c60e2c

File tree

13 files changed

+1481
-117
lines changed

13 files changed

+1481
-117
lines changed

.changeset/puny-pillows-film.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solana-program/token-wrap": minor
3+
---
4+
5+
Update idl for new metadata sync instructions
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {
2+
address,
3+
appendTransactionMessageInstructions,
4+
assertIsSendableTransaction,
5+
createKeyPairSignerFromBytes,
6+
createSolanaRpc,
7+
createSolanaRpcSubscriptions,
8+
createTransactionMessage,
9+
getSignatureFromTransaction,
10+
pipe,
11+
sendAndConfirmTransactionFactory,
12+
setTransactionMessageFeePayerSigner,
13+
setTransactionMessageLifetimeUsingBlockhash,
14+
signTransactionMessageWithSigners,
15+
} from '@solana/kit';
16+
import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022';
17+
import { getAddressEncoder, getProgramDerivedAddress, getUtf8Encoder } from '@solana/kit';
18+
import {
19+
findWrappedMintAuthorityPda,
20+
findWrappedMintPda,
21+
getSyncMetadataToToken2022Instruction,
22+
} from '../index';
23+
24+
// =================================================================
25+
// PREREQUISITES:
26+
// =================================================================
27+
// 1. An unwrapped SPL Token mint with Metaplex metadata must exist.
28+
// 2. The corresponding wrapped Token-2022 mint for it must have been created
29+
// via the `create-mint` command or `createMint` helper.
30+
//
31+
// This example ASSUMES these accounts already exist and focuses only on the
32+
// transaction to sync the metadata.
33+
34+
// Replace these consts with the addresses from your setup
35+
const PRIVATE_KEY_PAIR = new Uint8Array([
36+
242, 30, 38, 177, 152, 71, 235, 193, 93, 30, 119, 131, 42, 186, 202, 7, 45, 250, 126, 135, 107,
37+
137, 38, 91, 202, 212, 12, 8, 154, 213, 163, 200, 23, 237, 17, 163, 3, 135, 34, 126, 235, 146,
38+
251, 18, 199, 101, 153, 249, 134, 88, 219, 68, 167, 136, 234, 195, 12, 34, 184, 85, 234, 25, 125,
39+
94,
40+
]);
41+
42+
// Source Mint: An existing SPL Token mint with Metaplex metadata
43+
const UNWRAPPED_SPL_TOKEN_MINT = address('8owJWKMiKfMKYbPmobyZAwXibNFcY7Roj6quktaeqxGL');
44+
45+
async function main() {
46+
const rpc = createSolanaRpc('http://127.0.0.1:8899');
47+
const rpcSubscriptions = createSolanaRpcSubscriptions('ws://127.0.0.1:8900');
48+
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
49+
50+
const payer = await createKeyPairSignerFromBytes(PRIVATE_KEY_PAIR);
51+
const { value: blockhash } = await rpc.getLatestBlockhash().send();
52+
53+
console.log('======== Syncing: SPL Token -> Token-2022 ========');
54+
55+
// To sync from an SPL Token mint, the client must resolve and provide the
56+
// Metaplex Metadata PDA as the `sourceMetadata` account.
57+
// Derive it using the known Metadata program and seeds: ['metadata', programId, mint]
58+
const TOKEN_METADATA_PROGRAM_ADDRESS = address('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
59+
const [metaplexMetadataPda] = await getProgramDerivedAddress({
60+
programAddress: TOKEN_METADATA_PROGRAM_ADDRESS,
61+
seeds: [
62+
getUtf8Encoder().encode('metadata'),
63+
getAddressEncoder().encode(TOKEN_METADATA_PROGRAM_ADDRESS),
64+
getAddressEncoder().encode(UNWRAPPED_SPL_TOKEN_MINT),
65+
],
66+
});
67+
const [wrappedMint] = await findWrappedMintPda({
68+
unwrappedMint: UNWRAPPED_SPL_TOKEN_MINT,
69+
wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS,
70+
});
71+
const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({
72+
wrappedMint,
73+
});
74+
75+
const syncToT22Ix = getSyncMetadataToToken2022Instruction({
76+
wrappedMint,
77+
wrappedMintAuthority,
78+
unwrappedMint: UNWRAPPED_SPL_TOKEN_MINT,
79+
// When the source mint is a standard SPL Token, `sourceMetadata` MUST be
80+
// the address of its Metaplex Metadata PDA.
81+
sourceMetadata: metaplexMetadataPda,
82+
});
83+
84+
const transaction = await pipe(
85+
createTransactionMessage({ version: 0 }),
86+
tx => setTransactionMessageFeePayerSigner(payer, tx),
87+
tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
88+
tx => appendTransactionMessageInstructions([syncToT22Ix], tx),
89+
tx => signTransactionMessageWithSigners(tx),
90+
);
91+
assertIsSendableTransaction(transaction);
92+
const signature = getSignatureFromTransaction(transaction);
93+
await sendAndConfirm(transaction, { commitment: 'confirmed' });
94+
95+
console.log('Successfully synced metadata to Token-2022 mint.');
96+
console.log('Signature:', signature);
97+
}
98+
99+
void main();
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
address,
3+
appendTransactionMessageInstructions,
4+
assertIsSendableTransaction,
5+
createKeyPairSignerFromBytes,
6+
createSolanaRpc,
7+
createSolanaRpcSubscriptions,
8+
createTransactionMessage,
9+
getAddressEncoder,
10+
getProgramDerivedAddress,
11+
getSignatureFromTransaction,
12+
getUtf8Encoder,
13+
pipe,
14+
sendAndConfirmTransactionFactory,
15+
setTransactionMessageFeePayerSigner,
16+
setTransactionMessageLifetimeUsingBlockhash,
17+
signTransactionMessageWithSigners,
18+
} from '@solana/kit';
19+
import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
20+
import {
21+
findWrappedMintAuthorityPda,
22+
findWrappedMintPda,
23+
getSyncMetadataToSplTokenInstruction,
24+
} from '../index';
25+
26+
// =================================================================
27+
// PREREQUISITES:
28+
// =================================================================
29+
// 1. An unwrapped Token-2022 mint with `TokenMetadata` and `MetadataPointer`
30+
// extensions must exist. The pointer should point to the mint itself.
31+
// 2. The corresponding wrapped SPL Token mint for it must have been created
32+
// via the `create-mint` command or `createMint` helper.
33+
// 3. The wrapped mint authority PDA must be funded with enough SOL to pay for
34+
// the creation of the Metaplex metadata account, as it acts as the payer
35+
// for the CPI to the Metaplex program.
36+
//
37+
// This example ASSUMES these accounts already exist and focuses only on the
38+
// transaction to sync the metadata.
39+
40+
// Replace these consts with the addresses from your setup
41+
const PRIVATE_KEY_PAIR = new Uint8Array([
42+
242, 30, 38, 177, 152, 71, 235, 193, 93, 30, 119, 131, 42, 186, 202, 7, 45, 250, 126, 135, 107,
43+
137, 38, 91, 202, 212, 12, 8, 154, 213, 163, 200, 23, 237, 17, 163, 3, 135, 34, 126, 235, 146,
44+
251, 18, 199, 101, 153, 249, 134, 88, 219, 68, 167, 136, 234, 195, 12, 34, 184, 85, 234, 25, 125,
45+
94,
46+
]);
47+
48+
// Source Mint: An existing Token-2022 mint with metadata extensions
49+
const UNWRAPPED_TOKEN_2022_MINT = address('5xte8yNSUTrTtfdptekeA4QJyo8zZdanpDJojrRaXP1Y');
50+
51+
async function main() {
52+
const rpc = createSolanaRpc('http://127.0.0.1:8899');
53+
const rpcSubscriptions = createSolanaRpcSubscriptions('ws://127.0.0.1:8900');
54+
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
55+
56+
const payer = await createKeyPairSignerFromBytes(PRIVATE_KEY_PAIR);
57+
const { value: blockhash } = await rpc.getLatestBlockhash().send();
58+
59+
console.log('======== Syncing: Token-2022 -> SPL Token ========');
60+
61+
// Derive the wrapped SPL Token mint PDA
62+
const [wrappedMint] = await findWrappedMintPda({
63+
unwrappedMint: UNWRAPPED_TOKEN_2022_MINT,
64+
wrappedTokenProgram: TOKEN_PROGRAM_ADDRESS,
65+
});
66+
67+
// Derive the Metaplex Metadata PDA for the wrapped mint.
68+
const TOKEN_METADATA_PROGRAM_ADDRESS = address('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
69+
const [metaplexMetadataPda] = await getProgramDerivedAddress({
70+
programAddress: TOKEN_METADATA_PROGRAM_ADDRESS,
71+
seeds: [
72+
getUtf8Encoder().encode('metadata'),
73+
getAddressEncoder().encode(TOKEN_METADATA_PROGRAM_ADDRESS),
74+
getAddressEncoder().encode(wrappedMint),
75+
],
76+
});
77+
78+
const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({
79+
wrappedMint,
80+
});
81+
82+
const syncToSplIx = getSyncMetadataToSplTokenInstruction({
83+
metaplexMetadata: metaplexMetadataPda,
84+
wrappedMint,
85+
wrappedMintAuthority,
86+
unwrappedMint: UNWRAPPED_TOKEN_2022_MINT,
87+
});
88+
89+
const transaction = await pipe(
90+
createTransactionMessage({ version: 0 }),
91+
tx => setTransactionMessageFeePayerSigner(payer, tx),
92+
tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
93+
tx => appendTransactionMessageInstructions([syncToSplIx], tx),
94+
tx => signTransactionMessageWithSigners(tx),
95+
);
96+
assertIsSendableTransaction(transaction);
97+
const signature = getSignatureFromTransaction(transaction);
98+
await sendAndConfirm(transaction, { commitment: 'confirmed' });
99+
100+
console.log('Successfully synced metadata to SPL Token Metaplex account.');
101+
console.log('Signature:', signature);
102+
}
103+
104+
void main();

clients/js/src/generated/errors/tokenWrap.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,35 @@ export const TOKEN_WRAP_ERROR__ESCROW_MISMATCH = 0x7; // 7
3232

3333
export const TOKEN_WRAP_ERROR__ESCROW_IN_GOOD_STATE = 0x8; // 8
3434

35+
export const TOKEN_WRAP_ERROR__UNWRAPPED_MINT_HAS_NO_METADATA = 0x9; // 9
36+
37+
export const TOKEN_WRAP_ERROR__METAPLEX_METADATA_MISMATCH = 0xa; // 10
38+
39+
export const TOKEN_WRAP_ERROR__METADATA_POINTER_MISSING = 0xb; // 11
40+
41+
export const TOKEN_WRAP_ERROR__METADATA_POINTER_UNSET = 0xc; // 12
42+
43+
export const TOKEN_WRAP_ERROR__METADATA_POINTER_MISMATCH = 0xd; // 13
44+
45+
export const TOKEN_WRAP_ERROR__EXTERNAL_PROGRAM_RETURNED_NO_DATA = 0xe; // 14
46+
47+
export const TOKEN_WRAP_ERROR__NO_SYNCING_TO_TOKEN2022 = 0xf; // 15
48+
3549
export type TokenWrapError =
3650
| typeof TOKEN_WRAP_ERROR__BACKPOINTER_MISMATCH
3751
| typeof TOKEN_WRAP_ERROR__ESCROW_IN_GOOD_STATE
3852
| typeof TOKEN_WRAP_ERROR__ESCROW_MISMATCH
3953
| typeof TOKEN_WRAP_ERROR__ESCROW_OWNER_MISMATCH
54+
| typeof TOKEN_WRAP_ERROR__EXTERNAL_PROGRAM_RETURNED_NO_DATA
4055
| typeof TOKEN_WRAP_ERROR__INVALID_BACKPOINTER_OWNER
4156
| typeof TOKEN_WRAP_ERROR__INVALID_WRAPPED_MINT_OWNER
57+
| typeof TOKEN_WRAP_ERROR__METADATA_POINTER_MISMATCH
58+
| typeof TOKEN_WRAP_ERROR__METADATA_POINTER_MISSING
59+
| typeof TOKEN_WRAP_ERROR__METADATA_POINTER_UNSET
60+
| typeof TOKEN_WRAP_ERROR__METAPLEX_METADATA_MISMATCH
4261
| typeof TOKEN_WRAP_ERROR__MINT_AUTHORITY_MISMATCH
62+
| typeof TOKEN_WRAP_ERROR__NO_SYNCING_TO_TOKEN2022
63+
| typeof TOKEN_WRAP_ERROR__UNWRAPPED_MINT_HAS_NO_METADATA
4364
| typeof TOKEN_WRAP_ERROR__WRAPPED_MINT_MISMATCH
4465
| typeof TOKEN_WRAP_ERROR__ZERO_WRAP_AMOUNT;
4566

@@ -50,9 +71,16 @@ if (process.env.NODE_ENV !== 'production') {
5071
[TOKEN_WRAP_ERROR__ESCROW_IN_GOOD_STATE]: `The escrow account is in a good state and cannot be recreated`,
5172
[TOKEN_WRAP_ERROR__ESCROW_MISMATCH]: `Escrow account address does not match expected ATA`,
5273
[TOKEN_WRAP_ERROR__ESCROW_OWNER_MISMATCH]: `Unwrapped escrow token owner is not set to expected PDA`,
74+
[TOKEN_WRAP_ERROR__EXTERNAL_PROGRAM_RETURNED_NO_DATA]: `External metadata program returned no data`,
5375
[TOKEN_WRAP_ERROR__INVALID_BACKPOINTER_OWNER]: `Wrapped backpointer account owner is not the expected token wrap program`,
5476
[TOKEN_WRAP_ERROR__INVALID_WRAPPED_MINT_OWNER]: `Wrapped mint account owner is not the expected token program`,
77+
[TOKEN_WRAP_ERROR__METADATA_POINTER_MISMATCH]: `Provided source metadata account does not match pointer`,
78+
[TOKEN_WRAP_ERROR__METADATA_POINTER_MISSING]: `Metadata pointer extension missing on mint`,
79+
[TOKEN_WRAP_ERROR__METADATA_POINTER_UNSET]: `Metadata pointer is unset (None)`,
80+
[TOKEN_WRAP_ERROR__METAPLEX_METADATA_MISMATCH]: `Metaplex metadata account address does not match expected PDA`,
5581
[TOKEN_WRAP_ERROR__MINT_AUTHORITY_MISMATCH]: `Wrapped mint authority does not match expected PDA`,
82+
[TOKEN_WRAP_ERROR__NO_SYNCING_TO_TOKEN2022]: `Instruction can only be used with spl-token wrapped mints`,
83+
[TOKEN_WRAP_ERROR__UNWRAPPED_MINT_HAS_NO_METADATA]: `Unwrapped mint does not have the TokenMetadata extension`,
5684
[TOKEN_WRAP_ERROR__WRAPPED_MINT_MISMATCH]: `Wrapped mint account address does not match expected PDA`,
5785
[TOKEN_WRAP_ERROR__ZERO_WRAP_AMOUNT]: `Wrap amount should be positive`,
5886
};

clients/js/src/generated/instructions/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@
88

99
export * from './closeStuckEscrow';
1010
export * from './createMint';
11+
export * from './syncMetadataToSplToken';
12+
export * from './syncMetadataToToken2022';
1113
export * from './unwrap';
1214
export * from './wrap';

0 commit comments

Comments
 (0)