From 934cac07f27fc8e5b6c3259a5a943b7baac1cc6e Mon Sep 17 00:00:00 2001 From: Colin Ogoo Date: Wed, 6 Sep 2023 11:59:21 +0000 Subject: [PATCH 1/7] docs(token 2022): guide for permanent delegate extension --- .../guides/token-2022/permanent-delegate.md | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 content/guides/token-2022/permanent-delegate.md diff --git a/content/guides/token-2022/permanent-delegate.md b/content/guides/token-2022/permanent-delegate.md new file mode 100644 index 000000000..9c8f7cfe1 --- /dev/null +++ b/content/guides/token-2022/permanent-delegate.md @@ -0,0 +1,206 @@ +--- +date: Sep 01, 2023 +title: How to use the Permanent Delegate extension +description: + "With Token 2022, it's possible to specify a permanent account delegate for a + mint. This authority has **unlimited** delegate privileges over any account + associated with that mint, meaning that it can burn or transfer any amount of + tokens." +keywords: + - token 2022 + - token extensions + - token program +difficulty: beginner +tags: + - token 2022 + - token extensions +altRoutes: + - /developers/guides/permanent-delegate +--- + +With the Token program, the Mint may contain a `freezeAuthority` which can be +used to render an Account unusable. When an account is frozen, any Instructions +involving that account will fail until the account is reactivated. + +One of the uses of the `freezeAuthority` is to freeze accounts that are linked +to sanctioned individuals or linked to wallet hacks etc. Whilst the attacker +cannot benefit from the tokens, there's no means for restitution. + +With Token 2022, it's possible to specify a permanent account delegate for a +mint. This authority has **unlimited** delegate privileges over any account +associated with that mint, meaning that it can burn or transfer any amount of +tokens. + +## Understanding the implications? + +This is a very powerful feature, and it's implications have to be clearly stated +for both users and app developers. + +Previously, tokens could be frozen if there was a security breach, but there +wasn't a system in place to return them to their original owner. With the +introduction of the permanent delegate, this authority now has the capability to +either burn or transfer the tokens back. + +Due to the unlimited powers of the permanent delegate, if the delegates keys are +compromised, the attacker will have complete control on all token accounts for +that mint. + +This guide walks you through how to create a mint with a permanent delegate. + +Lets 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 + +Let's start by setting up our script to create a new token mint. + +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 { + Connection, + Keypair, + LAMPORTS_PER_SOL, + SystemProgram, + Transaction, + clusterApiUrl, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + ExtensionType, + TOKEN_2022_PROGRAM_ID, + createInitializeMintInstruction, + createInitializePermanentDelegateInstruction, + getMintLen, +} 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, + 2 * LAMPORTS_PER_SOL, +); +await connection.confirmTransaction({ + signature: airdropSignature, + ...(await connection.getLatestBlockhash()), +}); +``` + +## Mint setup + +Next, let's configure the properties of our token mint and generate the +necessary authorities. + +```javascript +const mintKeypair = Keypair.generate(); +// address for the mint +const mint = mintKeypair.publicKey; +// amount of decimals +const decimals = 9; +// authority that can mint tokens +const mintAuthority = Keypair.generate(); +// authority that can sign for transfers and burn on any account +const permanentDelegate = Keypair.generate(); + +const mintLen = getMintLen([ExtensionType.PermanentDelegate]); +const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); +``` + +We get the size of our new account and calculate the amount for rent exemption. +We use the helper `getMinLen` helper function, which takes an array of +extensions we want for this mint. + +## The Instructions + +```javascript +const createAccountInstruction = SystemProgram.createAccount({ + fromPubkey: payer.publicKey, // account that will transfer lamports to created account + newAccountPubkey: mint, // public key or the created account + space: mintLen, // amount of bytes to allocate to the created account + lamports, // amount of lamports to transfer to created account + programId: TOKEN_2022_PROGRAM_ID, // public key of the program to assign as owner of created account +}); +``` + +We create our mint account and assign ownership to the token 2022 program. + +```javascript +const initializePermanentDelegateInstruction = + createInitializePermanentDelegateInstruction( + mint, // token mint account + permanentDelegate.publicKey, // authority that may sign for transfers and burns on all accounts + TOKEN_2022_PROGRAM_ID, // SPL token program id + ); +``` + +Next, we initialize the Permanent Delegate extension for our mint. + +```javascript +const initializeMintInstruction = createInitializeMintInstruction( + mint, // token mint + decimals, // number of decimals + mintAuthority.publicKey, // minting authority + null, // optional authority that can freeze token accounts + TOKEN_2022_PROGRAM_ID, // SPL token program id +); +``` + +We then initialize our account as a mint account. + +## Send and confirm + +```javascript +const transaction = new Transaction().add( + createAccountInstruction, + initializePermanentDelegateInstruction, + initializeMintInstruction, +); +await sendAndConfirmTransaction( + connection, + transaction, + [payer, mintKeypair], + undefined, +); +``` + +Finally, we add the instructions to our transaction and send it to the network. +As a result, we've created a mint account with the permanent delegate extension. + +Important to note that when you want to transfer with the delegate, you have to +use the `transferChecked` helper from `@solana/spl-token` + +## Conclusion + +This could be a very devisive extension. It could easily be abused if users are +not aware that the token they're interacting with has a permanent delegate but +it does open up some "new" use-cases. + +Other use cases: + +Subscription Services: A company could issue tokens as access passes to a +subscription service. If a user doesn't renew their subscription, the permanent +delegate could automatically revoke their access by transferring the token back. + +Decentralized Autonomous Organizations (DAOs): DAOs could use this feature to +ensure that members actively participate in voting or other community +activities. Inactive members could have their tokens automatically +redistributed. + +Imagine you own a special digital collectible (NFT). There's a rule that says +you have to pay a tax to keep it. If you don't pay the tax, there's a system in +place that can automatically take it from you and give it to someone else who +wants to buy it. This rule and system together are called a Harberger Tax. From 1929eab28375a530a3bcec52df60bfc49c82d1ea Mon Sep 17 00:00:00 2001 From: Colin Ogoo Date: Wed, 6 Sep 2023 12:12:28 +0000 Subject: [PATCH 2/7] docs(token 2022): add missing section --- content/guides/token-2022/permanent-delegate.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/guides/token-2022/permanent-delegate.md b/content/guides/token-2022/permanent-delegate.md index 9c8f7cfe1..9b97eac10 100644 --- a/content/guides/token-2022/permanent-delegate.md +++ b/content/guides/token-2022/permanent-delegate.md @@ -126,6 +126,12 @@ extensions we want for this mint. ## The Instructions +Now, let's build the set of instructions to: + +- Create a new account +- Initialize the permanent delegate extension +- Initialize our new account as a token mint + ```javascript const createAccountInstruction = SystemProgram.createAccount({ fromPubkey: payer.publicKey, // account that will transfer lamports to created account From e09c634307561b50f3bf148948f6a76b83f5bc92 Mon Sep 17 00:00:00 2001 From: John <75003086+ZYJLiu@users.noreply.github.com> Date: Mon, 4 Dec 2023 17:47:52 -0600 Subject: [PATCH 3/7] Add Solana Playground --- .../guides/token-2022/permanent-delegate.md | 292 +++++++++++++----- 1 file changed, 208 insertions(+), 84 deletions(-) diff --git a/content/guides/token-2022/permanent-delegate.md b/content/guides/token-2022/permanent-delegate.md index 9b97eac10..710371327 100644 --- a/content/guides/token-2022/permanent-delegate.md +++ b/content/guides/token-2022/permanent-delegate.md @@ -1,5 +1,5 @@ --- -date: Sep 01, 2023 +date: Dec 04, 2023 title: How to use the Permanent Delegate extension description: "With Token 2022, it's possible to specify a permanent account delegate for a @@ -18,60 +18,61 @@ altRoutes: - /developers/guides/permanent-delegate --- -With the Token program, the Mint may contain a `freezeAuthority` which can be -used to render an Account unusable. When an account is frozen, any Instructions -involving that account will fail until the account is reactivated. - -One of the uses of the `freezeAuthority` is to freeze accounts that are linked -to sanctioned individuals or linked to wallet hacks etc. Whilst the attacker -cannot benefit from the tokens, there's no means for restitution. - With Token 2022, it's possible to specify a permanent account delegate for a -mint. This authority has **unlimited** delegate privileges over any account -associated with that mint, meaning that it can burn or transfer any amount of +mint. This authority has **unlimited** delegate privileges over any token +account for that mint, meaning that it can burn or transfer any amount of tokens. -## Understanding the implications? +In this guide, we'll walk through an example using Solana Playground. Here is a +[link](https://beta.solpg.io/656e3c06fb53fa325bfd0c47) to the final script. + +## Understanding the Implications This is a very powerful feature, and it's implications have to be clearly stated for both users and app developers. -Previously, tokens could be frozen if there was a security breach, but there -wasn't a system in place to return them to their original owner. With the -introduction of the permanent delegate, this authority now has the capability to -either burn or transfer the tokens back. +The permanent delegate is effectively a global owner of all token accounts for +the mint. Due to the unlimited powers of the permanent delegate, if the +delegate's keys are compromised, an attacker will have complete control over all +token accounts for that mint. + +## Getting Started -Due to the unlimited powers of the permanent delegate, if the delegates keys are -compromised, the attacker will have complete control on all token accounts for -that mint. +Start by opening this Solana Playground +[link](https://beta.solpg.io/656e19acfb53fa325bfd0c46) with the following +starter code. -This guide walks you through how to create a mint with a permanent delegate. +```javascript +// Client +console.log("My address:", pg.wallet.publicKey.toString()); +const balance = await pg.connection.getBalance(pg.wallet.publicKey); +console.log(`My balance: ${balance / web3.LAMPORTS_PER_SOL} SOL`); +``` -Lets get started! +If it is your first time using Solana Playground, you'll first need to create a +Playground Wallet and fund the wallet with devnet SOL. -## Install dependencies +To get devnet SOL, run the `solana airdrop` command in the Playground's +terminal, or visit this [devnet faucet](https://faucet.solana.com/). -```shell -npm i @solana/web3.js @solana/spl-token +``` +solana airdrop 5 ``` -Install the `@solana/web3.js` and `@solana/spl-token` packages. - -## Setting up +Once you've created and funded the Playground wallet, click the "Run" button to +run the starter code. -Let's start by setting up our script to create a new token mint. +## Add Dependencies -First, we will need to: +Let's start by setting up our script. We'll be using the `@solana/web3.js` and +`@solana/spl-token` libraries. -- Establish a connection to the devnet cluster -- Generate a payer account and fund it -- Create a new token mint using the Token 2022 program +Replace the starter code with the following: ```javascript import { Connection, Keypair, - LAMPORTS_PER_SOL, SystemProgram, Transaction, clusterApiUrl, @@ -80,69 +81,77 @@ import { import { ExtensionType, TOKEN_2022_PROGRAM_ID, - createInitializeMintInstruction, createInitializePermanentDelegateInstruction, + createInitializeMintInstruction, getMintLen, + createAccount, + mintTo, + transferChecked, + burnChecked, } 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, - 2 * LAMPORTS_PER_SOL, -); -await connection.confirmTransaction({ - signature: airdropSignature, - ...(await connection.getLatestBlockhash()), -}); +// Playground wallet +const payer = pg.wallet.keypair; + +// transaction signature return from sent transaction +let transactionSignature: string; ``` -## Mint setup +## Mint Setup -Next, let's configure the properties of our token mint and generate the -necessary authorities. +Next, let's configure the properties of our token mint and the define the +authorities. ```javascript const mintKeypair = Keypair.generate(); -// address for the mint +// address of our token mint const mint = mintKeypair.publicKey; -// amount of decimals +// the amount of decimals for our mint const decimals = 9; -// authority that can mint tokens -const mintAuthority = Keypair.generate(); +// authority that can mint new tokens +const mintAuthority = payer; // authority that can sign for transfers and burn on any account -const permanentDelegate = Keypair.generate(); +const permanentDelegate = payer; +``` +Next, let's get the size for the mint account and calculate the minimum lamports +required for rent exemption. We'll use the `getMinLen` helper function, which +takes an array of extensions we want for this mint. + +```javascript const mintLen = getMintLen([ExtensionType.PermanentDelegate]); const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); ``` -We get the size of our new account and calculate the amount for rent exemption. -We use the helper `getMinLen` helper function, which takes an array of -extensions we want for this mint. +With Token 2022, the size of the mint account will vary based on the extensions +enabled. -## The Instructions +## Build Instructions -Now, let's build the set of instructions to: +Next, let's build the set of instructions to: - Create a new account - Initialize the permanent delegate extension - Initialize our new account as a token mint +First, build the instruction to invoke the System Program to create an account +and assign ownership to the Token 2022 Program. + ```javascript const createAccountInstruction = SystemProgram.createAccount({ fromPubkey: payer.publicKey, // account that will transfer lamports to created account - newAccountPubkey: mint, // public key or the created account + newAccountPubkey: mint, // public key to use as the address of the created account space: mintLen, // amount of bytes to allocate to the created account lamports, // amount of lamports to transfer to created account programId: TOKEN_2022_PROGRAM_ID, // public key of the program to assign as owner of created account }); ``` -We create our mint account and assign ownership to the token 2022 program. +Next, build the instruction to initialize the Permanent Delegate extension for +the mint account. ```javascript const initializePermanentDelegateInstruction = @@ -153,7 +162,8 @@ const initializePermanentDelegateInstruction = ); ``` -Next, we initialize the Permanent Delegate extension for our mint. +Lastly, build the instruction to initialize the rest of the Mint Account data. +This is the same as with the original Token Program. ```javascript const initializeMintInstruction = createInitializeMintInstruction( @@ -165,9 +175,10 @@ const initializeMintInstruction = createInitializeMintInstruction( ); ``` -We then initialize our account as a mint account. +## Send Transaction -## Send and confirm +Finally, add the instructions to a new transaction and send it to the network. +This will create a mint account with the `PermanentDelegate` extension. ```javascript const transaction = new Transaction().add( @@ -175,38 +186,151 @@ const transaction = new Transaction().add( initializePermanentDelegateInstruction, initializeMintInstruction, ); -await sendAndConfirmTransaction( + +transactionSignature = await sendAndConfirmTransaction( connection, transaction, [payer, mintKeypair], - undefined, +); + +console.log( + "\n", + "Transaction Signature:", + `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, ); ``` -Finally, we add the instructions to our transaction and send it to the network. -As a result, we've created a mint account with the permanent delegate extension. +Run the script by clicking the `Run` button. You can then inspect the +transaction on the Solana Explorer. -Important to note that when you want to transfer with the delegate, you have to -use the `transferChecked` helper from `@solana/spl-token` +## Create Token Accounts -## Conclusion +Next, let's set up two token accounts to demonstrate the functionality of the +permanent delegate. + +First, generate a random keypair and use it as the owner of a +`sourceTokenAccount`. + +```javascript +// generate random keypair to use as owner of a token account +const randomKeypair = new Keypair(); + +// create associated token account for random keypair +const sourceTokenAccount = await createAccount( + connection, + payer, // payer + mint, // mint address + randomKeypair.publicKey, // token account owner + undefined, // optional keypair + undefined, // confirmOptions + TOKEN_2022_PROGRAM_ID, +); +``` + +Next, create a `destinationTokenAccount` owned by the Playground wallet. + +```javascript +// create associated token account for playground wallet +const destinationTokenAccount = await createAccount( + connection, + payer, // payer + mint, // mint address + payer.publicKey, // token account owner + undefined, // optional keypair + undefined, // confirmOptions + TOKEN_2022_PROGRAM_ID, +); +``` + +Lastly, mint 2 tokens to the `sourceTokenAccount` to fund it. + +```javascript +// mint tokens to sourceTokenAccount +transactionSignature = await mintTo( + connection, + payer, // payer + mint, // mint address + sourceTokenAccount, // destination + mintAuthority.publicKey, // mint authority + 2_000_000_000, // amount + undefined, // multiSigners + undefined, // confirmOptions + TOKEN_2022_PROGRAM_ID, // program ID +); + +console.log( + "\n", + "Transaction Signature:", + `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, +); +``` + +## Transfer with Permanent Delegate + +Let's send a transaction to transfer 1 token from the `sourceTokenAccount` to +the `destinationTokenAccount`. + +To transfer tokens with the permanent delegate, use the `transferChecked` +instruction. + +```javascript +// transfer tokens from source to destination token account +transactionSignature = await transferChecked( + connection, + payer, // payer + sourceTokenAccount, // transfer from + mint, // mint + destinationTokenAccount, // transfer to + permanentDelegate, // pass in permanent delegate as owner of token account + 1_000_000_000, // amount + decimals, // decimals + undefined, // multiSigners + undefined, // confirmOptions + TOKEN_2022_PROGRAM_ID, // program ID +); + +console.log( + "\n", + "Transaction Signature:", + `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, +); +``` + +## Burn with Permanent Delegate + +Let's send a transaction to burn 1 token from the `sourceTokenAccount`. -This could be a very devisive extension. It could easily be abused if users are -not aware that the token they're interacting with has a permanent delegate but -it does open up some "new" use-cases. +To burn tokens with the permanent delegate, use the `burnChecked` instruction. -Other use cases: +```javascript +transactionSignature = await burnChecked( + connection, + payer, // payer + sourceTokenAccount, // transfer from + mint, // mint + permanentDelegate, // pass in permanent delegate as owner of source token account + 1_000_000_000, // amount + decimals, // decimals + undefined, // multiSigners + undefined, // confirmOptions + TOKEN_2022_PROGRAM_ID, // program ID +); + +console.log( + "\n", + "Transaction Signature:", + `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, +); +``` -Subscription Services: A company could issue tokens as access passes to a -subscription service. If a user doesn't renew their subscription, the permanent -delegate could automatically revoke their access by transferring the token back. +Run the script by clicking the `Run` button. You can then inspect the +transactions on the Solana Explorer. -Decentralized Autonomous Organizations (DAOs): DAOs could use this feature to -ensure that members actively participate in voting or other community -activities. Inactive members could have their tokens automatically -redistributed. +Note that the both the transfer and burn transactions complete successfully, +even though the transactions are not signed by the owner of the token account. + +## Conclusion -Imagine you own a special digital collectible (NFT). There's a rule that says -you have to pay a tax to keep it. If you don't pay the tax, there's a system in -place that can automatically take it from you and give it to someone else who -wants to buy it. This rule and system together are called a Harberger Tax. +The permanent delegate extension is a powerful extension that potentially opens +up new use-cases for tokens. However, users should be aware of the implications +of holding tokens for a mint that has enabled this extension. From f91fc2d9ec519e49f906b82cde86e79719e692a5 Mon Sep 17 00:00:00 2001 From: John <75003086+ZYJLiu@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:48:24 -0600 Subject: [PATCH 4/7] Revision and print SolanaFM links --- .../guides/token-2022/permanent-delegate.md | 227 +++++++++--------- 1 file changed, 116 insertions(+), 111 deletions(-) diff --git a/content/guides/token-2022/permanent-delegate.md b/content/guides/token-2022/permanent-delegate.md index 710371327..0d189da4e 100644 --- a/content/guides/token-2022/permanent-delegate.md +++ b/content/guides/token-2022/permanent-delegate.md @@ -1,11 +1,11 @@ --- -date: Dec 04, 2023 +date: Dec 06, 2023 title: How to use the Permanent Delegate extension description: - "With Token 2022, it's possible to specify a permanent account delegate for a - mint. This authority has **unlimited** delegate privileges over any account - associated with that mint, meaning that it can burn or transfer any amount of - tokens." + "With Token Extensions, it's possible to specify a permanent account delegate + for a mint. This authority has **unlimited** delegate privileges over any + account associated with that mint, meaning that it can burn or transfer any + amount of tokens." keywords: - token 2022 - token extensions @@ -18,13 +18,14 @@ altRoutes: - /developers/guides/permanent-delegate --- -With Token 2022, it's possible to specify a permanent account delegate for a -mint. This authority has **unlimited** delegate privileges over any token +With Token Extensions, it's possible to specify a permanent account delegate for +a mint. This authority has **unlimited** delegate privileges over any token account for that mint, meaning that it can burn or transfer any amount of tokens. -In this guide, we'll walk through an example using Solana Playground. Here is a -[link](https://beta.solpg.io/656e3c06fb53fa325bfd0c47) to the final script. +In this guide, we'll walk through an example of creating a mint with the +`PermanentDelegate` extension enabled using Solana Playground. Here is the +[final script](https://beta.solpg.io/6570a56bfb53fa325bfd0c4b). ## Understanding the Implications @@ -90,75 +91,79 @@ import { burnChecked, } from "@solana/spl-token"; -// We establish a connection to the cluster -const connection = new Connection(clusterApiUrl("devnet"), "confirmed"); - // Playground wallet const payer = pg.wallet.keypair; -// transaction signature return from sent transaction +// Connection to devnet cluster +const connection = new Connection(clusterApiUrl("devnet"), "confirmed"); + +// Transaction signature returned from sent transaction let transactionSignature: string; ``` ## Mint Setup -Next, let's configure the properties of our token mint and the define the -authorities. +First, let's define the properties of the Mint Account we'll be creating in the +following step. ```javascript +// Generate new keypair for Mint Account const mintKeypair = Keypair.generate(); -// address of our token mint +// Address for Mint Account const mint = mintKeypair.publicKey; -// the amount of decimals for our mint -const decimals = 9; -// authority that can mint new tokens -const mintAuthority = payer; -// authority that can sign for transfers and burn on any account -const permanentDelegate = payer; +// Decimals for Mint Account +const decimals = 2; +// Authority that can mint new tokens +const mintAuthority = pg.wallet.publicKey; +// Authority that can transfer or burn from any token account +const permanentDelegate = pg.wallet.publicKey; ``` -Next, let's get the size for the mint account and calculate the minimum lamports -required for rent exemption. We'll use the `getMinLen` helper function, which -takes an array of extensions we want for this mint. +Next, let's determine the size of the new Mint Account and calculate the minimum +lamports needed for rent exemption. ```javascript +// Size of Mint Account with extension const mintLen = getMintLen([ExtensionType.PermanentDelegate]); +// Minimum lamports required for Mint Account const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); ``` -With Token 2022, the size of the mint account will vary based on the extensions -enabled. +With Token Extensions, the size of the mint account will vary based on the +extensions enabled. ## Build Instructions Next, let's build the set of instructions to: - Create a new account -- Initialize the permanent delegate extension -- Initialize our new account as a token mint +- Initialize the `PermanentDelegate` extension +- Initialize the remaining Mint Account data First, build the instruction to invoke the System Program to create an account -and assign ownership to the Token 2022 Program. +and assign ownership to the Token Extensions Program. ```javascript +// Instruction to invoke System Program to create new account const createAccountInstruction = SystemProgram.createAccount({ - fromPubkey: payer.publicKey, // account that will transfer lamports to created account - newAccountPubkey: mint, // public key to use as the address of the created account - space: mintLen, // amount of bytes to allocate to the created account - lamports, // amount of lamports to transfer to created account - programId: TOKEN_2022_PROGRAM_ID, // public key of the program to assign as owner of created account + fromPubkey: payer.publicKey, // Account that will transfer lamports to created account + newAccountPubkey: mint, // Address of the account to create + space: mintLen, // Amount of bytes to allocate to the created account + lamports, // Amount of lamports transferred to created account + programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account }); ``` -Next, build the instruction to initialize the Permanent Delegate extension for -the mint account. +Next, build the instruction to initialize the `PermanentDelegate` extension for +the Mint Account. ```javascript -const initializePermanentDelegateInstruction = +// Instruction to initialize the MintCloseAuthority Extension +const initializeMintCloseAuthorityInstruction = createInitializePermanentDelegateInstruction( - mint, // token mint account - permanentDelegate.publicKey, // authority that may sign for transfers and burns on all accounts - TOKEN_2022_PROGRAM_ID, // SPL token program id + mint, // Mint Account address + permanentDelegate, // Designated Permanent Delegate + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); ``` @@ -166,102 +171,103 @@ Lastly, build the instruction to initialize the rest of the Mint Account data. This is the same as with the original Token Program. ```javascript +// Instruction to initialize Mint Account data const initializeMintInstruction = createInitializeMintInstruction( - mint, // token mint - decimals, // number of decimals - mintAuthority.publicKey, // minting authority - null, // optional authority that can freeze token accounts - TOKEN_2022_PROGRAM_ID, // SPL token program id + mint, // Mint Account Address + decimals, // Decimals of Mint + mintAuthority, // Designated Mint Authority + null, // Optional Freeze Authority + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); ``` ## Send Transaction -Finally, add the instructions to a new transaction and send it to the network. -This will create a mint account with the `PermanentDelegate` extension. +Next, let's add the instructions to a new transaction and send it to the +network. This will create a Mint Account with the `PermanentDelegate` extension +enabled. ```javascript +// Add instructions to new transaction const transaction = new Transaction().add( createAccountInstruction, - initializePermanentDelegateInstruction, + initializeMintCloseAuthorityInstruction, initializeMintInstruction, ); +// Send transaction transactionSignature = await sendAndConfirmTransaction( connection, transaction, - [payer, mintKeypair], + [payer, mintKeypair], // Signers ); console.log( - "\n", - "Transaction Signature:", - `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, + "\nCreate Mint Account:", + `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`, ); ``` Run the script by clicking the `Run` button. You can then inspect the -transaction on the Solana Explorer. +transaction on the SolanaFM. ## Create Token Accounts -Next, let's set up two token accounts to demonstrate the functionality of the +Next, let's set up two Token Accounts to demonstrate the functionality of the permanent delegate. First, generate a random keypair and use it as the owner of a `sourceTokenAccount`. ```javascript -// generate random keypair to use as owner of a token account +// Random keypair to use as owner of Token Account const randomKeypair = new Keypair(); - -// create associated token account for random keypair +// Create Token Account for random keypair const sourceTokenAccount = await createAccount( connection, - payer, // payer - mint, // mint address - randomKeypair.publicKey, // token account owner - undefined, // optional keypair - undefined, // confirmOptions - TOKEN_2022_PROGRAM_ID, + payer, // Payer to create Token Account + mint, // Mint Account address + randomKeypair.publicKey, // Token Account owner + undefined, // Optional keypair, default to Associated Token Account + undefined, // Confirmation options + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); ``` Next, create a `destinationTokenAccount` owned by the Playground wallet. ```javascript -// create associated token account for playground wallet +// Create Token Account for Playground wallet const destinationTokenAccount = await createAccount( connection, - payer, // payer - mint, // mint address - payer.publicKey, // token account owner - undefined, // optional keypair - undefined, // confirmOptions - TOKEN_2022_PROGRAM_ID, + payer, // Payer to create Token Account + mint, // Mint Account address + payer.publicKey, // Token Account owner + undefined, // Optional keypair, default to Associated Token Account + undefined, // Confirmation options + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); ``` Lastly, mint 2 tokens to the `sourceTokenAccount` to fund it. ```javascript -// mint tokens to sourceTokenAccount +// Mint tokens to sourceTokenAccount transactionSignature = await mintTo( connection, - payer, // payer - mint, // mint address - sourceTokenAccount, // destination - mintAuthority.publicKey, // mint authority - 2_000_000_000, // amount - undefined, // multiSigners - undefined, // confirmOptions - TOKEN_2022_PROGRAM_ID, // program ID + payer, // Transaction fee payer + mint, // Mint Account address + sourceTokenAccount, // Mint to + mintAuthority, // Mint Authority address + 200, // Amount + undefined, // Additional signers + undefined, // Confirmation options + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); console.log( - "\n", - "Transaction Signature:", - `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, + "\nMint Tokens:", + `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`, ); ``` @@ -274,25 +280,24 @@ To transfer tokens with the permanent delegate, use the `transferChecked` instruction. ```javascript -// transfer tokens from source to destination token account +// Transfer tokens from source to destination transactionSignature = await transferChecked( connection, - payer, // payer - sourceTokenAccount, // transfer from - mint, // mint - destinationTokenAccount, // transfer to - permanentDelegate, // pass in permanent delegate as owner of token account - 1_000_000_000, // amount - decimals, // decimals - undefined, // multiSigners - undefined, // confirmOptions - TOKEN_2022_PROGRAM_ID, // program ID + payer, // Transaction fee payer + sourceTokenAccount, // Transfer from + mint, // Mint Account address + destinationTokenAccount, // Transfer to + permanentDelegate, // Use Permanent Delegate as owner + 100, // Amount + decimals, // Mint Account decimals + undefined, // Additional signers + undefined, // Confirmation options + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); console.log( - "\n", - "Transaction Signature:", - `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, + "\nTranfer Tokens:", + `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`, ); ``` @@ -305,29 +310,29 @@ To burn tokens with the permanent delegate, use the `burnChecked` instruction. ```javascript transactionSignature = await burnChecked( connection, - payer, // payer - sourceTokenAccount, // transfer from - mint, // mint - permanentDelegate, // pass in permanent delegate as owner of source token account - 1_000_000_000, // amount - decimals, // decimals - undefined, // multiSigners - undefined, // confirmOptions - TOKEN_2022_PROGRAM_ID, // program ID + payer, // Transaction fee payer + sourceTokenAccount, // Tranfer from + mint, // Mint Account address + permanentDelegate, // Use Permanent Delegate as owner + 100, // Amount + decimals, // Mint Account decimals + undefined, // Additional signers + undefined, // Confirmation options + TOKEN_2022_PROGRAM_ID, // Token Extension Program ID ); console.log( - "\n", - "Transaction Signature:", - `https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`, + "\nBurn Tokens:", + `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`, ); ``` Run the script by clicking the `Run` button. You can then inspect the -transactions on the Solana Explorer. +transactions on the SolanaFM. +Recall that the `sourceTokenAccount` is owned by a randomly generated keypair. Note that the both the transfer and burn transactions complete successfully, -even though the transactions are not signed by the owner of the token account. +even though the transactions are not signed by the owner of the Token Account. ## Conclusion From f97b802fe046e80a87f1d884e8b98f47ca13e89d Mon Sep 17 00:00:00 2001 From: John <75003086+ZYJLiu@users.noreply.github.com> Date: Sat, 9 Dec 2023 21:19:14 -0600 Subject: [PATCH 5/7] proofread --- .../guides/token-2022/permanent-delegate.md | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/content/guides/token-2022/permanent-delegate.md b/content/guides/token-2022/permanent-delegate.md index 0d189da4e..31a743c41 100644 --- a/content/guides/token-2022/permanent-delegate.md +++ b/content/guides/token-2022/permanent-delegate.md @@ -18,24 +18,23 @@ altRoutes: - /developers/guides/permanent-delegate --- -With Token Extensions, it's possible to specify a permanent account delegate for -a mint. This authority has **unlimited** delegate privileges over any token -account for that mint, meaning that it can burn or transfer any amount of -tokens. +The `PermanentDelegate` extension allows for a designated Permanent Delegate for +a Mint Account. The Permanent Delegate has unrestricted delegate privileges over +all Token Accounts for that mint, enabling them to burn or transfer tokens +without limitation. -In this guide, we'll walk through an example of creating a mint with the -`PermanentDelegate` extension enabled using Solana Playground. Here is the -[final script](https://beta.solpg.io/6570a56bfb53fa325bfd0c4b). +In this guide, we'll walk through an example of using Solana Playground. Here is +the [final script](https://beta.solpg.io/6570a56bfb53fa325bfd0c4b). ## Understanding the Implications This is a very powerful feature, and it's implications have to be clearly stated for both users and app developers. -The permanent delegate is effectively a global owner of all token accounts for -the mint. Due to the unlimited powers of the permanent delegate, if the +The Permanent Delegate is effectively a global owner of all Token Accounts for +the mint. Due to the unlimited powers of the Permanent Delegate, if the delegate's keys are compromised, an attacker will have complete control over all -token accounts for that mint. +Token Accounts for that mint. ## Getting Started @@ -129,7 +128,7 @@ const mintLen = getMintLen([ExtensionType.PermanentDelegate]); const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); ``` -With Token Extensions, the size of the mint account will vary based on the +With Token Extensions, the size of the Mint Account will vary based on the extensions enabled. ## Build Instructions @@ -214,7 +213,7 @@ transaction on the SolanaFM. ## Create Token Accounts Next, let's set up two Token Accounts to demonstrate the functionality of the -permanent delegate. +Permanent Delegate. First, generate a random keypair and use it as the owner of a `sourceTokenAccount`. @@ -273,11 +272,13 @@ console.log( ## Transfer with Permanent Delegate -Let's send a transaction to transfer 1 token from the `sourceTokenAccount` to -the `destinationTokenAccount`. +Next, let's send a transaction to transfer 1 token from the `sourceTokenAccount` +to the `destinationTokenAccount`. Remember, the `sourceTokenAccount` is owned by +a randomly generated keypair. -To transfer tokens with the permanent delegate, use the `transferChecked` -instruction. +To transfer tokens using the Permanent Delegate, use the `transferChecked` +instruction and specify the Permanent Delegate as the owner of the +`sourceTokenAccount`. ```javascript // Transfer tokens from source to destination @@ -303,15 +304,18 @@ console.log( ## Burn with Permanent Delegate -Let's send a transaction to burn 1 token from the `sourceTokenAccount`. +Next, let's also send a transaction to burn 1 token from the +`sourceTokenAccount`. -To burn tokens with the permanent delegate, use the `burnChecked` instruction. +To burn tokens using the `Permanent Delegate`, use the `burnChecked` instruction +and specify the Permanent Delegate as the owner of the `sourceTokenAccount`. ```javascript +// Burn tokens from token account transactionSignature = await burnChecked( connection, payer, // Transaction fee payer - sourceTokenAccount, // Tranfer from + sourceTokenAccount, // Burn from mint, // Mint Account address permanentDelegate, // Use Permanent Delegate as owner 100, // Amount @@ -330,12 +334,14 @@ console.log( Run the script by clicking the `Run` button. You can then inspect the transactions on the SolanaFM. -Recall that the `sourceTokenAccount` is owned by a randomly generated keypair. Note that the both the transfer and burn transactions complete successfully, even though the transactions are not signed by the owner of the Token Account. ## Conclusion -The permanent delegate extension is a powerful extension that potentially opens -up new use-cases for tokens. However, users should be aware of the implications -of holding tokens for a mint that has enabled this extension. +The `PermanentDelegate` extension is a powerful extension that enables +developers to have much greater control over tokens they create, such as the +ability to retrieve tokens that have been mistakenly transferred. While this +extension offers greater flexibility, it's essential for users to be aware of +the implications of holding tokens with this extension enabled, particularly the +risks associated with compromised delegate keys. From d53e93f065627d543dd09f1e64b9812f05329b40 Mon Sep 17 00:00:00 2001 From: nickfrosty Date: Tue, 12 Dec 2023 16:24:21 -0500 Subject: [PATCH 6/7] refactor: changed dir --- .../guides/{token-2022 => token-extensions}/permanent-delegate.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/guides/{token-2022 => token-extensions}/permanent-delegate.md (100%) diff --git a/content/guides/token-2022/permanent-delegate.md b/content/guides/token-extensions/permanent-delegate.md similarity index 100% rename from content/guides/token-2022/permanent-delegate.md rename to content/guides/token-extensions/permanent-delegate.md From 2e0be2fd3b09cffa8a26446376fe9adf3b0be53c Mon Sep 17 00:00:00 2001 From: nickfrosty Date: Tue, 12 Dec 2023 16:51:19 -0500 Subject: [PATCH 7/7] refactor: editorial changes --- .../token-extensions/permanent-delegate.md | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/content/guides/token-extensions/permanent-delegate.md b/content/guides/token-extensions/permanent-delegate.md index 31a743c41..d228d3bf1 100644 --- a/content/guides/token-extensions/permanent-delegate.md +++ b/content/guides/token-extensions/permanent-delegate.md @@ -1,11 +1,11 @@ --- date: Dec 06, 2023 +seoTitle: "Token Extensions: Permanent Delegate" title: How to use the Permanent Delegate extension description: - "With Token Extensions, it's possible to specify a permanent account delegate - for a mint. This authority has **unlimited** delegate privileges over any - account associated with that mint, meaning that it can burn or transfer any - amount of tokens." + "Token Extensions allow you to specify a permanent account delegate for a mint + with unlimited delegate privileges over any account associated with that mint, + including burning or transferring any amount of tokens." keywords: - token 2022 - token extensions @@ -14,27 +14,29 @@ difficulty: beginner tags: - token 2022 - token extensions -altRoutes: - - /developers/guides/permanent-delegate --- The `PermanentDelegate` extension allows for a designated Permanent Delegate for -a Mint Account. The Permanent Delegate has unrestricted delegate privileges over -all Token Accounts for that mint, enabling them to burn or transfer tokens +a Mint Account. This permanent delegate has unrestricted delegate privileges +over all Token Accounts for that mint, enabling them to burn or transfer tokens without limitation. -In this guide, we'll walk through an example of using Solana Playground. Here is -the [final script](https://beta.solpg.io/6570a56bfb53fa325bfd0c4b). +In this guide, we'll walk through an example of creating a token with the +Permanent Delegate using Solana Playground. Here is the +[final script](https://beta.solpg.io/6570a56bfb53fa325bfd0c4b) that this guide +will walkthrough. ## Understanding the Implications -This is a very powerful feature, and it's implications have to be clearly stated +This is a very powerful feature, and its implications have to be clearly stated for both users and app developers. + The Permanent Delegate is effectively a global owner of all Token Accounts for the mint. Due to the unlimited powers of the Permanent Delegate, if the delegate's keys are compromised, an attacker will have complete control over all Token Accounts for that mint. + ## Getting Started @@ -64,7 +66,7 @@ run the starter code. ## Add Dependencies -Let's start by setting up our script. We'll be using the `@solana/web3.js` and +Lets start by setting up our script. We'll be using the `@solana/web3.js` and `@solana/spl-token` libraries. Replace the starter code with the following: @@ -102,7 +104,7 @@ let transactionSignature: string; ## Mint Setup -First, let's define the properties of the Mint Account we'll be creating in the +First, lets define the properties of the Mint Account we'll be creating in the following step. ```javascript @@ -118,7 +120,7 @@ const mintAuthority = pg.wallet.publicKey; const permanentDelegate = pg.wallet.publicKey; ``` -Next, let's determine the size of the new Mint Account and calculate the minimum +Next, lets determine the size of the new Mint Account and calculate the minimum lamports needed for rent exemption. ```javascript @@ -133,7 +135,7 @@ extensions enabled. ## Build Instructions -Next, let's build the set of instructions to: +We will need to build a set of instructions to: - Create a new account - Initialize the `PermanentDelegate` extension @@ -182,9 +184,8 @@ const initializeMintInstruction = createInitializeMintInstruction( ## Send Transaction -Next, let's add the instructions to a new transaction and send it to the -network. This will create a Mint Account with the `PermanentDelegate` extension -enabled. +Now add the instructions to a new transaction and send it to the network. This +will create a Mint Account with the `PermanentDelegate` extension enabled. ```javascript // Add instructions to new transaction @@ -212,7 +213,7 @@ transaction on the SolanaFM. ## Create Token Accounts -Next, let's set up two Token Accounts to demonstrate the functionality of the +Next, we will set up two Token Accounts to demonstrate the functionality of the Permanent Delegate. First, generate a random keypair and use it as the owner of a @@ -272,7 +273,7 @@ console.log( ## Transfer with Permanent Delegate -Next, let's send a transaction to transfer 1 token from the `sourceTokenAccount` +Next, lets send a transaction to transfer 1 token from the `sourceTokenAccount` to the `destinationTokenAccount`. Remember, the `sourceTokenAccount` is owned by a randomly generated keypair. @@ -297,15 +298,14 @@ transactionSignature = await transferChecked( ); console.log( - "\nTranfer Tokens:", + "\nTransfer Tokens:", `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`, ); ``` ## Burn with Permanent Delegate -Next, let's also send a transaction to burn 1 token from the -`sourceTokenAccount`. +Lets also send a transaction to burn 1 token from the `sourceTokenAccount`. To burn tokens using the `Permanent Delegate`, use the `burnChecked` instruction and specify the Permanent Delegate as the owner of the `sourceTokenAccount`. @@ -334,8 +334,10 @@ console.log( Run the script by clicking the `Run` button. You can then inspect the transactions on the SolanaFM. -Note that the both the transfer and burn transactions complete successfully, + +Note that both the transfer and burn transactions complete successfully, even though the transactions are not signed by the owner of the Token Account. + ## Conclusion