πŸ“¬Virtual Key Address

Building a Virtualized Wallet Identity

Design Ethos

The VirtualKeyAddress is a per-key-instance contract that gives each individual key an on-line contract address to send and receive funds from. Operators of the virtual account must hold a specific key.

Operators have virtualized access to use all of the funds in the trust's collateral providers, and to external parties will see the msg.sender be the virtual address of the key's "inbox" and not the EOA account holding the key and signing the transaction.

This contract can use any funds from any asset the key has rights to for single or multi-call transaction.

The contract achieves and maintains the authorization and security by requiring a soulbound key to be attached to the contract so the contract can act as a definitive key holder with collateral providers and the Notary. For non-receive functions, both the caller and the contract must hold the same identity key.

Sending funds only requires gas and a key in the traditional wallet.
Sending funds to the contracts works like a traditional wallet.

The VirtualKeyAddress has further extendability to be able to:

  • Block senders or receivers based on address.

  • Block dustings or unwanted NFTs, tokens.

  • Provide multi-key multi-sig

This functionality is a trio of three contracts.

  • VirtualKeyAddress: The per-instance contract that enables each key to have a unique address and provides key security.

  • PostOffice: This is a registry that maintains a mapping of key IDs to contract addresses. This enables discoverability and management for UX. It also helps the root key holder from creating duplicate key addresses by accident.

  • KeyAddressFactory: This agent takes possession of a root key long enough to create a copy of the required key, deploy the inbox contract, soulbind the new key to the inbox address, and register it with the post office. The root key is given back to the message sender at the end of the transaction.

VirtualKeyAddress

Storage

The virtualized wallet keeps an internal model of the transaction history on-chain. It is modeled as such. One thing to note is receives of ERC20s will be upon "accept" because there is no reliable callback protocol.

The contract storage is as follows:

ethArn

Computed at initialization, stores the ethArn for easy use. Native gas is handled differently than tokens.

ownerKeyId

This is the key ID that legitimately owns the contract and is the only one who can upgrade it. The locksmith reference is the source of truth.

keyId

This is the key ID that the contract assumes the identity of. It will require that for operations that use vaulted collateral to hold this key for successful operation. The contract also assumes that a key with this ID is soulbound to the contract giving it the authority to act as a key holder on behalf of the caller.

keyInitialized

Determines if the key is properly initialized. Protects the keyId from looking like 0 when not initialized.

defaultEthDepositProvider

Wallet requires a default ether collateral provider to automatically service deposits. ERC20s do not provide callbacks and thus do not have a default provider.

depositHatch

This is a state variable that is used to model re-entrancy that comes from a collateral provider delivering the contract funds during a funding preparation step. Without this hatch, retrieving ether or 721s from vaults would look like incoming deposits from other senders and not preparation for an internal contract operation.

locksmith

The reference to the contract used to determine key possession.

transactions

The on-chain storage for this wallet's transaction history.

transactionCount

The number of transactions recorded in the history.

Operations

requiresKey

This modifier is used for methods that require the message sender holds keyId according to the locksmith.

send

This method is called by the key holder when they want to send the gas token from a collateral provider to another specific address outside of the trust model. This operations is logically equivalent to sending ether from Metamask to another address, but doing so from within the virtual wallet identity.

receive

This method acts as a callback when someone sends the virtual address ether. In this case, the contract takes the designated default ether collateral provider and deposits it directly into the vaults.

sendToken

This process is nearly identical to send(), however, uses the ERC20 standard and the ICollateralProvider arnWithdrawal interface to facilitate sending ERC20s.

acceptToken

Sending ERC20s do not enable a callback of an on-receive event. For traditional wallets, you also have to add unknown contract address to your wallet if they are not popular enough. Because of this, there is no protocol to automatically deposit ERC20s to a vault. Given a list of known contract addresses, it is possible for the VirtualKeyAddress user to detect tokens in their wallet and "accept" the token, in which the token is deposited into an ERC20 token provider attached to the trust.

This also has the side effect of automatically keeping scam ERC20s away from your private keys and other funds.

multicall

This method allows the key holder to call in and dynamically orchestrate multiple calls as a virtual identity. The key holder first prepares funds into the contract by specifying which assets should be made available. Then, once in the contract, the funds can be used by calls to execute as an abstracted account. This includes things like single-transaction swaps on Uniswap, among basically anything else valuable an account would want to do and compress into a single transaction.

The function takes two structures - which include enough metadata to describe both the funds needed for the transactions as well as the encoded data for the function calls.

prepareWithdrawalAllowance

This is an internal method that is used muiltiple places to ensure that withdrawals from storage providers are properly authorized at the Notary. Without this, the ledger will fail to notarize the transaction because the collateral provider isn't cleared to facilitate a transaction.

PostOffice

The Post Office is a simple singleton mapping of KeyIDs to their associated Virtual Key Address contract addresses. This ensures that root key holders can find their trust's inbox addresses easily, and to prevent multiple inboxes per key.

Storage

locksmith

The refernce to the locksmith that is used to verify key possession.

inboxes

An index mapping of each address to whether or not it is a valid inbox registered with the Post Office.

keyIdentityInboxes

For each Key that has an inbox, what is it's address?

ownerKeyInboxes

Find the key inboxes that are owned by an individual key. A key (like root), can own many inboxes. Ownership is separate from the inbox "identity" (keyId)

Operations

There are two introspection methods, getInboxesForKey(), and getKeyInboxes() which will not be covered in depth. They facilitate seeing what inbox addresses are owned by a given key, and what contract address exists, if any, for a given key Id.

registerInbox

This method is called by the inbox owner, who must be root, to register with the Post Office. The VirtualKeyAddress must already be deployed and configured.

Most of the work is to ensure that the request is valid. The address must be a unique address and identity key. The virtual address contract must also be owned by a root key, that the message sender also holds. The key identity for the virtual address needs to be a key within the root key's trust.

deregisterInbox

There may be circumstances where an inbox will want to be removed. This will remove the inbox from the index. This may be in the case where no upgrade path exists for a given virtual address implementation, so it may need to be fully replaced.

KeyAddressFactory

This is a simple contract that, upon receiving a root key, will create a brand new contract using CREATE2 to build a virtual address for a given key. It does this by using the root key to create a copy of the identity key, deploys the new contract, soulbinds the copied key to the new contract address, registers the address with the PostOffice, and then finally returns the root key to the message sender at the end of the transaction.

Storage

postOffice

This is a reference to the post office contract that the factory will use to register the inbox with.

struct InboxRequest

These two fields specify the request as serialized data along with sending the actual root key required to do the work in a single transaction. The virtualKeyId is the identity of the key that will be soulbound to the generated address, and the defaultEthDepositProvider is the trusted provider where gas will be sent when received.

Operations

There is only a single operation, onERC1155Received, and assumes the NFT sent into the contract belongs to a valid Locksmith.

onERC1155Received

Last updated