Factory
Abstract
The ONCHAINID Factory is a decentralized application that allows users to securely create, store, and manage unique digital identities on any EVM blockchain. It provides an easy and secure way to link wallets and tokens to on-chain identities, as well as the ability to register token factories, which can issue additional identities for tokens. The Factory is designed to maintain data privacy, security, and scalability, making it ideal for a wide range of use cases. The Factory is powered by smart contracts and is designed to ensure the secure management of digital identities.
Proxification of contracts
The ONCHAINID Factory is not deploying the direct implementation contracts, instead it is using a modified version
of the ERC-1822
Upgradeable proxy standard, the modification done to the proxy compared to the ERC-1822
is that
the
Proxy contracts used on ONCHAINID are all linked to a shared ImplementationAuthority
smart contract, that allows a
synchronous upgrade of all ONCHAINID proxies in one single upgrade call.
The smart contract implementation as well as the ImplementationAuthority
contract MUST be deployed before
deploying proxies, otherwise the proxy deployment will fail
Use of CREATE2 opcode
The deployments made from the Factory are using the CREATE2 opcode from EVM, which allow the deployment of contracts in a deterministic way, thus creating a predictable contract address.
The same user should have the same ONCHAINID address on any EVM compatible blockchain, which brings interesting
properties, including the cross-chain portability of claims, as the claim signature is a signature of keccak256(abi.
encode(identityHolder_address, topic, data))
with a claim key contained in the ClaimIssuer
contract, as long as
the data, topic and ONCHAINID addresses are the same on both chains, the claim signature remains valid and can be
added to the ONCHAINID.
It is very important to protect users from usurpation of identity, as the use of CREATE2 opcode allows to deploy an
Identity with the same address on multiple blockchains, and use the claims issued for the primary identity, it is
necessary to add checks to prevent any misuse of the function. CREATE2 is using a salt
parameter to determine the
address of the smart contract deployed. To protect users from Identity theft, the salt
generation has to be
protected. The basic implementation of the Factory protects the Identity Creation functions with an owner scope,
which keeps the door open to more complex mechanisms, as the owner role can be given to an external smart contract
used for the generation of salt
and roles management. The implementation of such smart contract can be done in
multiple ways, but it should always protect the users from identity theft!
How to deploy the factory
The deployment of a new ONCHAINID Factory has to be done in different steps, each step is described below and it is very important to respect these steps, otherwise the Factory will not be able to work properly.
Deployment of library contracts
The Library contract is an implementation of ONCHAINID, which is deployed separately, with the constructor parameter
_isLibrary
set on TRUE
at deployment. The initial management key for the deployment of the library contract
doesn't matter, as the actions are protected from interactions on the library contract, with a modifier
delegatedOnly
which prevents any direct calls to the implementation contract.
The Library contract is the one that needs to be redeployed at each new version of ONCHAINID to upgrade the proxies.
Deployment of the implementation authority
The Implementation Authority contract is used as a central point of reference for all proxies that are deployed.
This contract is implementing the Ownable
library from OpenZeppelin which is used to protect the most critical
function of the whole ONCHAINID ecosystem, the updateImplementation
function, which allows the smart contract
owner to update the address of the Library smart contract used by all proxies pointing to the same
ImplementationAuthority
contract, i.e. all proxies deployed by the same ONCHAINID Factory.
The updateImplementation
could be considered as a backdoor on ONCHAINID contracts as it allows to upgrade the
contract code to any new address, the new address could implement anything, including functions to steal funds of
users if they are storing funds on their ONCHAINID or if the ONCHAINID has some allowance on tokens held on external
wallets. This is the reason why the owner address should be protected with a top-level security, e.g. using multisig
smart contracts, MPC wallets, HSMs, or even a DAO to manage the upgradeability of ONCHAINID smart contracts.
The address of the Library contract, previously deployed, has to be given to the ImplementationAuthority
contract,
at deployment, as it is a parameter required by the ImplementationAuthority
constructor.
Deployment of the OID factory
Once the ImplementationAuthority
is properly deployed and setup, the Factory can be deployed as well.
The Factory implements the Ownable
library from OpenZeppelin to protect the ONCHAINID deployment functions as well
as the functions for whitelisting Token Factory addresses. The Token Factory addresses will be able to call the
function to create ONCHAINIDs for tokens.
As mentioned above, in the CREATE2
topic, the deployment of new identity proxies has to be protected to avoid
usurpation of identity, the owner role is used for that and can be given to an external contract implementing logic
to prevent the misuse of these critical functions, e.g. by using oracles to check that the salt
is not used on
other blockchains yet, by formatting the salt
in a way that only the same user can deploy on different blockchains
(by using a cryptographic signature or checking the msg.sender
address), or any other method. The implementation
of such contract can be done in a lot of different ways.
To deploy the Factory, the address of the ImplementationAuthority
contract, previously deployed and set has to be
given to the constructor of the Factory contract.
How to use the factory
Deployment of new ONCHAINID proxies
To deploy a new ONCHAINID proxy, the function createIdentity
needs to be called. The parameters of this function
are the wallet address of the ONCHAINID owner (that will be set as the first MANAGEMENT key of the ONCHAINID) and the
salt used
for the deployment and allowing CREATE2
to generate
a deterministic smart contract address, that address can be the same on all blockchains as long as the address of
the factory contract and the salt are identical. To make a successful ONCHAINID deployment some conditions need to
be respected, the salt cannot already be used, as you cannot deploy 2 times with the same salt (in the same way that
you cannot deploy 2 times with the same nonce in a classic transaction) and the wallet cannot be linked to an
ONCHAINID already. Multiples wallets can be linked to an ONCHAINID but a wallet cannot be shared by multiple
ONCHAINIDs. To ensure the security of ONCHAINIDs and prevent identity theft cross-chain (deployment of an ONCHAINID
with the same salt from a different person) the createIdentity
function is protected from external calls with an
onlyOwner
modifier, which allows only the address set as owner to call the function. This address can obviously be
an external smart contract, managing roles and accessibility to the function in any way that would fit the
requirements coming from the factory deployer. The function createIdentity
when called successfully emits an event
Deployed
and an event WalletLinked
with the first wallet that is linked to the ONCHAINID.
An easy way to implement that external contract could consist in simply using the address of the ONCHAINID deployer as part of the salt, therefore identity theft would not be possible as the resulting ONCHAINID would be managed by the same wallet on all chains by design.
Keep in mind that keys could be lost or compromised and make sure that a recovery is possible in case of lost key.
It is very important to keep in mind the potentiality of identity theft cross-chain if the external contract
managing access to the createIdentity
function is not implemented correctly.
Linking new wallets to an ONCHAINID
When a new ONCHAINID is deployed, the first wallet, used at deployment as the primary MANAGEMENT key, is "linked" to
the ONCHAINID, what it means is that the factory contract keeps into its storage, in a mapping, the correspondence
table between wallets and ONCHAINIDs and vice versa. This allows external contracts to query the factory to know
which ONCHAINID is linked to a wallet trying to interact with the latter and to then verify the eligibility of that
ONCHAINID to access their services. Having a link between wallets and ONCHAINIDs is necessary as each ONCHAINID is a
separate smart contract and without that link it would be impossible (at least onchain, because offchain you can
track events) to find the corresponding ONCHAINID to a wallet that you are interacting with.
The function linkWallet
is used for that. To link a new wallet to your ONCHAINID you need to call the function
linkWallet
with a wallet that is already linked to your ONCHAINID and the parameter of the function being the new
wallet you want to link.
The linked wallets on the factory contract can be seen as a central correspondence table between wallets and ONCHAINIDs and can be used as such in any application using ONCHAINIDs.
The function getIdentity
can be used to fetch the identity address linked to any wallet while the function
getWallets
can be used to fetch the array of wallets linked to an ONCHAINID.
It is not necessary to link the wallets on the factory if a similar link is made on another smart contract, e.g. on the Identity Registry of ERC-3643 a similar link exists and is used for managing the list of token holders.