Skip to content

Commit d507d37

Browse files
committed
Adds permissioned signer support
1 parent 9c30ae8 commit d507d37

File tree

10 files changed

+1244
-0
lines changed

10 files changed

+1244
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to the Aptos TypeScript SDK will be captured in this file. T
44

55
# Unreleased
66

7+
- Add permissioned signer support for FungibleAssetPermission, GasPermission, and NFTPermission using `Ed25519Account`s
8+
79
# 1.35.0 (2025-02-11)
810

911
- Add `MultiEd25519Account` to support the legacy MultiEd25519 authentication scheme.
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import {
2+
Ed25519Account,
3+
AbstractedAccount,
4+
FungibleAssetPermission,
5+
AccountAddress,
6+
Aptos,
7+
AptosConfig,
8+
Network,
9+
} from "@aptos-labs/ts-sdk";
10+
11+
// Initialize the Aptos client
12+
const config = new AptosConfig({ network: Network.DEVNET });
13+
const aptos = new Aptos(config);
14+
15+
async function demoPermissions() {
16+
/**
17+
* This would be the account that we want to request permissions from.
18+
* This would come back from the wallet.
19+
*/
20+
const primaryAccount = Ed25519Account.generate();
21+
22+
/**
23+
* This is not a true account on chain, just a key-pair that we will use to
24+
* request permissions. You can save the private key of the delegation account
25+
* and use it later.
26+
*/
27+
const delegationAccount = Ed25519Account.generate();
28+
29+
/**
30+
* We take the delegation account and create an abstract account from it. We
31+
* then use this abstract account to execute transactions on behalf of the
32+
* primary account.
33+
*/
34+
const abstractAccount = AbstractedAccount.fromPermissionedSigner({ signer: delegationAccount });
35+
36+
/**
37+
* This is the transaction that we will use to request permissions. Note that
38+
* we must specify the primary account address and the delegation public key.
39+
* We also must sign the request transactions with the primary account.
40+
*/
41+
const txn1 = await aptos.permissions.requestPermissions({
42+
primaryAccountAddress: primaryAccount.accountAddress,
43+
delegationPublicKey: delegationAccount.publicKey,
44+
permissions: [
45+
FungibleAssetPermission.from({
46+
asset: AccountAddress.A, // Replace with the actual asset address
47+
amount: 10, // Amount of the asset
48+
}),
49+
],
50+
});
51+
const txn1Result = await aptos.signAndSubmitTransaction({
52+
signer: primaryAccount,
53+
transaction: txn1,
54+
});
55+
await aptos.waitForTransaction({ transactionHash: txn1Result.hash });
56+
57+
/**
58+
* This is the transaction that we will use to execute the function on behalf
59+
* of the primary account. Here we are transferring 5 units of the asset to
60+
* another account. Note how the sender is the primary account address and the
61+
* signer is the abstract account.
62+
*/
63+
const txn2 = await aptos.signAndSubmitTransaction({
64+
signer: abstractAccount,
65+
transaction: await aptos.transaction.build.simple({
66+
sender: primaryAccount.accountAddress,
67+
data: {
68+
function: "0x1::primary_fungible_store::transfer",
69+
functionArguments: [AccountAddress.A, "receiver_account_address", 5], // Replace with actual receiver address
70+
typeArguments: ["0x1::fungible_asset::Metadata"],
71+
},
72+
}),
73+
});
74+
const txn2Result = await aptos.waitForTransaction({ transactionHash: txn2.hash, options: { checkSuccess: true } });
75+
console.log("Transaction success:", txn2Result.success);
76+
77+
/**
78+
* This is how we can fetch existing permissions for a delegated account.
79+
* Note, a primary account can have any number of delegated accounts.
80+
*/
81+
const permissions = await aptos.getPermissions({
82+
primaryAccountAddress: primaryAccount.accountAddress,
83+
delegationPublicKey: delegationAccount.publicKey,
84+
filter: FungibleAssetPermission,
85+
});
86+
87+
console.log("Existing permissions:", permissions);
88+
89+
/**
90+
* This is how we can revoke permissions.
91+
*/
92+
const txn3 = await aptos.signAndSubmitTransaction({
93+
signer: primaryAccount,
94+
transaction: await aptos.permissions.revokePermission({
95+
primaryAccountAddress: primaryAccount.accountAddress,
96+
delegationPublicKey: delegationAccount.publicKey,
97+
permissions: [FungibleAssetPermission.revoke({ asset: AccountAddress.A })],
98+
}),
99+
});
100+
const txn3Result = await aptos.waitForTransaction({ transactionHash: txn3.hash });
101+
console.log("Transaction success:", txn3Result.success);
102+
}
103+
104+
// Execute the demo
105+
demoPermissions().catch(console.error);

src/api/aptos.ts

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Faucet } from "./faucet";
1010
import { FungibleAsset } from "./fungibleAsset";
1111
import { General } from "./general";
1212
import { ANS } from "./ans";
13+
import { Permissions } from "./permissions";
1314
import { Staking } from "./staking";
1415
import { Transaction } from "./transaction";
1516
import { Table } from "./table";
@@ -43,13 +44,16 @@ import { Experimental } from "./experimental";
4344
* ```
4445
* @group Client
4546
*/
47+
4648
export class Aptos {
4749
readonly config: AptosConfig;
4850

4951
readonly account: Account;
5052

5153
readonly ans: ANS;
5254

55+
readonly permissions: Permissions;
56+
5357
readonly coin: Coin;
5458

5559
readonly digitalAsset: DigitalAsset;
@@ -100,6 +104,7 @@ export class Aptos {
100104
this.account = new Account(this.config);
101105
this.abstraction = new AccountAbstraction(this.config);
102106
this.ans = new ANS(this.config);
107+
this.permissions = new Permissions(this.config);
103108
this.coin = new Coin(this.config);
104109
this.digitalAsset = new DigitalAsset(this.config);
105110
this.event = new Event(this.config);
@@ -120,6 +125,7 @@ export class Aptos {
120125
export interface Aptos
121126
extends Account,
122127
ANS,
128+
Permissions,
123129
Coin,
124130
DigitalAsset,
125131
Event,
@@ -158,6 +164,7 @@ function applyMixin(targetClass: any, baseClass: any, baseClassProp: string) {
158164
applyMixin(Aptos, Account, "account");
159165
applyMixin(Aptos, AccountAbstraction, "abstraction");
160166
applyMixin(Aptos, ANS, "ans");
167+
applyMixin(Aptos, Permissions, "permissions");
161168
applyMixin(Aptos, Coin, "coin");
162169
applyMixin(Aptos, DigitalAsset, "digitalAsset");
163170
applyMixin(Aptos, Event, "event");

src/api/permissions.ts

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright © Aptos Foundation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { getPermissions, requestPermission, revokePermissions } from "../internal/permissions";
5+
import { AptosConfig } from "./aptosConfig";
6+
import { SimpleTransaction } from "../transactions/instances/simpleTransaction";
7+
import { Permission } from "../types/permissions";
8+
import { AccountAddress, PublicKey } from "../core";
9+
10+
/**
11+
* Manages permission operations for delegated accounts.
12+
* Handles granting, revoking, and querying permissions for fungible assets, gas, and NFTs.
13+
*/
14+
export class Permissions {
15+
constructor(readonly config: AptosConfig) {}
16+
17+
/**
18+
* Gets current permissions for a delegation key
19+
*
20+
* @example
21+
* ```typescript
22+
* const permissions = await aptos.permissions.getPermissions({
23+
* primaryAccountAddress: AccountAddress.fromString("0x1"),
24+
* delegationPublicKey: delegatedAccount.publicKey
25+
* filter: FungibleAssetPermission
26+
* });
27+
* ```
28+
*/
29+
async getPermissions<T extends Permission>(args: {
30+
primaryAccountAddress: AccountAddress;
31+
delegationPublicKey: PublicKey;
32+
filter?: new (...a: any) => T;
33+
}): Promise<T[]> {
34+
return getPermissions({
35+
aptosConfig: this.config,
36+
primaryAccountAddress: args.primaryAccountAddress,
37+
delegationPublicKey: args.delegationPublicKey,
38+
filter: args.filter,
39+
});
40+
}
41+
42+
/**
43+
* Requests permissions for a delegation key
44+
*
45+
* @param args
46+
* @param args.primaryAccountAddress - The primary account address
47+
* @param args.delegationPublicKey - The delegation public key
48+
* @param args.permissions - The permissions to request
49+
* @param args.expiration - The expiration time of the permissions, in epoch seconds. Defaults to 1 day from now.
50+
* @param args.refreshInterval - The refresh interval of the permissions, in seconds. Defaults to 60 seconds.
51+
* @param args.maxTransactionsPerInterval - The maximum number of transactions per interval. Defaults to 1000.
52+
*
53+
* @example
54+
* ```typescript
55+
* const txn = await aptos.permissions.requestPermissions({
56+
* primaryAccountAddress: AccountAddress.fromString("0x1"),
57+
* delegationPublicKey: delegatedAccount.publicKey,
58+
* permissions: [
59+
* FungibleAssetPermission.from({ asset: AccountAddress.A, amount: 100 })
60+
* ]
61+
* });
62+
*
63+
* await aptos.signAndSubmitTransaction({
64+
* signer: primaryAccount,
65+
* transaction: txn,
66+
* });
67+
* ```
68+
*/
69+
async requestPermissions(args: {
70+
primaryAccountAddress: AccountAddress;
71+
delegationPublicKey: PublicKey;
72+
permissions: Permission[];
73+
expiration?: number;
74+
refreshInterval?: number;
75+
maxTransactionsPerInterval?: number;
76+
}): Promise<SimpleTransaction> {
77+
return requestPermission({
78+
aptosConfig: this.config,
79+
primaryAccountAddress: args.primaryAccountAddress,
80+
delegationPublicKey: args.delegationPublicKey,
81+
permissions: args.permissions,
82+
expiration: args.expiration ?? Date.now() + 24 * 60 * 60 * 1000,
83+
refreshInterval: args.refreshInterval ?? 60,
84+
maxTransactionsPerInterval: args.maxTransactionsPerInterval ?? 1000,
85+
});
86+
}
87+
88+
/**
89+
* Revokes permissions from a delegation key. Note: You can pass an entire permission you get back from `getPermissions`
90+
* or call the static `revoke` function on the permission.
91+
*
92+
* @example
93+
* ```typescript
94+
* const txn = await aptos.permissions.revokePermission({
95+
* primaryAccountAddress: AccountAddress.fromString("0x1"),
96+
* delegationPublicKey: delegatedAccount.publicKey,
97+
* permissions: [
98+
* FungibleAssetPermission.revoke({ asset: AccountAddress.A })
99+
* ]
100+
* });
101+
*
102+
* await aptos.signAndSubmitTransaction({
103+
* signer: primaryAccount,
104+
* transaction: txn,
105+
* });
106+
* ```
107+
*/
108+
async revokePermission(args: {
109+
primaryAccountAddress: AccountAddress;
110+
delegationPublicKey: PublicKey;
111+
permissions: Permission[];
112+
}): Promise<SimpleTransaction> {
113+
return revokePermissions({
114+
aptosConfig: this.config,
115+
primaryAccountAddress: args.primaryAccountAddress,
116+
delegationPublicKey: args.delegationPublicKey,
117+
permissions: args.permissions,
118+
});
119+
}
120+
}

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export * from "./errors";
1010
export * from "./transactions";
1111
export * from "./transactions/management";
1212
export * from "./types";
13+
export * from "./types/permissions";
1314
export * from "./utils";

0 commit comments

Comments
 (0)