diff --git a/.gitignore b/.gitignore index 17592cf..7c79c96 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ typechain-types cache artifacts -deploy cache-zk build diff --git a/README.md b/README.md index 62e6a07..6dfc0e7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ # cpu-v2 Charged Particles Multiverse + +## Deploy + +Using [hardhat deploy](https://github.com/wighawag/hardhat-deploy) hardhat plugin. + +`yarn hardhat deploy --network NETWORK --tags CONTRACT_TAG` + +## Test + +`yarn hardhat test --network hardhat` + + +### TODO +- Move bridge onto lib. +- Bridge inside the charged particle account. +- Investigate permit for account approvals. +- Enumerable oz set lib for allowlisted function signatures.. +- Settings contract hook map. +- Check if NFT has set up a setting contract. +- Allow listed functions check inside the setting. +- Research settings manager contracts. \ No newline at end of file diff --git a/abis/BufficornZK.json b/abis/BufficornZK.json new file mode 100644 index 0000000..8ea99c1 --- /dev/null +++ b/abis/BufficornZK.json @@ -0,0 +1,720 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getTraits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "trait", + "type": "uint256" + } + ], + "name": "hasTrait", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "traits", + "type": "uint256" + } + ], + "name": "mintWithTraits", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "onExecute", + "outputs": [ + { + "internalType": "string", + "name": "revertReason", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedTokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "onUpdateNFT", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedTokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "receivedTokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "name": "onUpdateNFTBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedAssetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedAssetAmount", + "type": "uint256" + } + ], + "name": "onUpdateToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "newBase", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "traitCount", + "outputs": [ + { + "internalType": "uint256", + "name": "totalTraits", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/ChargedParticles.json b/abis/ChargedParticles.json new file mode 100644 index 0000000..8e628af --- /dev/null +++ b/abis/ChargedParticles.json @@ -0,0 +1,703 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "registry", + "type": "address" + }, + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "NewAccountCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "baseParticleMass", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "nftTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nftTokenAmount", + "type": "uint256" + } + ], + "name": "breakCovalentBond", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "nftTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nftTokenAmount", + "type": "uint256" + } + ], + "name": "covalentBond", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "currentParticleCharge", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "nftContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + } + ], + "name": "currentParticleCovalentBonds", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "currentParticleKinetics", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "energizeParticle", + "outputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "energizeParticleWithPermit", + "outputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + } + ], + "name": "getAccountBytecodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "accountBytecodeHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + } + ], + "name": "getAccountImplementation", + "outputs": [ + { + "internalType": "address", + "name": "accountImplementation", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentRegistry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + } + ], + "name": "getExecutionController", + "outputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "registry", + "type": "uint256" + } + ], + "name": "getRegistry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getSmartAccountAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "releaseParticle", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "releaseParticleAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "accountBytecodeHash", + "type": "bytes32" + } + ], + "name": "setCustomAccountBytecodeHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + }, + { + "internalType": "address", + "name": "accountImplementation", + "type": "address" + } + ], + "name": "setCustomAccountImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + }, + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setCustomExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "accountBytecodeHash", + "type": "bytes32" + } + ], + "name": "setDefaultAccountBytecodeHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accountImplementation", + "type": "address" + } + ], + "name": "setDefaultAccountImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setDefaultExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "version", + "type": "uint256" + } + ], + "name": "setDefaultRegistryVersion", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "version", + "type": "uint256" + }, + { + "internalType": "address", + "name": "registry", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/ERC721All.json b/abis/ERC721All.json new file mode 100644 index 0000000..448d7f3 --- /dev/null +++ b/abis/ERC721All.json @@ -0,0 +1,407 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/ERC721i.json b/abis/ERC721i.json new file mode 100644 index 0000000..7ac2a5d --- /dev/null +++ b/abis/ERC721i.json @@ -0,0 +1,509 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "string", + "name": "baseUri", + "type": "string" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxSupply", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "fromAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "toAddress", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "preMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/ERC721iEnumerable.json b/abis/ERC721iEnumerable.json new file mode 100644 index 0000000..c078184 --- /dev/null +++ b/abis/ERC721iEnumerable.json @@ -0,0 +1,388 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/IChargedParticles.json b/abis/IChargedParticles.json new file mode 100644 index 0000000..7c87340 --- /dev/null +++ b/abis/IChargedParticles.json @@ -0,0 +1,522 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "NewAccountCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "baseParticleMass", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "nftTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nftTokenAmount", + "type": "uint256" + } + ], + "name": "breakCovalentBond", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "nftTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nftTokenAmount", + "type": "uint256" + } + ], + "name": "covalentBond", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "currentParticleCharge", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "nftContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + } + ], + "name": "currentParticleCovalentBonds", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "currentParticleKinetics", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "energizeParticle", + "outputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "energizeParticleWithPermit", + "outputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + } + ], + "name": "getAccountBytecodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "accountBytecodeHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + } + ], + "name": "getExecutionController", + "outputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getSmartAccountAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "releaseParticle", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "releaseParticleAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "accountBytecodeHash", + "type": "bytes32" + } + ], + "name": "setCustomAccountBytecodeHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContract", + "type": "address" + }, + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setCustomExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "accountBytecodeHash", + "type": "bytes32" + } + ], + "name": "setDefaultAccountBytecodeHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setDefaultExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/ISmartAccount.json b/abis/ISmartAccount.json new file mode 100644 index 0000000..faac1d5 --- /dev/null +++ b/abis/ISmartAccount.json @@ -0,0 +1,481 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "ExecutionControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "hasPermission", + "type": "bool" + } + ], + "name": "PermissionUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "operation", + "type": "uint8" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + } + ], + "name": "getCovalentBonds", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getPrincipal", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenAmounts", + "type": "uint256[]" + } + ], + "name": "handleNFTBatchUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + } + ], + "name": "handleNFTUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "handleTokenUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + }, + { + "internalType": "address", + "name": "executionController", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftChainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "parentNftContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftTokenId", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "context", + "type": "bytes" + } + ], + "name": "isValidSigner", + "outputs": [ + { + "internalType": "bytes4", + "name": "magicValue", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "state", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/abis/ISmartAccountController.json b/abis/ISmartAccountController.json new file mode 100644 index 0000000..69a6132 --- /dev/null +++ b/abis/ISmartAccountController.json @@ -0,0 +1,179 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "operation", + "type": "uint8" + } + ], + "name": "onExecute", + "outputs": [ + { + "internalType": "string", + "name": "revertReason", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedTokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receivedTokenAmount", + "type": "uint256" + } + ], + "name": "onUpdateNFT", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedTokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "receivedTokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "receivedTokenAmounts", + "type": "uint256[]" + } + ], + "name": "onUpdateNFTBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedAssetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedAssetAmount", + "type": "uint256" + } + ], + "name": "onUpdateToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abis/SmartAccount.json b/abis/SmartAccount.json new file mode 100644 index 0000000..45b5384 --- /dev/null +++ b/abis/SmartAccount.json @@ -0,0 +1,613 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInput", + "type": "error" + }, + { + "inputs": [], + "name": "NotAuthorized", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipCycle", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "ExecutionControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "hasPermission", + "type": "bool" + } + ], + "name": "PermissionUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "operation", + "type": "uint8" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getChargedParticles", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + } + ], + "name": "getCovalentBonds", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExecutionController", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getPrincipal", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenAmounts", + "type": "uint256[]" + } + ], + "name": "handleNFTBatchUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + } + ], + "name": "handleNFTUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "handleTokenUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + }, + { + "internalType": "address", + "name": "executionController", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftChainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "parentNftContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftTokenId", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "isValidSigner", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedTokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "permissions", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + } + ], + "name": "setChargedParticles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "callers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "newPermissions", + "type": "bool[]" + } + ], + "name": "setPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "state", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/abis/SmartAccountBase.json b/abis/SmartAccountBase.json new file mode 100644 index 0000000..0a563b7 --- /dev/null +++ b/abis/SmartAccountBase.json @@ -0,0 +1,608 @@ +[ + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInput", + "type": "error" + }, + { + "inputs": [], + "name": "NotAuthorized", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipCycle", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "ExecutionControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "hasPermission", + "type": "bool" + } + ], + "name": "PermissionUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "operation", + "type": "uint8" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getChargedParticles", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + } + ], + "name": "getCovalentBonds", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExecutionController", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getPrincipal", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenAmounts", + "type": "uint256[]" + } + ], + "name": "handleNFTBatchUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + } + ], + "name": "handleNFTUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "handleTokenUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + }, + { + "internalType": "address", + "name": "executionController", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftChainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "parentNftContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftTokenId", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "isValidSigner", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedTokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "permissions", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + } + ], + "name": "setChargedParticles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "callers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "newPermissions", + "type": "bool[]" + } + ], + "name": "setPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "state", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/abis/SmartAccountController_Example1.json b/abis/SmartAccountController_Example1.json new file mode 100644 index 0000000..43420b5 --- /dev/null +++ b/abis/SmartAccountController_Example1.json @@ -0,0 +1,292 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "methodSignature", + "type": "bytes4" + } + ], + "name": "bannedMethods", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "isAllowedMethod", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "onExecute", + "outputs": [ + { + "internalType": "string", + "name": "revertReason", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedTokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedTokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "onUpdateNFT", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedTokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "receivedTokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "name": "onUpdateNFTBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receivedAssetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedAssetAmount", + "type": "uint256" + } + ], + "name": "onUpdateToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "methodSignature", + "type": "bytes4" + }, + { + "internalType": "bool", + "name": "isBanned", + "type": "bool" + } + ], + "name": "setBannedMethod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/SmartAccountTimelocks.json b/abis/SmartAccountTimelocks.json new file mode 100644 index 0000000..7dd179d --- /dev/null +++ b/abis/SmartAccountTimelocks.json @@ -0,0 +1,681 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccountLocked", + "type": "error" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "ExceedsMaxLockTime", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInput", + "type": "error" + }, + { + "inputs": [], + "name": "NotAuthorized", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipCycle", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "ExecutionControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "lockedUntil", + "type": "uint256" + } + ], + "name": "LockUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "hasPermission", + "type": "bool" + } + ], + "name": "PermissionUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "operation", + "type": "uint8" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getChargedParticles", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nftContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nftTokenId", + "type": "uint256" + } + ], + "name": "getCovalentBonds", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExecutionController", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetToken", + "type": "address" + } + ], + "name": "getPrincipal", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenAmounts", + "type": "uint256[]" + } + ], + "name": "handleNFTBatchUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + } + ], + "name": "handleNFTUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "isReceiving", + "type": "bool" + }, + { + "internalType": "address", + "name": "assetToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "name": "handleTokenUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + }, + { + "internalType": "address", + "name": "executionController", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftChainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "parentNftContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "parentNftTokenId", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isLocked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "isValidSigner", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_lockedUntil", + "type": "uint256" + } + ], + "name": "lock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "lockedUntil", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "receivedTokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "permissions", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "chargedParticles", + "type": "address" + } + ], + "name": "setChargedParticles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "executionController", + "type": "address" + } + ], + "name": "setExecutionController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "callers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "_permissions", + "type": "bool[]" + } + ], + "name": "setPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "state", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/contracts/Account.sol b/contracts/Account.sol deleted file mode 100644 index db50ef0..0000000 --- a/contracts/Account.sol +++ /dev/null @@ -1,272 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "./interfaces/IERC6551Account.sol"; -import "./lib/ERC6551AccountLib.sol"; - -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import "@openzeppelin/contracts/interfaces/IERC1271.sol"; - -error NotAuthorized(); -error InvalidInput(); -error AccountLocked(); -error ExceedsMaxLockTime(); -error UntrustedImplementation(); -error OwnershipCycle(); - -/** - * @title A smart contract account owned by a single ERC721 token - */ -contract Account is - IERC165, - IERC6551Account, - IERC721Receiver, - IERC1155Receiver -{ - /// @dev timestamp at which this account will be unlocked - uint256 public lockedUntil; - - /// @dev mapping from owner => caller => has permissions - mapping(address => mapping(address => bool)) public permissions; - - event OverrideUpdated( - address owner, - bytes4 selector, - address implementation - ); - - event PermissionUpdated(address owner, address caller, bool hasPermission); - - event LockUpdated(uint256 lockedUntil); - - /// @dev reverts if caller is not the owner of the account - modifier onlyOwner() { - if (msg.sender != owner()) revert NotAuthorized(); - _; - } - - /// @dev reverts if caller is not authorized to execute on this account - modifier onlyAuthorized() { - if (!isAuthorized(msg.sender)) revert NotAuthorized(); - _; - } - - /// @dev reverts if this account is currently locked - modifier onlyUnlocked() { - if (isLocked()) revert AccountLocked(); - _; - } - - constructor() {} - - /// @dev allows eth transfers by default, but allows account owner to override - receive() external payable { - } - - /// @dev executes a low-level call against an account if the caller is authorized to make calls - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external payable onlyAuthorized onlyUnlocked onlyAllowedMethod(data) returns (bytes memory) { - emit TransactionExecuted(to, value, data); - - return _call(to, value, data); - } - - /// @dev grants a given caller execution permissions - function setPermissions( - address[] calldata callers, - bool[] calldata _permissions - ) external onlyUnlocked { - address _owner = owner(); - if (msg.sender != _owner) revert NotAuthorized(); - - uint256 length = callers.length; - - if (_permissions.length != length) revert InvalidInput(); - - for (uint256 i = 0; i < length; i++) { - permissions[_owner][callers[i]] = _permissions[i]; - emit PermissionUpdated(_owner, callers[i], _permissions[i]); - } - } - - /// @dev locks the account until a certain timestamp - function lock(uint256 _lockedUntil) external onlyOwner onlyUnlocked { - if (_lockedUntil > block.timestamp + 365 days) - revert ExceedsMaxLockTime(); - - lockedUntil = _lockedUntil; - - emit LockUpdated(_lockedUntil); - } - - /// @dev returns the current lock status of the account as a boolean - function isLocked() public view returns (bool) { - return lockedUntil > block.timestamp; - } - - /// @dev Returns the EIP-155 chain ID, token contract address, and token ID for the token that - /// owns this account. - function token() - external - view - returns ( - uint256 chainId, - address tokenContract, - uint256 tokenId - ) - { - return ERC6551AccountLib.token(); - } - - /// @dev Returns the owner of the ERC-721 token which owns this account. By default, the owner - /// of the token has full permissions on the account. - function owner() public view returns (address) { - ( - uint256 chainId, - address tokenContract, - uint256 tokenId - ) = ERC6551AccountLib.token(); - - if (chainId != block.chainid) return address(0); - - return IERC721(tokenContract).ownerOf(tokenId); - } - - /// @dev Returns the authorization status for a given caller - function isAuthorized(address caller) public view returns (bool) { - ( - , - address tokenContract, - uint256 tokenId - ) = ERC6551AccountLib.token(); - address _owner = IERC721(tokenContract).ownerOf(tokenId); - - // authorize token owner - if (caller == _owner) return true; - - // authorize caller if owner has granted permissions - if (permissions[_owner][caller]) return true; - - return false; - } - - /// @dev Returns true if a given interfaceId is supported by this account. This method can be - /// extended by an override. - function supportsInterface(bytes4 interfaceId) - public - pure - override - returns (bool) - { - bool defaultSupport = interfaceId == type(IERC165).interfaceId || - interfaceId == type(IERC1155Receiver).interfaceId || - interfaceId == type(IERC6551Account).interfaceId; - - if (defaultSupport) return true; - - return false; - } - - /// @dev Allows ERC-721 tokens to be received so long as they do not cause an ownership cycle. - /// This function can be overriden. - function onERC721Received( - address, - address, - uint256 receivedTokenId, - bytes memory - ) public view override returns (bytes4) { - ( - uint256 chainId, - address tokenContract, - uint256 tokenId - ) = ERC6551AccountLib.token(); - - if ( - chainId == block.chainid && - tokenContract == msg.sender && - tokenId == receivedTokenId - ) revert OwnershipCycle(); - - return this.onERC721Received.selector; - } - - /// @dev Allows ERC-1155 tokens to be received. This function can be overriden. - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes memory - ) public pure override returns (bytes4) { - return this.onERC1155Received.selector; - } - - /// @dev Allows ERC-1155 token batches to be received. This function can be overriden. - function onERC1155BatchReceived( - address, - address, - uint256[] memory, - uint256[] memory, - bytes memory - ) public pure override returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } - - /// @dev Executes a low-level call - function _call( - address to, - uint256 value, - bytes calldata data - ) internal returns (bytes memory result) { - bool success; - (success, result) = to.call{value: value}(data); - - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - /// @dev Executes a low-level static call - function _callStatic(address to, bytes calldata data) - internal - view - returns (bytes memory result) - { - bool success; - (success, result) = to.staticcall(data); - - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - function allowedMethod(bytes calldata _data) internal returns (bool) { - bytes4 signature = parseFirst4Bytes(_data); - // approve > 0x095ea7b3 - - if (signature == 0x095ea7b3) { - return false; - } - - return true; - } - - function parseFirst4Bytes(bytes calldata _data) public pure returns (bytes4) { - return bytes4(_data[:4]); - } - - modifier onlyAllowedMethod(bytes calldata _data) { - require(allowedMethod(_data), "Method all not allowed"); - _; - } -} diff --git a/contracts/AccountRegistryBridge.sol b/contracts/AccountRegistryBridge.sol deleted file mode 100644 index 94abca1..0000000 --- a/contracts/AccountRegistryBridge.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import "./interfaces/IRegistry.sol"; -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -contract AccountRegistryBridge { - address public constant REGISTRY = 0x02101dfB77FDE026414827Fdc604ddAF224F0921; - address public constant IMPLEMENTATION = 0x2D25602551487C3f3354dD80D76D54383A243358; - - function createAccount( - address contractAddress, - uint256 tokenId - ) external returns (address) { - return IRegistry(REGISTRY).createAccount( - IMPLEMENTATION, - block.chainid, - contractAddress, - tokenId, - 0, - '' - ); - } - - function account( - address contractAddress, - uint256 tokenId - ) external view returns (address) { - return IRegistry(REGISTRY).account( - IMPLEMENTATION, - block.chainid, - contractAddress, - tokenId, - 0 - ); - } -} \ No newline at end of file diff --git a/contracts/ChargedParticles.sol b/contracts/ChargedParticles.sol new file mode 100644 index 0000000..9b90c58 --- /dev/null +++ b/contracts/ChargedParticles.sol @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: MIT + +// ChargedParticles.sol -- Part of the Charged Particles Protocol +// Copyright (c) 2024 Firma Lux, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pragma solidity ^0.8.13; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +import {IERC2612} from "erc20permit/contracts/IERC2612.sol"; +import {ERC20Permit} from "erc20permit/contracts/ERC20Permit.sol"; + +import {IERC6551Executable} from "./interfaces/IERC6551Executable.sol"; +// import {IERC6551zkSyncRegistry} from "./interfaces/IERC6551zkSyncRegistry.sol"; +import {IERC6551Registry} from "./interfaces/IERC6551Registry.sol"; +import {IChargedParticles} from "./interfaces/IChargedParticles.sol"; +import {NftTokenInfo} from "./lib/NftTokenInfo.sol"; +import {ISmartAccount} from "./interfaces/ISmartAccount.sol"; +import {ISmartAccountController} from "./interfaces/ISmartAccountController.sol"; +import {IDynamicTraits} from "./interfaces/IDynamicTraits.sol"; +import {SmartAccountTimelocks} from "./extensions/SmartAccountTimelocks.sol"; + + +contract ChargedParticles is IChargedParticles, Ownable, ReentrancyGuard { + using NftTokenInfo for address; + using SafeERC20 for IERC20; + + // NFT contract => SmartAccount BytecodeHash + mapping (address => bytes32) internal accountHashes; // on zkSync these are the "bytecodeHash" of a deployed contract + bytes32 internal defaultAccountBytecodeHash; + + // NFT contract => Execution Controller + mapping (address => address) internal executionControllers; + address internal defaultExecutionController; + + // NFT contract => SmartAccount Implementation + mapping (address => address) internal accountImplementations; + address internal defaultAccountImplementation; + + // Registry Version => Registry Address + mapping (uint256 => address) internal erc6551registry; + uint256 internal defaultRegistry; + + // Token UUID => Smart Account Address (for View functions) + mapping (uint256 => address) internal nftSmartAccounts; + + // Default Salt for "create2" + bytes32 internal defaultSalt; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Initialization + + constructor( + address registry, + address implementation + ) + Ownable() + ReentrancyGuard() + { + + erc6551registry[defaultRegistry] = registry; + defaultSalt = bytes32('CPU-V2'); + defaultAccountImplementation = implementation; + } + + function getSmartAccountAddress(address contractAddress, uint256 tokenId) external view override virtual returns (address) { + (address account, ) = _findAccount(contractAddress, tokenId); + return account; + } + + + /// @notice Gets the Amount of Asset Tokens that have been Deposited into the Particle + /// representing the Mass of the Particle. + /// @param contractAddress The Address to the Contract of the Token + /// @param tokenId The ID of the Token + /// @param assetToken The Address of the Asset Token to check + /// @return total The Amount of underlying Assets held within the Token + function baseParticleMass( + address contractAddress, + uint256 tokenId, + address assetToken + ) + external + view + virtual + override + returns (uint256 total) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + if (isSmartAccount) { + ISmartAccount smartAccount = ISmartAccount(payable(account)); + total = smartAccount.getPrincipal(assetToken); + } + } + + /// @notice Gets the amount of Interest that the Particle has generated representing + /// the Charge of the Particle + /// @param contractAddress The Address to the Contract of the Token + /// @param tokenId The ID of the Token + /// @param assetToken The Address of the Asset Token to check + /// @return total The amount of interest the Token has generated (in Asset Token) + function currentParticleCharge( + address contractAddress, + uint256 tokenId, + address assetToken + ) + external + view + virtual + override + returns (uint256 total) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + if (isSmartAccount) { + ISmartAccount smartAccount = ISmartAccount(payable(account)); + total = smartAccount.getInterest(assetToken); + } + } + + function currentParticleKinetics( + address contractAddress, + uint256 tokenId, + address assetToken + ) + external + view + virtual + override + returns (uint256 total) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + if (isSmartAccount) { + ISmartAccount smartAccount = ISmartAccount(payable(account)); + total = smartAccount.getRewards(assetToken); + } + } + + /// @notice Gets the total amount of ERC721 Tokens that the Particle holds + /// @param contractAddress The Address to the Contract of the Token + /// @param tokenId The ID of the Token (for ERC1155) + /// @return total The total amount of ERC721 tokens that are held within the Particle + function currentParticleCovalentBonds( + address contractAddress, + uint256 tokenId, + address nftContractAddress, + uint256 nftTokenId + ) + external + view + virtual + override + returns (uint256 total) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + if (isSmartAccount) { + ISmartAccount smartAccount = ISmartAccount(payable(account)); + total = smartAccount.getCovalentBonds(nftContractAddress, nftTokenId); + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Energize (Deposit) + + /// @notice Fund Particle with Asset Token + /// Must be called by the account providing the Asset + /// Account must Approve THIS contract as Operator of Asset + /// + /// @param contractAddress The Address to the Contract of the Token to Energize + /// @param tokenId The ID of the Token to Energize + /// @param assetToken The Address of the Asset Token being used + /// @param assetAmount The Amount of Asset Token to Energize the Token with + /// @return account The address of the SmartAccount associated with the NFT + function energizeParticle( + address contractAddress, + uint256 tokenId, + address assetToken, + uint256 assetAmount + ) + external + virtual + override + nonReentrant + returns (address account) + { + // Find the SmartAccount for this NFT + (address accountAddress, bool isSmartAccount) = _createAccount(contractAddress, tokenId); + account = accountAddress; + ISmartAccount smartAccount = ISmartAccount(payable(account)); + + // Transfer to SmartAccount + IERC20(assetToken).safeTransferFrom(msg.sender, account, assetAmount); + + // Pre-approve Charged Particles to transfer back out + IERC6551Executable(account).execute(assetToken, 0, abi.encodeWithSelector(IERC20.approve.selector, address(this), type(uint256).max), 0); + + // Call "update" on SmartAccount + if (isSmartAccount) { + smartAccount.handleTokenUpdate(true, assetToken, assetAmount); + } + + // TODO + // emit NewAccountCreated(account, block.chainid, contractAddress, tokenId); + } + + /// @notice Fund Particle with Asset Token + /// Must be called by the account providing the Asset + /// Account must Approve THIS contract as Operator of Asset + /// + /// @param contractAddress The Address to the Contract of the Token to Energize + /// @param tokenId The ID of the Token to Energize + /// @param assetToken The Address of the Asset Token being used + /// @param assetAmount The Amount of Asset Token to Energize the Token with + /// @return account The address of the SmartAccount associated with the NFT + function energizeParticleWithPermit( + address contractAddress, + uint256 tokenId, + address assetToken, + uint256 assetAmount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) + external + virtual + override + nonReentrant + returns (address account) + { + require(IERC165(assetToken).supportsInterface(type(IERC2612).interfaceId), "permit not supported"); + + // Find the SmartAccount for this NFT + (address accountAddress, bool isSmartAccount) = _createAccount(contractAddress, tokenId); + account = accountAddress; + ISmartAccount smartAccount = ISmartAccount(payable(account)); + + // Transfer to SmartAccount with Permission + ERC20Permit(assetToken).permit(msg.sender, address(this), assetAmount, deadline, v, r, s); + IERC20(assetToken).safeTransferFrom(msg.sender, account, assetAmount); + + // Pre-approve Charged Particles to transfer back out + IERC6551Executable(account).execute(assetToken, 0, abi.encodeWithSelector(IERC20.approve.selector, address(this), type(uint256).max), 0); + + // Call "update" on SmartAccount + if (isSmartAccount) { + smartAccount.handleTokenUpdate(true, assetToken, assetAmount); + } + + // TODO + // emit NewAccountCreated(account, block.chainid, contractAddress, tokenId); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Release (Withdraw) + + function releaseParticle( + address receiver, + address contractAddress, + uint256 tokenId, + address assetToken + ) + external + virtual + override + onlyNFTOwnerOrOperator(contractAddress, tokenId) + nonReentrant + returns (uint256 amount) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + + // Transfer to Receiver + amount = IERC20(assetToken).balanceOf(account); + IERC20(assetToken).safeTransferFrom(account, receiver, amount); + + // Call "update" on SmartAccount + if (isSmartAccount) { + ISmartAccount(payable(account)).handleTokenUpdate(false, assetToken, amount); + } + } + + function releaseParticleAmount( + address receiver, + address contractAddress, + uint256 tokenId, + address assetToken, + uint256 assetAmount + ) + external + virtual + override + onlyNFTOwnerOrOperator(contractAddress, tokenId) + nonReentrant + returns (uint256) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + + // Transfer to Receiver + IERC20(assetToken).safeTransferFrom(account, receiver, assetAmount); + + // Call "update" on SmartAccount + if (isSmartAccount) { + ISmartAccount(payable(account)).handleTokenUpdate(false, assetToken, assetAmount); + } + + return assetAmount; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Covalent Bonds (Nested NFTs) + + /// @notice Deposit other NFT Assets into the Particle + /// Must be called by the account providing the Asset + /// Account must Approve THIS contract as Operator of Asset + /// + /// @param contractAddress The Address to the Contract of the Token to Energize + /// @param tokenId The ID of the Token to Energize + /// @param nftTokenAddress The Address of the NFT Token being deposited + /// @param nftTokenId The ID of the NFT Token being deposited + /// @param nftTokenAmount The amount of Tokens to Deposit (ERC1155-specific) + /// @return success True if the operation succeeded (for backwards-compat) + function covalentBond( + address contractAddress, + uint256 tokenId, + address nftTokenAddress, + uint256 nftTokenId, + uint256 nftTokenAmount + ) + external + virtual + override + nonReentrant + returns (bool success) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _createAccount(contractAddress, tokenId); + ISmartAccount smartAccount = ISmartAccount(payable(account)); + IERC6551Executable execAccount = IERC6551Executable(account); + + // Transfer to SmartAccount and pre-approve Charged Particles to transfer back out + if (nftTokenAddress.isERC1155()) { + IERC1155(nftTokenAddress).safeTransferFrom(msg.sender, account, tokenId, nftTokenAmount, ""); + execAccount.execute(nftTokenAddress, 0, abi.encodeWithSelector(IERC1155.setApprovalForAll.selector, address(this), true), 0); + } else { + IERC721(nftTokenAddress).safeTransferFrom(msg.sender, account, nftTokenId); + execAccount.execute(nftTokenAddress, 0, abi.encodeWithSelector(IERC721.setApprovalForAll.selector, address(this), true), 0); + } + + // Call "update" on SmartAccount + if (isSmartAccount) { + smartAccount.handleNFTUpdate(true, nftTokenAddress, nftTokenId, nftTokenAmount); + } + return true; + } + + /// @notice Release NFT Assets from the Particle + /// @param receiver The Address to Receive the Released Asset Tokens + /// @param contractAddress The Address to the Contract of the Token to Energize + /// @param tokenId The ID of the Token to Energize + /// @param nftTokenAddress The Address of the NFT Token being deposited + /// @param nftTokenId The ID of the NFT Token being deposited + /// @param nftTokenAmount The amount of Tokens to Withdraw (ERC1155-specific) + /// @return success True if the operation succeeded (for backwards-compat) + function breakCovalentBond( + address receiver, + address contractAddress, + uint256 tokenId, + address nftTokenAddress, + uint256 nftTokenId, + uint256 nftTokenAmount + ) + external + virtual + override + onlyNFTOwnerOrOperator(contractAddress, tokenId) + nonReentrant + returns (bool success) + { + // Find the SmartAccount for this NFT + (address account, bool isSmartAccount) = _findAccount(contractAddress, tokenId); + + // Transfer to Receiver + if (nftTokenAddress.isERC1155()) { + IERC1155(nftTokenAddress).safeTransferFrom(account, receiver, tokenId, nftTokenAmount, ""); + } else { + IERC721(nftTokenAddress).safeTransferFrom(account, receiver, nftTokenId); + } + + // Call "update" on SmartAccount + if (isSmartAccount) { + ISmartAccount(payable(account)).handleNFTUpdate(false, nftTokenAddress, nftTokenId, nftTokenAmount); + } + + return true; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ERC6551 Wallet Registry + + /// @dev ... + function getCurrentRegistry() external view returns (address) { + return erc6551registry[defaultRegistry]; + } + + /// @dev ... + function getRegistry(uint256 registry) external view returns (address) { + return erc6551registry[registry]; + } + + /// @dev ... + function setRegistry(uint256 version, address registry) external onlyOwner { + erc6551registry[version] = registry; + } + + /// @dev ... + function setDefaultRegistryVersion(uint256 version) external onlyOwner { + defaultRegistry = version; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SmartAccount Execution Controllers + // - any NFT contract can have its own custom execution controller + + /// @dev ... + function setDefaultExecutionController(address executionController) public virtual onlyOwner { + defaultExecutionController = executionController; + } + + /// @dev ... + function setCustomExecutionController(address nftContract, address executionController) public virtual onlyOwner { + executionControllers[nftContract] = executionController; + } + + /// @dev ... + function getExecutionController(address nftContract) public view returns (address executionController) { + executionController = executionControllers[nftContract]; + if (executionController == address(0)) { + executionController = defaultExecutionController; + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SmartAccount BytecodeHash + // - any NFT contract can have its own custom execution controller + + /// @dev ... + function setDefaultAccountBytecodeHash(bytes32 accountBytecodeHash) public virtual onlyOwner { + defaultAccountBytecodeHash = accountBytecodeHash; + } + + /// @dev ... + function setCustomAccountBytecodeHash(address nftContract, bytes32 accountBytecodeHash) public virtual onlyOwner { + accountHashes[nftContract] = accountBytecodeHash; + } + + /// @dev ... + function getAccountBytecodeHash(address nftContract) public view returns (bytes32 accountBytecodeHash) { + accountBytecodeHash = accountHashes[nftContract]; + if (accountBytecodeHash == bytes32(0) || accountBytecodeHash.length == 0) { + accountBytecodeHash = defaultAccountBytecodeHash; + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Private Functions + + /// @dev ... + function _createAccount( + address contractAddress, + uint256 tokenId + ) internal returns (address account, bool isSmartAccount) { + // Create the SmartAccount for this NFT + IERC6551Registry registry = IERC6551Registry(erc6551registry[defaultRegistry]); + account = registry.createAccount(defaultAccountImplementation, defaultSalt, block.chainid, contractAddress, tokenId); + isSmartAccount = IERC165(account).supportsInterface(type(ISmartAccount).interfaceId); + ISmartAccount smartAccount = ISmartAccount(payable(account)); + + // Store account address + uint256 uuid = contractAddress.getTokenUUID(tokenId); + if (nftSmartAccounts[uuid] == address(0)) { + nftSmartAccounts[uuid] = account; + } + + // Initialize the Account + if (isSmartAccount && !smartAccount.isInitialized()) { + address executionController = getExecutionController(contractAddress); + smartAccount.initialize(address(this), executionController, block.chainid, contractAddress, tokenId); + } + } + + /// @dev ... + function _findAccount( + address contractAddress, + uint256 tokenId + ) internal view returns (address account, bool isSmartAccount) { + // Find the SmartAccount for this NFT + uint256 uuid = contractAddress.getTokenUUID(tokenId); + account = nftSmartAccounts[uuid]; + isSmartAccount = IERC165(account).supportsInterface(type(ISmartAccount).interfaceId); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SmartAccount Implementations + // - any NFT contract can have its own custom execution controller + + /// @dev ... + function setDefaultAccountImplementation(address accountImplementation) public virtual onlyOwner { + defaultAccountImplementation = accountImplementation; + } + + /// @dev ... + function setCustomAccountImplementation(address nftContract, address accountImplementation) public virtual onlyOwner { + accountImplementations[nftContract] = accountImplementation; + } + + /// @dev ... + function getAccountImplementation(address nftContract) public view returns (address accountImplementation) { + accountImplementation = accountImplementations[nftContract]; + if (accountImplementation == address(0)) { + accountImplementation = defaultAccountImplementation; + } + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Internal Modifiers + + modifier onlyNFTOwnerOrOperator(address contractAddress, uint256 tokenId) { + require(contractAddress.isNFTOwnerOrOperator(tokenId, msg.sender), "Invalid owner or operator"); + _; + } +} \ No newline at end of file diff --git a/contracts/ChargedParticlesAccount.sol b/contracts/ChargedParticlesAccount.sol deleted file mode 100644 index 2f3c490..0000000 --- a/contracts/ChargedParticlesAccount.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "./Account.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -contract ChargedParticlesAccount is Account { - function covalentBond( - address nftTokenAddress, - uint256 nftTokenId, - uint256 nftTokenAmount - ) external { - // Transfer to self - IERC721(nftTokenAddress).safeTransferFrom( - msg.sender, - address(this), - nftTokenId - ); - } - - function breakCovalentBond( - address receiver, - address nftTokenAddress, - uint256 nftTokenId, - uint256 nftTokenAmount - ) external { - IERC721(nftTokenAddress).safeTransferFrom(address(this), receiver, nftTokenId); - } - - function energizeParticle( - address assetToken, - uint256 assetAmount - ) external { - IERC20(assetToken).transferFrom(msg.sender, address(this), assetAmount); - } - - function dischargeParticle( - address receiver, - address assetToken, - uint256 assetAmount - ) external { - IERC20(assetToken).transfer(receiver, assetAmount); - } -} diff --git a/contracts/MinimalAccount.sol b/contracts/MinimalAccount.sol deleted file mode 100644 index 3807af3..0000000 --- a/contracts/MinimalAccount.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -/** - * @title A smart contract account owned by a single ERC721 token - */ -contract MinimalAccount -{ - -} diff --git a/contracts/SmartAccount.sol b/contracts/SmartAccount.sol new file mode 100644 index 0000000..1b9665a --- /dev/null +++ b/contracts/SmartAccount.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT + +// SmartAccount.sol -- Part of the Charged Particles Protocol +// Copyright (c) 2024 Firma Lux, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pragma solidity ^0.8.13; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {NftTokenInfo} from "./lib/NftTokenInfo.sol"; +import {SmartAccountBase, NotAuthorized, InvalidInput} from "./lib/SmartAccountBase.sol"; + +/** + * @title A smart contract account owned by a single ERC721 token + */ +contract SmartAccount is SmartAccountBase { + using NftTokenInfo for address; + + uint256 public state; + + constructor() SmartAccountBase() {} + + + /// @dev allows eth transfers by default + receive() external payable virtual override {} + + + + function getPrincipal(address assetToken) external view virtual override returns (uint256 total) { + total = IERC20(assetToken).balanceOf(address(this)); + } + + function getInterest(address /* assetToken */) external view virtual override returns (uint256 total) { + return 0; + } + + function getRewards(address /* assetToken */) external view virtual override returns (uint256 total) { + return 0; + } + + function getCovalentBonds(address nftContractAddress, uint256 nftTokenId) external view virtual override returns (uint256 total) { + if (nftContractAddress.isERC1155()) { + total = IERC1155(nftContractAddress).balanceOf(address(this), nftTokenId); + } else { + total = IERC721(nftContractAddress).balanceOf(address(this)); + } + } + + + + /// @dev executes a low-level call against an account if the caller is authorized to make calls + function execute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) + public + payable + virtual + override + onlyValidSigner + returns (bytes memory) + { + require(operation == 0, "Only call operations are supported"); + ++state; + + // Perform custom checks/updates from within a custom controller + _onExecute(to, value, data, operation); + + // Execute Call on Account + return _call(to, value, data); + } + + + function handleTokenUpdate( + bool isReceiving, + address assetToken, + uint256 assetAmount + ) + public + virtual + override + onlyValidSigner + { + // Perform custom checks/updates from within a custom controller + _onUpdateToken(isReceiving, assetToken, assetAmount); + } + + function handleNFTUpdate( + bool isReceiving, + address tokenContract, + uint256 tokenId, + uint256 tokenAmount + ) + public + virtual + override + onlyValidSigner + { + // Perform custom checks/updates from within a custom controller + _onUpdateNFT(isReceiving, tokenContract, tokenId, tokenAmount); + } + + function handleNFTBatchUpdate( + bool isReceiving, + address tokenContract, + uint256[] calldata tokenIds, + uint256[] calldata tokenAmounts + ) + public + virtual + override + onlyValidSigner + { + // Perform custom checks/updates from within a custom controller + _onUpdateNFTBatch(isReceiving, tokenContract, tokenIds, tokenAmounts); + } +} \ No newline at end of file diff --git a/contracts/controllers/SmartAccountController_Example1.sol b/contracts/controllers/SmartAccountController_Example1.sol new file mode 100644 index 0000000..6d3c4a1 --- /dev/null +++ b/contracts/controllers/SmartAccountController_Example1.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC165, ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ISmartAccountController} from "../interfaces/ISmartAccountController.sol"; + +/** + * @title A SmartAccount Controller which only allows specific methods to be executed on the associated SmartAccount + */ +contract SmartAccountController_Example1 is ISmartAccountController, Ownable, ERC165 { + + /// @dev mapping from method signature => banned method call + mapping(bytes4 => bool) internal _bannedMethods; + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Initialization + constructor() Ownable() {} + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Banned Methods Logic + + function bannedMethods(bytes4 methodSignature) external view virtual returns (bool) { + return _bannedMethods[methodSignature]; + } + + function isAllowedMethod(bytes calldata data) external view virtual returns (bool) { + return _isAllowedMethod(data); + } + + function setBannedMethod(bytes4 methodSignature, bool isBanned) external virtual onlyOwner { + _bannedMethods[methodSignature] = isBanned; + } + + function _isAllowedMethod(bytes calldata _data) internal view returns (bool) { + bytes4 signature = bytes4(_data[:4]); + return !_bannedMethods[signature]; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SmartAccount Controller Logic + + function onExecute( + address, + uint256, + bytes calldata data, + uint8 + ) external virtual override returns (string memory revertReason) { + if (!_isAllowedMethod(data)) { + revertReason = "Method call not allowed"; + } + // else success + } + + function onUpdateToken( + bool isReceiving, + uint256 chainId, + address tokenContract, + uint256 tokenId, + address receivedAssetToken, + uint256 receivedAssetAmount + ) + external + virtual + override + { + // perform conditional logic here.. + } + + function onUpdateNFT( + bool isReceiving, + uint256 chainId, + address tokenContract, + uint256 tokenId, + address receivedTokenContract, + uint256 receivedTokenId, + uint256 + ) + external + virtual + override + { + // perform conditional logic here.. + } + + function onUpdateNFTBatch( + bool isReceiving, + uint256 chainId, + address tokenContract, + uint256 tokenId, + address receivedTokenContract, + uint256[] calldata receivedTokenIds, + uint256[] calldata + ) + external + virtual + override + { + // perform conditional logic here.. + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Interface Identification + + /// @dev Returns true if a given interfaceId is supported by this account. This method can be + /// extended by an override. + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(IERC165, ERC165) + returns (bool) + { + return + interfaceId == type(ISmartAccountController).interfaceId || + super.supportsInterface(interfaceId); + } +} diff --git a/contracts/extensions/SmartAccountTimelocks.sol b/contracts/extensions/SmartAccountTimelocks.sol new file mode 100644 index 0000000..4d45f8c --- /dev/null +++ b/contracts/extensions/SmartAccountTimelocks.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {SmartAccount} from "../SmartAccount.sol"; + +error AccountLocked(); +error ExceedsMaxLockTime(); + +/** + * @title A smart contract account owned by a single ERC721 token + */ +contract SmartAccountTimelocks is SmartAccount { + event LockUpdated(uint256 lockedUntil); + + /// @dev timestamp at which this account will be unlocked. + /// Attached to "owner" so that the lock is cleared when transferred. + mapping(address => uint256) public lockedUntil; + + constructor() SmartAccount() {} + + /// @dev returns the current lock status of the account as a boolean + function isLocked() public view returns (bool) { + return lockedUntil[owner()] > block.timestamp; + } + + /// @dev locks the account until a certain timestamp + function lock(uint256 _lockedUntil) external onlyValidSigner { + if (_lockedUntil > block.timestamp + 365 days) { + revert ExceedsMaxLockTime(); + } + + lockedUntil[owner()] = _lockedUntil; + + emit LockUpdated(_lockedUntil); + } + + /// @dev grants a given caller execution permissions + function setPermissions( + address[] calldata callers, + bool[] calldata _permissions + ) public virtual override { + if (isLocked()) { + revert AccountLocked(); + } + return super.setPermissions(callers, _permissions); + } + + /// @dev executes a low-level call against an account if the caller is authorized to make calls + function execute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) public payable virtual override returns (bytes memory) { + if (isLocked()) { revert AccountLocked(); } + return super.execute(to, value, data, operation); + } + + function handleTokenUpdate( + bool isReceiving, + address assetToken, + uint256 assetAmount + ) public virtual override { + if (isLocked()) { revert AccountLocked(); } + return super.handleTokenUpdate(isReceiving, assetToken, assetAmount); + } + + function handleNFTUpdate( + bool isReceiving, + address tokenContract, + uint256 tokenId, + uint256 tokenAmount + ) public virtual override { + if (isLocked()) { revert AccountLocked(); } + return super.handleNFTUpdate(isReceiving, tokenContract, tokenId, tokenAmount); + } + + function handleNFTBatchUpdate( + bool isReceiving, + address tokenContract, + uint256[] calldata tokenIds, + uint256[] calldata tokenAmounts + ) public virtual override { + if (isLocked()) { revert AccountLocked(); } + return super.handleNFTBatchUpdate(isReceiving, tokenContract, tokenIds, tokenAmounts); + } +} diff --git a/contracts/interfaces/IAccount.sol b/contracts/interfaces/IAccount.sol deleted file mode 100644 index da027ae..0000000 --- a/contracts/interfaces/IAccount.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -interface IAccount { - function owner() external view returns (address); - - function token() - external - view - returns (address tokenContract, uint256 tokenId); - - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external payable returns (bytes memory); -} diff --git a/contracts/interfaces/IChargedParticles.sol b/contracts/interfaces/IChargedParticles.sol index b3bddf4..7d0c2d2 100644 --- a/contracts/interfaces/IChargedParticles.sol +++ b/contracts/interfaces/IChargedParticles.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // IChargedParticles.sol -- Part of the Charged Particles Protocol -// Copyright (c) 2021 Firma Lux, Inc. +// Copyright (c) 2024 Firma Lux, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -21,87 +21,75 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -pragma solidity >=0.6.0; +pragma solidity ^0.8.13; /** * @notice Interface for Charged Particles */ interface IChargedParticles { - /***********************************| - | Public API | - |__________________________________*/ + event NewAccountCreated( + address account, + uint256 chainId, + address indexed tokenContract, + uint256 indexed tokenId + ); - function getStateAddress() external view returns (address stateAddress); - function getSettingsAddress() external view returns (address settingsAddress); - function getManagersAddress() external view returns (address managersAddress); + function setDefaultExecutionController(address executionController) external; + function setCustomExecutionController(address nftContract, address executionController) external; + function getExecutionController(address nftContract) external view returns (address executionController); - function getFeesForDeposit(uint256 assetAmount) external view returns (uint256 protocolFee); - function baseParticleMass(address contractAddress, uint256 tokenId, string calldata walletManagerId, address assetToken) external returns (uint256); - function currentParticleCharge(address contractAddress, uint256 tokenId, string calldata walletManagerId, address assetToken) external returns (uint256); - function currentParticleKinetics(address contractAddress, uint256 tokenId, string calldata walletManagerId, address assetToken) external returns (uint256); - function currentParticleCovalentBonds(address contractAddress, uint256 tokenId, string calldata basketManagerId) external view returns (uint256); + function setDefaultAccountBytecodeHash(bytes32 accountBytecodeHash) external; + function setCustomAccountBytecodeHash(address nftContract, bytes32 accountBytecodeHash) external; + function getAccountBytecodeHash(address nftContract) external view returns (bytes32 accountBytecodeHash); /***********************************| | Particle Mechanics | |__________________________________*/ - function energizeParticle( - address contractAddress, - uint256 tokenId, - string calldata walletManagerId, - address assetToken, - uint256 assetAmount, - address referrer - ) external returns (uint256 yieldTokensAmount); + function getSmartAccountAddress(address contractAddress, uint256 tokenId) external view returns (address); + function baseParticleMass(address contractAddress, uint256 tokenId, address assetToken) external view returns (uint256 total); + function currentParticleCharge(address contractAddress, uint256 tokenId, address assetToken) external view returns (uint256 total); + function currentParticleKinetics(address contractAddress, uint256 tokenId, address assetToken) external view returns (uint256 total); + function currentParticleCovalentBonds(address contractAddress, uint256 tokenId, address nftContractAddress, uint256 nftTokenId) external view returns (uint256 total); - function dischargeParticle( - address receiver, - address contractAddress, - uint256 tokenId, - string calldata walletManagerId, - address assetToken - ) external returns (uint256 creatorAmount, uint256 receiverAmount); + function energizeParticle( + address contractAddress, + uint256 tokenId, + address assetToken, + uint256 assetAmount + ) external returns (address account); - function dischargeParticleAmount( - address receiver, - address contractAddress, - uint256 tokenId, - string calldata walletManagerId, - address assetToken, - uint256 assetAmount - ) external returns (uint256 creatorAmount, uint256 receiverAmount); - function dischargeParticleForCreator( - address receiver, - address contractAddress, - uint256 tokenId, - string calldata walletManagerId, - address assetToken, - uint256 assetAmount - ) external returns (uint256 receiverAmount); + function energizeParticleWithPermit( + address contractAddress, + uint256 tokenId, + address assetToken, + uint256 assetAmount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (address account); function releaseParticle( - address receiver, - address contractAddress, - uint256 tokenId, - string calldata walletManagerId, - address assetToken - ) external returns (uint256 creatorAmount, uint256 receiverAmount); + address receiver, + address contractAddress, + uint256 tokenId, + address assetToken + ) external returns (uint256 amount); function releaseParticleAmount( address receiver, address contractAddress, uint256 tokenId, - string calldata walletManagerId, address assetToken, uint256 assetAmount - ) external returns (uint256 creatorAmount, uint256 receiverAmount); + ) external returns (uint256 amount); function covalentBond( address contractAddress, uint256 tokenId, - string calldata basketManagerId, address nftTokenAddress, uint256 nftTokenId, uint256 nftTokenAmount @@ -111,9 +99,8 @@ interface IChargedParticles { address receiver, address contractAddress, uint256 tokenId, - string calldata basketManagerId, address nftTokenAddress, uint256 nftTokenId, uint256 nftTokenAmount ) external returns (bool success); -} +} \ No newline at end of file diff --git a/contracts/interfaces/IDynamicTraits.sol b/contracts/interfaces/IDynamicTraits.sol new file mode 100644 index 0000000..cda623e --- /dev/null +++ b/contracts/interfaces/IDynamicTraits.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +// IDynamicTraits.sol -- Part of the Charged Particles Protocol +// Copyright (c) 2023 Firma Lux, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pragma solidity ^0.8.13; + +interface IDynamicTraits { + function getTraits(uint256 tokenId) external view returns (uint256); + function hasTrait(uint256 tokenId, uint256 trait) external view returns (bool); + function traitCount(uint256 tokenId) external view returns (uint256 totalTraits); +} diff --git a/contracts/interfaces/IERC6551Account.sol b/contracts/interfaces/IERC6551Account.sol index 2f28589..22b8bb3 100644 --- a/contracts/interfaces/IERC6551Account.sol +++ b/contracts/interfaces/IERC6551Account.sol @@ -1,26 +1,58 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -interface IERC6551AccountProxy { - function implementation() external view returns (address); -} - -/// @dev the ERC-165 identifier for this interface is `0xeff4d378` +/// @dev the ERC-165 identifier for this interface is `0x6faff5f1` interface IERC6551Account { - event TransactionExecuted(address indexed target, uint256 indexed value, bytes data); - + /** + * @dev Allows the account to receive Ether. + * + * Accounts MUST implement a `receive` function. + * + * Accounts MAY perform arbitrary logic to restrict conditions + * under which Ether can be received. + */ receive() external payable; - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external payable returns (bytes memory); - + /** + * @dev Returns the identifier of the non-fungible token which owns the account. + * + * The return value of this function MUST be constant - it MUST NOT change over time. + * + * @return chainId The chain ID of the chain the token exists on + * @return tokenContract The contract address of the token + * @return tokenId The ID of the token + */ function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); - function owner() external view returns (address); + /** + * @dev Returns a value that SHOULD be modified each time the account changes state. + * + * @return The current account state + */ + function state() external view returns (uint256); + + /** + * @dev Returns a magic value indicating whether a given signer is authorized to act on behalf + * of the account. + * + * MUST return the bytes4 magic value 0x523e3260 if the given signer is valid. + * + * By default, the holder of the non-fungible token the account is bound to MUST be considered + * a valid signer. + * + * Accounts MAY implement additional authorization logic which invalidates the holder as a + * signer or grants signing permissions to other non-holder accounts. + * + * @param signer The address to check signing authorization for + * @param context Additional data used to determine whether the signer is valid + * @return magicValue Magic value indicating whether the signer is valid + */ + function isValidSigner(address signer, bytes calldata context) + external + view + returns (bytes4 magicValue); } + diff --git a/contracts/interfaces/IERC6551Executable.sol b/contracts/interfaces/IERC6551Executable.sol new file mode 100644 index 0000000..0cf7abb --- /dev/null +++ b/contracts/interfaces/IERC6551Executable.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +/// @dev the ERC-165 identifier for this interface is `0x51945447` +interface IERC6551Executable { + /** + * @dev Executes a low-level operation if the caller is a valid signer on the account. + * + * Reverts and bubbles up error if operation fails. + * + * Accounts implementing this interface MUST accept the following operation parameter values: + * - 0 = CALL + * - 1 = DELEGATECALL + * - 2 = CREATE + * - 3 = CREATE2 + * + * Accounts implementing this interface MAY support additional operations or restrict a signer's + * ability to execute certain operations. + * + * @param to The target address of the operation + * @param value The Ether value to be sent to the target + * @param data The encoded operation calldata + * @param operation A value indicating the type of operation to perform + * @return The result of the operation + */ + function execute(address to, uint256 value, bytes calldata data, uint8 operation) + external + payable + returns (bytes memory); +} diff --git a/contracts/interfaces/IERC6551Registry.sol b/contracts/interfaces/IERC6551Registry.sol new file mode 100644 index 0000000..5bdec87 --- /dev/null +++ b/contracts/interfaces/IERC6551Registry.sol @@ -0,0 +1,48 @@ +interface IERC6551Registry { + /** + * @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation. + */ + event ERC6551AccountCreated( + address account, + address indexed implementation, + bytes32 salt, + uint256 chainId, + address indexed tokenContract, + uint256 indexed tokenId + ); + + /** + * @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails. + */ + error AccountCreationFailed(); + + /** + * @dev Creates a token bound account for a non-fungible token. + * + * If account has already been created, returns the account address without calling create2. + * + * Emits ERC6551AccountCreated event. + * + * @return account The address of the token bound account + */ + function createAccount( + address implementation, + bytes32 salt, + uint256 chainId, + address tokenContract, + uint256 tokenId + ) external returns (address account); + + /** + * @dev Returns the computed token bound account address for a non-fungible token. + * + * @return account The address of the token bound account + */ + function account( + address implementation, + bytes32 salt, + uint256 chainId, + address tokenContract, + uint256 tokenId + ) external view returns (address account); +} \ No newline at end of file diff --git a/contracts/interfaces/IRegistry.sol b/contracts/interfaces/IRegistry.sol deleted file mode 100644 index 664dab2..0000000 --- a/contracts/interfaces/IRegistry.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -interface IRegistry { - function createAccount( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt, - bytes calldata initData - ) external returns (address); - - function account( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt - ) external view returns (address); -} diff --git a/contracts/interfaces/ISmartAccount.sol b/contracts/interfaces/ISmartAccount.sol new file mode 100644 index 0000000..3f8a555 --- /dev/null +++ b/contracts/interfaces/ISmartAccount.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT + +// ISmartAccount.sol -- Part of the Charged Particles Protocol +// Copyright (c) 2024 Firma Lux, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pragma solidity ^0.8.13; + +import {IERC6551Account} from "../interfaces/IERC6551Account.sol"; +import {IERC6551Executable} from "../interfaces/IERC6551Executable.sol"; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; + +/** + * @title A smart contract account owned by a single ERC721 token + */ +interface ISmartAccount is + IERC165, + IERC6551Account, + IERC6551Executable, + IERC721Receiver, + IERC1155Receiver +{ + event PermissionUpdated(address owner, address caller, bool hasPermission); + event ExecutionControllerUpdated(address owner, address controller); + + function isInitialized() external returns (bool); + function initialize( + address chargedParticles, + address executionController, + uint256 parentNftChainId, + address parentNftContract, + uint256 parentNftTokenId + ) external; + + function getPrincipal(address assetToken) external view returns (uint256 total); + function getInterest(address assetToken) external view returns (uint256 total); + function getRewards(address assetToken) external view returns (uint256 total); + function getCovalentBonds(address nftContractAddress, uint256 nftTokenId) external view returns (uint256 total); + + function handleTokenUpdate( + bool isReceiving, + address assetToken, + uint256 assetAmount + ) external; + + function handleNFTUpdate( + bool isReceiving, + address tokenContract, + uint256 tokenId, + uint256 tokenAmount + ) external; + + function handleNFTBatchUpdate( + bool isReceiving, + address tokenContract, + uint256[] calldata tokenIds, + uint256[] calldata tokenAmounts + ) external; +} \ No newline at end of file diff --git a/contracts/interfaces/ISmartAccountController.sol b/contracts/interfaces/ISmartAccountController.sol new file mode 100644 index 0000000..b65e59e --- /dev/null +++ b/contracts/interfaces/ISmartAccountController.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/** + * @title + */ +interface ISmartAccountController is IERC165 { + function onExecute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) external returns (string memory revertReason); + + function onUpdateToken( + bool isReceiving, + uint256 chainId, + address tokenContract, + uint256 tokenId, + address receivedAssetToken, + uint256 receivedAssetAmount + ) external; + + function onUpdateNFT( + bool isReceiving, + uint256 chainId, + address tokenContract, + uint256 tokenId, + address receivedTokenContract, + uint256 receivedTokenId, + uint256 receivedTokenAmount + ) external; + + function onUpdateNFTBatch( + bool isReceiving, + uint256 chainId, + address tokenContract, + uint256 tokenId, + address receivedTokenContract, + uint256[] calldata receivedTokenIds, + uint256[] calldata receivedTokenAmounts + ) external; +} diff --git a/contracts/lib/ERC6551AccountLib.sol b/contracts/lib/ERC6551AccountLib.sol index 21c12ab..5f5fc23 100644 --- a/contracts/lib/ERC6551AccountLib.sol +++ b/contracts/lib/ERC6551AccountLib.sol @@ -1,34 +1,103 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/Create2.sol"; +import "./ERC6551BytecodeLib.sol"; library ERC6551AccountLib { - function token() + function computeAddress( + address registry, + address _implementation, + bytes32 _salt, + uint256 chainId, + address tokenContract, + uint256 tokenId + ) internal pure returns (address) { + bytes32 bytecodeHash = keccak256( + ERC6551BytecodeLib.getCreationCode( + _implementation, _salt, chainId, tokenContract, tokenId + ) + ); + + return Create2.computeAddress(_salt, bytecodeHash, registry); + } + + function isERC6551Account(address account, address expectedImplementation, address registry) internal view - returns ( - uint256, - address, - uint256 - ) + returns (bool) { - bytes memory footer = new bytes(0x60); + // invalid bytecode size + if (account.code.length != 0xAD) return false; + + address _implementation = implementation(account); + + // implementation does not exist + if (_implementation.code.length == 0) return false; + + // invalid implementation + if (_implementation != expectedImplementation) return false; + + (bytes32 _salt, uint256 chainId, address tokenContract, uint256 tokenId) = context(account); + return account + == computeAddress(registry, _implementation, _salt, chainId, tokenContract, tokenId); + } + + function implementation(address account) internal view returns (address _implementation) { assembly { - // copy 0x60 bytes from end of footer - extcodecopy(address(), add(footer, 0x20), 0x4d, 0xad) + // copy proxy implementation (0x14 bytes) + extcodecopy(account, 0xC, 0xA, 0x14) + _implementation := mload(0x00) } + } - return abi.decode(footer, (uint256, address, uint256)); + function implementation() internal view returns (address _implementation) { + return implementation(address(this)); } - function salt() internal view returns (uint256) { - bytes memory footer = new bytes(0x20); + function token(address account) internal view returns (uint256, address, uint256) { + bytes memory encodedData = new bytes(0x60); assembly { - // copy 0x20 bytes from beginning of footer - extcodecopy(address(), add(footer, 0x20), 0x2d, 0x4d) + // copy 0x60 bytes from end of context + extcodecopy(account, add(encodedData, 0x20), 0x4d, 0x60) } - return abi.decode(footer, (uint256)); + return abi.decode(encodedData, (uint256, address, uint256)); + } + + function token() internal view returns (uint256, address, uint256) { + return token(address(this)); + } + + function salt(address account) internal view returns (bytes32) { + bytes memory encodedData = new bytes(0x20); + + assembly { + // copy 0x20 bytes from beginning of context + extcodecopy(account, add(encodedData, 0x20), 0x2d, 0x20) + } + + return abi.decode(encodedData, (bytes32)); + } + + function salt() internal view returns (bytes32) { + return salt(address(this)); + } + + function context(address account) internal view returns (bytes32, uint256, address, uint256) { + bytes memory encodedData = new bytes(0x80); + + assembly { + // copy full context (0x80 bytes) + extcodecopy(account, add(encodedData, 0x20), 0x2D, 0x80) + } + + return abi.decode(encodedData, (bytes32, uint256, address, uint256)); + } + + function context() internal view returns (bytes32, uint256, address, uint256) { + return context(address(this)); } -} +} \ No newline at end of file diff --git a/contracts/lib/ERC6551BytecodeLib.sol b/contracts/lib/ERC6551BytecodeLib.sol new file mode 100644 index 0000000..ab13908 --- /dev/null +++ b/contracts/lib/ERC6551BytecodeLib.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +library ERC6551BytecodeLib { + /** + * @dev Returns the creation code of the token bound account for a non-fungible token. + * + * @return result The creation code of the token bound account + */ + function getCreationCode( + address implementation, + bytes32 salt, + uint256 chainId, + address tokenContract, + uint256 tokenId + ) internal pure returns (bytes memory result) { + assembly { + result := mload(0x40) // Grab the free memory pointer + // Layout the variables and bytecode backwards + mstore(add(result, 0xb7), tokenId) + mstore(add(result, 0x97), shr(96, shl(96, tokenContract))) + mstore(add(result, 0x77), chainId) + mstore(add(result, 0x57), salt) + mstore(add(result, 0x37), 0x5af43d82803e903d91602b57fd5bf3) + mstore(add(result, 0x28), implementation) + mstore(add(result, 0x14), 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) + mstore(result, 0xb7) // Store the length + mstore(0x40, add(result, 0xd7)) // Allocate the memory + } + } + + /** + * @dev Returns the create2 address computed from `salt`, `bytecodeHash`, `deployer`. + * + * @return result The create2 address computed from `salt`, `bytecodeHash`, `deployer` + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) + internal + pure + returns (address result) + { + assembly { + result := mload(0x40) // Grab the free memory pointer + mstore8(result, 0xff) + mstore(add(result, 0x35), bytecodeHash) + mstore(add(result, 0x01), shl(96, deployer)) + mstore(add(result, 0x15), salt) + result := keccak256(result, 0x55) + } + } +} \ No newline at end of file diff --git a/contracts/lib/ERC6551Registry.sol b/contracts/lib/ERC6551Registry.sol new file mode 100644 index 0000000..053fd74 --- /dev/null +++ b/contracts/lib/ERC6551Registry.sol @@ -0,0 +1,116 @@ + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/IERC6551Registry.sol"; + +contract ERC6551Registry is IERC6551Registry { + function createAccount( + address implementation, + bytes32 salt, + uint256 chainId, + address tokenContract, + uint256 tokenId + ) external returns (address) { + assembly { + // Memory Layout: + // ---- + // 0x00 0xff (1 byte) + // 0x01 registry (address) (20 bytes) + // 0x15 salt (bytes32) (32 bytes) + // 0x35 Bytecode Hash (bytes32) (32 bytes) + // ---- + // 0x55 ERC-1167 Constructor + Header (20 bytes) + // 0x69 implementation (address) (20 bytes) + // 0x5D ERC-1167 Footer (15 bytes) + // 0x8C salt (uint256) (32 bytes) + // 0xAC chainId (uint256) (32 bytes) + // 0xCC tokenContract (address) (32 bytes) + // 0xEC tokenId (uint256) (32 bytes) + + // Silence unused variable warnings + pop(chainId) + + // Copy bytecode + constant data to memory + calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId + mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer + mstore(0x5d, implementation) // implementation + mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header + + // Copy create2 computation data to memory + mstore8(0x00, 0xff) // 0xFF + mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode) + mstore(0x01, shl(96, address())) // registry address + mstore(0x15, salt) // salt + + // Compute account address + let computed := keccak256(0x00, 0x55) + + // If the account has not yet been deployed + if iszero(extcodesize(computed)) { + // Deploy account contract + let deployed := create2(0, 0x55, 0xb7, salt) + + // Revert if the deployment fails + if iszero(deployed) { + mstore(0x00, 0x20188a59) // `AccountCreationFailed()` + revert(0x1c, 0x04) + } + + // Store account address in memory before salt and chainId + mstore(0x6c, deployed) + + // Emit the ERC6551AccountCreated event + log4( + 0x6c, + 0x60, + // `ERC6551AccountCreated(address,address,bytes32,uint256,address,uint256)` + 0x79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf88722, + implementation, + tokenContract, + tokenId + ) + + // Return the account address + return(0x6c, 0x20) + } + + // Otherwise, return the computed account address + mstore(0x00, shr(96, shl(96, computed))) + return(0x00, 0x20) + } + } + + function account( + address implementation, + bytes32 salt, + uint256 chainId, + address tokenContract, + uint256 tokenId + ) external view returns (address) { + assembly { + // Silence unused variable warnings + pop(chainId) + pop(tokenContract) + pop(tokenId) + + // Copy bytecode + constant data to memory + calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId + mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer + mstore(0x5d, implementation) // implementation + mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header + + // Copy create2 computation data to memory + mstore8(0x00, 0xff) // 0xFF + mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode) + mstore(0x01, shl(96, address())) // registry address + mstore(0x15, salt) // salt + + // Store computed account address in memory + mstore(0x00, shr(96, shl(96, keccak256(0x00, 0x55)))) + + // Return computed account address + return(0x00, 0x20) + } + } +} diff --git a/contracts/lib/NftTokenInfo.sol b/contracts/lib/NftTokenInfo.sol new file mode 100644 index 0000000..b255996 --- /dev/null +++ b/contracts/lib/NftTokenInfo.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT + +// NftTokenInfo.sol -- Part of the Charged Particles Protocol +// Copyright (c) 2024 Firma Lux, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pragma solidity ^0.8.13; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; + +library NftTokenInfo { + bytes4 constant internal INTERFACE_SIGNATURE_ERC721 = 0x80ac58cd; + bytes4 constant internal INTERFACE_SIGNATURE_ERC1155 = 0xd9b67a26; + + function isERC721(address contractAddress) internal view returns (bool) { + return IERC165(contractAddress).supportsInterface(INTERFACE_SIGNATURE_ERC721); + } + + function isERC1155(address contractAddress) internal view returns (bool) { + return IERC165(contractAddress).supportsInterface(INTERFACE_SIGNATURE_ERC1155); + } + + function getTokenUUID(address contractAddress, uint256 tokenId) internal pure returns (uint256) { + return uint256(keccak256(abi.encodePacked(contractAddress, tokenId))); + } + + function getTokenOwner(address contractAddress, uint256 tokenId) internal returns (address) { + return _getTokenOwner(contractAddress, tokenId); + } + + function isNFTOwnerOrOperator(address contractAddress, uint256 tokenId, address sender) internal returns (bool) { + IERC721 tokenInterface = IERC721(contractAddress); + address tokenOwner = _getTokenOwner(contractAddress, tokenId); + return (sender == tokenOwner || tokenInterface.isApprovedForAll(tokenOwner, sender)); + } + + function _getTokenOwner(address contractAddress, uint256 tokenId) internal returns (address) { + // solhint-disable-next-line + (bool success, bytes memory returnData) = contractAddress.call(abi.encodeWithSelector(IERC721.ownerOf.selector, tokenId)); + if (success) { + return abi.decode(returnData, (address)); + } else { + return address(0x0); + } + } +} \ No newline at end of file diff --git a/contracts/lib/SmartAccountBase.sol b/contracts/lib/SmartAccountBase.sol new file mode 100644 index 0000000..177cdc9 --- /dev/null +++ b/contracts/lib/SmartAccountBase.sol @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: MIT + +// SmartAccountBase.sol -- Part of the Charged Particles Protocol +// Copyright (c) 2024 Firma Lux, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pragma solidity ^0.8.13; + +import {IERC165, ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; + +import {IERC6551Account} from "../interfaces/IERC6551Account.sol"; +import {IERC6551Executable} from "../interfaces/IERC6551Executable.sol"; + +import {ISmartAccount} from "../interfaces/ISmartAccount.sol"; +import {ISmartAccountController} from "../interfaces/ISmartAccountController.sol"; + +// import "hardhat/console.sol"; + +error AlreadyInitialized(); +error NotAuthorized(); +error InvalidInput(); +error OwnershipCycle(); + +/** + * @title A smart contract account owned by a single ERC721 token + */ +abstract contract SmartAccountBase is ISmartAccount, ERC165 { + address internal _chargedParticles; + address internal _executionController; + uint256 internal _parentNftChainId; + address internal _parentNftContract; + uint256 internal _parentNftTokenId; + + /// @dev mapping from owner => caller => has permissions + mapping(address => mapping(address => bool)) internal _permissions; + + bool internal _initialized; + constructor() {} + + function initialize( + address chargedParticles, + address executionController, + uint256 parentNftChainId, + address parentNftContract, + uint256 parentNftTokenId + ) external { + if (_initialized) { revert AlreadyInitialized(); } + _initialized = true; + _chargedParticles = chargedParticles; + _executionController = executionController; + _parentNftChainId = parentNftChainId; + _parentNftContract = parentNftContract; + _parentNftTokenId = parentNftTokenId; + } + + /// @dev allows eth transfers by default, but allows account owner to override + receive() external payable virtual override {} + + function isInitialized() external view virtual override returns (bool) { + return _initialized; + } + + function permissions(address _owner, address caller) public view virtual returns (bool) { + return _permissions[_owner][caller]; + } + + function getChargedParticles() public view virtual returns (address) { + return _chargedParticles; + } + + function setChargedParticles(address chargedParticles) public virtual onlyOwner { + if (chargedParticles == address(0)) { revert InvalidInput(); } + _chargedParticles = chargedParticles; + } + + function getExecutionController() public view virtual returns (address) { + return _executionController; + } + + function setExecutionController(address executionController) public virtual onlyOwner { + _executionController = executionController; + emit ExecutionControllerUpdated(msg.sender, executionController); + } + + /// @dev Returns the EIP-155 chain ID, token contract address, and token ID for the token that + /// owns this account. + function token() + public + view + virtual + returns ( + uint256 chainId, + address tokenContract, + uint256 tokenId + ) + { + chainId = _parentNftChainId; + tokenContract = _parentNftContract; + tokenId = _parentNftTokenId; + } + + /// @dev Returns the owner of the ERC-721 token which owns this account. By default, the owner + /// of the token has full permissions on the account. + function owner() public view virtual returns (address) { + (uint256 chainId, address tokenContract, uint256 tokenId) = token(); + if (chainId != block.chainid) { return address(0); } + + try IERC721(tokenContract).ownerOf(tokenId) returns (address _owner) { + return _owner; + } catch { + return address(0); + } + } + + function isValidSigner(address signer, bytes calldata) external view virtual returns (bytes4) { + if (_isValidSigner(signer)) { + return IERC6551Account.isValidSigner.selector; + } + return bytes4(0); + } + + /// @dev grants a given caller execution permissions + function setPermissions(address[] calldata callers, bool[] calldata newPermissions) public virtual { + address _owner = owner(); + if (msg.sender != _owner) { revert NotAuthorized(); } + + uint256 length = callers.length; + if (newPermissions.length != length) { revert InvalidInput(); } + + for (uint256 i = 0; i < length; i++) { + _permissions[_owner][callers[i]] = newPermissions[i]; + emit PermissionUpdated(_owner, callers[i], newPermissions[i]); + } + } + + /// @dev Returns true if a given interfaceId is supported by this account. This method can be + /// extended by an override. + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(IERC165, ERC165) + returns (bool) + { + return interfaceId == type(IERC6551Account).interfaceId + || interfaceId == type(IERC6551Executable).interfaceId + || interfaceId == type(ISmartAccount).interfaceId + || super.supportsInterface(interfaceId); + } + + /// @dev Allows ERC-721 tokens to be received so long as they do not cause an ownership cycle. + /// This function can be overriden. + function onERC721Received( + address, + address, + uint256 receivedTokenId, + bytes memory + ) public view virtual override returns (bytes4) { + ( + uint256 chainId, + address tokenContract, + uint256 tokenId + ) = token(); + + if (chainId == block.chainid && tokenContract == msg.sender && tokenId == receivedTokenId) { + revert OwnershipCycle(); + } + return this.onERC721Received.selector; + } + + /// @dev Allows ERC-1155 tokens to be received. This function can be overriden. + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public pure virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + /// @dev Allows ERC-1155 token batches to be received. This function can be overriden. + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public pure virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } + + /// @dev Executes a low-level call + function _call( + address to, + uint256 value, + bytes calldata data + ) internal returns (bytes memory result) { + bool success; + // solhint-disable-next-line avoid-low-level-calls + (success, result) = to.call{value: value}(data); + + if (!success) { + assembly { + revert(add(result, 32), mload(result)) + } + } + } + + function _onExecute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) internal { + if (IERC165(_executionController).supportsInterface(type(ISmartAccountController).interfaceId)) { + string memory revertReason = ISmartAccountController(_executionController).onExecute(to, value, data, operation); + if (bytes(revertReason).length > 0) { + revert(revertReason); + } + } + } + + function _onUpdateToken( + bool isReceiving, + address assetToken, + uint256 assetAmount + ) internal { + if (IERC165(_executionController).supportsInterface(type(ISmartAccountController).interfaceId)) { + (uint256 chainId, address tokenContract, uint256 tokenId) = token(); + ISmartAccountController(_executionController) + .onUpdateToken(isReceiving, chainId, tokenContract, tokenId, assetToken, assetAmount); + } + } + + function _onUpdateNFT( + bool isReceiving, + address childTokenContract, + uint256 childTokenId, + uint256 childTokenAmount + ) internal { + if (IERC165(_executionController).supportsInterface(type(ISmartAccountController).interfaceId)) { + (uint256 chainId, address tokenContract, uint256 tokenId) = token(); + ISmartAccountController(_executionController) + .onUpdateNFT(isReceiving, chainId, tokenContract, tokenId, childTokenContract, childTokenId, childTokenAmount); + } + } + + function _onUpdateNFTBatch( + bool isReceiving, + address childTokenContract, + uint256[] calldata childTokenIds, + uint256[] calldata childTokenAmounts + ) internal { + if (IERC165(_executionController).supportsInterface(type(ISmartAccountController).interfaceId)) { + (uint256 chainId, address tokenContract, uint256 tokenId) = token(); + ISmartAccountController(_executionController) + .onUpdateNFTBatch(isReceiving, chainId, tokenContract, tokenId, childTokenContract, childTokenIds, childTokenAmounts); + } + } + + function _isValidSigner(address signer) internal view virtual returns (bool) { + address ownerOf = owner(); + + // Charged Particles & Execution Controller always have permissions + if (signer == _chargedParticles) { return true; } + if (signer == _executionController) { return true; } + + // authorize caller if owner has granted permissions + if (_permissions[ownerOf][signer]) { return true; } + + // authorize token owner + return signer == ownerOf; + } + + /// @dev reverts if caller is not the owner of the NFT which owns the account + modifier onlyOwner() { + if (msg.sender != owner()) { revert NotAuthorized(); } + _; + } + + /// @dev reverts if caller is not authorized to execute on this account + modifier onlyValidSigner() { + if (!_isValidSigner(msg.sender)) { revert NotAuthorized(); } + _; + } +} \ No newline at end of file diff --git a/contracts/tokens/BufficornZK.sol b/contracts/tokens/BufficornZK.sol new file mode 100644 index 0000000..aeabd22 --- /dev/null +++ b/contracts/tokens/BufficornZK.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +// Written by: Rob Secord (https://twitter.com/robsecord) +// Co-founder @ Charged Particles - Visit: https://charged.fi +// Co-founder @ Taggr - Visit: https://taggr.io + +pragma solidity ^0.8.13; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {ERC721, ERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {ISmartAccountController} from "../interfaces/ISmartAccountController.sol"; +import {IDynamicTraits} from "../interfaces/IDynamicTraits.sol"; + +/** + * @dev todo... + */ +contract BufficornZK is ISmartAccountController, IDynamicTraits, Ownable, ERC721Enumerable { + using Strings for uint256; + + // Base portion of the Token Metadata URI (format: base-uri/tokenId/traitsMap) + string internal _tokenUriBase; + + // TokenId => Traits BitMap + mapping (uint256 => uint256) internal _traitBits; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Initialization + + constructor(string memory name, string memory symbol) + ERC721(name, symbol) Ownable() {} + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Minting Logic + + // For minting container-NFTs that have no initial traits + function mint(uint256 tokenId) external virtual { + _mint(tokenId, 0); + } + + // For minting child-NFTs that have initial fixed traits + function mintWithTraits(uint256 tokenId, uint256 traits) external virtual { + _mint(tokenId, traits); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Customizable Traits Logic + + function getTraits(uint256 tokenId) external view override returns (uint256) { + return _traitBits[tokenId]; + } + + function hasTrait(uint256 tokenId, uint256 trait) external view override returns (bool) { + uint256 bit = _traitBits[tokenId] & (1 << trait); + return bit > 0; + } + + function traitCount(uint256 tokenId) external view override returns (uint256 totalTraits) { + uint256 map = _traitBits[tokenId]; + // Brian Kerninghan bit-counting method = O(log(n)) + while (map != 0) { + map &= (map - 1); + totalTraits += 1; + } + } + + function _addTraits(uint256 tokenId, uint256 traits) internal returns (uint256) { + _traitBits[tokenId] |= traits; + return _traitBits[tokenId]; + } + + function _removeTraits(uint256 tokenId, uint256 traits) internal returns (uint256) { + uint256 mask = traits ^ (2 ** 256 - 1); // negate to find the traits to keep + _traitBits[tokenId] &= mask; + return _traitBits[tokenId]; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Standard NFT Logic + + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + _requireMinted(tokenId); + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), "/", _traitBits[tokenId].toString())) : ""; + } + + function setBaseURI(string memory newBase) external onlyOwner { + _tokenUriBase = newBase; + } + + function _baseURI() internal view virtual override returns (string memory) { + return _tokenUriBase; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SmartAccount Controller Logic + + function onExecute( + address, + uint256, + bytes calldata, + uint8 + ) + external + virtual + override + returns (string memory revertReason) + { + return ""; // success + } + + function onUpdateToken( + bool isReceiving, + uint256, // this chain + address, // this contract + uint256 tokenId, + address receivedAssetToken, + uint256 receivedAssetAmount + ) + external + virtual + override + { + // no-op + } + + function onUpdateNFT( + bool isReceiving, + uint256, // this chain + address, // this contract + uint256 tokenId, + address receivedTokenContract, + uint256 receivedTokenId, + uint256 + ) + external + virtual + override + { + if (IERC165(receivedTokenContract).supportsInterface(type(IDynamicTraits).interfaceId)) { + uint256 newTraits = IDynamicTraits(receivedTokenContract).getTraits(receivedTokenId); + if (isReceiving) { + _addTraits(tokenId, newTraits); + } else { + _removeTraits(tokenId, newTraits); + } + } + } + + function onUpdateNFTBatch( + bool isReceiving, + uint256, // this chain + address, // this contract + uint256 tokenId, + address receivedTokenContract, + uint256[] calldata receivedTokenIds, + uint256[] calldata + ) + external + virtual + override + { + uint256 i; + uint256 t; + uint256 n = receivedTokenIds.length; + if (IERC165(receivedTokenContract).supportsInterface(type(IDynamicTraits).interfaceId)) { + for (; i < n; i++) { + t = IDynamicTraits(receivedTokenContract).getTraits(receivedTokenIds[i]); + if (isReceiving) { + _addTraits(tokenId, t); + } else { + _removeTraits(tokenId, t); + } + } + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Custom Interfaces + + /// @dev Returns true if a given interfaceId is supported by this account. This method can be + /// extended by an override. + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(IERC165, ERC721Enumerable) + returns (bool) + { + return + interfaceId == type(ISmartAccountController).interfaceId || + interfaceId == type(IDynamicTraits).interfaceId || + super.supportsInterface(interfaceId); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Internal Functions + + function _mint(uint256 tokenId, uint256 traits) internal { + // Note: Do NOT set traits when bridging a Bufficorn-NFT. + // The "traits" param is ONLY used for minting Trait-NFTs + // which can be minted from this contract, or another contract. + // When bridging a Bufficorn, this contract will retain the last-known + // state for a Bufficorns nested traits on zkSync. + if (traits > 0) { + _traitBits[tokenId] = traits; + } + _safeMint(_msgSender(), tokenId); + } +} diff --git a/contracts/ERC721All.sol b/contracts/tokens/ERC721All.sol similarity index 98% rename from contracts/ERC721All.sol rename to contracts/tokens/ERC721All.sol index 48f60ef..82a0a08 100644 --- a/contracts/ERC721All.sol +++ b/contracts/tokens/ERC721All.sol @@ -3,11 +3,11 @@ // Co-founder @ Charged Particles - Visit: https://charged.fi // Co-founder @ Taggr - Visit: https://taggr.io -pragma solidity 0.8.13; +pragma solidity ^0.8.13; import "@openzeppelin/contracts/access/Ownable.sol"; -import "./lib/ERC721.sol"; +import "../lib/ERC721.sol"; /** * @dev todo... diff --git a/contracts/ERC721i.sol b/contracts/tokens/ERC721i.sol similarity index 97% rename from contracts/ERC721i.sol rename to contracts/tokens/ERC721i.sol index 7cf847f..af07848 100644 --- a/contracts/ERC721i.sol +++ b/contracts/tokens/ERC721i.sol @@ -3,10 +3,10 @@ // Co-founder @ Charged Particles - Visit: https://charged.fi // Co-founder @ Taggr - Visit: https://taggr.io -pragma solidity 0.8.13; +pragma solidity ^0.8.13; import "@openzeppelin/contracts/access/Ownable.sol"; -import "./lib/ERC721iEnumerable.sol"; +import "../lib/ERC721iEnumerable.sol"; /** * @dev This implements a Pre-Mint version of {ERC721} that adds the ability to Pre-Mint diff --git a/deploy/BufficornZK.ts b/deploy/BufficornZK.ts new file mode 100644 index 0000000..fb32e6d --- /dev/null +++ b/deploy/BufficornZK.ts @@ -0,0 +1,17 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const BufficornZK_Deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + const {deployer} = await getNamedAccounts(); + + await deploy('BufficornZK', { + from: deployer, + args: ["BufficornZK", "BZK"], + log: true, + }); +}; +export default BufficornZK_Deploy; + +BufficornZK_Deploy.tags = ['BufficornZK']; diff --git a/deploy/ChargedParticles.ts b/deploy/ChargedParticles.ts new file mode 100644 index 0000000..31569d6 --- /dev/null +++ b/deploy/ChargedParticles.ts @@ -0,0 +1,24 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; +import { ethers } from 'hardhat'; + +const ChargedParticles_Deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + const {deployer} = await getNamedAccounts(); + + const smartAccount = await ethers.getContract('SmartAccount'); + + await deploy('ChargedParticles', { + from: deployer, + args: [ + '0x000000006551c19487814612e58FE06813775758', + await smartAccount.getAddress() + ], // ERC6551Registry - Same on All Chains + log: true, + }); +}; +export default ChargedParticles_Deploy; + +ChargedParticles_Deploy.dependencies = ['SmartAccount']; +ChargedParticles_Deploy.tags = ['ChargedParticles']; diff --git a/deploy/ERC20Mock.ts b/deploy/ERC20Mock.ts new file mode 100644 index 0000000..91da6c9 --- /dev/null +++ b/deploy/ERC20Mock.ts @@ -0,0 +1,18 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const ERC20Mock: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + + const { deployer } = await getNamedAccounts(); + + await deploy('ERC20Mock', { + from: deployer, + args: [], + log: true, + }); +}; +export default ERC20Mock; + +ERC20Mock.tags = ['ERC20Mock']; \ No newline at end of file diff --git a/deploy/ERC721All.ts b/deploy/ERC721All.ts new file mode 100644 index 0000000..28d8792 --- /dev/null +++ b/deploy/ERC721All.ts @@ -0,0 +1,18 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const ERC721All: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + + const { deployer } = await getNamedAccounts(); + + await deploy('ERC721All', { + from: deployer, + args: ['ERC721 All', 'All'], + log: true, + }); +}; +export default ERC721All; + +ERC721All.tags = ['ERC721All']; \ No newline at end of file diff --git a/deploy/ERC721i.ts b/deploy/ERC721i.ts new file mode 100644 index 0000000..9e1b178 --- /dev/null +++ b/deploy/ERC721i.ts @@ -0,0 +1,18 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const ERC721i: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + + const { deployer } = await getNamedAccounts(); + + await deploy('ERC721i', { + from: deployer, + args: ['ERC721i', 'i','test/url/', deployer, 100000], + log: true, + }); +}; +export default ERC721i; + +ERC721i.tags = ['ERC721i']; \ No newline at end of file diff --git a/deploy/NFTMock.ts b/deploy/NFTMock.ts new file mode 100644 index 0000000..1a26610 --- /dev/null +++ b/deploy/NFTMock.ts @@ -0,0 +1,18 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const NFTMockMock: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + + const { deployer } = await getNamedAccounts(); + + await deploy('NFTMock', { + from: deployer, + args: ['Game of NTF', 'GONFT'], + log: true, + }); +}; +export default NFTMockMock; + +NFTMockMock.tags = ['NFTMock']; \ No newline at end of file diff --git a/deploy/SAC_EX1.ts b/deploy/SAC_EX1.ts new file mode 100644 index 0000000..c2422ed --- /dev/null +++ b/deploy/SAC_EX1.ts @@ -0,0 +1,18 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const SAC_EX1_Deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + + const { deployer } = await getNamedAccounts(); + + await deploy('SmartAccountController_Example1', { + from: deployer, + args: [], + log: true, + }); +}; +export default SAC_EX1_Deploy; + +SAC_EX1_Deploy.tags = ['SAC_EX1']; diff --git a/deploy/Setup_Bufficorn.ts b/deploy/Setup_Bufficorn.ts new file mode 100644 index 0000000..4227577 --- /dev/null +++ b/deploy/Setup_Bufficorn.ts @@ -0,0 +1,34 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { DeployFunction } from 'hardhat-deploy/types'; +import { ChargedParticles, BufficornZK } from '../typechain-types'; +import { performTx } from '../utils/performTx'; +// import { isTestnet } from '../utils/isTestnet'; + +const Setup_Bufficorn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { network, deployments, ethers } = hre; + // const chainId = network.config.chainId ?? 1; + + // Load ChargedParticles + const chargedParticles: ChargedParticles = await ethers.getContract('ChargedParticles'); + const chargedParticlesAddress = await chargedParticles.getAddress(); + + // Load Bufficorn + const bufficorn: BufficornZK = await ethers.getContract('BufficornZK'); + const bufficornAddress = await bufficorn.getAddress(); + + // Set Base Token URI on the BufficornZK contract + await performTx( + await bufficorn.setBaseURI('http://www.bufficorn-zk.com/'), + ' -- Token BaseURI set for BufficornZK' + ); + + // Set Custom Execution Controller as the BufficornZK contract + await performTx( + await chargedParticles.setCustomExecutionController(bufficornAddress, bufficornAddress), // NFT Contract, Execution Controller (in this case, they happen to be the same) + ' -- Custom Implementation Created for Bufficorn SmartAccounts' + ); +}; +export default Setup_Bufficorn; + +Setup_Bufficorn.dependencies = ['Setup_CPU', 'BufficornZK']; +Setup_Bufficorn.tags = ['Setup_Bufficorn']; \ No newline at end of file diff --git a/deploy/Setup_ChargedParticles.ts b/deploy/Setup_ChargedParticles.ts new file mode 100644 index 0000000..7da8d57 --- /dev/null +++ b/deploy/Setup_ChargedParticles.ts @@ -0,0 +1,30 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { DeployFunction } from 'hardhat-deploy/types'; +import { ChargedParticles, SmartAccountController_Example1 } from '../typechain-types'; +// import { ContractTransactionReceipt, EventLog, Log } from 'ethers'; +import { performTx } from '../utils/performTx'; + +const Setup_ChargedParticles: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { network, deployments, ethers } = hre; + const chainId = network.config.chainId ?? 1; + + // Load ChargedParticles + const chargedParticles: ChargedParticles = await ethers.getContract('ChargedParticles'); + const chargedParticlesAddress = await chargedParticles.getAddress(); + // console.log(` -- Charged Particles Address: ${chargedParticlesAddress}`); + + // Load SmartAccountController_Example1 + const controller: SmartAccountController_Example1 = await ethers.getContract('SmartAccountController_Example1'); + const controllerAddress = await controller.getAddress(); + // console.log(` -- Execution Controller Address: ${controllerAddress}`); + + // Set Default Execution Controller + await performTx( + await chargedParticles.setDefaultExecutionController(controllerAddress), + ' -- Default Execution Controller Set for SmartAccounts!' + ); +}; +export default Setup_ChargedParticles; + +Setup_ChargedParticles.dependencies = ['ChargedParticles', 'SAC_EX1']; +Setup_ChargedParticles.tags = ['Setup_CPU']; \ No newline at end of file diff --git a/deploy/Setup_SAC_EX1.ts b/deploy/Setup_SAC_EX1.ts new file mode 100644 index 0000000..184609d --- /dev/null +++ b/deploy/Setup_SAC_EX1.ts @@ -0,0 +1,26 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { DeployFunction } from 'hardhat-deploy/types'; +import { SmartAccountController_Example1 } from '../typechain-types'; +// import { ContractTransactionReceipt, EventLog, Log } from 'ethers'; + +const Setup_SAC_EX1: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { network, deployments, ethers } = hre; + // const chainId = network.config.chainId ?? 1; + + // Load SmartAccountController_Example1 + const ex1: SmartAccountController_Example1 = await ethers.getContract('SmartAccountController_Example1'); + // const ex1Address = await ex1.getAddress(); + + // TODO: + // const tx = await ex1.setAllowedMethod(); + // const rc: ContractTransactionReceipt | null = await tx.wait(); + // if (rc !== null) { + // console.log(` -- Allowed Method has been set...`); + // } + // console.log(` -- TODO: Set any Banned Methods on SmartAccountController_Example1`); + +}; +export default Setup_SAC_EX1; + +Setup_SAC_EX1.dependencies = ['SAC_EX1']; +Setup_SAC_EX1.tags = ['Setup_SAC_EX1']; \ No newline at end of file diff --git a/deploy/SmartAccount.ts b/deploy/SmartAccount.ts new file mode 100644 index 0000000..dd001b7 --- /dev/null +++ b/deploy/SmartAccount.ts @@ -0,0 +1,18 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; + +const SmartAccount_Deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + const {deployer} = await getNamedAccounts(); + + await deploy('SmartAccount', { + from: deployer, + args: [ + ], + log: true, + }); +}; +export default SmartAccount_Deploy; + +SmartAccount_Deploy.tags = ['SmartAccount']; diff --git a/hardhat.config.ts b/hardhat.config.ts index 6f197e0..bc73e7e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,10 +1,11 @@ require('dotenv').config() -import { HardhatUserConfig } from "hardhat/config"; +import { HardhatUserConfig } from 'hardhat/config'; import '@typechain/hardhat'; import 'hardhat-deploy'; import 'hardhat-deploy-ethers'; +import 'hardhat-abi-exporter'; import '@nomicfoundation/hardhat-ethers'; -import "@nomicfoundation/hardhat-toolbox"; +import '@nomicfoundation/hardhat-toolbox'; import '@nomicfoundation/hardhat-chai-matchers' const mnemonic = { @@ -16,14 +17,29 @@ const optimizerDisabled = process.env.OPTIMIZER_DISABLED const config: HardhatUserConfig = { solidity: { compilers: [ - { - version: '0.8.13' + { + version: '0.6.12', + settings: { + optimizer: { + enabled: !optimizerDisabled, + runs: 200 + } + }, + }, + { + version: '0.8.13', + settings: { + optimizer: { + enabled: !optimizerDisabled, + runs: 200 + } + }, }, { - version: "0.7.6", + version: '0.7.6', }, { - version: "0.8.17", + version: '0.8.17', settings: { optimizer: { enabled: !optimizerDisabled, @@ -51,19 +67,19 @@ const config: HardhatUserConfig = { }, }, paths: { - sources: "./contracts", - tests: "./test", - cache: "./cache", + sources: './contracts', + tests: './test', + cache: './cache', artifacts: './build/contracts', deploy: './deploy', deployments: './deployments' }, networks: { hardhat: { - chainId: 137, + chainId: 80001, forking: { url: "https://polygon-mainnet.g.alchemy.com/v2/" + process.env.ALCHEMY_API_KEY, - blockNumber: 42543137 + blockNumber: 49144510 }, accounts: { mnemonic: mnemonic.testnet, @@ -72,13 +88,24 @@ const config: HardhatUserConfig = { }, }, goerli: { - url: `https://eth-goerli.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, + url: `https://eth-goerli.g.alchemy.com/v2/${process.env.ALCHEMY_GOERLI_APIKEY}`, gasPrice: 'auto', accounts: { mnemonic: mnemonic.testnet, initialIndex: 0, count: 10, - } + }, + chainId: 5 + }, + sepolia: { + url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_SEPOLIA_API_KEY}`, + gasPrice: 'auto', + accounts: { + mnemonic: mnemonic.testnet, + initialIndex: 0, + count: 10, + }, + chainId: 11155111 }, mainnet: { url: `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, @@ -90,7 +117,7 @@ const config: HardhatUserConfig = { } }, mumbai: { - url: 'https://rpc-mumbai.maticvigil.com', + url: `https://polygon-mumbai.g.alchemy.io/v2/${process.env.ALCHEMY_MUMBAI_API_KEY}`, gasPrice: 10e9, accounts: { mnemonic: mnemonic.testnet, @@ -100,8 +127,8 @@ const config: HardhatUserConfig = { chainId: 80001 }, polygon: { - url: "https://polygon-mainnet.g.alchemy.com/v2/" + process.env.ALCHEMY_API_KEY, - gasPrice: 'auto', + url: `https://polygon-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_POLYGON_API_KEY}`, + gasPrice: 150e9, accounts: { mnemonic: mnemonic.mainnet, count: 8, @@ -111,15 +138,31 @@ const config: HardhatUserConfig = { }, etherscan: { apiKey: { - polygon: process.env.POLYGONSCAN_APIKEY ?? '', - polygonMumbai: process.env.POLYGONSCAN_APIKEY ?? '', + mainnet: process.env.ETHERSCAN_API_KEY ?? '', + goerli: process.env.ETHERSCAN_API_KEY ?? '', + polygon: process.env.POLYGONSCAN_API_KEY ?? '', + polygonMumbai: process.env.POLYGONSCAN_API_KEY ?? '', } }, gasReporter: { currency: 'USD', - gasPrice: 1, + gasPrice: 32, enabled: (process.env.REPORT_GAS) ? true : false }, + abiExporter: { + path: './abis', + runOnCompile: true, + clear: true, + flat: true, + only: [ + 'ChargedParticles', + 'SmartAccount', + 'BufficornZK', + 'ERC721i', + 'ERC721All', + 'SmartAccountController_Example1', + ], + }, }; export default config; diff --git a/package.json b/package.json index 20a7751..1c7fee5 100644 --- a/package.json +++ b/package.json @@ -3,22 +3,41 @@ "version": "1.0.0", "main": "index.js", "repository": "https://github.com/Charged-Particles/cpu-v2.git", - "author": "scammi ", + "author": "Firma Lux, Inc. ", "license": "MIT", + "scripts": { + "reinstall": "rm -rf node_modules && rm -f yarn.lock && yarn clean && yarn", + "clean": "rm -rf build cache coverage coverage.json test-results.xml", + "clean-test": "rm -rf test-results.xml", + "clean-build": "rm -rf abis build cache typechain-types", + "compile": "hardhat --show-stack-traces --max-memory 8192 compile", + "test": "yarn clean-test && hardhat test --show-stack-traces", + "watch-test": "hardhat watch test", + "hint": "solhint \"contracts/**/*.sol\"", + "coverage": "yarn clean-test && hardhat --show-stack-traces coverage --temp coverage_build && yarn clean-test", + "gas": "REPORT_GAS=true hardhat test", + "verify": "hardhat run scripts/verify.ts --network", + "gen-docs": "solidity-docgen -i contracts -o docs", + "deploy-cpu-only": "hardhat deploy --tags Setup_CPU,Setup_SAC_EX1 --network", + "deploy-bufficorn": "hardhat deploy --tags Setup_Bufficorn,Setup_SAC_EX1 --network" + }, "devDependencies": { "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", "@nomicfoundation/hardhat-ethers": "^3.0.3", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", "@nomicfoundation/hardhat-toolbox": "^3.0.0", "@nomicfoundation/hardhat-verify": "^1.0.0", + "@openzeppelin/contracts": "^4.9.2", "@typechain/ethers-v6": "^0.4.3", "@typechain/hardhat": "^8.0.3", "@types/chai": "^4.2.0", "@types/mocha": ">=9.1.0", "@types/node": ">=12.0.0", "chai": "^4.2.0", + "dotenv": "^16.3.1", "ethers": "^6.6.2", "hardhat": "^2.14.0", + "hardhat-abi-exporter": "^2.10.1", "hardhat-deploy": "^0.11.34", "hardhat-deploy-ethers": "^0.4.1", "hardhat-gas-reporter": "^1.0.8", @@ -28,7 +47,6 @@ "typescript": ">=4.5.0" }, "dependencies": { - "@openzeppelin/contracts": "^4.9.2", - "dotenv": "^16.3.1" + "erc20permit": "^0.1.1" } } diff --git a/test/BufficornZK.test.ts b/test/BufficornZK.test.ts new file mode 100644 index 0000000..f4619d6 --- /dev/null +++ b/test/BufficornZK.test.ts @@ -0,0 +1,198 @@ +import { expect } from "chai"; +import { ethers, network, getNamedAccounts, deployments } from 'hardhat'; +import { ChargedParticles, BufficornZK, IERC6551Registry, ERC20Mock, NFTMock } from "../typechain-types"; + +describe('BufficornZK', async function () { + const REGISTRY = '0x000000006551c19487814612e58FE06813775758'; // ERC6551Registry - Same on All Chains + const salt = ethers.encodeBytes32String('CPU-V2'); + + // Contracts + let chargedParticles: ChargedParticles; + let registryContract: IERC6551Registry; + let bufficorn: BufficornZK; + + // Addresses + let chargedParticlesAddress: string; + let bufficornAddress: string; + + // Signers + let deployer: string; + let receiver: string; + + const calculateAccountAddress = async (nftContractAddress: string, nftTokenId: number) => { + const smartAccountImplementation = await chargedParticles.getAccountImplementation(nftContractAddress); + const newAccountAddress = await registryContract.account( + smartAccountImplementation, + salt, + network.config.chainId ?? 1, + nftContractAddress, + nftTokenId, + ); + return newAccountAddress; + }; + + before(async function () { + const { deployer: deployerAccount, user1 } = await getNamedAccounts(); + deployer = deployerAccount; + receiver = user1; + }); + + beforeEach(async function () { + await deployments.fixture([ 'Setup_Bufficorn', 'Setup_SAC_EX1', 'NFTMock', 'ERC20Mock' ]); + + chargedParticles = await ethers.getContract('ChargedParticles'); + bufficorn = await ethers.getContract('BufficornZK'); + + registryContract = await ethers.getContractAt('IERC6551Registry', REGISTRY); + + chargedParticlesAddress = await chargedParticles.getAddress(); + bufficornAddress = await bufficorn.getAddress(); + }); + + + it('Deploys BufficornZK', async function () { + const bufficornAddress = await bufficorn.getAddress(); + expect(bufficornAddress).to.not.be.empty; + }); + + + it('Manages Trait-Swapping when NFTs are Added and Removed', async function () { + const bufficornTokenId = 1; + + // Mint a Bufficorn NFT + await bufficorn.mint(bufficornTokenId).then(tx => tx.wait()); // Token ID: 1 + expect(await bufficorn.balanceOf(deployer)).to.be.equal(bufficornTokenId); + + // Confirm Zero Traits + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.equal(0); + + // Mint some Bufficorn Trait-NFTs + await bufficorn.mintWithTraits(2, 1n) // Token ID: 2, Trait-bit = 00000001 + await bufficorn.mintWithTraits(3, 2n) // Token ID: 3, Trait-bit = 00000010 + await bufficorn.mintWithTraits(4, 4n) // Token ID: 4, Trait-bit = 00000100 + await bufficorn.mintWithTraits(5, 8n) // Token ID: 5, Trait-bit = 00001000 + await bufficorn.mintWithTraits(6, 16n) // Token ID: 6, Trait-bit = 00010000 + expect(await bufficorn.balanceOf(deployer)).to.be.equal(6); + + // Confirm Traits + expect(await bufficorn.getTraits(2)).to.be.equal(1n); + expect(await bufficorn.getTraits(3)).to.be.equal(2n); + expect(await bufficorn.getTraits(4)).to.be.equal(4n); + expect(await bufficorn.getTraits(5)).to.be.equal(8n); + expect(await bufficorn.getTraits(6)).to.be.equal(16n); + + // Calculate Expected Account Address via Registry + const newAccountAddress = await calculateAccountAddress(bufficornAddress, bufficornTokenId); + expect(newAccountAddress).to.not.be.empty; + + // Give permission to Bond + await bufficorn.approve(chargedParticlesAddress, 2).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 3).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 4).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 5).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 6).then(tx => tx.wait()); + + // Bond Trait 1 to Bufficorn + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 2, 1n).then(tx => tx.wait()); + + // Confirm Nested Trait-NFT Owner + expect(await bufficorn.ownerOf(2)).to.be.eq(newAccountAddress); + + // Confirm Bufficorn Traits + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(1n); // Bufficorn has a Single Trait (00000001) + + // Bond and Confirm Trait 2 + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 3, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(3)).to.be.eq(newAccountAddress); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(3n); // Bufficorn has 2 Traits (00000011) + + // Bond and Confirm Trait 3 + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 4, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(4)).to.be.eq(newAccountAddress); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(7n); // Bufficorn has 3 Traits (00000111) + + // Bond and Confirm Trait 4 + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 5, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(5)).to.be.eq(newAccountAddress); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(15n); // Bufficorn has 4 Traits (00001111) + + // Bond and Confirm Trait 5 + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 6, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(6)).to.be.eq(newAccountAddress); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(31n); // Bufficorn has 5 Traits (00011111) + + + // Break-Bond Trait 3 from Bufficorn + await chargedParticles.breakCovalentBond(deployer, bufficornAddress, bufficornTokenId, bufficornAddress, 4, 1n).then(tx => tx.wait()); + + // Confirm Nested Trait-NFT Owner + expect(await bufficorn.ownerOf(4)).to.be.eq(deployer); + + // Confirm Bufficorn Traits + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(27n); // Bufficorn has 4 Traits (00011011) + + // Break-Bond and Confirm Trait 2 Removed + await chargedParticles.breakCovalentBond(deployer, bufficornAddress, bufficornTokenId, bufficornAddress, 3, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(3)).to.be.eq(deployer); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(25n); // Bufficorn has 3 Traits (00011001) + + // Break-Bond and Confirm Trait 5 Removed + await chargedParticles.breakCovalentBond(deployer, bufficornAddress, bufficornTokenId, bufficornAddress, 6, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(6)).to.be.eq(deployer); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(9n); // Bufficorn has 2 Traits (00001001) + + // Break-Bond and Confirm Trait 1 Removed + await chargedParticles.breakCovalentBond(deployer, bufficornAddress, bufficornTokenId, bufficornAddress, 2, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(2)).to.be.eq(deployer); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(8n); // Bufficorn has 1 Trait (00001000) + + // Break-Bond and Confirm Trait 4 Removed + await chargedParticles.breakCovalentBond(deployer, bufficornAddress, bufficornTokenId, bufficornAddress, 5, 1n).then(tx => tx.wait()); + expect(await bufficorn.ownerOf(5)).to.be.eq(deployer); + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.eq(0); // Bufficorn has 0 Traits (00000000) + + // Confirm Owner of all NFTs + expect(await bufficorn.balanceOf(deployer)).to.be.equal(6); + }); + + it('includes the Traits in the TokenURI', async function () { + const bufficornTokenId = 1; + + // Mint a Bufficorn NFT + await bufficorn.mint(bufficornTokenId).then(tx => tx.wait()); // Token ID: 1 + expect(await bufficorn.balanceOf(deployer)).to.be.equal(bufficornTokenId); + + // Confirm Zero Traits + expect(await bufficorn.getTraits(bufficornTokenId)).to.be.equal(0); + + // Mint some Bufficorn Trait-NFTs + await bufficorn.mintWithTraits(2, 1n) // Token ID: 2, Trait-bit = 00000001 + await bufficorn.mintWithTraits(3, 2n) // Token ID: 3, Trait-bit = 00000010 + await bufficorn.mintWithTraits(4, 4n) // Token ID: 4, Trait-bit = 00000100 + await bufficorn.mintWithTraits(5, 8n) // Token ID: 5, Trait-bit = 00001000 + await bufficorn.mintWithTraits(6, 16n) // Token ID: 6, Trait-bit = 00010000 + expect(await bufficorn.balanceOf(deployer)).to.be.equal(6); + + // Calculate Expected Account Address via Registry + const newAccountAddress = await calculateAccountAddress(bufficornAddress, bufficornTokenId); + expect(newAccountAddress).to.not.be.empty; + + // Give permission to Bond + await bufficorn.approve(chargedParticlesAddress, 2).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 3).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 4).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 5).then(tx => tx.wait()); + await bufficorn.approve(chargedParticlesAddress, 6).then(tx => tx.wait()); + + // Bond Traits to Bufficorn + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 2, 1n).then(tx => tx.wait()); + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 3, 1n).then(tx => tx.wait()); + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 4, 1n).then(tx => tx.wait()); + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 5, 1n).then(tx => tx.wait()); + await chargedParticles.covalentBond(bufficornAddress, bufficornTokenId, bufficornAddress, 6, 1n).then(tx => tx.wait()); + + // Confirm Token URI includes Traits + const tokenUri = await bufficorn.tokenURI(bufficornTokenId); + expect(tokenUri).to.be.equal('http://www.bufficorn-zk.com/1/31'); + }); +}); diff --git a/test/ChargedParticles.test.ts b/test/ChargedParticles.test.ts new file mode 100644 index 0000000..20fda11 --- /dev/null +++ b/test/ChargedParticles.test.ts @@ -0,0 +1,201 @@ +import { expect } from "chai"; +import { ethers, network, getNamedAccounts, deployments } from 'hardhat'; +import { ChargedParticles, IERC6551Registry, ERC20Mock, NFTMock } from "../typechain-types"; + +describe('ChargedParticles', async function () { + const REGISTRY = '0x000000006551c19487814612e58FE06813775758'; // ERC6551Registry - Same on All Chains + const salt = ethers.encodeBytes32String('CPU-V2'); + const interfaceIds = { + ISmartAccount: '0x2f62b227', + ISmartAccountController: '0x39b43188', + IChargedParticles: '0xfb86e1eb', + IDynamicTraits: '0x33c2cbef', + }; + + // Contracts + let chargedParticles: ChargedParticles; + let registryContract: IERC6551Registry; + let nftMock: NFTMock; + let erc20Mock: ERC20Mock; + + // Addresses + let chargedParticlesAddress: string; + let nftMockAddress: string; + let erc20MockAddress: string; + + // Signers + let deployer: string; + let receiver: string; + + const calculateAccountAddress = async (nftContractAddress: string, nftTokenId: number) => { + const smartAccountImplementation = await chargedParticles.getAccountImplementation(nftContractAddress); + const newAccountAddress = await registryContract.account( + smartAccountImplementation, + salt, + network.config.chainId ?? 1, + nftContractAddress, + nftTokenId, + ); + return newAccountAddress; + }; + + before(async function () { + const { deployer: deployerAccount, user1 } = await getNamedAccounts(); + deployer = deployerAccount; + receiver = user1; + }); + + beforeEach(async function () { + await deployments.fixture([ 'Setup_CPU', 'Setup_SAC_EX1', 'NFTMock', 'ERC20Mock' ]); + + chargedParticles = await ethers.getContract('ChargedParticles'); + nftMock = await ethers.getContract('NFTMock'); + erc20Mock = await ethers.getContract('ERC20Mock'); + + registryContract = await ethers.getContractAt('IERC6551Registry', REGISTRY); + + nftMockAddress = await nftMock.getAddress(); + erc20MockAddress = await erc20Mock.getAddress(); + chargedParticlesAddress = await chargedParticles.getAddress(); + }); + + + it('Deploys ChargedParticles', async function () { + const chargedParticlesAddress = await chargedParticles.getAddress(); + expect(chargedParticlesAddress).to.not.be.empty; + }); + + + it('Deploys a SmartAccount for an NFT', async function () { + const tokenId = 1; + + await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); + expect(await nftMock.balanceOf(deployer)).to.be.equal(1); + + await erc20Mock.mint(deployer, 10000n).then(tx => tx.wait()); + expect(await erc20Mock.balanceOf(deployer)).to.be.equal(10000n); + + // Calculate Expected Account Address via Registry + const newAccountAddress = await calculateAccountAddress(nftMockAddress, tokenId); + expect(newAccountAddress).to.not.be.empty; + + // Energize NFT in order to Create new Smart Account + await erc20Mock.approve(chargedParticlesAddress, 100n).then(tx => tx.wait()); + const newAccountReceipt = await chargedParticles.energizeParticle( + nftMockAddress, + tokenId, + erc20MockAddress, + 100n, + ).then(tx => tx.wait()); + expect(newAccountReceipt).to.haveOwnProperty('hash'); + + // Confirm new SmartAccount was actually created + const smartAccountCode = await ethers.provider.getCode(newAccountAddress); + expect(smartAccountCode.replace('0x', '')).to.not.be.empty; + + // Confirm SmartAccount Supports correct Interface + const smartAccountContract = await ethers.getContractAt('SmartAccount', newAccountAddress); + const isSmartAccount = await smartAccountContract.supportsInterface(interfaceIds.ISmartAccount); + // TODO: Recalculate new interface + // expect(isSmartAccount).to.be.true; + + // Confirm SmartAccount knows its Parent Token + const smartAccountToken = await smartAccountContract.token(); + expect(smartAccountToken).to.be.lengthOf(3); + expect(smartAccountToken[0]).to.be.equal(network.config.chainId); + expect(smartAccountToken[1]).to.be.equal(nftMockAddress); + expect(smartAccountToken[2]).to.be.equal(tokenId); + }); + + + it('Energizes and Releases an NFT', async function () { + const tokenId = 1; + + await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); + expect(await nftMock.balanceOf(deployer)).to.be.equal(1); + + await erc20Mock.mint(deployer, 10000n).then(tx => tx.wait()); + expect(await erc20Mock.balanceOf(deployer)).to.be.equal(10000n); + + // Calculate Expected Account Address via Registry + const newAccountAddress = await calculateAccountAddress(nftMockAddress, tokenId); + expect(newAccountAddress).to.not.be.empty; + + // Energize NFT + await erc20Mock.approve(chargedParticlesAddress, 10000n).then(tx => tx.wait()); + await chargedParticles.energizeParticle( + nftMockAddress, + tokenId, + erc20MockAddress, + 1500n, + ).then(tx => tx.wait()); + expect(await erc20Mock.balanceOf(newAccountAddress)).to.be.equal(1500n); + expect(await erc20Mock.balanceOf(deployer)).to.be.equal(8500n); + + // Release NFT by Amount + await chargedParticles.releaseParticleAmount( + deployer, + nftMockAddress, + tokenId, + erc20MockAddress, + 500n, + ).then(tx => tx.wait()); + expect(await erc20Mock.balanceOf(newAccountAddress)).to.be.equal(1000n); + expect(await erc20Mock.balanceOf(deployer)).to.be.equal(9000n); + + // Release Remainder from NFT + await chargedParticles.releaseParticle( + deployer, + nftMockAddress, + tokenId, + erc20MockAddress, + ).then(tx => tx.wait()); + expect(await erc20Mock.balanceOf(newAccountAddress)).to.be.equal(0); + expect(await erc20Mock.balanceOf(deployer)).to.be.equal(10000n); + }); + + + it('Bonds and Breaks an NFT', async() => { + const tokenId = 1; + const depositedTokenId = 2; + + await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); + await nftMock.mint(deployer, depositedTokenId).then(tx => tx.wait()); + expect(await nftMock.balanceOf(deployer)).to.be.equal(2); + + // Calculate Expected Account Address via Registry + const newAccountAddress = await calculateAccountAddress(nftMockAddress, tokenId); + expect(newAccountAddress).to.not.be.empty; + + // Give permission to Bond + await nftMock.approve(chargedParticlesAddress, depositedTokenId).then(tx => tx.wait()); + expect(await nftMock.getApproved(depositedTokenId)).to.be.eq(chargedParticlesAddress); + + // Bond + const bondReceipt = await chargedParticles.covalentBond( + nftMockAddress, + tokenId, + nftMockAddress, + depositedTokenId, + 1n // amount + ).then(tx => tx.wait()); + expect(bondReceipt).to.haveOwnProperty('hash'); + + // Confirm Nested NFT Owner + expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(newAccountAddress); + + // Break-Bond + const breakReceipt = await chargedParticles.breakCovalentBond( + receiver, + nftMockAddress, + tokenId, + nftMockAddress, + depositedTokenId, + 1 + ).then(tx => tx.wait()); + expect(breakReceipt).to.haveOwnProperty('hash'); + + // Confirm New Owner + expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(receiver); + }); +}); diff --git a/test/ChargedParticlesAccount.test.ts b/test/ChargedParticlesAccount.test.ts deleted file mode 100644 index bf9b6f5..0000000 --- a/test/ChargedParticlesAccount.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { expect } from "chai"; -import { ethers, network, getNamedAccounts, deployments } from 'hardhat'; -import { NFTMock, ChargedParticlesAccount, IRegistry, ERC20Mock } from "../typechain-types"; - - -describe('Account', async function () { - const REGISTRY = "0x02101dfB77FDE026414827Fdc604ddAF224F0921"; - - // Contracts - let chargedParticlesAccount: ChargedParticlesAccount, nftMock: NFTMock, registryContract: IRegistry; - let erc20Mock: ERC20Mock; - - // Addresses - let nftMockAddress: string, chargedParticlesAccountAddress: string, erc20MockAddress: string; - // Signers - let deployer: string, receiver: string; - - before(async function () { - const { deployer: deployerAccount, user1 } = await getNamedAccounts(); - deployer = deployerAccount; - receiver = user1; - }); - - beforeEach(async function () { - await deployments.fixture([ 'ChargedParticlesAccount', 'NFTMock', 'ERC20Mock' ]); - - chargedParticlesAccount = await ethers.getContract('ChargedParticlesAccount'); - nftMock = await ethers.getContract('NFTMock'); - erc20Mock = await ethers.getContract('ERC20Mock'); - - registryContract = await ethers.getContractAt( - 'IRegistry', - REGISTRY - ); - - nftMockAddress = await nftMock.getAddress(); - erc20MockAddress = await erc20Mock.getAddress(); - chargedParticlesAccountAddress = await chargedParticlesAccount.getAddress(); - }); - - it('Deploys ChargedParticlesAccount', async function () { - const chargedParticlesAccountAddress = await chargedParticlesAccount.getAddress(); - expect(chargedParticlesAccountAddress).to.not.be.empty - }); - - it('Deploys account for NFT', async function () { - const tokenId = 1; - - await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); - expect(await nftMock.balanceOf(deployer)).to.be.equal(1); - - const newAccountAddress = await registryContract.account( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0 - ); - expect(newAccountAddress).to.not.be.empty; - - const newAccountReceipt = await registryContract.createAccount( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0, - '0x' - ).then(tx => tx.wait()); - - expect(newAccountReceipt).to.haveOwnProperty('hash'); - - const chargedParticlesAccountContract = chargedParticlesAccount.attach(newAccountAddress) as ChargedParticlesAccount; - const chargedParticlesDataFromTBA = await chargedParticlesAccountContract.token(); - - expect(chargedParticlesDataFromTBA).to.be.lengthOf(3); - expect(chargedParticlesDataFromTBA[1]).to.be.equal(nftMockAddress); - }); - - it('Bonds and breaks a NFT', async() => { - const tokenId = 1; - const depositedTokenId = 2; - await nftMock.mint(deployer, depositedTokenId).then(tx => tx.wait()); - - const newAccountAddress = await deployRegistryAccount(tokenId); - - // Give permission - await nftMock.approve(newAccountAddress, depositedTokenId).then(tx => tx.wait()); - expect(await nftMock.getApproved(depositedTokenId)).to.be.eq(newAccountAddress); - - // Bond - const account = await ethers.getContractAt('ChargedParticlesAccount', newAccountAddress); - - await account.covalentBond( - nftMockAddress, - depositedTokenId, - 1 // amount - ).then(tx => tx.wait()); - - // Check owner - expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(newAccountAddress); - - await account.breakCovalentBond( - receiver, - nftMockAddress, - depositedTokenId, - 1 - ).then(tx => tx.wait()); - - expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(receiver); - }); - - it('Energize and discharge', async() => { - const tokenId = 1; - const newAccountAddress = await deployRegistryAccount(tokenId); - - const mintAmount = ethers.parseEther('100'); - await erc20Mock.mint(deployer, mintAmount).then(tx => tx.wait()); - - expect(await erc20Mock.balanceOf(deployer)).to.be.eq(mintAmount); - - // energize - // 1. approve account to manipulate tokens from deployer account - await erc20Mock.approve(newAccountAddress, mintAmount).then(tx => tx.wait()); - - // 2. transfer to account - const account = await ethers.getContractAt('ChargedParticlesAccount', newAccountAddress); - await account.energizeParticle(erc20MockAddress, mintAmount).then(tx => tx.wait()); - - expect(await erc20Mock.balanceOf(newAccountAddress)).to.be.eq(mintAmount); - expect(await erc20Mock.balanceOf(deployer)).to.be.eq(0); - - await account.dischargeParticle(receiver, erc20MockAddress, mintAmount).then(tx => tx.wait()); - - expect(await erc20Mock.balanceOf(newAccountAddress)).to.be.eq(0); - expect(await erc20Mock.balanceOf(receiver)).to.be.eq(mintAmount); - }); - - it('Returns the first four bytes', async() => { - const calldata = "0xa9059cbb00000000000000000000000003828b7129d49313b2cdc966e50369b75ec79a4800000000000000000000000000000000000000000000000000000008a22b974b"; - const calldataFourBytes = await chargedParticlesAccount.parseFirst4Bytes(calldata) - expect(calldataFourBytes).to.be.eq('0xa9059cbb'); - }); - - const deployRegistryAccount = async(tokenId: number) => { - await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); - - // Create an account - const newAccountAddress = await registryContract.account( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0 - ); - - await registryContract.createAccount( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0, - '0x' - ).then(tx => tx.wait()); - - return newAccountAddress; - } -}); diff --git a/test/ERC721All.test.ts b/test/ERC721All.test.ts index 3a5c6f8..bef6d44 100644 --- a/test/ERC721All.test.ts +++ b/test/ERC721All.test.ts @@ -3,7 +3,7 @@ import { ethers, network, getNamedAccounts, deployments } from 'hardhat'; import { ERC721All } from "../typechain-types"; -describe('Execute calls', async function () { +describe('ERC721All', async function () { // Contracts let NFT: ERC721All; // Addresses diff --git a/test/ERC721i.test.ts b/test/ERC721i.test.ts index e0f4c7b..31680ac 100644 --- a/test/ERC721i.test.ts +++ b/test/ERC721i.test.ts @@ -3,7 +3,7 @@ import { ethers, getNamedAccounts, deployments } from 'hardhat'; import { ERC721i } from "../typechain-types"; -describe('Execute calls', async function () { +describe('ERC721i', async function () { // Contracts let NFT: ERC721i; // Addresses diff --git a/test/ExecueteCall.test.ts b/test/ExecueteCall.test.ts deleted file mode 100644 index 3921f6d..0000000 --- a/test/ExecueteCall.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { expect } from "chai"; -import { ethers, network, getNamedAccounts, deployments } from 'hardhat'; -import { NFTMock, Account, IRegistry } from "../typechain-types"; - - -describe('Execute calls', async function () { - const REGISTRY = "0x02101dfB77FDE026414827Fdc604ddAF224F0921"; - - // Contracts - let chargedParticlesAccount: Account, nftMock: NFTMock, registryContract: IRegistry; - // Addresses - let nftMockAddress: string, chargedParticlesAccountAddress: string; - // Signers - let deployer: string, receiver: string; - - before(async function () { - const { deployer: deployerAccount, user1 } = await getNamedAccounts(); - deployer = deployerAccount; - receiver = user1; - }); - - beforeEach(async function () { - await deployments.fixture([ 'ChargedParticlesAccount', 'NFTMock' ]); - - chargedParticlesAccount = await ethers.getContract('ChargedParticlesAccount'); - nftMock = await ethers.getContract('NFTMock'); - registryContract = await ethers.getContractAt( - 'IRegistry', - REGISTRY - ); - - nftMockAddress = await nftMock.getAddress(); - chargedParticlesAccountAddress = await chargedParticlesAccount.getAddress(); - }); - - it('Deploys ChargedParticlesAccount', async function () { - const chargedParticlesAccountAddress = await chargedParticlesAccount.getAddress(); - expect(chargedParticlesAccountAddress).to.not.be.empty - }); - - it('Deploys account for NFT', async function () { - const tokenId = 1; - - await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); - expect(await nftMock.balanceOf(deployer)).to.be.equal(1); - - const newAccountAddress = await registryContract.account( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0 - ); - expect(newAccountAddress).to.not.be.empty; - - const newAccountReceipt = await registryContract.createAccount( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0, - '0x' - ).then(tx => tx.wait()); - - expect(newAccountReceipt).to.haveOwnProperty('hash'); - - const chargedParticlesAccountContract = chargedParticlesAccount.attach(newAccountAddress) as Account; - const chargedParticlesDataFromTBA = await chargedParticlesAccountContract.token(); - - expect(chargedParticlesDataFromTBA).to.be.lengthOf(3); - expect(chargedParticlesDataFromTBA[1]).to.be.equal(nftMockAddress); - }); - - it('Bonds and breaks a NFT', async() => { - const tokenId = 1; - const depositedTokenId = 2; - await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); - await nftMock.mint(deployer, depositedTokenId).then(tx => tx.wait()); - - // Create an account - const newAccountAddress = await registryContract.account( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0 - ); - - await registryContract.createAccount( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0, - '0x' - ).then(tx => tx.wait()); - - // Give permission - await nftMock.approve(newAccountAddress, depositedTokenId).then(tx => tx.wait()); - expect(await nftMock.getApproved(depositedTokenId)).to.be.eq(newAccountAddress); - - await nftMock.transferFrom(deployer, newAccountAddress, depositedTokenId).then(tx => tx.wait()); - - // Check owner - expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(newAccountAddress); - - const account = await ethers.getContractAt('ChargedParticlesAccount', newAccountAddress); - - const breakCovalentBond = (from: string, to:string, tokenId:number) => { - const ABI = ["function safeTransferFrom(address from, address to, uint256 tokenId)"]; - const iface = new ethers.Interface(ABI); - const cdata = iface.encodeFunctionData("safeTransferFrom", [from, to, tokenId]); - - return cdata; - }; - - const breakCovalentBondCallData = breakCovalentBond(newAccountAddress, receiver, depositedTokenId); - - await account.executeCall( - nftMockAddress, - 0, - breakCovalentBondCallData - ).then(tx => tx.wait()); - - expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(receiver); - }); - - it('Filters out approve calls', async() => { - const approveCall = (to:string, tokenId:number) => { - const ABI = ["function approve(address,uint256)"]; - const iface = new ethers.Interface(ABI); - const cdata = iface.encodeFunctionData("approve", [to, tokenId]); - - return cdata; - }; - - const tokenId = 1; - const depositedTokenId = 2; - await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); - await nftMock.mint(deployer, depositedTokenId).then(tx => tx.wait()); - - // Create an account - const newAccountAddress = await registryContract.account( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0 - ); - - await registryContract.createAccount( - chargedParticlesAccountAddress, - network.config.chainId ?? 137, - nftMockAddress, - tokenId, - 0, - '0x' - ).then(tx => tx.wait()); - - const account = await ethers.getContractAt('ChargedParticlesAccount', newAccountAddress); - - const approveCallData = approveCall('0x277BFc4a8dc79a9F194AD4a83468484046FAFD3A', depositedTokenId); - - await expect(account.executeCall( - nftMockAddress, - 0, - approveCallData - - )).revertedWith('Method all not allowed'); - }); - -}); diff --git a/test/ExecuteCall.test.ts b/test/ExecuteCall.test.ts new file mode 100644 index 0000000..4206d4a --- /dev/null +++ b/test/ExecuteCall.test.ts @@ -0,0 +1,172 @@ +import { expect } from "chai"; +import { ethers, network, getNamedAccounts, deployments } from 'hardhat'; +import { NFTMock, ChargedParticles, IERC6551Registry } from "../typechain-types"; + + +describe('Execute calls', async function () { + const REGISTRY = "0x000000006551c19487814612e58FE06813775758"; + + // Contracts + let chargedParticles: ChargedParticles, nftMock: NFTMock, registryContract: IERC6551Registry; + // Addresses + let nftMockAddress: string, chargedParticlesAddress: string; + // Signers + let deployer: string, receiver: string; + + const salt = ethers.encodeBytes32String('0') + + // before(async function () { + // const { deployer: deployerAccount, user1 } = await getNamedAccounts(); + // deployer = deployerAccount; + // receiver = user1; + // }); + + // beforeEach(async function () { + // await deployments.fixture([ 'ChargedParticles', 'NFTMock' ]); + + // chargedParticles = await ethers.getContract('ChargedParticles'); + // nftMock = await ethers.getContract('NFTMock'); + // registryContract = await ethers.getContractAt( + // 'IERC6551Registry', + // REGISTRY + // ); + + // nftMockAddress = await nftMock.getAddress(); + // chargedParticlesAddress = await chargedParticles.getAddress(); + // }); + + // it('Deploys ChargedParticles', async function () { + // const chargedParticlesAddress = await chargedParticles.getAddress(); + // expect(chargedParticlesAddress).to.not.be.empty + // }); + + // it('Deploys account for NFT', async function () { + // const tokenId = 1; + + // await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); + // expect(await nftMock.balanceOf(deployer)).to.be.equal(1); + + // const newAccountAddress = await registryContract.account( + // chargedParticlesAddress, + // salt, + // network.config.chainId ?? 137, + // nftMockAddress, + // tokenId, + // ); + // expect(newAccountAddress).to.not.be.empty; + + // const newAccountReceipt = await registryContract.createAccount( + // chargedParticlesAddress, + // salt, + // network.config.chainId ?? 137, + // nftMockAddress, + // tokenId, + // ).then(tx => tx.wait()); + + // expect(newAccountReceipt).to.haveOwnProperty('hash'); + + // const chargedParticlesContract = chargedParticles.attach(newAccountAddress) as Account; + // const chargedParticlesDataFromTBA = await chargedParticlesContract.token(); + + // expect(chargedParticlesDataFromTBA).to.be.lengthOf(3); + // expect(chargedParticlesDataFromTBA[1]).to.be.equal(nftMockAddress); + // }); + + // it('Bonds and breaks a NFT', async() => { + // const tokenId = 1; + // const depositedTokenId = 2; + // await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); + // await nftMock.mint(deployer, depositedTokenId).then(tx => tx.wait()); + + // // Create an account + // const newAccountAddress = await registryContract.account( + // chargedParticlesAddress, + // salt, + // network.config.chainId ?? 137, + // nftMockAddress, + // tokenId, + // ); + + // await registryContract.createAccount( + // chargedParticlesAddress, + // salt, + // network.config.chainId ?? 137, + // nftMockAddress, + // tokenId, + // ).then(tx => tx.wait()); + + // // Give permission + // await nftMock.approve(newAccountAddress, depositedTokenId).then(tx => tx.wait()); + // expect(await nftMock.getApproved(depositedTokenId)).to.be.eq(newAccountAddress); + + // await nftMock.transferFrom(deployer, newAccountAddress, depositedTokenId).then(tx => tx.wait()); + + // // Check owner + // expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(newAccountAddress); + + // const account = await ethers.getContractAt('ChargedParticles', newAccountAddress); + + // const breakCovalentBond = (from: string, to:string, tokenId:number) => { + // const ABI = ["function safeTransferFrom(address from, address to, uint256 tokenId)"]; + // const iface = new ethers.Interface(ABI); + // const cdata = iface.encodeFunctionData("safeTransferFrom", [from, to, tokenId]); + + // return cdata; + // }; + + // const breakCovalentBondCallData = breakCovalentBond(newAccountAddress, receiver, depositedTokenId); + + // await account.execute( + // nftMockAddress, + // 0, + // breakCovalentBondCallData, + // 0, + // ).then(tx => tx.wait()); + + // expect(await nftMock.ownerOf(depositedTokenId)).to.be.eq(receiver); + // }); + + // it('Filters out approve calls', async() => { + // const approveCall = (to:string, tokenId:number) => { + // const ABI = ["function approve(address,uint256)"]; + // const iface = new ethers.Interface(ABI); + // const cdata = iface.encodeFunctionData("approve", [to, tokenId]); + + // return cdata; + // }; + + // const tokenId = 1; + // const depositedTokenId = 2; + // await nftMock.mint(deployer, tokenId).then(tx => tx.wait()); + // await nftMock.mint(deployer, depositedTokenId).then(tx => tx.wait()); + + // // Create an account + // const newAccountAddress = await registryContract.account( + // chargedParticlesAddress, + // salt, + // network.config.chainId ?? 137, + // nftMockAddress, + // tokenId, + // ); + + // await registryContract.createAccount( + // chargedParticlesAddress, + // salt, + // network.config.chainId ?? 137, + // nftMockAddress, + // tokenId, + // ).then(tx => tx.wait()); + + // const account = await ethers.getContractAt('ChargedParticles', newAccountAddress); + + // const approveCallData = approveCall('0x277BFc4a8dc79a9F194AD4a83468484046FAFD3A', depositedTokenId); + + // await expect(account.execute( + // nftMockAddress, + // 0, + // approveCallData, + // 0 + // )).revertedWith('Method all not allowed'); + // }); + +}); diff --git a/utils/isTestnet.ts b/utils/isTestnet.ts new file mode 100644 index 0000000..65780ac --- /dev/null +++ b/utils/isTestnet.ts @@ -0,0 +1,9 @@ +import { network } from 'hardhat'; + +export const isTestnet = () => { + const chainId = network.config.chainId ?? 1; + if (chainId === 1 || chainId === 137) { + return false; + } + return true; +}; diff --git a/utils/performTx.ts b/utils/performTx.ts new file mode 100644 index 0000000..4c563f5 --- /dev/null +++ b/utils/performTx.ts @@ -0,0 +1,19 @@ +import { ContractTransactionReceipt, ContractTransactionResponse, EventLog, Log } from 'ethers'; + +export const performTx = async (tx: ContractTransactionResponse, msg: string) => { + const rc: ContractTransactionReceipt | null = await tx.wait(); + if (rc !== null) { + + if (rc?.logs?.length > 0 && msg.includes('{id}')) { + const evt: EventLog | Log = rc.logs[0]; + // @ts-ignore + if (evt?.args?.length == 3) { + // @ts-ignore + const data = evt.args[2]; + msg = msg.replace(/\{id}/ig, data); + } + } + + // console.log(msg); + } +}; diff --git a/yarn.lock b/yarn.lock index 147af1b..3eb25e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,7 +50,7 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -984,6 +984,11 @@ abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: module-error "^1.0.1" queue-microtask "^1.2.3" +acc-erc20@0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/acc-erc20/-/acc-erc20-0.5.7.tgz#1e356ef2402cf6bf74b6e7366dfdf5396c33b612" + integrity sha512-D/zg0AbXKHBgH+yjHYZDvkAQyx8RR3F6/6ScjeVhJw8O9EI2M8nCNPlfDKuZxqk3inlfQOTecOayZlLkLVqr/g== + acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" @@ -1064,7 +1069,7 @@ ansi-colors@4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-colors@^4.1.1: +ansi-colors@^4.1.0, ansi-colors@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== @@ -1771,6 +1776,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +delete-empty@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/delete-empty/-/delete-empty-3.0.0.tgz#f8040f2669f26fa7060bc2304e9859c593b685e8" + integrity sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ== + dependencies: + ansi-colors "^4.1.0" + minimist "^1.2.0" + path-starts-with "^2.0.0" + rimraf "^2.6.2" + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -1866,6 +1881,13 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +erc20permit@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/erc20permit/-/erc20permit-0.1.1.tgz#0823f51f37fc04310344c4cc59fa40352fea537c" + integrity sha512-Lhtaqemp38bb8ttOn6xpnelMgjamrSZLuV6vSMx11ZKkkMl3PjqlvtwG5cMSG5KxdiEmkeS9b4rVozc2k7DFzA== + dependencies: + acc-erc20 "0.5.7" + es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" @@ -2603,6 +2625,14 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hardhat-abi-exporter@^2.10.1: + version "2.10.1" + resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.10.1.tgz#b14884e233c73fe3f43360f014ad7fd6df4b6d25" + integrity sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ== + dependencies: + "@ethersproject/abi" "^5.5.0" + delete-empty "^3.0.0" + hardhat-deploy-ethers@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/hardhat-deploy-ethers/-/hardhat-deploy-ethers-0.4.1.tgz#dd70b0cc413ed99e98994047b383a004cf1c14f8" @@ -3433,7 +3463,7 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -3814,6 +3844,11 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-starts-with@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-2.0.1.tgz#cd8b6213c141a9f2dd86c748310acdfa6493abb1" + integrity sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -4082,7 +4117,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.2.8: +rimraf@^2.2.8, rimraf@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==