owned this note
owned this note
Published
Linked with GitHub
# Nani Accounts
## *Why Nani:*
Nani is a new kind of smart account that optimizes for user safety and readability. It is meant to accelerate your ability to actually take advantage of DeFi and other open financial tools on the internet without losing all your money or giving up control to sketchy bots or other people (i.e., "smarter accounts").
Specific to this goal is the acknowledgment that blockchains are notoriously difficult to understand, and loss-of-funds from mistakes are still fairly common. We simply cannot expect users to enjoy "gas-less" or "batched" transactions if they still have a heart attack everytime a userOp is sent to a bundler (*abstracted UX, but not abstracted funds*).
To make this ideal work today (full control with much more safety), the [Nani app](https://nani.ooo/) directly integrates an onchain translator for contract calldata (see below, *LLM integrations*). The vision for Nani is for true user sovereignty ("who's in your wallet?"), allowing anyone (*and we mean, anyone*) to enage with state-of-the-art ([uncensored](https://www.alignmentforum.org/posts/jGuXSZgv6qfdhMCuJ/refusal-in-llms-is-mediated-by-a-single-direction)) agents to do their finance all while under explicitly scoped permissions governed by immutable contract code. Intelligent finance for all.
## LLM integrations
For more reference on our approach to translating intents into onchain commands, check out our docs on [`IE`](https://hackmd.io/@z0r0z/ByH6XCaV0), the Intents Engine. The current Nani app uses a [jailbroken version of Llama3-70B](https://huggingface.co/failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5) to answer user questions and convert a variety of requests (such as sending or swapping tokens) into IE commands that are then forwarded as bundled userOps (saving you gas and worry).
## Self-repaying Gas
Nani is also spearheading [`NEETH`](https://arbiscan.io/address/0x00000000000009b4ab3f1bc2b029bd7513fbd8ed#code), a special paymaster that leverages Lido DeFi yield to automatically charge up your Nani account (or any other smart account) with gas. For example, since L2 transaction costs now typically are sub-cent, a relatively small ETH savings deposit (0.1) can theoretically pay for all your gas and convert any unspent yield into profits. This is an area of DeFi + AA integration that we are excited to expand upon with our users and other platforms (for example, see our [`Safe NEETH`](https://github.com/NaniDAO/safe-neeth) integration).
![image](https://hackmd.io/_uploads/rkywOYyrC.png)
## For the Devs:
[Nani Accounts](https://github.com/NaniDAO/accounts/blob/main/src/Account.sol) are Smart Contract Accounts built on the [ERC-4337 standard](https://www.erc4337.io/), incorporating a minimal "app store" concept - enabling the addition of plugins and composable logic to the accounts without requiring upgrades or migrations. They are highly optimized being built on the [Solady smart contract library](https://github.com/Vectorized/solady/commits/main/src/accounts/ERC4337.sol) and with [efficient (six-leading-zero) addresses for our factory and implementation](https://github.com/NaniDAO/accounts/tree/main?tab=readme-ov-file#deployments). They currently are the [most gas-efficient smart accounts](https://github.com/zerodevapp/aa-benchmark) on the market, but we are always up for the challenge of advancing our latest implementation.
### Minimal Modularity
In order to achieve a simple plugin UX, Nani Accounts leverage the ERC-4337 semi-abstracted nonces as a pointer to validator addresses. When a non-sequential nonce (greater than `2^64`) is used in an ERC-4337 Entry Point UserOperation (userOp), the `validateUserOp` function in the sender contract extracts the corresponding validator contract address from storage. This mechanism enables Nani Accounts to delegate validation logic to external validator contracts, facilitating the addition of custom plugins and features without requiring upgrades or migrations. Making Nani Accounts essentially [modular](https://ethereum-magicians.org/t/erc-7582-modular-accounts-with-delegated-validation/17640/1).
### Accounts.sol
Breakdown of the main functions in the `Account.sol` contract
Factory Address (Arbitrum, `v1.1.1`): [0x0000000000008dd2574908774527FD6DA397d75B](https://arbiscan.io/address/0x0000000000008dd2574908774527FD6DA397d75B)
#### validateUserOp()
This function is very similar to standard `validateUserOP` functions when the `userOp.nonce` is sequential (this is your typical nonce used for transaction ordering). If the `userOp.nonce` is non-sequential (greater or equal to `2^64`) then it is treated as a special pointer, the function then calls `_validateUserOp()` to handle the operation using a validator plugin, allowing more advanced logic.
![NaniAccountUserOp](https://hackmd.io/_uploads/S1b0VWCVC.png)
````
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32,
uint256 missingAccountFunds
)
external
payable
virtual
override(ERC4337)
onlyEntryPoint
payPrefund(missingAccountFunds)
returns (uint256)
{
return userOp.nonce < type(uint64).max ? _validateUserOpSignature(userOp) : _validateUserOp();
}
````
#### _validateUserOpSignature()
This function is responsible for validating the signature of a `UserOperation` when the nonce is sequential, using EIP-712 encoding, ensuring the user operation is authorized by the account owner.
````
function _validateUserOpSignature(PackedUserOperation calldata userOp)
internal
virtual
returns (uint256)
{
(uint48 validUntil, uint48 validAfter) =
(uint48(bytes6(userOp.signature[:6])), uint48(bytes6(userOp.signature[6:12])));
bool valid = SignatureCheckerLib.isValidSignatureNowCalldata(
owner(), __hashTypedData(userOp, validUntil, validAfter), userOp.signature[12:]
);
return (valid ? 0 : 1) | (uint256(validUntil) << 160) | (uint256(validAfter) << 208);
}
````
#### _validateUserOp()
When the nonce is non-sequential, this function is called to handle the validation of user operations using an external validator plugin, leveraging the nonce as a pointer to the validator contract address. Making Nani Accounts more modular and extensible than regular Smart Contract Accounts.
````
function _validateUserOp() internal virtual returns (uint256 validationData) {
assembly ("memory-safe") {
calldatacopy(0x00, 0x00, calldatasize())
if iszero(
call(
gas(),
/*validator*/
shr(96, sload(shl(64, /*key*/ shr(64, /*nonce*/ calldataload(0x84))))),
0,
0x00,
calldatasize(),
0x00,
0x20
)
) {
returndatacopy(0x00, 0x00, returndatasize())
revert(0x00, returndatasize())
}
validationData := mload(0x00)
}
}
````
#### Custom time ranges
Nani Accounts also support custom time ranges (`validUntil`, `validAfter`) that can be set by the user for any userOp. These are packed into the first 12 bytes of the `userOp.signature` and are validated by the ERC-4337 Entry Point.
#### Predeploy signatures (ERC-6492)
Even before a Nani Account is deployed, the user can login into apps as their Account and sign userOps by utilizing Nani's [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) pre-deploy signature support.