From 17f1eb7c0eebcd2aaa8ace8852d8235cb0671181 Mon Sep 17 00:00:00 2001 From: ross <92001561+z0r0z@users.noreply.github.com> Date: Thu, 25 Jan 2024 09:07:49 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=95=AF=EF=B8=8F=20Summoner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gas-snapshot | 40 ++++++----- abi/Summoner.json | 70 +++++++++++++++++++ docs/src/SUMMARY.md | 2 + docs/src/src/Dagon.sol/contract.Dagon.md | 2 +- docs/src/src/Dagon.sol/interface.IAuth.md | 2 +- docs/src/src/Dagon.sol/interface.IOwnable.md | 2 +- docs/src/src/README.md | 2 + .../src/src/Summoner.sol/contract.Summoner.md | 55 +++++++++++++++ .../src/Summoner.sol/interface.IAccounts.md | 21 ++++++ src/Summoner.sol | 42 +++++++++++ test/Dagon.t.sol | 6 +- test/DagonScales.t.sol | 2 +- test/Summoner.t.sol | 37 ++++++++++ 13 files changed, 258 insertions(+), 25 deletions(-) create mode 100644 abi/Summoner.json create mode 100644 docs/src/src/Summoner.sol/contract.Summoner.md create mode 100644 docs/src/src/Summoner.sol/interface.IAccounts.md create mode 100644 src/Summoner.sol create mode 100644 test/Summoner.t.sol diff --git a/.gas-snapshot b/.gas-snapshot index 0e37c80..fd0d533 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,38 +1,42 @@ DagonScalesTest:testIsValidSignatureALOT() (gas: 34615892725) DagonTest:testBurn(address,uint96) (runs: 256, μ: 152642, ~: 152642) DagonTest:testDeploy() (gas: 2071165) -DagonTest:testFailBurnOverBalance(address,uint96) (runs: 256, μ: 187133, ~: 188222) -DagonTest:testFailBurnOverThreshold(address,uint96) (runs: 256, μ: 219319, ~: 220408) +DagonTest:testFailBurnOverBalance(address,uint96) (runs: 256, μ: 187211, ~: 188222) +DagonTest:testFailBurnOverThreshold(address,uint96) (runs: 256, μ: 219419, ~: 220430) DagonTest:testFailInvalidThresholdExceedsSupply() (gas: 159203) -DagonTest:testFailInvalidThresholdExceedsSupply2() (gas: 164582) +DagonTest:testFailInvalidThresholdExceedsSupply2() (gas: 164604) DagonTest:testFailInvalidThresholdNull() (gas: 158981) -DagonTest:testFailIsValidSignature2of3ForInsufficientSignatures() (gas: 199625) -DagonTest:testFailIsValidSignatureWeighted() (gas: 228757) -DagonTest:testFailIsValidSignatureWeightedERC1155() (gas: 37189) -DagonTest:testFailIsValidSignatureWeightedERC20() (gas: 237823) -DagonTest:testFailIsValidSignatureWeightedERC6909() (gas: 230379) +DagonTest:testFailIsValidSignature2of3ForInsufficientSignatures() (gas: 199647) +DagonTest:testFailIsValidSignatureOutOfOrder() (gas: 236802) +DagonTest:testFailIsValidSignatureWeighted() (gas: 228779) +DagonTest:testFailIsValidSignatureWeightedERC1155() (gas: 37211) +DagonTest:testFailIsValidSignatureWeightedERC20() (gas: 237845) +DagonTest:testFailIsValidSignatureWeightedERC6909() (gas: 230401) DagonTest:testFailIsValidSignatureWeightedERC721() (gas: 208731) -DagonTest:testFailSetTokenInvalidStd(address) (runs: 256, μ: 158162, ~: 158162) +DagonTest:testFailSetTokenInvalidStd(address) (runs: 256, μ: 158184, ~: 158184) DagonTest:testFailTransferFromInactiveAuth(address,address,uint96) (runs: 256, μ: 191540, ~: 192396) -DagonTest:testFailTransferOverBalance(address,address,uint96) (runs: 256, μ: 186281, ~: 187137) +DagonTest:testFailTransferOverBalance(address,address,uint96) (runs: 256, μ: 186459, ~: 187159) DagonTest:testInstall() (gas: 137318) DagonTest:testIsValidSignature() (gas: 150134) -DagonTest:testIsValidSignature2of3() (gas: 195901) -DagonTest:testIsValidSignature3of3() (gas: 203429) +DagonTest:testIsValidSignature2of3() (gas: 195923) +DagonTest:testIsValidSignature3of3() (gas: 203451) DagonTest:testIsValidSignatureMany() (gas: 433994) DagonTest:testIsValidSignatureOnchain() (gas: 196848) DagonTest:testIsValidSignatureVeryMany() (gas: 1236406) -DagonTest:testIsValidSignatureWeighted() (gas: 232474) +DagonTest:testIsValidSignatureWeighted() (gas: 232496) DagonTest:testIsValidSignatureWeightedERC1155() (gas: 247382) DagonTest:testIsValidSignatureWeightedERC20() (gas: 247118) -DagonTest:testIsValidSignatureWeightedERC6909() (gas: 247788) +DagonTest:testIsValidSignatureWeightedERC6909() (gas: 247810) DagonTest:testIsValidSignatureWeightedERC721() (gas: 208432) DagonTest:testNameAndSymbolAndDecimals(uint256) (runs: 256, μ: 15447, ~: 15447) DagonTest:testSetAuth(address) (runs: 256, μ: 144744, ~: 144744) DagonTest:testSetThreshold() (gas: 147272) DagonTest:testSetToken(address) (runs: 256, μ: 148626, ~: 148626) -DagonTest:testSetURI() (gas: 165570) +DagonTest:testSetURI() (gas: 165592) DagonTest:testSpoofSignatures(bytes) (runs: 256, μ: 16508, ~: 16437) -DagonTest:testTransfer(address,address,uint88) (runs: 256, μ: 176009, ~: 176787) -DagonTest:testTransferWithAuth(address,address,uint96) (runs: 256, μ: 179558, ~: 180278) -DagonTest:testUserVoted() (gas: 194822) \ No newline at end of file +DagonTest:testTransfer(address,address,uint88) (runs: 256, μ: 176031, ~: 176809) +DagonTest:testTransferWithAuth(address,address,uint96) (runs: 256, μ: 179160, ~: 180278) +DagonTest:testUserVoted() (gas: 194822) +SummonerTest:testSummoning(bytes12) (runs: 256, μ: 193633, ~: 193633) +SummonerTest:testSummoningForERC20(bytes12) (runs: 256, μ: 151767, ~: 151767) +SummonerTest:testSummoningForNFT(bytes12) (runs: 256, μ: 151789, ~: 151789) \ No newline at end of file diff --git a/abi/Summoner.json b/abi/Summoner.json new file mode 100644 index 0000000..156cd4c --- /dev/null +++ b/abi/Summoner.json @@ -0,0 +1,70 @@ +[ + { + "type": "function", + "name": "summon", + "inputs": [ + { + "name": "summoner", + "type": "address", + "internalType": "address" + }, + { + "name": "shares", + "type": "uint88", + "internalType": "uint88" + }, + { + "name": "locked", + "type": "bool", + "internalType": "bool" + }, + { + "name": "salt", + "type": "bytes12", + "internalType": "bytes12" + } + ], + "outputs": [ + { + "name": "account", + "type": "address", + "internalType": "contract IAccounts" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "summonForToken", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "nft", + "type": "bool", + "internalType": "bool" + }, + { + "name": "threshold", + "type": "uint88", + "internalType": "uint88" + }, + { + "name": "salt", + "type": "bytes12", + "internalType": "bytes12" + } + ], + "outputs": [ + { + "name": "account", + "type": "address", + "internalType": "contract IAccounts" + } + ], + "stateMutability": "payable" + } + ] \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 6d3d9be..e915708 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -4,3 +4,5 @@ - [Dagon](src/Dagon.sol/contract.Dagon.md) - [IAuth](src/Dagon.sol/interface.IAuth.md) - [IOwnable](src/Dagon.sol/interface.IOwnable.md) + - [Summoner](src/Summoner.sol/contract.Summoner.md) + - [IAccounts](src/Summoner.sol/interface.IAccounts.md) diff --git a/docs/src/src/Dagon.sol/contract.Dagon.md b/docs/src/src/Dagon.sol/contract.Dagon.md index 0025398..e520eb7 100644 --- a/docs/src/src/Dagon.sol/contract.Dagon.md +++ b/docs/src/src/Dagon.sol/contract.Dagon.md @@ -1,5 +1,5 @@ # Dagon -[Git Source](https://github.com/Moloch-Mystics/dagon/blob/c2b041fa6461441e320461b10ebb5c5d514a6859/src/Dagon.sol) +[Git Source](https://github.com/Moloch-Mystics/dagon/blob/65b43bfbebe7dc8176f84027fc17e3554a0b2583/src/Dagon.sol) **Inherits:** ERC6909 diff --git a/docs/src/src/Dagon.sol/interface.IAuth.md b/docs/src/src/Dagon.sol/interface.IAuth.md index cd6f820..2b57b9b 100644 --- a/docs/src/src/Dagon.sol/interface.IAuth.md +++ b/docs/src/src/Dagon.sol/interface.IAuth.md @@ -1,5 +1,5 @@ # IAuth -[Git Source](https://github.com/Moloch-Mystics/dagon/blob/c2b041fa6461441e320461b10ebb5c5d514a6859/src/Dagon.sol) +[Git Source](https://github.com/Moloch-Mystics/dagon/blob/65b43bfbebe7dc8176f84027fc17e3554a0b2583/src/Dagon.sol) Simple authority interface for contracts. diff --git a/docs/src/src/Dagon.sol/interface.IOwnable.md b/docs/src/src/Dagon.sol/interface.IOwnable.md index 8dd6e3b..6e7ee17 100644 --- a/docs/src/src/Dagon.sol/interface.IOwnable.md +++ b/docs/src/src/Dagon.sol/interface.IOwnable.md @@ -1,5 +1,5 @@ # IOwnable -[Git Source](https://github.com/Moloch-Mystics/dagon/blob/c2b041fa6461441e320461b10ebb5c5d514a6859/src/Dagon.sol) +[Git Source](https://github.com/Moloch-Mystics/dagon/blob/65b43bfbebe7dc8176f84027fc17e3554a0b2583/src/Dagon.sol) Simple ownership interface for handover requests. diff --git a/docs/src/src/README.md b/docs/src/src/README.md index 675a9ca..6b358fb 100644 --- a/docs/src/src/README.md +++ b/docs/src/src/README.md @@ -4,3 +4,5 @@ - [Dagon](Dagon.sol/contract.Dagon.md) - [IAuth](Dagon.sol/interface.IAuth.md) - [IOwnable](Dagon.sol/interface.IOwnable.md) +- [Summoner](Summoner.sol/contract.Summoner.md) +- [IAccounts](Summoner.sol/interface.IAccounts.md) diff --git a/docs/src/src/Summoner.sol/contract.Summoner.md b/docs/src/src/Summoner.sol/contract.Summoner.md new file mode 100644 index 0000000..cee5798 --- /dev/null +++ b/docs/src/src/Summoner.sol/contract.Summoner.md @@ -0,0 +1,55 @@ +# Summoner +[Git Source](https://github.com/Moloch-Mystics/dagon/blob/65b43bfbebe7dc8176f84027fc17e3554a0b2583/src/Summoner.sol) + +Simple summoner for Dagon (𒀭) group accounts. + + +## State Variables +### DAGON + +```solidity +address internal constant DAGON = 0x0000000000001D4B1320bB3c47380a3D1C3A1A0C; +``` + + +### FACTORY + +```solidity +IAccounts internal constant FACTORY = IAccounts(0x000000000000dD366cc2E4432bB998e41DFD47C7); +``` + + +## Functions +### summon + + +```solidity +function summon(address summoner, uint88 shares, bool locked, bytes12 salt) + public + payable + returns (IAccounts account); +``` + +### summonForToken + + +```solidity +function summonForToken(address token, bool nft, uint88 threshold, bytes12 salt) + public + payable + returns (IAccounts account); +``` + +## Enums +### Standard + +```solidity +enum Standard { + DAGON, + ERC20, + ERC721, + ERC1155, + ERC6909 +} +``` + diff --git a/docs/src/src/Summoner.sol/interface.IAccounts.md b/docs/src/src/Summoner.sol/interface.IAccounts.md new file mode 100644 index 0000000..54d1313 --- /dev/null +++ b/docs/src/src/Summoner.sol/interface.IAccounts.md @@ -0,0 +1,21 @@ +# IAccounts +[Git Source](https://github.com/Moloch-Mystics/dagon/blob/65b43bfbebe7dc8176f84027fc17e3554a0b2583/src/Summoner.sol) + +*Simple interface for Nani (𒀭) user account creation.* + + +## Functions +### createAccount + + +```solidity +function createAccount(address, bytes32) external payable returns (address); +``` + +### execute + + +```solidity +function execute(address, uint256, bytes calldata) external payable returns (bytes memory); +``` + diff --git a/src/Summoner.sol b/src/Summoner.sol new file mode 100644 index 0000000..8aa3744 --- /dev/null +++ b/src/Summoner.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.23; + +/// @notice Simple summoner for Dagon (𒀭) group accounts. +/// @custom:version 0.0.0 +contract Summoner { + // Constants: + address internal constant DAGON = 0x0000000000001D4B1320bB3c47380a3D1C3A1A0C; + IAccounts internal constant FACTORY = IAccounts(0x000000000000dD366cc2E4432bB998e41DFD47C7); + + // Tokens: + enum Standard { + DAGON, + ERC20, + ERC721, + ERC1155, + ERC6909 + } + + // Dagon-native: + function summon(address summoner, uint88 shares, bool locked, bytes12 salt) public payable returns (IAccounts account) { + account = IAccounts(FACTORY.createAccount(address(this), bytes32(abi.encodePacked(address(this), salt)))); + account.execute(DAGON, 0, abi.encodeWithSignature("mint(address,uint96)", summoner, uint96(shares))); + if (locked) account.execute(DAGON, 0, abi.encodeWithSignature("setAuth(address)", address(0xdead))); + account.execute(DAGON, 0, abi.encodeWithSignature("setThreshold(uint88)", shares)); + account.execute(address(account), 0, abi.encodeWithSignature("transferOwnership(address)", DAGON)); + } + + // External token: + function summonForToken(address token, bool nft, uint88 threshold, bytes12 salt) public payable returns (IAccounts account) { + account = IAccounts(FACTORY.createAccount(address(this), bytes32(abi.encodePacked(address(this), salt)))); + account.execute(DAGON, 0,abi.encodeWithSignature("setToken(address,uint8)", token, nft ? Standard.ERC721 : Standard.ERC20)); + account.execute(DAGON, 0, abi.encodeWithSignature("setThreshold(uint88)", threshold)); + account.execute(address(account), 0, abi.encodeWithSignature("transferOwnership(address)", DAGON)); + } +} + +/// @dev Simple interface for Nani (𒀭) user account creation. +interface IAccounts { + function createAccount(address, bytes32) external payable returns (address); + function execute(address, uint256, bytes calldata) external payable returns (bytes memory); +} diff --git a/test/Dagon.t.sol b/test/Dagon.t.sol index ee8adb6..e587e72 100644 --- a/test/Dagon.t.sol +++ b/test/Dagon.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.23; import "@forge/Test.sol"; @@ -1384,7 +1384,7 @@ contract DagonTest is Test { assertEq(validationData, 0x00); } - /*function testFailIsValidSignatureOutOfOrder() public payable { + function testFailIsValidSignatureOutOfOrder() public payable { Dagon.Ownership[] memory _owners = new Dagon.Ownership[](4); _owners[0].owner = alice; _owners[0].shares = 40; @@ -1441,7 +1441,7 @@ contract DagonTest is Test { vm.prank(_ENTRY_POINT); uint256 validationData = account.validateUserOp(userOp, userOpHash, 0); assertEq(validationData, 0x00); - }*/ + } /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/DagonScales.t.sol b/test/DagonScales.t.sol index 7fe93db..08e583e 100644 --- a/test/DagonScales.t.sol +++ b/test/DagonScales.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.23; import "@forge/Test.sol"; diff --git a/test/Summoner.t.sol b/test/Summoner.t.sol new file mode 100644 index 0000000..84e6693 --- /dev/null +++ b/test/Summoner.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.23; + +import "@forge/Test.sol"; +import "@solady/test/utils/mocks/MockERC20.sol"; +import "@solady/test/utils/mocks/MockERC721.sol"; + +import {IAuth, Dagon} from "../src/Dagon.sol"; +import {IAccounts, Summoner} from "../src/Summoner.sol"; + +contract SummonerTest is Test { + Dagon internal constant DAGON = Dagon(0x0000000000001D4B1320bB3c47380a3D1C3A1A0C); + address internal constant FACTORY = 0x000000000000dD366cc2E4432bB998e41DFD47C7; + address internal constant TOKEN = 0x6B3595068778DD592e39A122f4f5a5cF09C90fE2; + address internal constant NFT = 0x5Af0D9827E0c53E4799BB226655A1de152A425a5; + + Summoner internal summoner; + + function setUp() public payable { + vm.createSelectFork(vm.rpcUrl("main")); // Ethereum mainnet fork. + summoner = new Summoner(); + } + + function testSummoning(bytes12 salt) public payable { + IAccounts account = summoner.summon(address(this), 1 ether, false, salt); + assertEq(DAGON.totalSupply(uint256(uint160(address(account)))), 1 ether); + assertEq(DAGON.balanceOf(address(this), uint256(uint160(address(account)))), 1 ether); + } + + function testSummoningForERC20(bytes12 salt) public payable { + summoner.summonForToken(TOKEN, false, 1 ether, salt); + } + + function testSummoningForNFT(bytes12 salt) public payable { + summoner.summonForToken(NFT, true, 999, salt); + } +}