-
Notifications
You must be signed in to change notification settings - Fork 256
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
docs/immutable owner #14
Merged
nickfrosty
merged 9 commits into
solana-foundation:main
from
cogoo:docs/immutable-owner
Dec 14, 2023
Merged
Changes from 4 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
64cf47d
docs(guides): add guide for immutable owner extension
cogoo 6d9ee5e
style(guides): add metadata for guide
cogoo 4d72ce9
chore(docs): reduce amount of SOL requested for airdrop
cogoo 7a5bbf5
docs(token 2022): changed to be consistent to how we refer to the acc…
cogoo ee6262c
style: format with prettier
cogoo 49015b7
Add Solana Playground
ZYJLiu 13541b1
Merge branch 'main' into pr/cogoo/14
nickfrosty c96efba
refactor: changed dir
nickfrosty 08bef3a
refactor: editorial changes
nickfrosty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,199 @@ | ||||||
--- | ||||||
date: Aug 17, 2023 | ||||||
difficulty: beginner | ||||||
title: "Immutable Owner: Token 2022 Account Extension" | ||||||
description: | ||||||
"This guide walks you through how to use the Immutable Owner extension to prevent the transfer of ownership of a token account." | ||||||
tags: | ||||||
- token-2022 | ||||||
keywords: | ||||||
- beginner | ||||||
- spl | ||||||
- spl token | ||||||
- token 2022 | ||||||
- token 2022 extensions | ||||||
altRoutes: | ||||||
- /developers/guides/immutable-owner | ||||||
--- | ||||||
|
||||||
With the Token program, the `SetAuthority` instruction can be used for various use cases. Among them, an Account's owner may transfer ownership of an account to another. | ||||||
|
||||||
The immutable owner extension ensures that ownership of a token account cannot be reassigned. | ||||||
|
||||||
## Understanding the implications | ||||||
|
||||||
So, why is this important? The addresses for Associated Token Accounts are derived based on the owner and the mint. This makes it easy to find the related token account for a specific owner. If the account owner has reassigned ownership of this account, then applications may derive the address for that account and use it, not knowing that it no longer belongs to the owner. | ||||||
|
||||||
This guide walks you through how to use the Immutable Owner extension to prevent the transfer of ownership of a token account. | ||||||
|
||||||
Let's get started! | ||||||
|
||||||
## Install dependencies | ||||||
|
||||||
```shell | ||||||
npm i @solana/web3.js @solana/spl-token | ||||||
``` | ||||||
|
||||||
Install the `@solana/web3.js` and `@solana/spl-token` packages. | ||||||
|
||||||
## Setting up | ||||||
|
||||||
We'll begin by setting up our script to create a new token account. | ||||||
|
||||||
First, we will need to: | ||||||
|
||||||
- Establish a connection to the devnet cluster | ||||||
- Generate a payer account and fund it | ||||||
- Create a new token mint using the Token 2022 program | ||||||
|
||||||
```javascript | ||||||
import { | ||||||
clusterApiUrl, | ||||||
sendAndConfirmTransaction, | ||||||
Connection, | ||||||
Keypair, | ||||||
SystemProgram, | ||||||
Transaction, | ||||||
LAMPORTS_PER_SOL, | ||||||
} from '@solana/web3.js'; | ||||||
import { | ||||||
createAccount, | ||||||
createMint, | ||||||
createInitializeImmutableOwnerInstruction, | ||||||
createInitializeAccountInstruction, | ||||||
getAccountLen, | ||||||
ExtensionType, | ||||||
TOKEN_2022_PROGRAM_ID, | ||||||
} from '@solana/spl-token'; | ||||||
|
||||||
// We establish a connection to the cluster | ||||||
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); | ||||||
|
||||||
// Next, we create and fund the payer account | ||||||
const payer = Keypair.generate(); | ||||||
const airdropSignature = await connection.requestAirdrop(payer.publicKey, 10000000); | ||||||
await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) }); | ||||||
|
||||||
// Next, we create a new keypair that will be the mint authority | ||||||
const mintAuthority = Keypair.generate(); | ||||||
const decimals = 9; | ||||||
|
||||||
// Next, we create a new token mint | ||||||
const mint = await createMint( | ||||||
connection, // connection | ||||||
payer, // fee payer | ||||||
mintAuthority.publicKey, // mint authority | ||||||
mintAuthority.publicKey, // freeze authority | ||||||
decimals, // decimals | ||||||
undefined, // keypair | ||||||
undefined, // confirm options | ||||||
TOKEN_2022_PROGRAM_ID // Token Program ID | ||||||
); | ||||||
``` | ||||||
|
||||||
> The [Mint](https://spl.solana.com/token#creating-a-new-token-type) is used to create or "mint" new tokens that are stored in token accounts. | ||||||
|
||||||
Lets explore two options of using the extension: | ||||||
|
||||||
1. Creating an account with immutable ownership | ||||||
2. Creating an associated token account with immutable ownership | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
## Option 1: Creating an account with immutable ownership | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
### Account setup | ||||||
|
||||||
```javascript | ||||||
const ownerKeypair = Keypair.generate(); | ||||||
const accountKeypair = Keypair.generate(); | ||||||
|
||||||
// Explanation of the two keypairs: | ||||||
// ownerKeypair: The owner of the token account | ||||||
// accountKeypair: The address of our token account | ||||||
|
||||||
const account = accountKeypair.publicKey; | ||||||
|
||||||
const accountLen = getAccountLen([ExtensionType.ImmutableOwner]); | ||||||
const lamports = await connection.getMinimumBalanceForRentExemption(accountLen); | ||||||
``` | ||||||
|
||||||
Next, we get the size of our new account and calculate the amount for rent exemption. We use the helper `getAccountLen` helper function, which takes an array of extensions we want for this account. | ||||||
|
||||||
> The total size of the token account is 165 bytes (the size of a token account) + the size of the added extensions | ||||||
|
||||||
### The Instructions | ||||||
|
||||||
```javascript | ||||||
const createAccountInstruction = SystemProgram.createAccount({ | ||||||
fromPubkey: payer.publicKey, | ||||||
newAccountPubkey: account, | ||||||
space: accountLen, | ||||||
lamports, | ||||||
programId: TOKEN_2022_PROGRAM_ID, | ||||||
}); | ||||||
``` | ||||||
|
||||||
We create a new account and assign ownership to the token 2022 program. | ||||||
|
||||||
```javascript | ||||||
const initializeImmutableOwnerInstruction = createInitializeImmutableOwnerInstruction( | ||||||
account, | ||||||
TOKEN_2022_PROGRAM_ID | ||||||
); | ||||||
``` | ||||||
|
||||||
We then initialize the Immutable Owner extension for the given account. It's important to note that this can only be done for accounts that have not been initialized yet. | ||||||
|
||||||
```javascript | ||||||
const initializeAccountInstruction = createInitializeAccountInstruction( | ||||||
account, | ||||||
mint, | ||||||
ownerKeypair.publicKey, | ||||||
TOKEN_2022_PROGRAM_ID | ||||||
); | ||||||
``` | ||||||
|
||||||
Next, we initialize our newly created account to hold tokens. | ||||||
|
||||||
### Send and confirm | ||||||
|
||||||
```javascript | ||||||
const transaction = new Transaction().add( | ||||||
createAccountInstruction, | ||||||
initializeImmutableOwnerInstruction, | ||||||
initializeAccountInstruction | ||||||
); | ||||||
await sendAndConfirmTransaction(connection, transaction, [payer, accountKeypair], undefined); | ||||||
``` | ||||||
|
||||||
Finally, we add the instructions to our transaction and send it to the network. As a result, we've created a token account for our new mint with the immutable owner extension applied. | ||||||
|
||||||
If we attempt to change the owner of this account, we get an error: | ||||||
|
||||||
```shell | ||||||
"Program log: Instruction: SetAuthority", | ||||||
"Program log: The owner authority cannot be changed" | ||||||
``` | ||||||
|
||||||
## Option 2: Creating an associated token account with immutable ownership | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
By default, all associated token accounts have the immutable owner extension applied. | ||||||
|
||||||
### Create Account | ||||||
|
||||||
```javascript | ||||||
const associatedTokenAccount = await createAccount( | ||||||
connection, // connection | ||||||
payer, // fee payer | ||||||
mint, // token mint | ||||||
ownerKeypair.publicKey, // owner | ||||||
undefined, // keypair | defaults to ATA | ||||||
undefined, // confirm options | ||||||
TOKEN_2022_PROGRAM_ID // program id | ||||||
); | ||||||
``` | ||||||
|
||||||
The newly created `associatedTokenAccount` has the immutable owner extension applied as the Associated Token Account program always uses the extension when creating accounts. | ||||||
|
||||||
## Conclusion | ||||||
|
||||||
With the Immutable Owner extension, Token 2022 removes a potential foot gun. By ensuring that a token account's derived address genuinely reflects its owner. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make these internal links to the guide