Skip to content

Instantly share code, notes, and snippets.

@AndreiMVP
Last active March 10, 2023 17:55
Show Gist options
  • Save AndreiMVP/63fe2376400ebb89ece633646aa8c625 to your computer and use it in GitHub Desktop.
Save AndreiMVP/63fe2376400ebb89ece633646aa8c625 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
interface IProofOfHumanity {
function isHuman(address _account) external view returns (bool);
function isClaimed(bytes20 _humanityId) external view returns (bool);
function humanityOf(address _account) external view returns (bytes20);
function boundTo(bytes20 _humanityId) external view returns (address);
}
interface ERC20 {
function transfer(address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success);
}
interface IGCT is ERC20 {
function addDelegatedTrustee(address _member) external;
function addMemberToken(address _member) external;
function removeMemberToken(address _member) external;
function mintDelegate(
address _trustedBy,
address _collateral,
uint256 _amount
) external returns (uint256);
}
interface Token is ERC20 {
function owner() external view returns (address);
}
interface IHub {
function organizationSignup() external;
function userToToken(address) external returns (address);
function tokenToUser(address) external returns (address);
function trust(address, uint256) external;
function transferThrough(
address[] memory tokenOwners,
address[] memory srcs,
address[] memory dests,
uint256[] memory wads
) external;
}
/** @title PoHGroupCirclesCurrencyManager
* @dev Is GCT Owner
* @dev Is GCT treasury
* @dev Is Organization in hub?
*/
contract PoHGroupCirclesCurrencyManager {
// ========== STORAGE ==========
address public governor;
IProofOfHumanity public poh;
IGCT public gct;
IHub public hub;
mapping(bytes20 => address) public humanityIdToToken;
mapping(address => bytes20) public tokenToHumanityId;
mapping(address => bool) public removedMember;
// ========== CONSTRUCTOR ==========
constructor(
address _poh,
address _gct,
address _hub
) {
governor = msg.sender;
poh = IProofOfHumanity(_poh);
gct = IGCT(_gct);
hub = IHub(_hub);
}
// ========== ACCESS ==========
modifier onlyGovernor() {
require(msg.sender == governor, "!governor");
_;
}
// ========== GOVERNANCE ==========
function changeGovernor(address _governor) external onlyGovernor {
governor = _governor;
}
function changePoH(address _poh) external onlyGovernor {
poh = IProofOfHumanity(_poh);
}
function changeGCT(address _gct) external onlyGovernor {
gct = IGCT(_gct);
}
function changeHub(address _hub) external onlyGovernor {
hub = IHub(_hub);
}
function setup() external onlyGovernor {
hub.organizationSignup();
gct.addDelegatedTrustee(address(this));
}
// Trust must be called by this contract (as a delegate) on Hub
function trust(address _trustee) external onlyGovernor {
hub.trust(_trustee, 100);
}
function executeGovernorTx(address _destination, bytes memory _data) external onlyGovernor {
(bool success, ) = _destination.call(_data);
require(success, "!call");
}
// ========== FUNCTIONS ==========
/**
* In order to add member to group 2 calls are needed, in order:
* - first from wallet owning the token `confirmToken` confirming the humanityId
* - second from wallet registered on PoH `confirmHuman` confirming the token
*
* Once humanityIdToToken[humanityId] == tokenToHumanityId[token],
* member is added to group. Token to humanity correspondence
* can't be changed anymore.
*/
function confirmToken(bytes20 _humanityId) external {
address token = address(hub.userToToken(msg.sender));
require(token != address(0x0), "!hub");
require(humanityIdToToken[_humanityId] == address(0x0), "member!");
tokenToHumanityId[token] = _humanityId;
}
function confirmHuman(address _token) external {
bytes20 humanityId = poh.humanityOf(msg.sender);
require(humanityId != bytes20(0x0), "!poh");
require(tokenToHumanityId[_token] == humanityId, "!token");
humanityIdToToken[humanityId] = _token;
gct.addMemberToken(_token);
}
/**
* Remove member from group. Member can be added back with `addMemberBack`.
* Can be removed by the owner of the pohId or by anyone if the pohId is
* not bound anyomre to any address.
* It is marked as removed in
*/
function removeMember(bytes20 _humanityId) external {
address pohAccount = poh.boundTo(_humanityId);
if (pohAccount != address(0x0)) require(pohAccount == msg.sender, "!remove");
address member = humanityIdToToken[_humanityId];
removedMember[member] = true;
gct.removeMemberToken(member);
}
function addMemberBack() external {
bytes20 humanityId = poh.humanityOf(msg.sender);
require(humanityId != bytes20(0x0), "!poh");
require(removedMember[humanityIdToToken[humanityId]], "!removed");
address member = humanityIdToToken[humanityId];
removedMember[member] = false;
gct.addMemberToken(member);
}
/**
* Minting is done is gct contract.
* Burning in order to redeem other tokens is done here.
*/
function redeem(address _collateral, uint256 _wad) external {
_redeem(msg.sender, _collateral, _wad);
}
function redeem(address _redeemer, address _collateral, uint256 _wad) external {
_redeem(_redeemer, _collateral, _wad);
}
function redeemMany(address _redeemer, address[] memory _collateral, uint256[] memory _wads) external {
for (uint256 i = 0; i < _collateral.length; i++) {
_redeem(_redeemer, _collateral[i], _wads[i]);
}
}
function _redeem(
address _redeemer,
address _collateral,
uint256 _wad
) internal {
bytes20 humanityId = tokenToHumanityId[_collateral];
require(poh.isClaimed(humanityId), "!poh");
require(gct.transferFrom(_redeemer, address(0x0), _wad), "!burn");
Token(_collateral).transfer(_redeemer, _wad);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment