This smart contract allows trustless gifting of ERC20/ERC721/ERC1115/native tokens (hereafter referred to as tokens) to unknown recipients.
Basic Glossary:
creator- the person giving away tokens by sharing asecret sharing codewith the receiverreceiver- the person who has the secret sharing code and is allowed to claim a giftsharing code- a set of random/custom strings used as a base "salt" to generate a private key of theverifierverifier- a signer used in the verification process to verify the validity of a gift and the receiver
- The
creatorcreates a gift with giftable content and the address of a verifier (the verifier is generated in the UI based on the provided customsharing codeor randomly generatedsharing code) - The
creatorshares thesharing codewith the receiver - The
receiveropens a Gifting UI where thesharing codeis decoded to theverifiersigner. The claiming signature is generated in the background when thereceiverconnects their wallet. - The signature contains the
receiveraddress. - This signature can be used by the
receiveror any "operator" (if thereceiverhas no RON for gas fees) to claim the gift usingclaimGift(giftID, receiver, signature). The gift is transferred to the receiver.
- The
creatorcan set additional restrictions during gift creation, such as:- Atia's Blessing status
- Atia's Blessing strike higher than
x - Holding a specific amount of ERC721, ERC20 or native tokens
- Whoever has the
sharing codemust meet the defined criteria to successfully claim the gift.
Gifts.sol- Ownable contract logic. The owner can change the logic address of theRestriction Controllerand introduce new restrictions.RestrictionControl.sol- Disposable contract that introduces gift restrictions. It can be redeployed and the implementation address can be changed inGifts.sol.
All active/claimed/cancelled gifts are stored in this mapping. Should we be concerned about the potential size? If so, we can remove claimed/cancelled gifts from the mapping to reduce its size. The downside would be that thereceiverwould not know the reason why they cannot claim their gift (whether the creator cancelled it or someone else claimed it quicker). This could lead to higher gas usage for claiming/cancellations.
Over time, it will collect addresses of all used verifiers in the past (for active/claimed/cancelled gifts). The purpose is to block the re-use of already used verifiers. If this is a valid concern, then I could delete verifiers when a gift is being cancelled or claimed. This could lead to higher gas usage for claiming/cancellations.
#3 Giving away too much info could benefit attackers? ✅ No risk exposing wheter invalid gift was claimed or cancelled
ShouldgetGiftreturn cancelled/claimed gifts?ShouldcancelGiftandclaimGiftrevert with specific reasons why a gift cannot be cancelled/claimed? (already claimed/cancelled)?
Maybe this is a completely pointless check, and removing it could save some gas.
The main contract is not upgradable on purpose - it holds gifted tokens, and a malicious upgrade could introduce ways to take them all.
There are 2 options for handling the upgradeability of the restriction controller (currently I went with option#1):
Gifts.solis Ownable, and onlyOwner can change the implementation address. If a new version is out, then the owner would just change the address.Gifts.solwould not need to be Ownable. We would deploy theRestriction Controlleras an upgradable transparent proxy and use the proxy address in theGifts.solconstructor. If a new version is out, then we would upgrade the proxy, andGifts.solwould remain untouched.
The gift creator can passbytesduring the gift creation. The Restriction Controller'scheckRestrictionis called with such bytes to verify the restriction exists, and then it is stored inallGifts[giftID].restrictions.args.My concern is if a malicious gift creator can use specifically craftedbytesto somehow exploit the restriction controller, allowing them to do harm to the contract or allowing them to use the created gift with maliciousbytesto claim/retrieve tokens from the contract that they are not supposed to.
-
Should we add an onlyOwner function to cancel gifts on behalf of someone else?
-
Or make contract pausable and add emergencyExit to cancel all existing gifts, return assets to owners and pause contract?
-
Should the contract be pausable?
✅ Added
emergencyExit, just in case
yarn hardhat deploy --network <ronin|saigon>