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 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) agents to do their finance all while under explicitly scoped permissions governed by immutable contract code. Intelligent finance for all.
For more reference on our approach to translating intents into onchain commands, check out our docs on IE
, the Intents Engine. The current Nani app uses a jailbroken version of Llama3-70B 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).
Nani is also spearheading NEETH
, 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
integration).
Nani Accounts are Smart Contract Accounts built on the ERC-4337 standard, 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 and with efficient (six-leading-zero) addresses for our factory and implementation. They currently are the most gas-efficient smart accounts on the market, but we are always up for the challenge of advancing our latest implementation.
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.
Breakdown of the main functions in the Account.sol
contract
Factory Address (Arbitrum, v1.1.1
): 0x0000000000008dd2574908774527FD6DA397d75B
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.
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();
}
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);
}
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)
}
}
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.
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 pre-deploy signature support.