-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(protocol): Add votes and snapshot plugins to BridgedERC20 #15715
Conversation
Dani, I also suggest suggest this into two PRs, one to add snapshot and delegate function to the bridgedERC20 token; the other one to solve the airdrop-and-delegate issue -- if we can do it in on tx, it's great, otherwise, we can still use more than one transaction, but BridgedERC20 should not use tx.origin just because we have the airdrop-and-delegate objective. |
We can use this function in /**
* @dev Delegates votes from signer to `delegatee`
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
...
Before the airdrop transaction, we collect a delegateSignature from the user, making sure its valid; then the user proceed with the airdrop claim function. Two signatures, one transaction. Later, we asynchronously fire many We can also ask the user to transact his |
I rather do it in one PR here - as this is the reason why we doing it. The changes are not so big, i dont think we shall fractionalize that much. |
Great suggestion. Made it working via this way, and it will be in 1 transaction as you proposed - together with the claim - as you and Keng proposed for. This way the user pays and transact, no need to store it. Anyways, if we were about to do with 2 transactions, then i would do the :
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think delegating with a signature is a good approach. Claiming for other addresses should still be allowed I feel so that users don't have to interact with each address that has an airdrop separately, but this would be off the default path so something they could just use etherscan for or something. The UI requiring the same address claiming the tokens to also sign and delegate seems like a good default flow.
Anyways, if we were about to do with 2 transactions, then i would do the :
Seems easy (and I think even necessary for general case) to support both cases.
@@ -59,7 +76,15 @@ abstract contract MerkleClaimable is EssentialContract { | |||
} | |||
|
|||
isClaimed[hash] = true; | |||
_claimWithData(data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems a bit like a strange setup where the claiming has an extra internal function to extend the functionality. More logical to me would be to have an external claimAndDelegate()
function (or something like that) that would call claim
and also calls delegateBySig()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think from the op's perspective, we want to make sure if some one is in the airdrop list, the address must claim by itself, no way can claim for it, so we filter out dead addresses. And we also want to make sure whenever you claim, you must delegate to someone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, the extra function shall go to ERC20Aridrop contracts, not in this parent contract.
contract BridgedERC20 is | ||
BridgedERC20Base, | ||
IERC20MetadataUpgradeable, | ||
ERC20SnapshotUpgradeable, | ||
ERC20VotesUpgradeable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The downside of having to do one size fits all, now all bridged tokens need to support the onchain voting functionality because we want it for TKO (will be nicer in the next protocol version!). :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, my proposal was a special check in Bridge -> if TKO then mint into a modified contract.
Now every bridge contract can basically basically used in a DAO, which i'd say does not hurt, right ? Maybe it is a feature.. ?
Deployment size does not matter bc. we anyway pre-deploy the implementation. If snapsot and voting is not used frequently also the transfer is not 'heavy' (same cost) so i dont see it a bug but rather maybe a cool feature(?).
What do you see the downsizes or side effects of it ?
Taiko can even be a chain which boost your token and make "DAO-able" :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see #15726
abi.encodePacked( | ||
"\x19\x01", | ||
DOMAIN_SEPARATOR, | ||
getStructHash(_permit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is exactly the same. It is just a helper sig util. The true "thing" is to re-create on the UI..!
We need to test it back and forth.
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
// Creating the hashTypedDataV4 hash type signing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OZ has library code to support EIP 712,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See:
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
contract MessageSigner is EIP712 {
string private constant SIGNING_DOMAIN = "MessageSignerDomain";
string private constant SIGNATURE_VERSION = "1";
struct Message {
address from;
string message;
}
bytes32 private constant MESSAGE_TYPEHASH = keccak256(
"Message(address from,string message)"
);
function getMessageHash(Message memory message) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(
MESSAGE_TYPEHASH,
message.from,
keccak256(bytes(message.message)) // Correctly hash the string message
)));
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer to move the code to ERC20Airrop.sol or its parent contract.
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
// Creating the hashTypedDataV4 hash type signing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See:
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
contract MessageSigner is EIP712 {
string private constant SIGNING_DOMAIN = "MessageSignerDomain";
string private constant SIGNATURE_VERSION = "1";
struct Message {
address from;
string message;
}
bytes32 private constant MESSAGE_TYPEHASH = keccak256(
"Message(address from,string message)"
);
function getMessageHash(Message memory message) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(
MESSAGE_TYPEHASH,
message.from,
keccak256(bytes(message.message)) // Correctly hash the string message
)));
}
}
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
// Creating the hashTypedDataV4 hash type signing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer to move the code to ERC20Airrop.sol or its parent contract.
uint8 v, | ||
bytes32 r, | ||
bytes32 s | ||
) internal override { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If both ERC20Airdrop and ERC20Airdrop2 allow claiming, we shall create a parent contract and move shared code there.
@@ -40,7 +40,15 @@ contract ERC721Airdrop is MerkleClaimable { | |||
vault = _vault; | |||
} | |||
|
|||
function _claimWithData(bytes calldata data) internal override { | |||
function _claimWithData( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERC721Airdrop shall not be changed at all -- delegation shall not be something MerkleClaimable shall care.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then how do you achieve the desired one transaction per airdrop ? I already told you and Keng i prefer the 2 time transactions, because of errors too for example (UI does not consturcts sig things properly, etc.)
@adaki2004 I still think we should split snapshot from airdrop and have 2 PRs. |
Please proceed as you wish, i'll be OOF most of today and tomorrow- or wait till monday. Also would be good if the requirements would be more clear not changing from day to day. If requirements are not perfectly known (and changing) it creates wasted efforts like this and double work. |
@adaki2004 please review: #15727, if this PR is merged, I think you can revert all files except BridgedERC20.sol |
contract BridgedERC20 is | ||
BridgedERC20Base, | ||
IERC20MetadataUpgradeable, | ||
ERC20SnapshotUpgradeable, | ||
ERC20VotesUpgradeable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see #15726
What if we collect and the signature will not be valid afterwards ? I guess it is not a huge issue as it means the user delegated on his own. |
Closing as we implemented it in 2 separate PRs. (Was easier to open up a new one with BridgedERC20 than revert some changes here.) |
So we were discussing "delegation" during the airdrop. I'm fine with it but if i recall, Daniel and Keng vouched for a one transaction: claim and delegation bundled together (so from inside the airdrop claiming contract).
To achieve this we can use the
delegateBySig()
function.Daniel's advice was make
BridgeERC20
implementERC20Votes
andERC20Snapshot
since this way all bridge contracts could have this feature and we dont need to make extra changes to the protoocl to work with Taiko tokens. (I could not put the modules intoBridgedERC20Base
because of inheritance linearization)