Skip to content

Commit

Permalink
Merge branch 'main' of github.com:greenpill-dev-guild/green-goods int…
Browse files Browse the repository at this point in the history
…o contracts/attestations
  • Loading branch information
Oba-One committed Aug 18, 2024
2 parents 34bfbf5 + d579329 commit e8a4be0
Show file tree
Hide file tree
Showing 12 changed files with 638 additions and 295 deletions.
1 change: 1 addition & 0 deletions packages/contracts/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export API_KEY_ALCHEMY="YOUR_API_KEY_ALCHEMY"
export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN"
export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN"
export API_KEY_INFURA="YOUR_API_KEY_INFURA"
export PRIVATE_KEY="YOUR_MNEMONIC"
export FOUNDRY_PROFILE="default"
10 changes: 6 additions & 4 deletions packages/contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test = 'test'
verbosity = 4

[etherscan]
sepolia = { key = "${API_KEY_ETHERSCAN}"}
arbitrum = { key = "${API_KEY_ARBISCAN}" }

[fmt]
Expand All @@ -37,13 +38,14 @@ extra_output_files = [
"abi",
"evm.bytecode"
]

fs_permissions = [{ access = "read", path = "./"}]
eth_rpc_url = "http://localhost:8545"

[profile.arbitrum]
eth_rpc_url = "https://arb1.arbitrum.io/rpc"
[profile.sepolia]
eth_rpc_url = "https://arb-mainnet.g.alchemy.com/v2/i2qnBKk5GQ8pVGPLA-G3D9il5o0ULQO3"

[profile.arbitrum-sepolia]
eth_rpc_url = "https://sepolia-rollup.arbitrum.io/rpc"
[profile.arbitrum]
eth_rpc_url = "https://arb-mainnet.g.alchemy.com/v2/i2qnBKk5GQ8pVGPLA-G3D9il5o0ULQO3"

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
3 changes: 1 addition & 2 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
},
"dependencies": {
"@ethereum-attestation-service/eas-contracts": "1.7.1",
"@openzeppelin/contracts": "4.8.3",
"@openzeppelin/contracts-upgradeable": "4.8.3"
"@openzeppelin/contracts-upgradeable": "4.9.6"
},
"devDependencies": {
"@types/prettier": "2",
Expand Down
3 changes: 2 additions & 1 deletion packages/contracts/remappings.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ds-test=./node_modules/ds-test/src/
forge-std=./node_modules/forge-std/src/
@openzeppelin/contracts=./node_modules/@openzeppelin/contracts/
@openzeppelin/contracts=lib/tokenbound/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable=./node_modules/@openzeppelin/contracts-upgradeable/
@eas=./node_modules/@ethereum-attestation-service/eas-contracts/contracts/
@tokenbound=./lib/tokenbound/src/
erc6551/=lib/tokenbound/lib/erc6551/src/
130 changes: 130 additions & 0 deletions packages/contracts/script/DeployGardenAccount.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: UNLICENSED
/* solhint-disable max-line-length */

pragma solidity ^0.8.25;

import { Script, console } from "forge-std/Script.sol";
import { AccountProxy } from "@tokenbound/AccountProxy.sol";
import { AccountGuardian } from "@tokenbound/AccountGuardian.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";

import { GardenToken } from "../src/tokens/Garden.sol";
import { GardenAccount } from "../src/accounts/Garden.sol";
import { TOKENBOUND_REGISTRY } from "../src/Constants.sol";

contract Deploy is Script {
function run() external {
bytes32 salt = 0x6551655165516551655165516551655165516551655165516551655165516551;
address factory = 0x4e59b44847b379578588920cA78FbF26c0B4956C;

address tokenboundSafe = 0x1B9Ac97Ea62f69521A14cbe6F45eb24aD6612C19; // ToDo: Deploy with same address on Sepolia
address erc4337EntryPoint = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789;
address multicallForwarder = 0xcA11bde05977b3631167028862bE2a173976CA11;

address guardian = Create2.computeAddress(
salt,
keccak256(
abi.encodePacked(type(AccountGuardian).creationCode, abi.encode(tokenboundSafe))
),
factory
);
address implementation = Create2.computeAddress(
salt,
keccak256(
abi.encodePacked(
type(GardenAccount).creationCode,
abi.encode(erc4337EntryPoint, multicallForwarder, TOKENBOUND_REGISTRY, guardian)
)
),
factory
);
address proxy = Create2.computeAddress(
salt,
keccak256(
abi.encodePacked(
type(AccountProxy).creationCode, abi.encode(guardian, implementation)
)
),
factory
);

// Deploy AccountGuardian
if (guardian.code.length == 0) {
vm.startBroadcast();
new AccountGuardian{salt: salt}(tokenboundSafe);
vm.stopBroadcast();

console.log("AccountGuardian:", guardian, "(deployed)");
} else {
console.log("AccountGuardian:", guardian, "(exists)");
}

// Deploy Account implementation
if (implementation.code.length == 0) {
vm.startBroadcast();
new GardenAccount{salt: salt}(
erc4337EntryPoint,
multicallForwarder,
TOKENBOUND_REGISTRY,
guardian
);
vm.stopBroadcast();

console.log("GardenAccount:", implementation, "(deployed)");
} else {
console.log("GardenAccount:", implementation, "(exists)");
}

// Deploy AccountProxy
if (proxy.code.length == 0) {
vm.startBroadcast();
new AccountProxy{salt: salt}(guardian, implementation);
vm.stopBroadcast();

console.log("AccountProxy:", proxy, "(deployed)");
} else {
console.log("AccountProxy:", proxy, "(exists)");
}

console.log("\nVerification Commands:\n");
console.log(
"AccountGuardian: forge verify-contract --num-of-optimizations 200 --chain-id",
block.chainid,
guardian,
string.concat(
"src/AccountGuardian.sol:AccountGuardian --constructor-args $(cast abi-encode \"constructor(address)\" ",
Strings.toHexString(tokenboundSafe),
")\n"
)
);
console.log(
"GardenAccount: forge verify-contract --num-of-optimizations 200 --chain-id",
block.chainid,
implementation,
string.concat(
"src/GardenAccount.sol:GardenAccount --constructor-args $(cast abi-encode \"constructor(address,address,address,address)\" ",
Strings.toHexString(erc4337EntryPoint),
" ",
Strings.toHexString(multicallForwarder),
" ",
Strings.toHexString(TOKENBOUND_REGISTRY),
" ",
Strings.toHexString(guardian),
")\n"
)
);
console.log(
"AccountProxy: forge verify-contract --num-of-optimizations 200 --chain-id",
block.chainid,
proxy,
string.concat(
"src/AccountProxy.sol:AccountProxy --constructor-args $(cast abi-encode \"constructor(address,address)\" ",
Strings.toHexString(guardian),
" ",
Strings.toHexString(implementation),
")\n"
)
);
}
}
4 changes: 4 additions & 0 deletions packages/contracts/src/Constants.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

// TOKENBOUND (FUTURE PRIMTIVE)
address constant TOKENBOUND_REGISTRY = 0x002c0c13181038780F552f0eC1B72e8C720147E6; // Same address on all EVM chains
address constant TOKENBOUND_ACCOUNT = 0x9FFDEb36540e1a12b1F27751508715174122C090; // Same address on all EVM chains

// EAS (ETHEREUM ATTESTATION SERVICE)
address constant EAS_ARB = 0xbD75f629A22Dc1ceD33dDA0b68c546A1c035c458;
address constant EAS_SEPOLIA = 0xC2679fBD37d54388Ce493F1DB75320D236e1815e;
Expand Down
81 changes: 81 additions & 0 deletions packages/contracts/src/accounts/Garden.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.25;

import { AccountV3Upgradable } from "@tokenbound/AccountV3Upgradable.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";

error NotGardenOwner();
error TransferNotStarted();
error NotGoodTransferResolver();

contract GardenAccount is AccountV3Upgradable, Initializable {
address public communityToken;
string public name;

mapping(address gardener => bool isGardener) private gardeners;
mapping(address operator => bool isOperator) private gardenOperators;

constructor(
address erc4337EntryPoint,
address multicallForwarder,
address erc6551Registry,
address guardian
) AccountV3Upgradable(erc4337EntryPoint, multicallForwarder, erc6551Registry, guardian) {}

function initialize(
address _communityToken,
string calldata _name,
address[] calldata _gardeners,
address[] calldata _gardenOperators) external initializer {
communityToken = _communityToken;
name = _name;

for (uint256 i = 0; i < _gardeners.length; i++) {
gardeners[_gardeners[i]] = true;
}

for (uint256 i = 0; i < _gardenOperators.length; i++) {
gardenOperators[_gardenOperators[i]] = true;
}
}

function updateName(string memory _name) external {
if (_isValidSigner(msg.sender, "")) {
revert NotGardenOwner();
}

name = _name;
}

function addGardener(address gardener) external {
if (_isValidSigner(msg.sender, "")) {
revert NotGardenOwner();
}

gardeners[gardener] = true;
}

function removeGardener(address gardener) external {
if (_isValidSigner(msg.sender, "")) {
revert NotGardenOwner();
}

gardeners[gardener] = false;
}

function addGardenOperator(address operator) external {
if (_isValidSigner(msg.sender, "")) {
revert NotGardenOwner();
}

gardenOperators[operator] = true;
}

function removeGardenOperator(address operator) external {
if (_isValidSigner(msg.sender, "")) {
revert NotGardenOwner();
}

gardenOperators[operator] = false;
}
}
30 changes: 30 additions & 0 deletions packages/contracts/src/interfaces/IERC6551Registry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

interface IERC6551Registry {
event AccountCreated(
address account,
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
);

function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 seed,
bytes calldata initData
) external returns (address);

function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address);
}
65 changes: 65 additions & 0 deletions packages/contracts/src/lib/TBA.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {TOKENBOUND_REGISTRY} from "../Constants.sol";
import {IERC6551Registry} from "../interfaces/IERC6551Registry.sol";

error InvalidChainId();

library TBALib {
function createAccount(address implmentation, address tokenContract, uint256 tokenId) external returns (address) {
address account;

if (block.chainid == 42161) {
account = IERC6551Registry(TOKENBOUND_REGISTRY).createAccount(
implmentation,
42161,
tokenContract,
tokenId,
7,
""
);

} else if (block.chainid == 11155111) {
account = IERC6551Registry(TOKENBOUND_REGISTRY).createAccount(
implmentation,
11155111,
tokenContract,
tokenId,
7,
""
);
} else {
revert InvalidChainId();
}

return account;
}

function getAccount(address implmentation, address tokenContract, uint256 tokenId) external view returns (address) {
address account;

if (block.chainid == 42161) {
account = IERC6551Registry(TOKENBOUND_REGISTRY).account(
implmentation,
42161,
tokenContract,
tokenId,
7
);

} else if (block.chainid == 11155111) {
account = IERC6551Registry(TOKENBOUND_REGISTRY).account(
implmentation,
11155111,
tokenContract,
tokenId,
7
);
} else {
revert InvalidChainId();
}

return account;
}
}
Loading

0 comments on commit e8a4be0

Please sign in to comment.