The IPAccountImpl contract is Story’s implementation of the IP Account, which follows the ERC-6551 standard for token-bound accounts. It provides functionality for IP assets to own and manage other assets, execute transactions, and interact with other contracts in a permissioned manner.

State Variables

ACCESS_CONTROLLER

address public immutable ACCESS_CONTROLLER

The address of the AccessController contract used for permission checks. This is immutable and set during construction.

Inheritance

IPAccountImpl inherits from:

  • ERC6551: Base implementation of the ERC-6551 standard
  • IPAccountStorage: Storage contract for IP Account data
  • IIPAccount: Interface for IP Account functionality

Functions

constructor

constructor(
    address accessController,
    address ipAssetRegistry,
    address licenseRegistry,
    address moduleRegistry
)

Creates a new IPAccountImpl contract instance.

Parameters:

  • accessController: The address of the AccessController contract to be used for permission checks
  • ipAssetRegistry: The address of the IP Asset Registry
  • licenseRegistry: The address of the License Registry
  • moduleRegistry: The address of the Module Registry

supportsInterface

function supportsInterface(bytes4 interfaceId) public view returns (bool)

Checks if the contract supports a specific interface.

Parameters:

  • interfaceId: The interface identifier, as specified in ERC-165

Returns:

  • Boolean indicating if the contract supports the interface

token

function token() public view returns (uint256, address, uint256)

Returns the identifier of the non-fungible token which owns the account.

Returns:

  • chainId: The EIP-155 ID of the chain the token exists on
  • tokenContract: The contract address of the token
  • tokenId: The ID of the token

isValidSigner

function isValidSigner(address signer, bytes calldata data) public view returns (bytes4 result)

Checks if the signer is valid for executing specific actions on behalf of the IP Account.

Parameters:

  • signer: The signer to check
  • data: The data to be checked, encoded as abi.encode(address to, bytes calldata)

Returns:

  • The function selector if the signer is valid, 0 otherwise

isValidSigner

function isValidSigner(address signer, address to, bytes calldata data) public view returns (bool)

Checks if the signer is valid for the given data and recipient via the AccessController permission system.

Parameters:

  • signer: The signer to check
  • to: The recipient of the transaction
  • data: The calldata to check against

Returns:

  • Boolean indicating if the signer is valid

owner

function owner() public view returns (address)

Returns the owner of the IP Account.

Returns:

  • The address of the owner

state

function state() public view returns (bytes32 result)

Returns the IPAccount’s internal nonce for transaction ordering.

Returns:

  • The current state (nonce) of the account

updateStateForValidSigner

function updateStateForValidSigner(address signer, address to, bytes calldata data) external

Updates the IP Account’s state if the signer is valid for the given data and recipient.

Parameters:

  • signer: The signer to check
  • to: The recipient of the transaction
  • data: The calldata to check against

executeWithSig

function executeWithSig(
    address to,
    uint256 value,
    bytes calldata data,
    address signer,
    uint256 deadline,
    bytes calldata signature
) external payable returns (bytes memory result)

Executes a transaction from the IP Account on behalf of the signer.

Parameters:

  • to: The recipient of the transaction
  • value: The amount of Ether to send
  • data: The data to send along with the transaction
  • signer: The signer of the transaction
  • deadline: The deadline of the transaction signature
  • signature: The signature of the transaction, EIP-712 encoded

Returns:

  • The return data from the transaction

execute

function execute(address to, uint256 value, bytes calldata data) external payable returns (bytes memory result)

Executes a transaction from the IP Account.

Parameters:

  • to: The recipient of the transaction
  • value: The amount of Ether to send
  • data: The data to send along with the transaction

Returns:

  • The return data from the transaction

execute

function execute(
    address to,
    uint256 value,
    bytes calldata data,
    uint8 operation
) public payable returns (bytes memory result)

Executes a transaction from the IP Account with a specified operation type.

Parameters:

  • to: The recipient of the transaction
  • value: The amount of Ether to send
  • data: The data to send along with the transaction
  • operation: The operation type to perform, only 0 - CALL is supported

Returns:

  • The return data from the transaction

executeBatch

function executeBatch(
    Call[] calldata calls,
    uint8 operation
) public payable returns (bytes[] memory results)

Executes a batch of transactions from the IP Account.

Parameters:

  • calls: The array of calls to execute
  • operation: The operation type to perform, only 0 - CALL is supported

Returns:

  • The return data from the transactions

isValidSignature

function isValidSignature(bytes32 hash, bytes calldata signature) public view returns (bytes4 result)

ERC1271 signature verification is disabled for the IP Account.

Parameters:

  • hash: The hash of the data to be signed
  • signature: The signature to verify

Returns:

  • Always returns 0xffffffff (disabled)

Events

Executed

event Executed(address to, uint256 value, bytes data, bytes32 state)

Emitted when a transaction is executed from the IP Account.

Parameters:

  • to: The recipient of the transaction
  • value: The amount of Ether sent
  • data: The data sent along with the transaction
  • state: The new state (nonce) of the account

ExecutedWithSig

event ExecutedWithSig(address to, uint256 value, bytes data, bytes32 state, uint256 deadline, address signer, bytes signature)

Emitted when a transaction is executed from the IP Account on behalf of a signer.

Parameters:

  • to: The recipient of the transaction
  • value: The amount of Ether sent
  • data: The data sent along with the transaction
  • state: The new state (nonce) of the account
  • deadline: The deadline of the transaction signature
  • signer: The signer of the transaction
  • signature: The signature of the transaction

Security Considerations

The IPAccountImpl contract implements several security measures:

  1. Permission System: Uses an AccessController to manage permissions for different signers and operations.

  2. Signature Verification: Implements EIP-712 typed data signing for secure transaction authorization.

  3. Deadline Checking: Includes transaction deadlines to prevent replay attacks.

  4. Nonce Management: Uses a state (nonce) system to prevent transaction replay.

  5. Input Validation: Validates inputs and checks for edge cases, such as preventing invalid operations.

  6. Signature Malleability Protection: Includes protections against signature malleability attacks.

  7. Limited Operations: Only supports the CALL operation (0) for security reasons, restricting potentially dangerous operations.

  8. Upgradability Disabled: The contract disables UUPS upgradability to ensure contract immutability.

Usage Examples

Executing a Transaction

An IP asset owner can execute a transaction through their IP Account:

// Assuming 'ipAccount' is an instance of IPAccountImpl
ipAccount.execute(
    targetContract,
    0, // No ETH sent
    abi.encodeWithSignature("someFunction(uint256)", 123)
);

Executing with a Signature

A permitted signer can execute a transaction on behalf of the IP Account:

// Generate signature off-chain
bytes signature = signEIP712Message(...);

// Execute transaction
ipAccount.executeWithSig(
    targetContract,
    0, // No ETH sent
    abi.encodeWithSignature("someFunction(uint256)", 123),
    signer,
    deadline,
    signature
);