Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/create-gator-app/src/lib/choices/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const NEXTJS_TEMPLATE_OPTIONS: ITemplate[] = [
areLLMRulesSupported: false,
},
{
name: "Experimental: ERC7715 Permissions starter",
name: "ERC-7715 Permissions Starter",
value: "erc7715-starter",
description: "A basic Next.js ERC7715 Permissions starter",
framework: "nextjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@metamask/delegation-toolkit": "^0.12.0",
"@metamask/delegation-toolkit": "^0.13.0",
"@tailwindcss/postcss": "^4.1.1",
"lucide-react": "^0.487.0",
"next": "15.3.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useState } from "react";
import { createClient, custom } from "viem";
import { createClient, custom, parseEther } from "viem";
import { sepolia } from "viem/chains";
import { erc7715ProviderActions } from "@metamask/delegation-toolkit/experimental";
import { useSessionAccount } from "@/providers/SessionAccountProvider";
Expand All @@ -15,15 +15,15 @@ export default function GrantPermissionsButton() {
const [isLoading, setIsLoading] = useState<boolean>(false);

/**
* Handles the permission granting process for native token streaming.
* Handles the permission granting process for native token periodic transfer.
*
* This function:
* 1. Creates a Viem client with ERC-7715 provider actions
* 2. Sets up permission parameters including:
* - Chain ID (Sepolia testnet)
* - Expiry time (24 hours from current time)
* - Signer details (delegate smart account)
* - Native token stream permission configuration
* - Native token periodic transfer permission configuration
* 3. Grants the permissions through the MetaMask snap
* 4. Stores the granted permissions using the PermissionProvider
* 5. Updates the application step
Expand All @@ -47,7 +47,7 @@ export default function GrantPermissionsButton() {
const oneDayInSeconds = 24 * 60 * 60;
const expiry = currentTime + oneDayInSeconds;

const permissions = await client.grantPermissions([
const permissions = await client.requestExecutionPermissions([
{
chainId: sepolia.id,
expiry,
Expand All @@ -57,14 +57,15 @@ export default function GrantPermissionsButton() {
address: sessionAccount.address,
},
},
isAdjustmentAllowed: false,
permission: {
type: "native-token-stream",
type: "native-token-periodic",
data: {
initialAmount: 1n, // 1 WEI
amountPerSecond: 1n, // 1 WEI per second
startTime: currentTime,
maxAmount: 10n, // 10 WEI
justification: "Payment for a subscription service",
// 0.001 ETH in WEI format.
periodAmount: parseEther("0.001"),
// 1 day in seconds
periodDuration: 86400,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the next update you'll be able to use the duration constant / enum

justification: "Permission to transfer 0.001 ETH every day",
},
},
},
Expand Down Expand Up @@ -95,4 +96,4 @@ export default function GrantPermissionsButton() {
</Button>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,7 @@ export default function PermissionInfo() {
<div>
<p className="text-gray-600 dark:text-gray-400">Expiry:</p>
<p className="text-gray-900 dark:text-white">
{formatDate(permission.expiry)}
</p>
</div>
<div>
<p className="text-gray-600 dark:text-gray-400">Start:</p>
<p className="text-gray-900 dark:text-white">
{formatDate(permission.permission.data.startTime as number)}
{formatDate(permission.permission.data.expiry as number)}
</p>
</div>
<div>
Expand Down Expand Up @@ -117,4 +111,4 @@ function formatPermissionData(
}

return data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function RedeemPermissionButton() {
setLoading(true);

try {
const { accountMeta, context, signerMeta } = permission;
const { context, signerMeta } = permission;

if (!signerMeta) {
console.error("No signer meta found");
Expand Down Expand Up @@ -75,7 +75,6 @@ export default function RedeemPermissionButton() {
},
],
...fee,
accountMetadata: accountMeta,
});

const { receipt } = await bundlerClient.waitForUserOperationReceipt({
Expand Down Expand Up @@ -104,7 +103,7 @@ export default function RedeemPermissionButton() {
</p>

<Button
className="w-full bg-gradient-to-br from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 space-x-2"
className="w-full space-x-2"
onClick={() =>
window.open(`${config.ethScanerUrl}/tx/${txHash}`, '_blank')
}
Expand Down Expand Up @@ -152,4 +151,4 @@ export default function RedeemPermissionButton() {
</Button>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import {
useEffect,
useState,
} from "react";
import { GrantPermissionsReturnType } from "@metamask/delegation-toolkit/experimental";
import { RequestExecutionPermissionsReturnType } from "@metamask/delegation-toolkit/experimental";
import { Address } from "viem";

export type Permission = NonNullable<GrantPermissionsReturnType>[number];
export type Permission = NonNullable<RequestExecutionPermissionsReturnType>[number];

interface PermissionContextType {
permission: Permission | null;
Expand All @@ -23,9 +23,9 @@ interface PermissionContextType {
export const PermissionContext = createContext<PermissionContextType>({
permission: null,
smartAccount: null,
savePermission: () => {},
savePermission: () => { },
fetchPermission: () => null,
removePermission: () => {},
removePermission: () => { },
});

const PERMISSION_STORAGE_KEY = "gator-permission";
Expand Down Expand Up @@ -90,4 +90,4 @@ export const PermissionProvider = ({

export const usePermissions = () => {
return useContext(PermissionContext);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ export const SessionAccountProvider = ({
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);

const createSessionAccount = useCallback(async (privateKey?: `0x${string}`) => {
const createSessionAccount = useCallback(async (privateKey?: `0x${string}`) => {
try {
setIsLoading(true);
setError(null);

const key = privateKey || generatePrivateKey();
const account = privateKeyToAccount(key as `0x${string}`);

Expand All @@ -44,11 +44,11 @@ export const SessionAccountProvider = ({
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
signer: { account },
});

setSessionAccount(newSessionAccount);

// Save the private key to session storage
if (!privateKey) {
sessionStorage.setItem(PRIVATE_KEY_STORAGE_KEY, key);
Expand All @@ -73,7 +73,7 @@ export const SessionAccountProvider = ({
try {
setIsLoading(true);
const storedPrivateKey = sessionStorage.getItem(PRIVATE_KEY_STORAGE_KEY);

if (storedPrivateKey) {
await createSessionAccount(storedPrivateKey as `0x${string}`);
}
Expand All @@ -89,11 +89,11 @@ export const SessionAccountProvider = ({
}, [createSessionAccount]);

return (
<SessionAccountContext.Provider
value={{
sessionAccount,
createSessionAccount,
isLoading,
<SessionAccountContext.Provider
value={{
sessionAccount,
createSessionAccount,
isLoading,
error,
clearSessionAccount
}}
Expand All @@ -105,4 +105,4 @@ export const SessionAccountProvider = ({

export const useSessionAccount = () => {
return useContext(SessionAccountContext);
};
};
Loading