ERC-7715: Permission Grants for Smart Accounts
ERC-7715 standardises scoped, time-limited permission grants between a smart account (per ERC-4337 / ERC-7579) and a dApp. Replaces the per-transaction signing dance with a one-shot grant that lets the dApp execute pre-approved actions (swap up to 100 USDC per day, mint these specific NFTs, vote on this DAO) until the grant expires or is revoked.
Goals:
- Better UX — sign once, dApp performs N actions automatically.
- Bounded risk — each grant has a permission spec (allowlist, caps, expiry) the dApp cannot exceed.
- Programmable revocation — grant can be revoked by the user or expire automatically.
The wallet UX surface is a wallet_grantPermissions JSON-RPC call where the dApp specifies what permissions it needs; the wallet prompts the user, then returns an authorisation the dApp uses to make subsequent calls.
Required JSON-RPC Method
wallet_grantPermissions({
chainId: '0x...',
address: '0x...', // user's smart account
expiry: 1735689600,
permissions: [{
type: 'erc20-spending-cap',
data: { token: '0xUSDC', amount: '100000000', period: 'daily' }
}, {
type: 'contract-action',
data: { contract: '0xDEX', selector: 'swapExactTokensForTokens' }
}],
signer: { type: 'session', data: { publicKey: '0x...' } }
})
→ { permissionsContext: '0x...' } // opaque blob the dApp passes with each callThe signer describes who the dApp will use to sign on behalf of the user — typically a session key the dApp generates and the wallet authorises. Each subsequent dApp action signs with the session key plus the permissionsContext; the smart account's permission module validates the call against the grant's allowlist.
Neo Equivalent: Native Witness Scopes + Per-Grant Allowlist
Neo's witness model already provides scoped, signed authorisation — ERC-7715's permission spec maps directly to the rich witness scope combinator (CalledByEntry, CustomContracts, WitnessRule) that Neo bakes into every transaction.
The "session-key" affordance is the new part: instead of signing each tx with the user's primary key, the wallet authorises a session key to sign on behalf of the user, with on-chain permission-module validation:
[DisplayName("PermissionModule")]
[ContractPermission("*", "*")]
public class PermissionModule : SmartContract
{
private const byte Prefix_Grant = 0x10;
public static void GrantPermission(
UInt160 user, UInt160 sessionKey, BigInteger expiry,
UInt160[] allowedContracts, BigInteger maxAmountPerCall)
{
if (!Runtime.CheckWitness(user)) throw new Exception("AA:NotUser");
if (expiry <= Runtime.Time) throw new Exception("AA:ExpiryInPast");
var grant = new Map<string, object>
{
["sessionKey"] = sessionKey,
["expiry"] = expiry,
["allowedContracts"] = allowedContracts,
["maxAmountPerCall"] = maxAmountPerCall,
};
Storage.Put(Storage.CurrentContext, GrantKey(user), StdLib.Serialize(grant));
}
public static bool ValidateAction(UInt160 user, UInt160 contract, BigInteger amount)
{
var raw = Storage.Get(Storage.CurrentContext, GrantKey(user));
if (raw is null) return false;
var grant = (Map<string, object>)StdLib.Deserialize((ByteString)raw);
if ((BigInteger)grant["expiry"] < Runtime.Time) return false;
if (amount > (BigInteger)grant["maxAmountPerCall"]) return false;
var allowed = (UInt160[])grant["allowedContracts"];
for (var i = 0; i < allowed.Length; i++)
if (allowed[i].Equals(contract)) return true;
return false;
}
}| ERC-7715 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
wallet_grantPermissions JSON-RPC | Wallet-side method that signs + records grant | Off-chain wallet API |
permissionsContext blob | Reference to on-chain grant via grant id | Smaller — Neo storage holds the spec |
| Session key | Session-key UInt160 stored in the grant | Witness check uses session key |
| Allowlist of contracts | allowedContracts[] in grant storage | Direct port |
| Spending caps (per period) | maxAmountPerCall + period accounting | Custom logic |
| Permission module on smart account | Permission contract (ERC-7715 mirror) integrated with NEP-30 verify | Native to NEP-30 flow |
| Expiry timestamp | expiry field, validated against Runtime.Time | Direct port |
| Revocation | RevokeGrant(user) storage delete | User-witness-checked |
Composition
- ERC-4337 — account abstraction. ERC-7715 is the permission layer on top.
- ERC-7579 — modular smart account. The permission module is one of the smart account's modules.
- ERC-7677 — paymaster web service. Combine: dApp has a session-key grant + sponsored gas for friction-free UX.
- ERC-1271 — contract signatures. The permission module's signature validation uses ERC-1271 patterns.
Why The Neo Story Is Smoother
Ethereum requires the smart account to be deployed and equipped with a permission module to enforce scoped grants. Every Neo account is already a contract that can implement NEP-30 Verify with arbitrary logic — no deployment required, no module loading. The permission module is just an extension to the existing Verify call.
The off-chain UX (wallet grantPermissions API) is what needs adoption. The on-chain primitives are already there.
Migration Notes
For Solidity dApps using ERC-7715:
- The on-chain permission module ports cleanly — store the grant, validate calls against the spec.
- The session-key UX needs Neo wallet adoption — wallets need to support generating session keys and signing the grant transaction on the user's behalf.
- The smart account's
Verify(NEP-30) extends to call the permission module'sValidateActionfor grant-authorised calls.
