diff --git a/LICENSE b/LICENSE index 67471c4..9fd406f 100644 --- a/LICENSE +++ b/LICENSE @@ -10,7 +10,7 @@ Parameters Licensor: Angle Labs, Inc. Licensed Work: Merkl Smart Contracts -The Licensed Work is (c) 2024 Angle Labs, Inc. +The Licensed Work is (c) 2025 Angle Labs, Inc. Additional Use Grant: Any uses listed and defined at merkl-license-grants.angle-labs.eth diff --git a/README.md b/README.md index ede2730..083ee92 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,10 @@ function run() external broadcast { The Merkl smart contracts have been audited by Code4rena, find the audit report [here](https://code4rena.com/reports/2023-06-angle). +## Access Control + + + ## Media Don't hesitate to reach out on [Twitter](https://x.com/merkl_xyz) 🐦 diff --git a/contracts/partners/tokenWrappers/PointToken.sol b/contracts/partners/tokenWrappers/PointToken.sol new file mode 100644 index 0000000..7d2005a --- /dev/null +++ b/contracts/partners/tokenWrappers/PointToken.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.7; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IAccessControlManager } from "../../interfaces/IAccessControlManager.sol"; +import "../../utils/Errors.sol"; + +/// @title PointToken +/// @author Angle Labs, Inc. +/// @notice Reference contract for points systems within Merkl +contract PointToken is ERC20 { + mapping(address => bool) public minters; + mapping(address => bool) public whitelistedRecipients; + IAccessControlManager public accessControlManager; + uint8 public allowedTransfers; + + constructor( + string memory name_, + string memory symbol_, + address _minter, + address _accessControlManager + ) ERC20(name_, symbol_) { + if (_accessControlManager == address(0) || _minter == address(0)) revert Errors.ZeroAddress(); + accessControlManager = IAccessControlManager(_accessControlManager); + minters[_minter] = true; + } + + modifier onlyGovernorOrGuardian() { + if (!accessControlManager.isGovernorOrGuardian(msg.sender)) revert Errors.NotGovernorOrGuardian(); + _; + } + + modifier onlyMinter() { + if (!minters[msg.sender]) revert Errors.NotTrusted(); + _; + } + + function mint(address account, uint256 amount) external onlyMinter { + _mint(account, amount); + } + + function burn(address account, uint256 amount) external onlyMinter { + _burn(account, amount); + } + + function mintBatch(address[] memory accounts, uint256[] memory amounts) external onlyMinter { + uint256 length = accounts.length; + for (uint256 i = 0; i < length; ++i) { + _mint(accounts[i], amounts[i]); + } + } + + function toggleMinter(address minter) external onlyGovernorOrGuardian { + minters[minter] = !minters[minter]; + } + + function toggleAllowedTransfers() external onlyGovernorOrGuardian { + allowedTransfers = 1 - allowedTransfers; + } + + function toggleWhitelistedRecipient(address recipient) external onlyGovernorOrGuardian { + whitelistedRecipients[recipient] = !whitelistedRecipients[recipient]; + } + + function _beforeTokenTransfer(address from, address to, uint256) internal view override { + if ( + allowedTransfers == 0 && + from != address(0) && + to != address(0) && + !whitelistedRecipients[from] && + !whitelistedRecipients[to] + ) revert Errors.NotAllowed(); + } +} diff --git a/contracts/partners/tokenWrappers/PufferPointTokenWrapper.sol b/contracts/partners/tokenWrappers/PufferPointTokenWrapper.sol index 3352e2c..25edfaf 100644 --- a/contracts/partners/tokenWrappers/PufferPointTokenWrapper.sol +++ b/contracts/partners/tokenWrappers/PufferPointTokenWrapper.sol @@ -6,7 +6,7 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import { IAccessControlManager } from "../../interfaces/IAccessControlManager.sol"; +import { IAccessControlManager } from "./BaseTokenWrapper.sol"; import { UUPSHelper } from "../../utils/UUPSHelper.sol"; import { Errors } from "../../utils/Errors.sol"; @@ -38,7 +38,7 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// VARIABLES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ - /// @notice `AccessControlManager` contract handling access control + /// @notice `accessControlManager` contract handling access control IAccessControlManager public accessControlManager; /// @notice Merkl main functions address public distributor; @@ -53,6 +53,9 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { mapping(address => VestingData) public vestingData; event Recovered(address indexed token, address indexed to, uint256 amount); + event MerklAddressesUpdated(address indexed _distributionCreator, address indexed _distributor); + event CliffDurationUpdated(uint32 _newCliffDuration); + event FeeRecipientUpdated(address indexed _feeRecipient); // ================================= FUNCTIONS ================================= @@ -71,6 +74,7 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { underlying = _underlying; accessControlManager = _accessControlManager; cliffDuration = _cliffDuration; + distributionCreator = _distributionCreator; distributor = IDistributionCreator(_distributionCreator).distributor(); feeRecipient = IDistributionCreator(_distributionCreator).feeRecipient(); } @@ -105,15 +109,24 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { if (from == distributor) { _burn(to, amount); - // Creates a vesting for the `to` address - VestingData storage userVestingData = vestingData[to]; - VestingID[] storage userAllVestings = userVestingData.allVestings; - userAllVestings.push(VestingID(uint128(amount), uint128(block.timestamp + cliffDuration))); + uint128 endTimestamp = uint128(block.timestamp + cliffDuration); + if (endTimestamp > block.timestamp) { + // Creates a vesting for the `to` address + VestingData storage userVestingData = vestingData[to]; + VestingID[] storage userAllVestings = userVestingData.allVestings; + userAllVestings.push(VestingID(uint128(amount), uint128(block.timestamp + cliffDuration))); + } else { + IERC20(token()).safeTransfer(to, amount); + } } } function claim(address user) external returns (uint256) { - (uint256 claimed, uint256 nextClaimIndex) = _claimable(user); + return claim(user, type(uint256).max); + } + + function claim(address user, uint256 maxClaimIndex) public returns (uint256) { + (uint256 claimed, uint256 nextClaimIndex) = _claimable(user, maxClaimIndex); if (claimed > 0) { vestingData[user].nextClaimIndex = nextClaimIndex; IERC20(token()).safeTransfer(user, claimed); @@ -122,7 +135,11 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { } function claimable(address user) external view returns (uint256 amountClaimable) { - (amountClaimable, ) = _claimable(user); + return claimable(user, type(uint256).max); + } + + function claimable(address user, uint256 maxClaimIndex) public view returns (uint256 amountClaimable) { + (amountClaimable, ) = _claimable(user, maxClaimIndex); } function getUserVestings( @@ -133,12 +150,15 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { nextClaimIndex = userVestingData.nextClaimIndex; } - function _claimable(address user) internal view returns (uint256 amountClaimable, uint256 nextClaimIndex) { + function _claimable( + address user, + uint256 maxClaimIndex + ) internal view returns (uint256 amountClaimable, uint256 nextClaimIndex) { VestingData storage userVestingData = vestingData[user]; VestingID[] storage userAllVestings = userVestingData.allVestings; uint256 i = userVestingData.nextClaimIndex; uint256 length = userAllVestings.length; - while (i < length) { + while (i < length && i <= maxClaimIndex) { VestingID storage userCurrentVesting = userAllVestings[i]; if (block.timestamp > userCurrentVesting.unlockTimestamp) { amountClaimable += userCurrentVesting.amount; @@ -157,7 +177,12 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { _; } - /// @inheritdoc UUPSHelper + /// @notice Checks whether the `msg.sender` has the governor role or the guardian role + modifier onlyGuardian() { + if (!accessControlManager.isGovernorOrGuardian(msg.sender)) revert Errors.NotGovernorOrGuardian(); + _; + } + function _authorizeUpgrade(address) internal view override onlyGovernorUpgrader(accessControlManager) {} /// @notice Recovers any ERC20 token @@ -168,11 +193,26 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable { } function setDistributor(address _distributionCreator) external onlyGovernor { - distributor = IDistributionCreator(_distributionCreator).distributor(); + address _distributor = IDistributionCreator(_distributionCreator).distributor(); + distributor = _distributor; distributionCreator = _distributionCreator; + emit MerklAddressesUpdated(_distributionCreator, _distributor); + _setFeeRecipient(); + } + + function setCliffDuration(uint32 _newCliffDuration) external onlyGuardian { + if (_newCliffDuration < cliffDuration && _newCliffDuration != 0) revert Errors.InvalidParam(); + cliffDuration = _newCliffDuration; + emit CliffDurationUpdated(_newCliffDuration); } function setFeeRecipient() external { - feeRecipient = IDistributionCreator(distributionCreator).feeRecipient(); + _setFeeRecipient(); + } + + function _setFeeRecipient() internal { + address _feeRecipient = IDistributionCreator(distributionCreator).feeRecipient(); + feeRecipient = _feeRecipient; + emit FeeRecipientUpdated(_feeRecipient); } } diff --git a/contracts/partners/tokenWrappers/SonicFragment.sol b/contracts/partners/tokenWrappers/SonicFragment.sol new file mode 100644 index 0000000..12aa035 --- /dev/null +++ b/contracts/partners/tokenWrappers/SonicFragment.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.17; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +import { IAccessControlManager } from "../../interfaces/IAccessControlManager.sol"; +import { Errors } from "../../utils/Errors.sol"; + +/// @title SonicFragment +/// @notice Contract for Sonic fragments which can be converted upon activation into S tokens +/// @author Angle Labs, Inc. +contract SonicFragment is ERC20 { + using SafeERC20 for IERC20; + + /// @notice Contract handling access control + IAccessControlManager public immutable accessControlManager; + /// @notice Address for the S token + address public immutable sToken; + + /// @notice Amount of S tokens sent on the contract at the activation of redemption + /// @dev Used to compute the exchange rate between fragments and S tokens + uint128 public sTokenAmount; + /// @notice Total supply of the contract + /// @dev Needs to be stored to compute the exchange rate between fragments and sTokens + uint120 public supply; + /// @notice Whether redemption for S tokens has been activated or not + uint8 public contractSettled; + + constructor( + address _accessControlManager, + address recipient, + address _sToken, + uint256 _totalSupply, + string memory _name, + string memory _symbol + ) ERC20(_name, _symbol) { + // Zero address check + if (_sToken == address(0)) revert Errors.ZeroAddress(); + IAccessControlManager(_accessControlManager).isGovernor(msg.sender); + sToken = _sToken; + accessControlManager = IAccessControlManager(_accessControlManager); + supply = uint120(_totalSupply); + _mint(recipient, _totalSupply); + } + + // ================================= MODIFIERS ================================= + + /// @notice Checks whether the `msg.sender` has the governor role + modifier onlyGovernor() { + if (!accessControlManager.isGovernor(msg.sender)) revert Errors.NotAllowed(); + _; + } + + /// @notice Activates the contract settlement and enables redemption of fragments into S + /// @dev Can only be called once + function settleContract(uint256 _sTokenAmount) external onlyGovernor { + if (contractSettled > 0) revert Errors.NotAllowed(); + contractSettled = 1; + IERC20(sToken).safeTransferFrom(msg.sender, address(this), sTokenAmount); + sTokenAmount = uint128(_sTokenAmount); + } + + /// @notice Recovers leftover tokens after sometime + function recover(uint256 amount, address recipient) external onlyGovernor { + IERC20(sToken).safeTransfer(recipient, amount); + sTokenAmount = 0; + } + + /// @notice Redeems fragments against S based on a predefined exchange rate + function redeem(uint256 amount, address recipient) external returns (uint256 amountToSend) { + uint128 _sTokenAmount = sTokenAmount; + if (_sTokenAmount == 0) revert Errors.NotAllowed(); + _burn(msg.sender, amount); + amountToSend = (amount * _sTokenAmount) / supply; + IERC20(sToken).safeTransfer(recipient, amountToSend); + } +} diff --git a/docs/access_control.svg b/docs/access_control.svg new file mode 100644 index 0000000..06b9d3d --- /dev/null +++ b/docs/access_control.svg @@ -0,0 +1,11 @@ +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1295.1600152403353 2124.048828125" width="2590.3200304806705" height="4248.09765625"> + <!-- svg-source:excalidraw --> + + <defs> + <style class="style-fonts"> + @font-face { font-family: Virgil; src: url(data:font/woff2;base64,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); } + @font-face { font-family: Excalifont; src: url(data:font/woff2;base64,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); } + </style> + + </defs> + <rect x="0" y="0" width="1295.1600152403353" height="2124.048828125" fill="#ffffff"></rect><g stroke-linecap="round" transform="translate(582.1522027403353 641.9637217601776) rotate(0 351.50390625 702.44921875)"><path d="M0 0 C275.12 1.24, 550.43 0.72, 703.01 0 M0 0 C184.2 -0.99, 368.25 -0.77, 703.01 0 M703.01 0 C697.96 529.17, 698.21 1060.43, 703.01 1404.9 M703.01 0 C701.09 486.05, 700.42 971.76, 703.01 1404.9 M703.01 1404.9 C557.63 1405.95, 410.68 1406.42, 0 1404.9 M703.01 1404.9 C523.38 1403.7, 344.04 1403.35, 0 1404.9 M0 1404.9 C-1.45 849, -1.54 291.67, 0 0 M0 1404.9 C2.48 1018.52, 2.51 632.37, 0 0" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(885.016139656329 646.9637217601776) rotate(0 48.63996933400631 12.5)"><text x="48.63996933400631" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Contracts</text></g><g stroke-linecap="round" transform="translate(1043.1001652772725 60.74249820311866) rotate(0 111.18423612612014 278.7992127057492)"><path d="M0 0 C60.29 -1.25, 121.05 0.28, 222.37 0 M0 0 C73.07 1.11, 145.81 0.36, 222.37 0 M222.37 0 C223.52 114.81, 223.41 227.97, 222.37 557.6 M222.37 0 C223.78 220.71, 223.76 442.1, 222.37 557.6 M222.37 557.6 C147 559.13, 73.32 557.42, 0 557.6 M222.37 557.6 C166.87 558.42, 112.36 558.99, 0 557.6 M0 557.6 C-1.08 389.91, -1.75 222.4, 0 0 M0 557.6 C0.4 342.89, 0.32 128.13, 0 0" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(1127.6354156002653 65.74249820311866) rotate(0 26.64898580312729 12.341623918858431)"><text x="26.64898580312729" y="17.39675307602284" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="19.746598270173486px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Roles</text></g><g stroke-linecap="round" transform="translate(10 10) rotate(0 155.34765625 1052.0244140625)"><path d="M0 0 C112.17 1.29, 225.81 0.76, 310.7 0 M0 0 C74.82 -0.52, 147.85 0.37, 310.7 0 M310.7 0 C316.17 444.73, 316.57 890.72, 310.7 2104.05 M310.7 0 C302.88 619.13, 303.39 1238.71, 310.7 2104.05 M310.7 2104.05 C212.45 2105.3, 114.76 2105.09, 0 2104.05 M310.7 2104.05 C200.63 2103.62, 89.18 2103.86, 0 2104.05 M0 2104.05 C-5.11 1319.74, -4.23 536.01, 0 0 M0 2104.05 C0.93 1563.6, 1.45 1023.57, 0 0" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(132.46768188476562 15) rotate(0 32.879974365234375 12.5)"><text x="32.879974365234375" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Actors</text></g><g stroke-linecap="round" transform="translate(55.375 916.41015625) rotate(0 109.97265625 109.97265625)"><path d="M136.41 4.01 C148.97 5.13, 161.41 11.73, 172.3 19.07 C183.2 26.41, 194.16 36.75, 201.76 48.07 C209.37 59.38, 215.04 73.95, 217.93 86.96 C220.83 99.97, 221.09 113.03, 219.15 126.12 C217.22 139.2, 213.01 154.06, 206.34 165.46 C199.67 176.87, 189.73 186.51, 179.12 194.56 C168.5 202.61, 155.29 209.49, 142.65 213.75 C130.01 218, 116.22 220.69, 103.27 220.09 C90.32 219.49, 77.05 215.59, 64.93 210.16 C52.81 204.73, 39.97 197.04, 30.54 187.5 C21.12 177.95, 13.54 165.25, 8.38 152.88 C3.22 140.51, -0.17 126.48, -0.45 113.27 C-0.72 100.07, 2.31 85.92, 6.71 73.64 C11.1 61.37, 17.39 49.7, 25.94 39.63 C34.49 29.57, 46.2 19.58, 58.01 13.24 C69.83 6.9, 81.58 2.68, 96.83 1.6 C112.07 0.52, 139.05 4.72, 149.5 6.78 C159.96 8.84, 160.15 12.21, 159.55 13.95 M85.88 1.53 C98.07 -2.85, 115.1 -1.27, 128.22 0.82 C141.34 2.91, 153.4 7.32, 164.61 14.08 C175.81 20.83, 187.33 31.05, 195.45 41.33 C203.56 51.61, 209.12 63.01, 213.3 75.77 C217.47 88.52, 221.19 104.63, 220.49 117.85 C219.79 131.07, 214.66 143.32, 209.12 155.09 C203.57 166.85, 196.7 178.92, 187.22 188.43 C177.74 197.93, 164.49 207.03, 152.22 212.1 C139.95 217.17, 126.49 218.79, 113.61 218.86 C100.73 218.94, 87.5 216.55, 74.93 212.55 C62.37 208.56, 48.57 203.23, 38.2 194.88 C27.83 186.53, 18.82 174.43, 12.73 162.46 C6.65 150.49, 3.45 136.28, 1.68 123.08 C-0.1 109.88, -0.77 96.08, 2.1 83.28 C4.98 70.48, 10.86 57.25, 18.94 46.26 C27.02 35.27, 39.52 24.74, 50.58 17.34 C61.65 9.94, 79.34 3.87, 85.35 1.86 C91.37 -0.14, 86.01 3.43, 86.67 5.3" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(118.22529043654345 1001.6204015205278) rotate(0 47.359954833984375 25)"><text x="47.359954833984375" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">AngleLabs</text><text x="47.359954833984375" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Multisig</text></g><g stroke-linecap="round" transform="translate(81.90625 127.603515625) rotate(0 83.44140625 83.44140625)"><path d="M106.22 2.49 C116.95 4.05, 129.09 11.48, 137.95 19.12 C146.81 26.77, 154.48 38.08, 159.37 48.37 C164.27 58.67, 166.96 69.58, 167.31 80.89 C167.67 92.19, 165.91 105.82, 161.49 116.19 C157.07 126.56, 149.34 135.6, 140.8 143.1 C132.27 150.6, 120.99 157.2, 110.29 161.18 C99.59 165.16, 87.62 167.67, 76.61 166.96 C65.6 166.24, 54.24 162.24, 44.23 156.9 C34.21 151.57, 23.58 143.99, 16.52 134.95 C9.46 125.91, 4.46 113.83, 1.85 102.63 C-0.75 91.44, -1.34 78.89, 0.89 67.8 C3.13 56.7, 8.81 45.16, 15.25 36.07 C21.7 26.98, 29.94 19.14, 39.56 13.24 C49.17 7.35, 60.87 2, 72.93 0.71 C85 -0.58, 104.9 4.36, 111.94 5.5 C118.99 6.63, 115.82 6.38, 115.2 7.5 M117.6 6.97 C127.89 10.17, 137.46 18.47, 145.26 26.91 C153.06 35.35, 160.93 46.77, 164.4 57.62 C167.87 68.47, 167.61 80.61, 166.1 91.99 C164.58 103.38, 161.23 116.18, 155.33 125.91 C149.42 135.65, 139.71 143.68, 130.68 150.42 C121.64 157.16, 112.07 164.04, 101.1 166.34 C90.13 168.63, 75.93 166.87, 64.86 164.19 C53.79 161.51, 43.77 156.66, 34.67 150.24 C25.57 143.82, 15.94 135.58, 10.26 125.66 C4.58 115.73, 1.35 102.29, 0.58 90.69 C-0.19 79.09, 2.18 66.41, 5.66 56.06 C9.14 45.72, 14.14 36.72, 21.45 28.62 C28.76 20.51, 38.98 12.27, 49.52 7.42 C60.07 2.57, 73.55 -0.49, 84.72 -0.49 C95.9 -0.48, 111.4 5.92, 116.59 7.45 C121.79 8.98, 116.01 7.59, 115.88 8.68" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(123.73569540959909 186.04293768388345) rotate(0 41.60997664928436 25)"><text x="41.60997664928436" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Deployer</text><text x="41.60997664928436" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">EOA</text></g><g stroke-linecap="round" transform="translate(107.57732965959235 1379.0762444102272) rotate(0 47.586335170203824 47.82613560738639)"><path d="M65.8 2.63 C73.29 5.14, 80.85 12.91, 85.75 19.28 C90.64 25.65, 94.03 32.97, 95.18 40.86 C96.33 48.75, 95.71 59.31, 92.64 66.61 C89.57 73.92, 83.34 80.07, 76.76 84.68 C70.17 89.29, 61.03 92.98, 53.14 94.27 C45.25 95.57, 36.5 95.29, 29.42 92.47 C22.34 89.65, 15.6 83.57, 10.65 77.34 C5.71 71.12, 1.01 63.09, -0.26 55.13 C-1.53 47.16, 0.03 37.14, 3.05 29.57 C6.07 21.99, 11.42 14.54, 17.85 9.67 C24.28 4.8, 32.87 0.82, 41.62 0.36 C50.37 -0.09, 64.74 5.37, 70.35 6.96 C75.96 8.54, 75.5 9.2, 75.28 9.85 M60.58 3.29 C68.03 4.76, 76.4 8.86, 81.92 14.38 C87.44 19.9, 91.92 28.7, 93.71 36.4 C95.5 44.09, 94.68 52.91, 92.68 60.54 C90.67 68.16, 87.42 76.63, 81.68 82.17 C75.94 87.7, 66.24 91.78, 58.25 93.75 C50.26 95.72, 41.55 96.32, 33.76 94 C25.97 91.68, 16.79 85.5, 11.51 79.83 C6.22 74.17, 3.74 67.77, 2.03 60.01 C0.31 52.26, -1.05 41.21, 1.21 33.3 C3.47 25.4, 9.8 17.77, 15.59 12.58 C21.37 7.39, 28.31 4.1, 35.95 2.17 C43.59 0.24, 57.02 1.06, 61.43 0.99 C65.84 0.92, 62.73 0.61, 62.42 1.77" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(127.05506741896806 1414.5841952116832) rotate(0 27.959977120161057 12.5)"><text x="27.959977120161057" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Users</text></g><g stroke-linecap="round" transform="translate(35 1514.533203125) rotate(0 130.34765625 130.34765625)"><path d="M71.17 15.35 C82.17 7.46, 97.86 3.04, 111.88 0.98 C125.89 -1.07, 141.5 0.5, 155.28 3.01 C169.06 5.53, 182.61 9.39, 194.56 16.06 C206.51 22.73, 217.67 32.27, 226.98 43.04 C236.3 53.8, 244.78 67.46, 250.46 80.67 C256.14 93.88, 260.24 108.13, 261.06 122.27 C261.87 136.42, 259.25 152.01, 255.35 165.55 C251.44 179.09, 245.52 192.04, 237.61 203.5 C229.69 214.97, 219 225.87, 207.87 234.34 C196.74 242.82, 184.21 249.8, 170.81 254.35 C157.42 258.9, 141.63 261.89, 127.49 261.63 C113.35 261.37, 99.06 257.66, 85.97 252.78 C72.89 247.9, 59.9 241.29, 48.98 232.35 C38.06 223.41, 28.07 211.3, 20.43 199.14 C12.79 186.99, 6.29 173.13, 3.14 159.41 C-0.02 145.69, 0.12 130.82, 1.53 116.84 C2.94 102.86, 5.86 88.36, 11.59 75.53 C17.32 62.71, 25.79 50.09, 35.91 39.88 C46.02 29.67, 65.57 18.54, 72.28 14.28 C78.99 10.02, 75.18 12.55, 76.18 14.32 M146.82 2.14 C160.49 1.72, 173.77 6.41, 186.44 12.29 C199.12 18.18, 212.85 27.23, 222.87 37.46 C232.89 47.68, 240.42 61.07, 246.58 73.63 C252.74 86.18, 257.67 99.15, 259.81 112.8 C261.95 126.45, 262.37 141.33, 259.44 155.54 C256.51 169.75, 249.73 185.67, 242.24 198.07 C234.75 210.47, 225.06 221.1, 214.51 229.94 C203.95 238.79, 192.19 246.03, 178.93 251.15 C165.67 256.26, 149.37 259.95, 134.94 260.62 C120.5 261.28, 105.41 259.31, 92.31 255.15 C79.21 250.99, 67.83 243.9, 56.36 235.66 C44.88 227.42, 31.98 216.91, 23.48 205.71 C14.98 194.52, 9.16 181.94, 5.37 168.51 C1.57 155.08, 0.33 139.34, 0.71 125.12 C1.08 110.9, 2.45 96.36, 7.61 83.19 C12.76 70.03, 22.48 56.88, 31.62 46.11 C40.76 35.35, 50.5 25.96, 62.46 18.6 C74.41 11.25, 89.59 5.09, 103.37 1.98 C117.15 -1.13, 138.17 -0.47, 145.12 -0.05 C152.08 0.37, 145.44 2.2, 145.11 4.5" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(110.96798217623586 1619.711147728852) rotate(0 54.20996242761612 25.00000000000003)"><text x="54.20996242761612" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Whitelisted</text><text x="54.20996242761612" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Operators</text></g><g stroke-linecap="round" transform="translate(38.4375 1825.228515625) rotate(0 126.91015625 126.91015625)"><path d="M73.04 11.82 C84.67 4.13, 101.73 2.77, 116.05 1.34 C130.37 -0.09, 145.28 -0.13, 158.95 3.24 C172.63 6.62, 186.22 13.16, 198.09 21.59 C209.96 30.02, 221.53 41.84, 230.16 53.82 C238.8 65.8, 246.16 79.44, 249.92 93.46 C253.67 107.48, 254.04 123.58, 252.69 137.94 C251.34 152.29, 247.81 166.62, 241.81 179.58 C235.81 192.54, 226.69 205.37, 216.69 215.69 C206.69 226.01, 194.85 235.09, 181.81 241.51 C168.77 247.94, 152.87 252.86, 138.43 254.23 C123.99 255.59, 109 253.39, 95.17 249.7 C81.34 246, 67.31 240.32, 55.44 232.04 C43.58 223.75, 32.51 211.99, 23.98 199.99 C15.45 187.99, 8.06 173.97, 4.28 160.05 C0.49 146.12, 0.12 130.78, 1.27 116.42 C2.41 102.05, 5.29 87.02, 11.13 73.85 C16.98 60.68, 25.19 47.96, 36.34 37.39 C47.49 26.83, 69.85 15.15, 78.05 10.47 C86.26 5.79, 84.72 7.54, 85.57 9.31 M142.97 2.11 C156.97 1.8, 170.71 6.7, 183.59 12.99 C196.47 19.27, 210.36 28.96, 220.25 39.8 C230.15 50.65, 237.39 64.85, 242.98 78.06 C248.57 91.28, 252.68 104.98, 253.81 119.09 C254.94 133.19, 254.04 148.46, 249.75 162.7 C245.47 176.94, 237.07 192.68, 228.07 204.5 C219.08 216.33, 207.71 226.06, 195.77 233.65 C183.83 241.24, 170.67 246.83, 156.44 250.05 C142.21 253.26, 125.11 254.65, 110.4 252.94 C95.69 251.24, 80.67 246.55, 68.21 239.82 C55.74 233.09, 45.47 223.22, 35.62 212.57 C25.78 201.93, 15.07 188.99, 9.13 175.94 C3.2 162.89, 0.52 148.66, 0.02 134.29 C-0.49 119.92, 2.07 103.67, 6.11 89.71 C10.16 75.75, 15.54 62.09, 24.3 50.51 C33.05 38.94, 46.47 28.13, 58.65 20.26 C70.83 12.39, 83.43 6.68, 97.38 3.31 C111.34 -0.05, 135.12 0.2, 142.38 0.05 C149.65 -0.09, 141.36 0.04, 140.96 2.44" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(110.89866173656458 1926.8996397891806) rotate(0 54.20996242761612 25)"><text x="54.20996242761612" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Whitelisted</text><text x="54.20996242761612" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Disputers</text></g><g stroke-linecap="round" transform="translate(579.7943822395994 191.88122107518848) rotate(0 87.69140625 67.5)"><path d="M-0.92 1.18 L175.61 -1.37 L177.3 133.44 L-0.38 136.79" stroke="none" stroke-width="0" fill="#ffeaa7"></path><path d="M0 0 C37.12 3.22, 72.72 0.76, 175.38 0 M0 0 C68.66 -0.88, 138.16 -0.16, 175.38 0 M175.38 0 C174.8 46.48, 175.33 89.5, 175.38 135 M175.38 0 C174.73 33.34, 175.78 67.65, 175.38 135 M175.38 135 C123.89 135.58, 69.57 135.36, 0 135 M175.38 135 C107.76 133.95, 40.07 133.55, 0 135 M0 135 C-0.4 81.14, 0.9 25.74, 0 0 M0 135 C-1.2 101.1, -0.25 67.94, 0 0" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(594.4358464729978 209.38122107518848) rotate(0 73.04994201660156 50)"><text x="73.04994201660156" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">2-of-3 Multisig</text><text x="73.04994201660156" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">0x8f..02b4</text><text x="73.04994201660156" y="67.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">0xf8..c349</text><text x="73.04994201660156" y="92.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">0x34..c41d</text></g><g stroke-linecap="round" transform="translate(1071.894716582959 167.9831551810214) rotate(0 85.15673838137673 77.10446611408184)"><path d="M68.29 0.63 C78.46 -2.76, 91.54 -1.55, 102.78 0.94 C114.01 3.42, 126.15 9.14, 135.69 15.55 C145.22 21.96, 154.39 30.24, 159.98 39.41 C165.57 48.59, 168.32 60.26, 169.23 70.6 C170.13 80.94, 169.06 91.74, 165.41 101.45 C161.76 111.17, 154.97 121.17, 147.32 128.89 C139.67 136.62, 130.09 143.41, 119.52 147.79 C108.96 152.16, 95.52 155.26, 83.94 155.17 C72.36 155.07, 60.38 151.6, 50.05 147.21 C39.72 142.81, 29.55 136.71, 21.96 128.81 C14.36 120.9, 8.08 109.97, 4.47 99.8 C0.87 89.62, -1.06 78.13, 0.32 67.77 C1.69 57.41, 6.87 46.58, 12.72 37.64 C18.57 28.7, 24.07 20.43, 35.41 14.12 C46.76 7.81, 71.19 1.63, 80.79 -0.24 C90.4 -2.1, 92.98 1.61, 93.04 2.92 M66.32 0.83 C76.94 -2.21, 92.53 0.72, 103.93 2.93 C115.32 5.15, 125.38 8.22, 134.68 14.12 C143.97 20.03, 153.95 29.02, 159.7 38.37 C165.45 47.72, 168.1 59.92, 169.19 70.22 C170.27 80.52, 169.47 90.59, 166.21 100.18 C162.96 109.77, 157.49 119.61, 149.66 127.75 C141.83 135.88, 130.08 144.53, 119.21 149 C108.34 153.48, 95.8 154.94, 84.45 154.6 C73.09 154.25, 61.72 151.28, 51.06 146.94 C40.39 142.59, 28.5 136.28, 20.46 128.53 C12.42 120.78, 5.93 110.55, 2.84 100.42 C-0.25 90.29, 0.64 78.22, 1.93 67.75 C3.21 57.28, 5.01 46.37, 10.56 37.6 C16.1 28.83, 25.56 21.18, 35.2 15.14 C44.84 9.1, 63.09 3.41, 68.38 1.38 C73.67 -0.64, 66.6 1.74, 66.95 2.99" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(1102.99573510803 232.7249065272092) rotate(0 53.840812683105476 12.341623918858431)"><text x="53.84081268310547" y="17.39675307602284" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="19.746598270173486px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">GUARDIAN</text></g><g stroke-linecap="round" transform="translate(1067.7834131149893 417.63415173972317) rotate(0 90.76137234255384 82.7871827154267)"><path d="M124.68 5.44 C135.97 8.58, 147.74 17.92, 156.46 26.25 C165.18 34.58, 172.99 44.79, 177 55.44 C181.01 66.1, 181.69 79.05, 180.52 90.17 C179.35 101.3, 175.85 112.51, 169.99 122.17 C164.13 131.83, 154.97 141.3, 145.37 148.15 C135.76 154.99, 124.28 160.41, 112.34 163.23 C100.41 166.06, 85.87 167.17, 73.74 165.12 C61.61 163.07, 49.53 157.34, 39.57 150.93 C29.61 144.51, 20.35 136.27, 13.98 126.63 C7.61 116.99, 3.08 104.42, 1.34 93.09 C-0.4 81.76, -0.03 69.4, 3.54 58.67 C7.1 47.93, 14.73 37.17, 22.71 28.7 C30.69 20.23, 40.49 12.7, 51.41 7.87 C62.34 3.04, 74.39 -0.94, 88.27 -0.3 C102.16 0.34, 125.59 8.77, 134.74 11.69 C143.88 14.62, 143.87 16.23, 143.17 17.25 M102.46 1.79 C114.34 1.83, 126.08 5.5, 136.62 11 C147.15 16.49, 158.59 25.32, 165.68 34.78 C172.77 44.24, 176.86 56.86, 179.14 67.78 C181.43 78.69, 181.79 89.69, 179.39 100.29 C176.99 110.88, 172.22 122, 164.75 131.36 C157.28 140.73, 145.63 150.77, 134.57 156.49 C123.52 162.2, 110.46 164.95, 98.39 165.64 C86.32 166.33, 73.92 164.31, 62.15 160.61 C50.37 156.91, 37.1 151.06, 27.75 143.45 C18.39 135.85, 10.42 125.49, 6.03 114.99 C1.65 104.49, 1.2 91.74, 1.44 80.44 C1.67 69.14, 2.52 57.11, 7.44 47.19 C12.36 37.28, 21.42 28.26, 30.97 20.96 C40.51 13.66, 53.13 6.82, 64.73 3.4 C76.33 -0.01, 94.25 0.7, 100.55 0.46 C106.84 0.22, 102.61 0.55, 102.49 1.95" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(1102.1374487459277 488.04033224288344) rotate(0 56.229354858398445 12.341623918858431)"><text x="56.22935485839844" y="17.39675307602284" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="19.746598270173486px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">GOVERNOR</text></g><g stroke-linecap="round" transform="translate(668.2138387426623 709.2589817915712) rotate(0 43.23560088455986 42.5)"><path d="M24.48 4.42 C31.18 0.95, 41.77 -0.56, 49.42 0.5 C57.08 1.55, 64.61 5.87, 70.39 10.74 C76.17 15.61, 81.77 22.58, 84.1 29.71 C86.43 36.84, 86.3 46.09, 84.37 53.5 C82.45 60.91, 78.34 68.82, 72.56 74.15 C66.77 79.47, 57.4 84.23, 49.67 85.44 C41.93 86.65, 33.14 84.52, 26.17 81.4 C19.21 78.27, 12.14 73.13, 7.88 66.69 C3.62 60.24, 0.85 50.59, 0.59 42.73 C0.32 34.88, 1.18 26.39, 6.3 19.56 C11.41 12.72, 26.31 4.76, 31.27 1.71 C36.24 -1.34, 36.05 0.6, 36.07 1.25 M28.32 1.89 C34.81 -0.99, 45.37 -0.24, 52.77 1.44 C60.17 3.13, 67.12 6.39, 72.71 12.01 C78.31 17.62, 84.66 27.63, 86.34 35.13 C88.01 42.64, 85.6 50.16, 82.77 57.04 C79.95 63.91, 75.65 71.58, 69.38 76.38 C63.1 81.17, 52.98 85.45, 45.13 85.81 C37.27 86.17, 28.68 82.63, 22.24 78.54 C15.8 74.45, 10.22 67.64, 6.49 61.27 C2.76 54.9, -0.48 47.69, -0.14 40.32 C0.21 32.94, 3.61 23.41, 8.54 17.03 C13.47 10.64, 26.24 4.3, 29.43 2.02 C32.62 -0.25, 27.79 2.65, 27.69 3.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(689.7872619752941 739.2069435911428) rotate(0 21.589991077780724 12.5)"><text x="21.589991077780724" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Core</text></g><g stroke-linecap="round" transform="translate(1123.978009261782 1276.7316380415712) rotate(0 67.48828125 67.48828125)"><path d="M38 7.23 C45.77 2.25, 56.27 0.94, 65.86 0.74 C75.44 0.54, 86.6 2.1, 95.49 6.03 C104.37 9.95, 112.81 16.7, 119.16 24.28 C125.52 31.85, 131.3 41.77, 133.64 51.48 C135.97 61.2, 135.25 73.01, 133.18 82.57 C131.1 92.13, 126.99 101.17, 121.18 108.84 C115.37 116.52, 106.99 124.32, 98.29 128.61 C89.59 132.91, 78.91 134.69, 68.99 134.62 C59.08 134.56, 47.6 132.32, 38.79 128.23 C29.98 124.14, 22.37 117.58, 16.15 110.08 C9.93 102.59, 3.96 92.78, 1.47 83.28 C-1.02 73.77, -0.79 62.41, 1.21 53.05 C3.2 43.69, 7.25 34.92, 13.46 27.11 C19.68 19.31, 34 9.59, 38.48 6.22 C42.97 2.85, 39.79 6, 40.36 6.89 M49.11 1.55 C57.78 -2.11, 70.94 -1.36, 80.55 0.71 C90.16 2.79, 98.99 8.32, 106.78 14 C114.56 19.69, 122.34 26.54, 127.27 34.82 C132.2 43.1, 136.05 53.53, 136.36 63.68 C136.67 73.83, 133.3 86.5, 129.11 95.71 C124.92 104.92, 118.41 112.88, 111.21 118.95 C104.01 125.03, 95.46 129.67, 85.9 132.16 C76.34 134.65, 63.8 135.63, 53.86 133.87 C43.92 132.12, 33.71 127.58, 26.28 121.62 C18.85 115.67, 13.84 106.82, 9.3 98.12 C4.76 89.43, -0.32 78.92, -0.96 69.43 C-1.61 59.94, 1.26 50.07, 5.42 41.19 C9.58 32.32, 16.77 22.67, 23.99 16.17 C31.21 9.67, 44.2 4.33, 48.74 2.19 C53.28 0.05, 50.9 2.22, 51.25 3.33" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(1152.2549094724852 1318.9984979690712) rotate(0 38.989959716796875 25)"><text x="38.989959716796875" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Distribu</text><text x="38.989959716796875" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">tor</text></g><g stroke-linecap="round" transform="translate(607.8256655117821 825.9855442915712) rotate(0 113.49609375 113.49609375)"><path d="M131.66 2.31 C144.58 2.67, 159.3 8.9, 171.08 15.69 C182.85 22.49, 193.74 32.3, 202.33 43.09 C210.93 53.88, 218.65 67.11, 222.66 80.42 C226.66 93.72, 227.42 109.28, 226.37 122.92 C225.31 136.57, 222.04 149.98, 216.33 162.28 C210.63 174.58, 202.02 187.4, 192.13 196.73 C182.23 206.05, 169.76 213.27, 156.95 218.24 C144.14 223.2, 128.88 226.25, 115.26 226.52 C101.64 226.78, 88.09 224.49, 75.23 219.84 C62.37 215.18, 48.55 207.64, 38.1 198.59 C27.65 189.54, 18.88 177.5, 12.52 165.56 C6.17 153.62, 1.5 140.51, 0 126.95 C-1.51 113.38, -0.06 97.58, 3.49 84.19 C7.04 70.8, 13.18 57.8, 21.31 46.62 C29.45 35.44, 40.82 24.41, 52.29 17.11 C63.76 9.81, 75.91 5.35, 90.14 2.82 C104.37 0.29, 129.32 1.36, 137.68 1.91 C146.03 2.45, 140.86 4.23, 140.29 6.1 M147.91 3.73 C160.77 6.21, 172.74 16.8, 183.19 25.49 C193.65 34.17, 203.47 44.41, 210.64 55.85 C217.82 67.29, 224.1 80.71, 226.24 94.13 C228.38 107.55, 226.57 122.94, 223.48 136.37 C220.39 149.79, 214.66 163.39, 207.7 174.7 C200.74 186.02, 192.72 196.29, 181.71 204.26 C170.7 212.22, 155.08 218.72, 141.61 222.49 C128.14 226.26, 114.18 228.32, 100.87 226.89 C87.55 225.47, 74.03 220.37, 61.73 213.94 C49.44 207.52, 36.08 198.59, 27.12 188.32 C18.16 178.05, 12.6 165.12, 7.95 152.32 C3.3 139.52, -0.91 125.28, -0.81 111.52 C-0.71 97.76, 3.42 82.59, 8.56 69.76 C13.7 56.94, 20.78 44.19, 30.03 34.56 C39.28 24.93, 51.79 17.72, 64.05 11.98 C76.32 6.25, 89.96 1.24, 103.61 0.15 C117.26 -0.94, 138.68 4.25, 145.95 5.45 C153.22 6.66, 147.74 5.8, 147.22 7.38" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(666.8979427838839 914.727780512762) rotate(0 54.66995894908905 25)"><text x="54.66995894908905" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Distributor</text><text x="54.66995894908905" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Creator</text></g><g stroke-linecap="round" transform="translate(667.1030092617821 1888.4545717972742) rotate(0 67.6148029537726 64.31093124167649)"><path d="M71.95 -0.1 C81.98 -0.57, 93.61 3.94, 102.68 9.21 C111.76 14.48, 121.04 22.72, 126.4 31.52 C131.76 40.33, 134.35 52.09, 134.84 62.05 C135.33 72.02, 133.57 82.31, 129.34 91.31 C125.11 100.32, 117.73 110.07, 109.45 116.07 C101.18 122.07, 90.17 125.84, 79.7 127.34 C69.22 128.84, 56.5 128.09, 46.58 125.05 C36.66 122.01, 27.52 116.17, 20.16 109.11 C12.8 102.05, 5.63 92.24, 2.42 82.68 C-0.78 73.12, -1 61.34, 0.95 51.74 C2.9 42.14, 7.59 32.86, 14.13 25.1 C20.67 17.35, 28.76 9.12, 40.19 5.21 C51.63 1.29, 73.91 1.64, 82.76 1.62 C91.6 1.61, 93.54 3.99, 93.26 5.14 M94.28 4.52 C103.87 7.56, 112.37 16.58, 118.97 24.15 C125.58 31.72, 131.4 40.55, 133.93 49.95 C136.45 59.35, 137.05 70.59, 134.13 80.57 C131.22 90.56, 123.95 102.36, 116.44 109.85 C108.94 117.35, 98.84 122.63, 89.12 125.55 C79.39 128.47, 68.59 128.96, 58.1 127.38 C47.62 125.8, 34.97 121.94, 26.2 116.06 C17.44 110.18, 9.64 101.22, 5.52 92.08 C1.4 82.94, 1.08 71.23, 1.46 61.2 C1.84 51.16, 2.89 40.27, 7.79 31.87 C12.7 23.47, 21.8 16.11, 30.88 10.8 C39.97 5.49, 52.02 1, 62.28 0.01 C72.55 -0.99, 87.18 3.84, 92.48 4.82 C97.79 5.79, 94.5 4.83, 94.12 5.85" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(693.3369584339873 1940.2908074535394) rotate(0 41.56996810436249 12.5)"><text x="41.56996810436249" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Disputer</text></g><g stroke-linecap="round"><g transform="translate(187.2504156549894 917.5931091010671) rotate(0 195.77198329230498 -330.1061471379393)"><path d="M0.17 -0.85 C22.4 -111.1, 94.29 -550.75, 133.8 -660.75 C173.31 -770.74, 202.6 -661.02, 237.25 -660.82 C271.9 -660.62, 316.01 -659.57, 341.7 -659.53 C367.4 -659.49, 382.94 -660.54, 391.44 -660.58 M-1.2 1.32 C20.92 -108.84, 93.73 -549.3, 133.31 -659.49 C172.9 -769.68, 201.59 -659.45, 236.32 -659.82 C271.04 -660.2, 316.09 -661.64, 341.69 -661.73 C367.29 -661.83, 381.68 -660.57, 389.91 -660.38" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask></mask><g mask="url(#mask-A9eMdhgdG1xSxJ5qyRgCL)" stroke-linecap="round"><g transform="translate(751.9173223400027 734.1915948669428) rotate(0 193.08462714597385 -205.74051379903275)"><path d="M0.88 -0.54 C27.58 -5.99, 111.95 -28.29, 160.35 -33.59 C208.76 -38.88, 253.69 30.98, 291.31 -32.3 C328.93 -95.58, 370.09 -349.91, 386.1 -413.28 M-0.12 1.8 C26.38 -3.43, 110.84 -26.36, 159.24 -32.37 C207.64 -38.38, 252.44 29.05, 290.28 -34.29 C328.12 -97.62, 370.25 -349.44, 386.29 -412.37" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(751.9173223400027 734.1915948669428) rotate(0 193.08462714597385 -205.74051379903275)"><path d="M389.78 -387.61 C387.96 -395.89, 386.75 -403.97, 386.29 -412.37 M389.78 -387.61 C389.52 -396.09, 387.31 -404.58, 386.29 -412.37" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(751.9173223400027 734.1915948669428) rotate(0 193.08462714597385 -205.74051379903275)"><path d="M373.05 -391.16 C376.47 -398.44, 380.5 -405.41, 386.29 -412.37 M373.05 -391.16 C378.75 -398.32, 382.51 -405.55, 386.29 -412.37" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-A9eMdhgdG1xSxJ5qyRgCL"><rect x="0" y="0" fill="#fff" width="1238.120723896051" height="1247.0998190372266"></rect><rect x="942.383521906" y="709.7024230739764" fill="#000" width="74.97996211051941" height="25" opacity="1"></rect></mask><g transform="translate(942.3835219060002 709.7024230739764) rotate(0 2.6184275799764123 -181.25134200606644)"><text x="37.489981055259705" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">setRole</text></g><g mask="url(#mask-DdvfdWehPyZdUhaFGr5d_)" stroke-linecap="round"><g transform="translate(754.3611823293334 740.1232639629166) rotate(0 195.39643833213893 -43.24941679685193)"><path d="M-0.31 -0.61 C27.23 11.39, 107.76 67.88, 166.13 71.43 C224.51 74.98, 312.43 58.99, 349.93 20.69 C387.42 -17.62, 384.44 -128.63, 391.11 -158.41 M1.72 1.69 C28.99 13.38, 107.03 66.31, 164.93 69.67 C222.82 73.02, 311.88 59.6, 349.11 21.81 C386.34 -15.98, 381.74 -127.25, 388.3 -157.08" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(754.3611823293334 740.1232639629166) rotate(0 195.39643833213893 -43.24941679685193)"><path d="M394.55 -132.88 C391.31 -142.76, 391.12 -151.14, 388.3 -157.08 M394.55 -132.88 C392.63 -138.68, 391.68 -145.31, 388.3 -157.08" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(754.3611823293334 740.1232639629166) rotate(0 195.39643833213893 -43.24941679685193)"><path d="M377.52 -134.52 C380.63 -143.75, 386.8 -151.52, 388.3 -157.08 M377.52 -134.52 C380.21 -139.94, 383.84 -146.13, 388.3 -157.08" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-DdvfdWehPyZdUhaFGr5d_"><rect x="0" y="0" fill="#fff" width="1244.3152485430276" height="1069.7183570477023"></rect><rect x="980.9255417251371" y="789.816572631367" fill="#000" width="74.97996211051941" height="25" opacity="1"></rect></mask><g transform="translate(980.9255417251371 789.816572631367) rotate(0 -31.167921063664835 -92.94272546530235)"><text x="37.489981055259705" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">setRole</text></g><g mask="url(#mask-m4e7gVG0ER4oEvJdcOxhB)" stroke-linecap="round"><g transform="translate(197.68667590182156 920.2267093163405) rotate(0 434.69093693051855 -206.6026298408713)"><path d="M-0.5 0.68 C19.95 -66.29, 84.25 -335.38, 122.38 -402.43 C160.51 -469.48, 193.38 -401.83, 228.28 -401.63 C263.18 -401.43, 291.29 -401.27, 331.77 -401.23 C372.24 -401.18, 407.35 -401.17, 471.13 -401.36 C534.92 -401.55, 648.2 -400.56, 714.48 -402.36 C780.76 -404.17, 843.06 -410.29, 868.83 -412.19 M1.44 -0.01 C21.75 -66.76, 83.59 -333.8, 121.37 -401.08 C159.15 -468.37, 193.01 -403.37, 228.12 -403.75 C263.22 -404.13, 291.68 -403.87, 332.02 -403.35 C372.35 -402.82, 406.18 -400.57, 470.12 -400.6 C534.06 -400.64, 649.16 -401.49, 715.67 -403.56 C782.19 -405.63, 843.8 -411.2, 869.19 -413" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(197.68667590182156 920.2267093163405) rotate(0 434.69093693051855 -206.6026298408713)"><path d="M846.42 -402.67 C853.84 -405.59, 858.33 -406.31, 869.19 -413 M846.42 -402.67 C854.79 -405.71, 861.36 -410.02, 869.19 -413" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(197.68667590182156 920.2267093163405) rotate(0 434.69093693051855 -206.6026298408713)"><path d="M845.11 -419.72 C852.8 -417.96, 857.65 -414.02, 869.19 -413 M845.11 -419.72 C853.92 -417.05, 860.93 -415.63, 869.19 -413" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-m4e7gVG0ER4oEvJdcOxhB"><rect x="0" y="0" fill="#fff" width="1167.0685497628588" height="1433.431968998083"></rect><rect x="491.30184656882625" y="505.6668148251884" fill="#000" width="76.29995965957642" height="25" opacity="1"></rect></mask><g transform="translate(491.30184656882625 505.66681482518845) rotate(0 140.73093732700966 198.8647542330259)"><text x="38.14997982978821" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">hasRole</text></g><g mask="url(#mask-QXFLdNl1e3L2wHkdw_DJV)" stroke-linecap="round"><g transform="translate(239.86682063206604 171.33037505300854) rotate(0 429.38754641977386 -16.961214129158066)"><path d="M-0.11 -0.37 C13.55 -7.73, 50.61 -36.35, 81.46 -43.47 C112.3 -50.58, 150.11 -43.11, 184.95 -43.06 C219.78 -43.02, 250 -43.01, 290.46 -43.2 C330.91 -43.39, 364.05 -44.25, 427.66 -44.2 C491.28 -44.14, 609.51 -42.84, 672.17 -42.88 C734.83 -42.91, 772.25 -53.99, 803.62 -44.42 C834.99 -34.84, 850.97 4.81, 860.4 14.56 M-1.62 -1.61 C12.02 -9.39, 50.15 -38.14, 81.11 -45.37 C112.08 -52.59, 149.17 -44.97, 184.18 -44.97 C219.19 -44.97, 250.25 -45.55, 291.16 -45.39 C332.07 -45.22, 365.95 -44.3, 429.64 -43.97 C493.32 -43.63, 610.88 -43.65, 673.29 -43.38 C735.71 -43.12, 773.13 -51.94, 804.13 -42.37 C835.13 -32.8, 849.66 4.32, 859.29 14.03" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(239.86682063206604 171.33037505300854) rotate(0 429.38754641977386 -16.961214129158066)"><path d="M838.79 -0.27 C843.56 5.37, 850.19 7.15, 859.29 14.03 M838.79 -0.27 C846.12 4.64, 853.58 11.02, 859.29 14.03" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(239.86682063206604 171.33037505300854) rotate(0 429.38754641977386 -16.961214129158066)"><path d="M852.78 -10.1 C853.98 -1.93, 857 2.39, 859.29 14.03 M852.78 -10.1 C855.01 -1.48, 857.31 8.54, 859.29 14.03" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-QXFLdNl1e3L2wHkdw_DJV"><rect x="0" y="0" fill="#fff" width="1200.4153416677198" height="330.12683725257295"></rect><rect x="559.7597907805699" y="114.96101516471242" fill="#000" width="76.29995965957642" height="25" opacity="1"></rect></mask><g transform="translate(559.7597907805699 114.96101516471242) rotate(0 109.49457627126998 39.40814575913805)"><text x="38.14997982978821" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">hasRole</text></g><g mask="url(#mask-gw_sX0ivpq_ab7Art6HG9)" stroke-linecap="round"><g transform="translate(190.98865707132222 918.0883751488128) rotate(0 457.41468994467976 -305.823642283691)"><path d="M1.15 0.43 C22.54 -87.88, 89.43 -441.75, 128.19 -530.21 C166.95 -618.67, 198.89 -530.15, 233.7 -530.34 C268.51 -530.53, 296.77 -531.4, 337.05 -531.34 C377.34 -531.29, 411.53 -529.99, 475.42 -530.02 C539.3 -530.06, 657.66 -531.33, 720.36 -531.56 C783.06 -531.79, 819.23 -518.1, 851.59 -531.39 C883.95 -544.68, 903.78 -598.06, 914.53 -611.3 M0.3 -0.39 C21.53 -89, 88.39 -443.24, 127.24 -531.9 C166.09 -620.55, 198.42 -532.57, 233.39 -532.32 C268.36 -532.07, 296.43 -530.32, 337.08 -530.4 C377.73 -530.47, 413.22 -532.96, 477.32 -532.76 C541.41 -532.56, 659.17 -529.8, 721.65 -529.21 C784.12 -528.63, 820.21 -515.44, 852.15 -529.25 C884.1 -543.06, 902.82 -598.82, 913.3 -612.08" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(190.98865707132222 918.0883751488128) rotate(0 457.41468994467976 -305.823642283691)"><path d="M909.32 -587.4 C911.27 -594.61, 913.1 -600.35, 913.3 -612.08 M909.32 -587.4 C910.37 -595.38, 912.06 -605.5, 913.3 -612.08" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(190.98865707132222 918.0883751488128) rotate(0 457.41468994467976 -305.823642283691)"><path d="M894.38 -595.73 C900.65 -600.67, 906.83 -603.98, 913.3 -612.08 M894.38 -595.73 C900.76 -600.69, 907.8 -607.83, 913.3 -612.08" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-gw_sX0ivpq_ab7Art6HG9"><rect x="0" y="0" fill="#fff" width="1205.907645232493" height="1628.559217867712"></rect><rect x="558.3159038569796" y="375.0551636269936" fill="#000" width="75.83995056152344" height="25" opacity="1"></rect></mask><g transform="translate(558.3159038569796 375.0551636269936) rotate(0 90.08744315902237 237.2095692381282)"><text x="37.91997528076172" y="17.619999999999997" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">hasRole</text></g><g mask="url(#mask-hJ5Hw52m5tEEoWlVBrHx1)" stroke-linecap="round"><g transform="translate(261.83161188365295 1080.5928762819826) rotate(0 445.01955493907406 100.80140462529047)"><path d="M0 0.83 C9.74 6.44, 32.42 28.69, 59.58 34.06 C86.73 39.43, 106.62 33.06, 162.93 33.06 C219.24 33.06, 345.88 34.09, 397.45 34.05 C449.01 34.02, 427.85 33.22, 472.32 32.84 C516.79 32.46, 612.66 31.83, 664.25 31.78 C715.85 31.72, 744 4.26, 781.87 32.53 C819.75 60.8, 873.46 173.26, 891.49 201.38 M-1.45 0.22 C8.1 5.45, 31.85 26.64, 59.09 32.3 C86.32 37.97, 105.56 34.26, 161.95 34.22 C218.33 34.17, 345.33 32.24, 397.41 32.03 C449.48 31.82, 429.69 33.2, 474.39 32.96 C519.08 32.71, 614.27 30.31, 665.59 30.55 C716.91 30.8, 744.73 6.03, 782.31 34.41 C819.89 62.79, 872.81 172.7, 891.08 200.84" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(261.83161188365295 1080.5928762819826) rotate(0 445.01955493907406 100.80140462529047)"><path d="M872.04 184.65 C881.03 192.51, 888.35 196.78, 891.08 200.84 M872.04 184.65 C877 188.51, 882.31 193.1, 891.08 200.84" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(261.83161188365295 1080.5928762819826) rotate(0 445.01955493907406 100.80140462529047)"><path d="M886.9 176.19 C890.03 187.41, 891.47 195.03, 891.08 200.84 M886.9 176.19 C887.74 182.5, 888.96 189.42, 891.08 200.84" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-hJ5Hw52m5tEEoWlVBrHx1"><rect x="0" y="0" fill="#fff" width="1252.155250982654" height="1382.3457053277689"></rect><rect x="599.1137876392145" y="1089.3272958285222" fill="#000" width="190.99989140033722" height="50" opacity="1"></rect></mask><g transform="translate(599.1137876392145 1089.3272958285222) rotate(0 107.73737918351253 92.06698507875072)"><text x="95.49994570016861" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">upgrade</text><text x="95.49994570016861" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[core.onlyGovernor]</text></g><g mask="url(#mask-emLXyYHfZX3Gyo2MpLxRy)" stroke-linecap="round"><g transform="translate(226.47063807558743 933.7603987551101) rotate(0 199.8458304983218 -69.9723547008256)"><path d="M0.87 0.7 C16.33 -22.81, 60.04 -116.97, 92.78 -140.25 C125.52 -163.52, 162.38 -138.89, 197.29 -138.93 C232.2 -138.96, 268.53 -152.54, 302.24 -140.46 C335.95 -128.38, 383.36 -78.7, 399.54 -66.45 M-0.13 0.02 C15.14 -23.39, 58.89 -115.41, 91.61 -138.87 C124.34 -162.33, 161.11 -140.61, 196.24 -140.73 C231.36 -140.86, 268.73 -152.05, 302.36 -139.63 C335.99 -127.22, 381.63 -78.72, 397.99 -66.24" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(226.47063807558743 933.7603987551101) rotate(0 199.8458304983218 -69.9723547008256)"><path d="M374.72 -75.39 C382.94 -73.5, 388.26 -70.97, 397.99 -66.24 M374.72 -75.39 C383.53 -72.05, 392.29 -68.94, 397.99 -66.24" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(226.47063807558743 933.7603987551101) rotate(0 199.8458304983218 -69.9723547008256)"><path d="M386.05 -88.2 C390.95 -82.39, 392.87 -76.01, 397.99 -66.24 M386.05 -88.2 C390.47 -79.98, 394.87 -71.94, 397.99 -66.24" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-emLXyYHfZX3Gyo2MpLxRy"><rect x="0" y="0" fill="#fff" width="726.162299072231" height="1173.7051081567613"></rect><rect x="328.8090581200154" y="768.815689353459" fill="#000" width="190.99989140033722" height="50" opacity="1"></rect></mask><g transform="translate(328.8090581200154 768.8156893534589) rotate(0 97.36661139219129 90.16971572232562)"><text x="95.49994570016861" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">upgrade</text><text x="95.49994570016861" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[core.onlyGovernor]</text></g><g mask="url(#mask-GQifOXxuV6i8FG52EAGCY)" stroke-linecap="round"><g transform="translate(241.2482201068657 1107.3398062768845) rotate(0 449.42518282265075 91.49874062212032)"><path d="M-0.42 -0.3 C12.53 13.91, 40.29 70.86, 78.56 85.95 C116.82 101.04, 177.6 89.91, 229.19 90.25 C280.77 90.58, 334.41 88.63, 388.09 87.95 C441.77 87.27, 503.84 86.73, 551.28 86.17 C598.71 85.6, 630.83 84.84, 672.69 84.56 C714.55 84.27, 764.67 67.78, 802.43 84.44 C840.2 101.09, 883.3 167.98, 899.27 184.5 M1.57 -1.5 C14.23 12.33, 39.53 68.89, 77.32 84.36 C115.11 99.83, 176.47 90.54, 228.29 91.29 C280.12 92.05, 334.09 89.74, 388.26 88.88 C442.42 88.02, 506.13 87.05, 553.26 86.12 C600.39 85.19, 629.44 83.26, 671.05 83.32 C712.67 83.38, 765.1 69.66, 802.96 86.49 C840.81 103.32, 882.07 167.81, 898.18 184.31" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(241.2482201068657 1107.3398062768845) rotate(0 449.42518282265075 91.49874062212032)"><path d="M876.72 171.48 C884.29 175.43, 894.48 180.72, 898.18 184.31 M876.72 171.48 C881.89 174.15, 888.7 177.82, 898.18 184.31" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(241.2482201068657 1107.3398062768845) rotate(0 449.42518282265075 91.49874062212032)"><path d="M889.99 160.69 C892.68 168.61, 897.93 177.92, 898.18 184.31 M889.99 160.69 C891.74 166.09, 895.14 172.53, 898.18 184.31" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-GQifOXxuV6i8FG52EAGCY"><rect x="0" y="0" fill="#fff" width="1240.6347091165858" height="1391.5519510063205"></rect><rect x="602.4552725688155" y="1156.8756708250626" fill="#000" width="218.01981937885284" height="75" opacity="1"></rect></mask><g transform="translate(602.4552725688154 1156.8756708250626) rotate(0 88.21813036070091 41.9628760739423)"><text x="109.00990968942642" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">updateTree</text><text x="109.00990968942642" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[canUpdateMerkleTree</text><text x="109.00990968942642" y="67.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">or core.governor]</text></g><g mask="url(#mask-Ac9mNNKoz3H0_a6mJfNzY)" stroke-linecap="round"><g transform="translate(224.7207333698925 1120.136575883109) rotate(0 450.9331713335652 95.79958142582198)"><path d="M-0.55 1.02 C15.4 26.11, 61.82 125.36, 95.24 150.26 C128.67 175.16, 165.12 150.49, 199.98 150.43 C234.83 150.38, 232.04 149.57, 304.38 149.95 C376.72 150.33, 570.15 152.63, 634.03 152.69 C697.91 152.74, 656.86 150.55, 687.67 150.29 C718.49 150.02, 783.47 144.27, 818.92 151.07 C854.37 157.87, 886.52 184.16, 900.39 191.09 M1.37 0.51 C17.17 25.79, 61.21 126.35, 94.17 151.52 C127.13 176.7, 164.09 151.72, 199.13 151.57 C234.17 151.42, 232.08 150.4, 304.42 150.62 C376.76 150.84, 569.08 153.15, 633.17 152.88 C697.27 152.61, 657.91 149.49, 688.97 148.99 C720.02 148.49, 783.93 142.97, 819.5 149.88 C855.07 156.78, 888.87 183.43, 902.41 190.42" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(224.7207333698925 1120.136575883109) rotate(0 450.9331713335652 95.79958142582198)"><path d="M877.92 185.42 C886.1 186.46, 890.04 187.93, 902.41 190.42 M877.92 185.42 C885.52 186.63, 891.01 187.89, 902.41 190.42" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(224.7207333698925 1120.136575883109) rotate(0 450.9331713335652 95.79958142582198)"><path d="M886.87 170.84 C892.64 175.71, 894.2 181.05, 902.41 190.42 M886.87 170.84 C892 175.95, 895.05 181.16, 902.41 190.42" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-Ac9mNNKoz3H0_a6mJfNzY"><rect x="0" y="0" fill="#fff" width="1226.2662496044113" height="1411.8610337296695"></rect><rect x="602.5895584158648" y="1247.0831483023705" fill="#000" width="190.99989140033722" height="50" opacity="1"></rect></mask><g transform="translate(602.5895584158648 1247.0831483023708) rotate(0 73.06434628759297 -31.14699099343983)"><text x="95.49994570016861" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">resolveDispute</text><text x="95.49994570016861" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[core.onlyGovernor]</text></g><g mask="url(#mask-LneCoIGoUOb7xLaMoNFDa)" stroke-linecap="round"><g transform="translate(187.52662139658855 1135.1165420424336) rotate(0 247.62119014013587 387.4909969128357)"><path d="M-0.24 -0.52 C21.88 107.39, 92.72 540.38, 132.19 648.47 C171.67 756.55, 201.5 647.99, 236.6 647.98 C271.7 647.98, 299.73 627.36, 342.8 648.44 C385.87 669.52, 469.63 753.35, 495.02 774.49 M-1.83 1.82 C20.17 110.43, 91.59 541.98, 131.17 649.83 C170.74 757.67, 200.82 648.95, 235.63 648.87 C270.43 648.79, 297.03 628.39, 340 649.35 C382.96 670.31, 467.35 753.78, 493.43 774.62" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(187.52662139658855 1135.1165420424336) rotate(0 247.62119014013587 387.4909969128357)"><path d="M470.16 765.47 C475.29 767.59, 481.5 769.52, 493.43 774.62 M470.16 765.47 C475.44 767.24, 480.9 770.01, 493.43 774.62" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(187.52662139658855 1135.1165420424336) rotate(0 247.62119014013587 387.4909969128357)"><path d="M481.49 752.65 C483.85 758.01, 487.18 763.2, 493.43 774.62 M481.49 752.65 C484.27 757.35, 487.12 763.08, 493.43 774.62" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-LneCoIGoUOb7xLaMoNFDa"><rect x="0" y="0" fill="#fff" width="782.7690016768603" height="2010.098535868105"></rect><rect x="400.56174047417664" y="1771.4295994771533" fill="#000" width="47.8999719619751" height="25" opacity="1"></rect></mask><g transform="translate(400.56174047417664 1771.4295994771533) rotate(0 33.560390105213685 -249.2626455679499)"><text x="23.94998598098755" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">owns</text></g><g mask="url(#mask-H4D0NMoaoGccig1ffVvHW)" stroke-linecap="round"><g transform="translate(175.49623277119463 294.13901753993423) rotate(0 470.97318266349527 548.2872676274154)"><path d="M-0.15 -0.34 C23.51 167.54, 100.18 831.13, 141.75 1006.76 C183.31 1182.4, 214.12 1045.72, 249.25 1053.48 C284.37 1061.25, 281.94 1052.84, 352.49 1053.36 C423.04 1053.88, 608.85 1056.62, 672.55 1056.6 C736.25 1056.57, 702.18 1053.52, 734.69 1053.22 C767.2 1052.92, 833.15 1055.17, 867.59 1054.8 C902.03 1054.44, 928.85 1051.61, 941.34 1051.03 M-1.69 -1.57 C21.8 166.49, 98.8 831.84, 140.59 1007.87 C182.39 1183.89, 213.76 1046.88, 249.09 1054.6 C284.43 1062.33, 282.3 1053.78, 352.62 1054.21 C422.94 1054.63, 607.21 1056.92, 671.02 1057.14 C734.83 1057.36, 702.57 1056.04, 735.49 1055.51 C768.42 1054.98, 833.86 1054.82, 868.55 1053.96 C903.24 1053.1, 931.55 1050.45, 943.64 1050.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(175.49623277119463 294.13901753993423) rotate(0 470.97318266349527 548.2872676274154)"><path d="M920.54 1059.93 C926.47 1056.63, 932.13 1056.55, 943.64 1050.36 M920.54 1059.93 C926.67 1057.69, 932.01 1054.35, 943.64 1050.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(175.49623277119463 294.13901753993423) rotate(0 470.97318266349527 548.2872676274154)"><path d="M919.79 1042.85 C925.86 1044.33, 931.73 1049.03, 943.64 1050.36 M919.79 1042.85 C926.09 1045.21, 931.64 1046.46, 943.64 1050.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-H4D0NMoaoGccig1ffVvHW"><rect x="0" y="0" fill="#fff" width="1217.4672236383" height="1450.4447981892138"></rect><rect x="583.3587937828163" y="1311.842521119676" fill="#000" width="218.01981937885284" height="75" opacity="1"></rect></mask><g transform="translate(583.3587937828163 1311.842521119676) rotate(0 63.1106216518736 -469.41623595232625)"><text x="109.00990968942642" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">updateTree</text><text x="109.00990968942642" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[canUpdateMerkleTree</text><text x="109.00990968942642" y="67.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">or core.governor]</text></g><g mask="url(#mask-EWcig9V8x60_EHf4q_52T)" stroke-linecap="round"><g transform="translate(203.57394329958197 1422.7495631823017) rotate(0 462.21569945872176 -17.730370539760656)"><path d="M-0.39 -0.83 C19.26 -0.2, 80.87 2.76, 117.77 3.45 C154.67 4.14, 186.35 3.22, 221.01 3.33 C255.68 3.44, 256.11 3.72, 325.76 4.11 C395.4 4.5, 574.9 5.54, 638.88 5.65 C702.85 5.76, 676.17 5.18, 709.61 4.77 C743.05 4.36, 803.65 11.24, 839.52 3.2 C875.38 -4.84, 910.78 -35.85, 924.82 -43.44 M1.61 1.35 C21.22 2.18, 81.01 4.28, 117.43 4.79 C153.85 5.3, 185.38 4.29, 220.13 4.39 C254.88 4.48, 255.89 5.15, 325.94 5.37 C396 5.59, 576.84 5.9, 640.46 5.71 C704.08 5.52, 674.45 4.31, 707.68 4.23 C740.9 4.14, 803.76 13.22, 839.82 5.19 C875.89 -2.85, 909.79 -35.87, 924.06 -43.97" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(203.57394329958197 1422.7495631823017) rotate(0 462.21569945872176 -17.730370539760656)"><path d="M909.95 -23.33 C912.68 -27.27, 917.5 -35.53, 924.06 -43.97 M909.95 -23.33 C912.66 -29.23, 917.78 -33.26, 924.06 -43.97" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(203.57394329958197 1422.7495631823017) rotate(0 462.21569945872176 -17.730370539760656)"><path d="M899.99 -37.23 C905.54 -37.14, 913.22 -41.4, 924.06 -43.97 M899.99 -37.23 C905.26 -39.56, 912.96 -39.99, 924.06 -43.97" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-EWcig9V8x60_EHf4q_52T"><rect x="0" y="0" fill="#fff" width="1227.8878302703174" height="1572.121533574437"></rect><rect x="655.1219539769656" y="1403.443057687802" fill="#000" width="71.65993320941925" height="50" opacity="1"></rect></mask><g transform="translate(655.1219539769656 1403.443057687802) rotate(0 10.667688781338029 1.576134954739132)"><text x="35.829966604709625" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">claim</text><text x="35.829966604709625" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[public]</text></g><g mask="url(#mask-c_TDxKkVcBELJOPMcg9tQ)" stroke-linecap="round"><g transform="translate(197.10214475311045 1450.4846082646443) rotate(0 475.68548710398244 24.54377745538426)"><path d="M1.17 -0.37 C21.61 13.84, 84.6 70.2, 122.24 84.47 C159.87 98.74, 192.26 85.27, 226.98 85.25 C261.71 85.23, 259.05 85.23, 330.59 84.33 C402.12 83.43, 592.25 79.86, 656.19 79.86 C720.14 79.86, 682.52 83.55, 714.24 84.34 C745.96 85.13, 807.45 106.66, 846.52 84.61 C885.6 62.56, 931.56 -25.61, 948.7 -47.96 M0.33 -1.61 C20.62 12.84, 83.53 71.02, 121.17 85.75 C158.81 100.47, 191.31 86.83, 226.15 86.72 C261 86.61, 258.75 86.71, 330.23 85.1 C401.71 83.49, 590.86 76.83, 655.04 77.09 C719.22 77.34, 683.25 85.02, 715.32 86.63 C747.39 88.24, 808.15 109.17, 847.43 86.74 C886.72 64.31, 934.32 -25.53, 951.05 -47.93" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(197.10214475311045 1450.4846082646443) rotate(0 475.68548710398244 24.54377745538426)"><path d="M945.53 -23.54 C946.55 -30.5, 947.83 -36.85, 951.05 -47.93 M945.53 -23.54 C948.05 -30.63, 948.81 -39.84, 951.05 -47.93" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(197.10214475311045 1450.4846082646443) rotate(0 475.68548710398244 24.54377745538426)"><path d="M931.15 -32.79 C935.95 -37.41, 941.08 -41.29, 951.05 -47.93 M931.15 -32.79 C938.24 -36.8, 943.67 -43.02, 951.05 -47.93" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-c_TDxKkVcBELJOPMcg9tQ"><rect x="0" y="0" fill="#fff" width="1246.35929775651" height="1684.110311661492"></rect><rect x="596.6872101427186" y="1507.214766546951" fill="#000" width="196.59986698627472" height="50" opacity="1"></rect></mask><g transform="translate(596.6872101427186 1507.214766546951) rotate(0 76.1004217143743 -32.18638082692257)"><text x="98.29993349313736" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">disputeTree</text><text x="98.29993349313736" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[public (100 EURA)]</text></g><g mask="url(#mask-U54GaWX9db7Y2Wwlbt2vk)" stroke-linecap="round"><g transform="translate(170.96780049132371 1380.731510230893) rotate(0 218.64301019812308 -202.40347933147274)"><path d="M-0.23 -0.49 C24.43 -63.62, 106.93 -315.86, 149.08 -379.12 C191.23 -442.38, 217.68 -380.15, 252.69 -380.04 C287.69 -379.93, 328.42 -374.23, 359.08 -378.46 C389.74 -382.69, 423.53 -400.98, 436.65 -405.43 M-1.8 -1.8 C22.58 -65.27, 105.28 -318.21, 148.07 -381.09 C190.87 -443.96, 219.71 -379.15, 254.97 -379.06 C290.23 -378.96, 329.06 -376.13, 359.65 -380.52 C390.23 -384.9, 425.81 -401.05, 438.51 -405.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(170.96780049132371 1380.731510230893) rotate(0 218.64301019812308 -202.40347933147274)"><path d="M419.61 -388.99 C426.35 -395.66, 435.17 -403.9, 438.51 -405.36 M419.61 -388.99 C423.67 -393.71, 429.3 -397.53, 438.51 -405.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(170.96780049132371 1380.731510230893) rotate(0 218.64301019812308 -202.40347933147274)"><path d="M413.51 -404.97 C422.62 -405.28, 433.87 -407.16, 438.51 -405.36 M413.51 -404.97 C419.16 -405.78, 426.29 -405.65, 438.51 -405.36" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-U54GaWX9db7Y2Wwlbt2vk"><rect x="0" y="0" fill="#fff" width="708.2538208875699" height="1885.5384688938384"></rect><rect x="388.98085979390476" y="976.3228148251886" fill="#000" width="71.65993320941925" height="50" opacity="1"></rect></mask><g transform="translate(388.98085979390476 976.3228148251885) rotate(0 0.33782254404363243 199.89361213517554)"><text x="35.829966604709625" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">create</text><text x="35.829966604709625" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[public]</text></g><g mask="url(#mask-We6kRjalSYj1nRQgvoFzd)" stroke-linecap="round"><g transform="translate(296.60212220687174 1614.3337511624509) rotate(0 430.95438422509386 -75.56544326119817)"><path d="M-0.12 0.29 C3.65 5.29, 0.92 24.36, 22.41 29.49 C43.9 34.62, 93.84 31.07, 128.81 31.07 C163.78 31.07, 162.78 29.62, 232.21 29.5 C301.65 29.39, 481.56 30.15, 545.43 30.37 C609.3 30.59, 581.79 31.01, 615.43 30.83 C649.06 30.65, 705.9 67.48, 747.26 29.3 C788.61 -8.87, 844.4 -160.64, 863.55 -198.23 M-1.64 -0.6 C2.5 4.59, 2.85 25.72, 24.51 30.7 C46.18 35.67, 93.76 29.3, 128.36 29.23 C162.96 29.16, 162.82 30.06, 232.13 30.28 C301.44 30.5, 480.16 30.59, 544.23 30.57 C608.3 30.55, 582.59 30.06, 616.55 30.16 C650.51 30.27, 706.87 69.39, 747.97 31.19 C789.07 -7.02, 843.78 -161.02, 863.13 -199.06" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(296.60212220687174 1614.3337511624509) rotate(0 430.95438422509386 -75.56544326119817)"><path d="M861.65 -174.1 C862.65 -183.05, 861.53 -187.13, 863.13 -199.06 M861.65 -174.1 C862.77 -183.85, 862.44 -192.42, 863.13 -199.06" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(296.60212220687174 1614.3337511624509) rotate(0 430.95438422509386 -75.56544326119817)"><path d="M845.95 -180.89 C851.34 -187.91, 854.63 -190.09, 863.13 -199.06 M845.95 -180.89 C852.98 -187.98, 858.61 -193.98, 863.13 -199.06" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-We6kRjalSYj1nRQgvoFzd"><rect x="0" y="0" fill="#fff" width="1259.0053771261828" height="1942.345426939691"></rect><rect x="617.7086442578726" y="1619.148401076286" fill="#000" width="143.69991886615753" height="50" opacity="1"></rect></mask><g transform="translate(617.7086442578726 1619.1484010762867) rotate(0 109.847862174093 -80.38009317503369)"><text x="71.84995943307877" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">claim</text><text x="71.84995943307877" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[onlyOperator]</text></g><g mask="url(#mask-WhVRyyxQdMzJ6dTchJPXI)" stroke-linecap="round"><g transform="translate(296.94638116056285 1952.2910930116032) rotate(0 182.12910316348743 -0.5612607754691226)"><path d="M-1.16 -0.63 C7.55 -0.64, 26.02 0.35, 52.14 0.24 C78.25 0.12, 120.68 -1.12, 155.54 -1.33 C190.41 -1.55, 226.63 -1.22, 261.33 -1.06 C296.02 -0.91, 346.53 -0.27, 363.7 -0.41 M0.44 1.65 C9.05 1.29, 25.84 -1.06, 51.5 -1.39 C77.17 -1.72, 119.39 -0.55, 154.44 -0.34 C189.5 -0.14, 226.65 -0.26, 261.84 -0.15 C297.03 -0.03, 348.26 0.28, 365.6 0.35" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(296.94638116056285 1952.2910930116032) rotate(0 182.12910316348743 -0.5612607754691226)"><path d="M342.07 8.78 C347.82 7.56, 357.28 3.06, 365.6 0.35 M342.07 8.78 C347.41 7.35, 353.83 3.98, 365.6 0.35" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(296.94638116056285 1952.2910930116032) rotate(0 182.12910316348743 -0.5612607754691226)"><path d="M342.15 -8.32 C347.92 -4.58, 357.35 -4.13, 365.6 0.35 M342.15 -8.32 C347.55 -5.51, 353.94 -4.63, 365.6 0.35" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-WhVRyyxQdMzJ6dTchJPXI"><rect x="0" y="0" fill="#fff" width="761.2045874875378" height="2053.4136145625416"></rect><rect x="328.745638982816" y="1926.5788148251884" fill="#000" width="248.75980961322784" height="50" opacity="1"></rect></mask><g transform="translate(328.745638982816 1926.5788148251886) rotate(0 150.42254009921552 25.81366278263772)"><text x="124.37990480661392" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">toggleDispute</text><text x="124.37990480661392" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[disputer.onlyWhitelisted]</text></g><g mask="url(#mask-fp5M6ZnFgtiEqL09mqD8B)" stroke-linecap="round"><g transform="translate(803.3323691484737 1952.5906079821139) rotate(0 187.70129253648741 -247.6339267865191)"><path d="M0.6 0.95 C27.79 1.18, 113.04 0.76, 161.99 0.77 C210.94 0.79, 258.61 90.38, 294.27 1.04 C329.93 -88.29, 362.22 -445.6, 375.95 -535.23 M-0.54 0.4 C27.14 0.84, 115.31 1.69, 164.37 1.98 C213.42 2.28, 258.52 92.08, 293.77 2.18 C329.03 -87.72, 362.33 -447.59, 375.9 -537.42" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(803.3323691484737 1952.5906079821139) rotate(0 187.70129253648741 -247.6339267865191)"><path d="M381.53 -513.06 C382.1 -519.05, 378.33 -522.1, 375.9 -537.42 M381.53 -513.06 C380.23 -520.3, 379.55 -526.99, 375.9 -537.42" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g><g transform="translate(803.3323691484737 1952.5906079821139) rotate(0 187.70129253648741 -247.6339267865191)"><path d="M364.56 -515.14 C368.56 -520.79, 368.26 -523.42, 375.9 -537.42 M364.56 -515.14 C367.84 -521.92, 371.76 -528.04, 375.9 -537.42" stroke="#1e1e1e" stroke-width="2" fill="none"></path></g></g><mask id="mask-fp5M6ZnFgtiEqL09mqD8B"><rect x="0" y="0" fill="#fff" width="1279.834103011782" height="2589.9246079821137"></rect><rect x="936.8181072970307" y="1961.3305211789939" fill="#000" width="196.59986698627472" height="50" opacity="1"></rect></mask><g transform="translate(936.8181072970307 1961.3305211789943) rotate(0 54.215554387930496 -256.37383998339925)"><text x="98.29993349313736" y="17.619999999999997" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">disputeTree</text><text x="98.29993349313736" y="42.62" font-family="Excalifont, Xiaolai, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="middle" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">[public (100 EURA)]</text></g></svg> \ No newline at end of file diff --git a/package.json b/package.json index 2fc505b..36e1b16 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "foundry:compile": "forge build --optimize --optimizer-runs 1000", "foundry:coverage": "forge coverage --ir-minimum --report lcov && yarn lcov:clean && yarn lcov:generate-html", "foundry:script": "forge script -vvvv", - "foundry:deploy": "forge script --broadcast --verify -vvvv", + "foundry:deploy": "source .env && forge script --broadcast --verify -vvvv", "foundry:gas": "forge test --gas-report", "foundry:run": "docker run -it --rm -v $(pwd):/app -w /app ghcr.io/foundry-rs/foundry sh", "foundry:setup": "curl -L https://foundry.paradigm.xyz | bash && foundryup && git submodule update --init --recursive", diff --git a/scripts/DistributionCreator.s.sol b/scripts/DistributionCreator.s.sol index 55100a3..69d3259 100644 --- a/scripts/DistributionCreator.s.sol +++ b/scripts/DistributionCreator.s.sol @@ -192,10 +192,14 @@ contract SetUserFeeRebate is DistributionCreatorScript { // SetRewardTokenMinAmounts script contract SetRewardTokenMinAmounts is DistributionCreatorScript { + // forge script scripts/DistributionCreator.s.sol:SetRewardTokenMinAmounts --rpc-url bsc --sender 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701 --broadcast -i 1 function run() external { + console.log("DEPLOYER_ADDRESS:", broadcaster); // MODIFY THESE VALUES TO SET YOUR DESIRED TOKENS AND AMOUNTS - address[] memory tokens = new address[](0); - uint256[] memory amounts = new uint256[](0); + address[] memory tokens = new address[](1); + uint256[] memory amounts = new uint256[](1); + tokens[0] = 0x56fA5F7BF457454Be33D8B978C86A5f5B9DD84C2; + amounts[0] = 3 * 10 ** 17; _run(tokens, amounts); } diff --git a/scripts/deployPufferPointTokenWrapper.s.sol b/scripts/deployPufferPointTokenWrapper.s.sol new file mode 100644 index 0000000..49b7757 --- /dev/null +++ b/scripts/deployPufferPointTokenWrapper.s.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; + +import { BaseScript } from "./utils/Base.s.sol"; + +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { JsonReader } from "@utils/JsonReader.sol"; +import { ContractType } from "@utils/Constants.sol"; + +import { PufferPointTokenWrapper } from "../contracts/partners/tokenWrappers/PufferPointTokenWrapper.sol"; +import { DistributionCreator } from "../contracts/DistributionCreator.sol"; +import { IAccessControlManager } from "../contracts/interfaces/IAccessControlManager.sol"; +import { MockToken } from "../contracts/mock/MockToken.sol"; + +contract DeployPufferPointTokenWrapper is BaseScript { + function run() public { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + address underlying = 0x282A69142bac47855C3fbE1693FcC4bA3B4d5Ed6; + uint32 cliffDuration = 500; + // uint32 cliffDuration = 1 weeks; + IAccessControlManager manager = IAccessControlManager(0x0E632a15EbCBa463151B5367B4fCF91313e389a6); + address distributionCreator = 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd; + + // ARBITRUM TEST + /* + // aglaMerkl + address underlying = 0xE0688A2FE90d0f93F17f273235031062a210d691; + uint32 cliffDuration = 500; + // uint32 cliffDuration = 1 weeks; + IAccessControlManager manager = IAccessControlManager(0xA86CC1ae2D94C6ED2aB3bF68fB128c2825673267); + address distributionCreator = 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd; + */ + + // Deploy implementation + PufferPointTokenWrapper implementation = new PufferPointTokenWrapper(); + console.log("PufferPointTokenWrapper Implementation:", address(implementation)); + + // Deploy proxy + ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), ""); + console.log("PufferPointTokenWrapper Proxy:", address(proxy)); + + // Initialize + PufferPointTokenWrapper(address(proxy)).initialize(underlying, cliffDuration, manager, distributionCreator); + vm.stopBroadcast(); + } +} diff --git a/scripts/deploySonicFragment.s.sol b/scripts/deploySonicFragment.s.sol new file mode 100644 index 0000000..51bc795 --- /dev/null +++ b/scripts/deploySonicFragment.s.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; + +import { BaseScript } from "./utils/Base.s.sol"; + +import { console } from "forge-std/console.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { JsonReader } from "@utils/JsonReader.sol"; +import { ContractType } from "@utils/Constants.sol"; + +import { SonicFragment } from "../contracts/partners/tokenWrappers/SonicFragment.sol"; +import { DistributionCreator } from "../contracts/DistributionCreator.sol"; +import { IAccessControlManager } from "../contracts/interfaces/IAccessControlManager.sol"; +import { MockToken } from "../contracts/mock/MockToken.sol"; + +contract DeploySonicFragment is BaseScript { + function run() public broadcast { + console.log("DEPLOYER_ADDRESS:", broadcaster); + + // Sonic address - to check + IAccessControlManager manager = IAccessControlManager(0xa25c30044142d2fA243E7Fd3a6a9713117b3c396); + address recipient = address(broadcaster); + // TODO this is the wrapped Sonic address + address sToken = address(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38); + uint256 totalSupply = 100_000_000 ether; + string memory name = "Fragment xxx"; + string memory symbol = "frgxxx"; + + // Deploy implementation + SonicFragment implementation = new SonicFragment( + address(manager), + recipient, + sToken, + totalSupply, + name, + symbol + ); + console.log("SonicFragment deployed at:", address(implementation)); + } +} diff --git a/test/deprecated/hardhat/tokenWrappers/pufferPointTokenWrapper.test.ts b/test/deprecated/hardhat/tokenWrappers/pufferPointTokenWrapper.test.ts new file mode 100644 index 0000000..cd04a69 --- /dev/null +++ b/test/deprecated/hardhat/tokenWrappers/pufferPointTokenWrapper.test.ts @@ -0,0 +1,248 @@ +/* +// TODO: write tests back in Foundry +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { expect } from 'chai'; +import { parseEther, solidityKeccak256 } from 'ethers/lib/utils'; +import hre, { contract, ethers, web3,network } from 'hardhat'; +import { Signer } from 'ethers'; + +import { + DistributionCreator, + DistributionCreator__factory, + MockCoreBorrow, + MockCoreBorrow__factory, + MockToken, + MockToken__factory, + MockUniswapV3Pool, + MockUniswapV3Pool__factory, + PufferPointTokenWrapper, + PufferPointTokenWrapper__factory +} from '../../../typechain'; +import { parseAmount } from '../../../utils/bignumber'; +import { inReceipt } from '../utils/expectEvent'; +import { deployUpgradeableUUPS, increaseTime, latestTime, MAX_UINT256, ZERO_ADDRESS } from '../utils/helpers'; + +contract('PufferPointTokenWrapper', () => { + let deployer: SignerWithAddress; + let alice: SignerWithAddress; + let bob: SignerWithAddress; + let governor: string; + let guardian: string; + let distributor: string; + let distributionCreator: string; + let feeRecipient: string; + let angle: MockToken; + let agEUR: MockToken; + let tokenWrapper: PufferPointTokenWrapper; + let cliffDuration: number; + + let manager: DistributionCreator; + let core: MockCoreBorrow; + + const impersonatedSigners: { [key: string]: Signer } = {}; + + beforeEach(async () => { + [deployer, alice, bob] = await ethers.getSigners(); + await network.provider.request({ + method: 'hardhat_reset', + params: [ + { + forking: { + jsonRpcUrl: process.env.ETH_NODE_URI_MAINNET, + blockNumber: 21313975, + }, + }, + ], + }); + // add any addresses you want to impersonate here + governor = '0xdC4e6DFe07EFCa50a197DF15D9200883eF4Eb1c8'; + guardian = '0x0C2553e4B9dFA9f83b1A6D3EAB96c4bAaB42d430'; + distributor = '0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae'; + distributionCreator = '0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd'; + feeRecipient = '0xeaC6A75e19beB1283352d24c0311De865a867DAB' + const impersonatedAddresses = [governor, guardian, distributor, distributionCreator, feeRecipient]; + + for (const address of impersonatedAddresses) { + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [address], + }); + await hre.network.provider.send('hardhat_setBalance', [address, '0x10000000000000000000000000000']); + impersonatedSigners[address] = await ethers.getSigner(address); + } + + angle = (await new MockToken__factory(deployer).deploy('ANGLE', 'ANGLE', 18)) as MockToken; + core = (await new MockCoreBorrow__factory(deployer).deploy()) as MockCoreBorrow; + await core.toggleGuardian(guardian); + await core.toggleGovernor(governor); + cliffDuration = 2592000; + + tokenWrapper = (await deployUpgradeableUUPS(new PufferPointTokenWrapper__factory(deployer))) as PufferPointTokenWrapper; + await tokenWrapper.initialize(angle.address, cliffDuration, core.address, distributionCreator); + await angle.mint(alice.address, parseEther('1000')); + }); + describe('upgrade', () => { + it('success - upgrades to new implementation', async () => { + const newImplementation = await new PufferPointTokenWrapper__factory(deployer).deploy(); + await tokenWrapper.connect(impersonatedSigners[governor]).upgradeTo(newImplementation.address); + }); + it('reverts - when called by unallowed address', async () => { + const newImplementation = await new PufferPointTokenWrapper__factory(deployer).deploy(); + await expect(tokenWrapper.connect(alice).upgradeTo(newImplementation.address)).to.be.revertedWithCustomError( + tokenWrapper, + 'NotGovernor', + ); + }); + }); + describe('initializer', () => { + it('success - treasury', async () => { + expect(await tokenWrapper.cliffDuration()).to.be.equal(cliffDuration); + expect(await tokenWrapper.core()).to.be.equal(core.address); + expect(await tokenWrapper.underlying()).to.be.equal(angle.address); + }); + it('reverts - already initialized', async () => { + await expect(tokenWrapper.initialize(angle.address, cliffDuration,core.address, distributionCreator)).to.be.revertedWith( + 'Initializable: contract is already initialized', + ); + }); + it('reverts - zero address', async () => { + const tokenWrapperRevert = (await deployUpgradeableUUPS(new PufferPointTokenWrapper__factory(deployer))) as PufferPointTokenWrapper; + await expect( + tokenWrapperRevert.initialize(ZERO_ADDRESS,cliffDuration, core.address, distributionCreator), + ).to.be.reverted; + }); + }); + describe('createCampaign', () => { + it('success - balance credited', async () => { + await angle.connect(alice).approve(tokenWrapper.address, MAX_UINT256); + await tokenWrapper.connect(alice).transfer(distributor,parseEther('1')); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('1')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('1')); + }); + it('success - balance credited - feeRecipient', async () => { + await angle.connect(alice).approve(tokenWrapper.address, MAX_UINT256); + await tokenWrapper.connect(alice).transfer(feeRecipient,parseEther('1')); + expect(await tokenWrapper.balanceOf(feeRecipient)).to.be.equal(0); + expect(await angle.balanceOf(feeRecipient)).to.be.equal(parseEther('1')); + }); + it('reverts - when other contract', async () => { + await angle.connect(alice).approve(tokenWrapper.address, MAX_UINT256); + await expect(tokenWrapper.connect(alice).transfer(governor,parseEther('1'))).to.be.reverted; + }); + }); + describe('claimRewards', () => { + it('success - balance credited', async () => { + await angle.connect(alice).approve(tokenWrapper.address, MAX_UINT256); + await tokenWrapper.connect(alice).transfer(distributor,parseEther('1')); + await tokenWrapper.connect(impersonatedSigners[distributor]).transfer(bob.address, parseEther('0.5')); + const endData = await latestTime(); + expect(await tokenWrapper.balanceOf(bob.address)).to.be.equal(0); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.5')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('1')); + + const vestings = await tokenWrapper.getUserVestings(bob.address); + expect(vestings[0][0].amount).to.be.equal(parseEther('0.5')); + expect(vestings[0][0].unlockTimestamp).to.be.equal(endData+cliffDuration); + expect(vestings[1]).to.be.equal(0); + + await increaseTime(cliffDuration/2); + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(0); + await increaseTime(cliffDuration*2); + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(parseEther('0.5')); + await tokenWrapper.claim(bob.address); + expect(await angle.balanceOf(bob.address)).to.be.equal(parseEther('0.5')) + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.5')); + + const vestings2 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings2[0][0].amount).to.be.equal(parseEther('0.5')); + expect(vestings2[0][0].unlockTimestamp).to.be.equal(endData+cliffDuration); + expect(vestings2[1]).to.be.equal(1); + + await tokenWrapper.connect(impersonatedSigners[distributor]).transfer(bob.address, parseEther('0.2')); + const endTime2 = await latestTime(); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.3')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.5')); + const vestings3 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings3[0][1].amount).to.be.equal(parseEther('0.2')); + expect(vestings3[0][1].unlockTimestamp).to.be.equal(endTime2+cliffDuration); + expect(vestings3[1]).to.be.equal(1); + + await increaseTime(cliffDuration/2); + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(0); + await tokenWrapper.connect(impersonatedSigners[distributor]).transfer(bob.address, parseEther('0.12')); + const endTime3 = await latestTime(); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.18')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.5')); + + const vestings4 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings4[0][1].amount).to.be.equal(parseEther('0.2')); + expect(vestings4[0][1].unlockTimestamp).to.be.equal(endTime2+cliffDuration); + expect(vestings4[1]).to.be.equal(1); + expect(vestings4[0][2].amount).to.be.equal(parseEther('0.12')); + expect(vestings4[0][2].unlockTimestamp).to.be.equal(endTime3+cliffDuration); + + await increaseTime(cliffDuration*3/4); + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(parseEther('0.2')); + + await tokenWrapper.claim(bob.address) + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(parseEther('0')); + expect(await angle.balanceOf(bob.address)).to.be.equal(parseEther('0.7')) + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.3')); + + const vestings5 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings5[0][1].amount).to.be.equal(parseEther('0.2')); + expect(vestings5[0][1].unlockTimestamp).to.be.equal(endTime2+cliffDuration); + expect(vestings5[1]).to.be.equal(2); + + await tokenWrapper.connect(impersonatedSigners[distributor]).transfer(bob.address, parseEther('0.1')); + const endTime4 = await latestTime(); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.08')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.3')); + const vestings6 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings6[0][3].amount).to.be.equal(parseEther('0.1')); + expect(vestings6[0][3].unlockTimestamp).to.be.equal(endTime4+cliffDuration); + expect(vestings6[1]).to.be.equal(2); + + await tokenWrapper.connect(impersonatedSigners[distributor]).transfer(alice.address, parseEther('0.05')); + const endTime5 = await latestTime(); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.03')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.3')); + const vestings7 = await tokenWrapper.getUserVestings(alice.address); + expect(vestings7[0][0].amount).to.be.equal(parseEther('0.05')); + expect(vestings7[0][0].unlockTimestamp).to.be.equal(endTime5+cliffDuration); + expect(vestings7[1]).to.be.equal(0); + + await increaseTime(cliffDuration*2); + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(parseEther('0.22')); + expect(await tokenWrapper.claimable(alice.address)).to.be.equal(parseEther('0.05')); + await tokenWrapper.claim(bob.address); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.03')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.08')); + expect(await angle.balanceOf(bob.address)).to.be.equal(parseEther('0.92')); + + const vestings8 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings8[1]).to.be.equal(4); + expect(await tokenWrapper.claimable(bob.address)).to.be.equal(0); + + await tokenWrapper.claim(alice.address); + const vestings9 = await tokenWrapper.getUserVestings(alice.address); + expect(vestings9[1]).to.be.equal(1); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.03')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.03')); + expect(await angle.balanceOf(bob.address)).to.be.equal(parseEther('0.92')); + expect(await angle.balanceOf(alice.address)).to.be.equal(parseEther('999.05')); + + await tokenWrapper.claim(alice.address); + await tokenWrapper.claim(bob.address); + const vestings10 = await tokenWrapper.getUserVestings(alice.address); + const vestings11 = await tokenWrapper.getUserVestings(bob.address); + expect(vestings10[1]).to.be.equal(1); + expect(vestings11[1]).to.be.equal(4); + expect(await tokenWrapper.balanceOf(distributor)).to.be.equal(parseEther('0.03')); + expect(await angle.balanceOf(tokenWrapper.address)).to.be.equal(parseEther('0.03')); + expect(await angle.balanceOf(bob.address)).to.be.equal(parseEther('0.92')); + expect(await angle.balanceOf(alice.address)).to.be.equal(parseEther('999.05')); + }); + }); +}); +*/ \ No newline at end of file