License
Node operators, hardware manufacturers, etc. can stake $DIMO to mint a license.
Address: 0xaA32a9E74486dd136cCEdF17061Fe4EFC95A621e
Flow
User/Manufacturer stakes the minimum amount of DIMO tokens
The contract tracks your staked balance
`checkUserIsWhitelisted()` returns a boolean based on stake requirements
The Aftermarket contract checks for eligibility and mints a license to the specified user
License
The original idea for soulbound tokens comes from Vitalik Butterin. There is no official spec nor EIP for soulbound tokens, but there are several implementations with different trade offs.
Design Decisions
We had some requirements to consider for Licensing
Keep pushing towards a web3/decentralized ecosystem
Prevent manufacturers from minting infinite licenses
Track the issuances of licenses
Easier onboarding of future Manufacturers
Include specific metadata to prevent abuse
In order to deliver the functionality inachieve the diagram above, where Hardware Manufacturers are licensed to create new DIMO-enabled devices we are heavily influenced by Account Bound Tokens (EIP-4973).
Advantages of ABT
Soulbound tokens override the ERC721
's transfer function to revert. This allows for the user to hold a token indefinitely, but it also binds the asset to the user's private key. If a user or institution loses access to their wallet, that asset is permanently unrecoverable. However, with an ABT, the token can be revoked and reassigned (granted they prove their identity/ownership first).
There may be a situation in which an attacker gains access to a SBT and is able to take out a loan in their name for example, whereas this situation can be mitigated with an ABT by simply revoking access.
Interfaces
In order to be maximally backward-compatible with existing ERC721 Infrastructure, ABTs (Account Bound Tokens) implement existing functions purposefully. Things like EIP-165
and ERC721Metadata
which wallets will already be familiar with.
Additionally there is a checkUserIsWhiteListed(address user)
function that returns a boolean and checks to see that the user has staked greater than the minStakeAmount
in order to return true.
ERC165
the IERC165 Interface is a single function supportsInterface()
with takes in a bytes4 interfaceId
as the parameter. The interfaceId is the 8bit hex string for the calldata of the function. The purpose of this method is for a contract to "ask" a second contract if it supports a specific method. This prevents sending tokens to a contract that cannot read them. For example, sending an ERC1155
token to an ERC20
contract would leave them stuck.
By hashing the function name and it's parameters, you can use the result to check if the method is supported.
keccak256("tokenByIndex(uint256)") == '0xasdfasdf"
you would then do
supportsInterface("0xasdfasdf")
and the resulting boolean would tell you whether it's safe to proceed or not.
Events
Our License
has two main events;
Attest
Emits when a new token is related and bound to an account by any mechanism
Revoke
Emits when an existing License is revoked from an account and destroyed by any mechanism.
Fund recovery
In the event a user accidentally sends DIMO tokens directly to the contract, the tokens would not be credited to the sender and the tokens would effectively be “stuck” or “lost”. This is a limitation of the ERC20 standard and would apply to any token being sent to any smart contract.
To recover lost DIMO, we implemented a emergencyWithdraw()
function that first checks for stuck tokens by subtracting the dimoTotalAmountStaked
value from the total number of DIMO held on the contract. The returning value is only ever positive when the user accidentally sends tokens directly rather than interacting with the stake()
function.
If there is a positive amount, and the user has a staked balance, the tokens are transferred to the specified user and the EmergencyWithdrawal(user,amount)
event is emitted.
Upgradeability
To implement future features for posterity, we are using the Universal Upgradeable Proxy Standard, aka EIP 1822. This pattern is a standard for proxy contracts which is universally compatible with all contracts, and does not create incompatibility between the proxy and business-logic contracts. This is achieved by utilizing a unique storage position in the proxy contract to store the Logic Contract’s address. A compatibility check ensures successful upgrades. Upgrading can be performed unlimited times, or as determined by custom logic. In addition, a method for selecting from multiple constructors is provided, which does not inhibit the ability to verify bytecode.
Last updated