@@ -1308,6 +1308,146 @@ import {
1308
1308
</TabItem >
1309
1309
</Tabs >
1310
1310
1311
+ ### CPI Guard
1312
+
1313
+ CPI Guard is an extension that prohibits certain actions inside cross-program
1314
+ invocations, to protect users from implicitly signing for actions they can't see,
1315
+ hidden in programs that aren't the System or Token programs.
1316
+
1317
+ Users may choose to enable or disable the CPI Guard extension on their token
1318
+ account at will. When enabled, it has the following effects during CPI:
1319
+
1320
+ * Transfer: the signing authority must be the account delegate
1321
+ * Burn: the signing authority must be the account delegate
1322
+ * Approve: prohibited
1323
+ * Close Account: the lamport destination must be the account owner
1324
+ * Set Close Authority: prohibited unless unsetting
1325
+ * Set Owner: always prohibited, including outside CPI
1326
+
1327
+ #### Background
1328
+
1329
+ When interacting with a dapp, users sign transactions that are constructed by
1330
+ frontend code. Given a user's signature, there are three fundamental ways for a
1331
+ dapp to transfer funds from the user to the dapp (or, equivalently, burn them):
1332
+
1333
+ * Insert a transfer instruction in the transaction
1334
+ * Insert an approve instruction in the transaction, and perform a CPI transfer
1335
+ under program authority
1336
+ * Insert an opaque program instruction, and perform a CPI transfer with the user's
1337
+ authorization
1338
+
1339
+ The first two are safe, in that the user can see exactly what is being done, with
1340
+ zero ambiguity. The third is quite dangerous. A wallet signature allows the program
1341
+ to perform any action as the user, without any visibility into its actions. There
1342
+ have been some attempts at workarounds, for instance, simulating the transaction
1343
+ and warning about balance changes. But, fundamentally, this is intractable.
1344
+
1345
+ There are two ways to make this much safer:
1346
+
1347
+ * Wallets warn whenever a wallet signature is made available to an opaque
1348
+ (non-system, non-token) instruction. Users should be educated to treat the request
1349
+ for a signature on such an instruction as highly suspect
1350
+ * The token program prohibits CPI calls with the user authority, forcing opaque
1351
+ programs to directly ask for the user's authority
1352
+
1353
+ The CPI Guard covers the second instance.
1354
+
1355
+ #### Example: Enable CPI Guard on a token account
1356
+
1357
+ <Tabs className = " unique-tabs" groupId = " language-selection" >
1358
+ <TabItem value = " cli" label = " CLI" default >
1359
+ ``` console
1360
+ $ spl-token enable-cpi-guard 4YfkXX89TrsWqSSxb3av36Rk8EZBoDqxGzuaDNXr7UnL
1361
+
1362
+ Signature: 2fohon7oraTCgBZB3dfzhpGsBobYmYPgA8nvgCqKzjqpdX6EYZaBY3VwzjNuwDpsFYYNbpTVYBjxqiaMBrvXM8S2
1363
+ ```
1364
+ </TabItem >
1365
+ <TabItem value = " jsx" label = " JS" >
1366
+ ``` jsx
1367
+ import {
1368
+ clusterApiUrl ,
1369
+ sendAndConfirmTransaction ,
1370
+ Connection ,
1371
+ Keypair ,
1372
+ SystemProgram ,
1373
+ Transaction ,
1374
+ LAMPORTS_PER_SOL ,
1375
+ } from ' @solana/web3.js' ;
1376
+ import {
1377
+ createMint ,
1378
+ createEnableCpiGuardInstruction ,
1379
+ createInitializeAccountInstruction ,
1380
+ disableCpiGuard ,
1381
+ enableCpiGuard ,
1382
+ getAccountLen ,
1383
+ ExtensionType ,
1384
+ TOKEN_2022_PROGRAM_ID ,
1385
+ } from ' ../src' ;
1386
+
1387
+ (async () => {
1388
+ const connection = new Connection (clusterApiUrl (' devnet' ), ' confirmed' );
1389
+
1390
+ const payer = Keypair .generate ();
1391
+ const airdropSignature = await connection .requestAirdrop (payer .publicKey , 2 * LAMPORTS_PER_SOL );
1392
+ await connection .confirmTransaction ({ signature: airdropSignature, ... (await connection .getLatestBlockhash ()) });
1393
+
1394
+ const mintAuthority = Keypair .generate ();
1395
+ const decimals = 9 ;
1396
+ const mint = await createMint (
1397
+ connection,
1398
+ payer,
1399
+ mintAuthority .publicKey ,
1400
+ mintAuthority .publicKey ,
1401
+ decimals,
1402
+ undefined ,
1403
+ undefined ,
1404
+ TOKEN_2022_PROGRAM_ID
1405
+ );
1406
+
1407
+ const accountLen = getAccountLen ([ExtensionType .CpiGuard ]);
1408
+ const lamports = await connection .getMinimumBalanceForRentExemption (accountLen);
1409
+
1410
+ const owner = Keypair .generate ();
1411
+ const destinationKeypair = Keypair .generate ();
1412
+ const destination = destinationKeypair .publicKey ;
1413
+ const transaction = new Transaction ().add (
1414
+ SystemProgram .createAccount ({
1415
+ fromPubkey: payer .publicKey ,
1416
+ newAccountPubkey: destination,
1417
+ space: accountLen,
1418
+ lamports,
1419
+ programId: TOKEN_2022_PROGRAM_ID ,
1420
+ }),
1421
+ createInitializeAccountInstruction (destination, mint, owner .publicKey , TOKEN_2022_PROGRAM_ID ),
1422
+ createEnableCpiGuardInstruction (destination, owner .publicKey , [], TOKEN_2022_PROGRAM_ID )
1423
+ );
1424
+
1425
+ await sendAndConfirmTransaction (connection, transaction, [payer, owner, destinationKeypair], undefined );
1426
+
1427
+ // OR
1428
+ await enableCpiGuard (connection, payer, destination, owner, [], undefined , TOKEN_2022_PROGRAM_ID );
1429
+ })();
1430
+ ```
1431
+ </TabItem >
1432
+ </Tabs >
1433
+
1434
+ #### Example: Disable CPI Guard on a token account
1435
+
1436
+ <Tabs className = " unique-tabs" groupId = " language-selection" >
1437
+ <TabItem value = " cli" label = " CLI" default >
1438
+ ``` console
1439
+ $ spl-token disable-cpi-guard 4YfkXX89TrsWqSSxb3av36Rk8EZBoDqxGzuaDNXr7UnL
1440
+
1441
+ Signature: 4JJSBSc1UAtArbBqYRpTk9264WwJuZ8n6NqyXtCSmyVQpmHoetzyVDwHxtxrdK8wQawoocDxFD9rRPhpAMzJ6EdG
1442
+ ```
1443
+ </TabItem >
1444
+ <TabItem value = " jsx" label = " JS" >
1445
+ ``` jsx
1446
+ await disableCpiGuard (connection, payer, destination, owner, [], undefined , TOKEN_2022_PROGRAM_ID );
1447
+ ```
1448
+ </TabItem >
1449
+ </Tabs >
1450
+
1311
1451
### Transfer Hook
1312
1452
1313
1453
#### Motivation
0 commit comments