Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8b99a3b
First set of changes for MTX on core contracts
area May 10, 2021
aa02c18
First test of metatransactions
area May 13, 2021
0f2ec21
ColonyNetwork metatransactions
area May 18, 2021
c820be8
Extensions and Tokenlocking
area May 18, 2021
435bda0
Move tests to using metatx helper
area May 18, 2021
84d0028
Remove .only from metatransaction tests
area May 18, 2021
68ade4f
Remove event leftover from testing
area May 24, 2021
731ba78
BasicMetatransaction does not need recovery mods
area May 24, 2021
5493e09
Fix BasicMetaTransaction for Slither
area May 24, 2021
8c7534d
First pass at recovery-mode-meta-transactions
area May 27, 2021
ae07466
Add protected nonce mapping to colonyNetwork
area May 27, 2021
e730eef
Remove msg.sender and cleanups following rebase
area May 28, 2021
ee6808c
Update helper for ganache/coverage inconsistency
area Jun 1, 2021
8b708ed
MTX test for all extensions, update upgrade tests
area Jun 2, 2021
c34be20
Low hanging fruit from first review
area Jun 15, 2021
9972066
Remove unneeded extra recovery contracts
area Jun 17, 2021
9137c9b
Add token that supports Permit and Metatxs
area Jun 29, 2021
a2f0289
Import what contracts we can
area Jun 29, 2021
125f99e
Changes following first metaTxToken review
area Jun 30, 2021
c811aab
Split out contracts that were in metatxtoken.sol
area Jun 30, 2021
1a1b9ce
Update storage and recovery check scripts
area Jun 30, 2021
b58e446
Minor non-functional changes to metatxtoken
area Jul 1, 2021
fb91366
Use ++ for incrementing nonces
area Jul 2, 2021
9a6eee3
Use a helper in beforeEach
area Jul 6, 2021
d4a81a5
Allow Token deployment via metatransaction
area Jul 27, 2021
9c81001
Update protected variable test post-rebase
area Jul 28, 2021
281129d
Changes following second review
area Aug 9, 2021
cb3e722
Improve coverage
area Aug 9, 2021
a03ebd6
Add ability to deploy TokenAuthority via MTX
area Sep 30, 2021
e7ba5c7
Rename deployToken
area Sep 30, 2021
096849d
Changes following review
area Sep 30, 2021
da68f3b
Add new TokenAuthority to build scripts
kronosapiens Oct 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 44 additions & 162 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ pragma experimental ABIEncoderV2;

import "./../common/ERC20Extended.sol";
import "./../common/IEtherRouter.sol";
import "./../common/MultiChain.sol";
import "./../common/BasicMetaTransaction.sol";
import "./../tokenLocking/ITokenLocking.sol";
import "./ColonyStorage.sol";

contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs {

// V8: Ebony Lightweight Spaceship
// This function, exactly as defined, is used in build scripts. Take care when updating.
Expand All @@ -39,144 +39,8 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
return token;
}

bytes4 constant APPROVE_SIG = bytes4(keccak256("approve(address,uint256)"));
bytes4 constant TRANSFER_SIG = bytes4(keccak256("transfer(address,uint256)"));
bytes4 constant TRANSFER_FROM_SIG = bytes4(keccak256("transferFrom(address,address,uint256)"));
bytes4 constant BURN_SIG = bytes4(keccak256("burn(uint256)"));
bytes4 constant BURN_GUY_SIG = bytes4(keccak256("burn(address,uint256)"));

function makeArbitraryTransaction(address _to, bytes memory _action)
public stoppable auth
returns (bool)
{
return this.makeSingleArbitraryTransaction(_to, _action);
}

function makeArbitraryTransactions(address[] memory _targets, bytes[] memory _actions, bool _strict)
public stoppable auth
returns (bool)
{
require(_targets.length == _actions.length, "colony-targets-and-actions-length-mismatch");
for (uint256 i; i < _targets.length; i += 1){
bool success = true;
// slither-disable-next-line unused-return
try this.makeSingleArbitraryTransaction(_targets[i], _actions[i]) returns (bool ret){
if (_strict){
success = ret;
}
} catch {
// We failed in a require, which is only okay if we're not in strict mode
if (_strict){
success = false;
}
}
require(success, "colony-arbitrary-transaction-failed");
}
return true;
}

function makeSingleArbitraryTransaction(address _to, bytes memory _action)
external stoppable self
returns (bool)
{
// Prevent transactions to network contracts
require(_to != address(this), "colony-cannot-target-self");
require(_to != colonyNetworkAddress, "colony-cannot-target-network");
require(_to != tokenLockingAddress, "colony-cannot-target-token-locking");

// Prevent transactions to transfer held tokens
bytes4 sig;
assembly { sig := mload(add(_action, 0x20)) }

if (sig == APPROVE_SIG) { approveTransactionPreparation(_to, _action); }
else if (sig == BURN_SIG) { burnTransactionPreparation(_to, _action); }
else if (sig == TRANSFER_SIG) { transferTransactionPreparation(_to, _action); }
else if (sig == BURN_GUY_SIG || sig == TRANSFER_FROM_SIG) { burnGuyOrTransferFromTransactionPreparation(_action); }

// Prevent transactions to network-managed extensions installed in this colony
require(isContract(_to), "colony-to-must-be-contract");
// slither-disable-next-line unused-return
try ColonyExtension(_to).identifier() returns (bytes32 extensionId) {
require(
IColonyNetwork(colonyNetworkAddress).getExtensionInstallation(extensionId, address(this)) != _to,
"colony-cannot-target-extensions"
);
} catch {}

bool res = executeCall(_to, 0, _action);

if (sig == APPROVE_SIG) { approveTransactionCleanup(_to, _action); }

return res;
}

function approveTransactionPreparation(address _to, bytes memory _action) internal {
address spender;
assembly {
spender := mload(add(_action, 0x24))
}
updateApprovalAmountInternal(_to, spender, false);
}

function approveTransactionCleanup(address _to, bytes memory _action) internal {
address spender;
assembly {
spender := mload(add(_action, 0x24))
}
updateApprovalAmountInternal(_to, spender, true);
}

function burnTransactionPreparation(address _to, bytes memory _action) internal {
uint256 amount;
assembly {
amount := mload(add(_action, 0x24))
}
fundingPots[1].balance[_to] = sub(fundingPots[1].balance[_to], amount);
require(fundingPots[1].balance[_to] >= tokenApprovalTotals[_to], "colony-not-enough-tokens");
}

function transferTransactionPreparation(address _to, bytes memory _action) internal {
uint256 amount;
assembly {
amount := mload(add(_action, 0x44))
}
fundingPots[1].balance[_to] = sub(fundingPots[1].balance[_to], amount);
require(fundingPots[1].balance[_to] >= tokenApprovalTotals[_to], "colony-not-enough-tokens");
}

function burnGuyOrTransferFromTransactionPreparation(bytes memory _action) internal {
address spender;
assembly {
spender := mload(add(_action, 0x24))
}
require(spender != address(this), "colony-cannot-spend-own-allowance");
}

function updateApprovalAmount(address _token, address _spender) stoppable public {
updateApprovalAmountInternal(_token, _spender, false);
}

function updateApprovalAmountInternal(address _token, address _spender, bool _postApproval) internal {
uint256 recordedApproval = tokenApprovals[_token][_spender];
uint256 actualApproval = ERC20Extended(_token).allowance(address(this), _spender);
if (recordedApproval == actualApproval) {
return;
}

if (recordedApproval > actualApproval && !_postApproval){
// They've spend some tokens out of root. Adjust balances accordingly
// If we are post approval, then they have not spent tokens
fundingPots[1].balance[_token] = add(sub(fundingPots[1].balance[_token], recordedApproval), actualApproval);
}

tokenApprovalTotals[_token] = add(sub(tokenApprovalTotals[_token], recordedApproval), actualApproval);
require(fundingPots[1].balance[_token] >= tokenApprovalTotals[_token], "colony-approval-exceeds-balance");

tokenApprovals[_token][_spender] = actualApproval;
}

function annotateTransaction(bytes32 _txHash, string memory _metadata) public always {
emit Annotation(msg.sender, _txHash, _metadata);
emit Annotation(msgSender(), _txHash, _metadata);
}

function emitDomainReputationReward(uint256 _domainId, address _user, int256 _amount)
Expand Down Expand Up @@ -256,19 +120,13 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
// Set initial colony reward inverse amount to the max indicating a zero rewards to start with
rewardInverse = 2**256 - 1;

emit ColonyInitialised(msg.sender, _colonyNetworkAddress, _token);
}

function initialiseColony(address _colonyNetworkAddress, address _token, string memory _metadata) public stoppable {
initialiseColony(_colonyNetworkAddress, _token);

emit ColonyMetadata(msg.sender, _metadata);
emit ColonyInitialised(msgSender(), _colonyNetworkAddress, _token);
}

function editColony(string memory _metadata) public
stoppable
auth {
emit ColonyMetadata(msg.sender, _metadata);
emit ColonyMetadata(msgSender(), _metadata);
}

function bootstrapColony(address[] memory _users, int[] memory _amounts) public
Expand All @@ -288,7 +146,17 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_users[i], _amounts[i], domains[1].skillId);
}

emit ColonyBootstrapped(msg.sender, _users, _amounts);
emit ColonyBootstrapped(msgSender(), _users, _amounts);
}

function burnTokens(address _token, uint256 _amount) public stoppable auth {
// Check the root funding pot has enought
require(fundingPots[1].balance[_token] >= _amount, "colony-not-enough-tokens");
fundingPots[1].balance[_token] -= _amount;

ERC20Extended(_token).burn(_amount);

emit TokensBurned(msgSender(), _token, _amount);
}

function mintTokens(uint _wad) public
Expand All @@ -297,7 +165,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
{
ERC20Extended(token).mint(address(this), _wad); // ignore-swc-107

emit TokensMinted(msg.sender, address(this), _wad);
emit TokensMinted(msgSender(), address(this), _wad);
}

function mintTokensFor(address _guy, uint _wad) public
Expand All @@ -306,12 +174,12 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
{
ERC20Extended(token).mint(_guy, _wad); // ignore-swc-107

emit TokensMinted(msg.sender, _guy, _wad);
emit TokensMinted(msgSender(), _guy, _wad);
}

function mintTokensForColonyNetwork(uint _wad) public stoppable {
// Only the colony Network can call this function
require(msg.sender == colonyNetworkAddress, "colony-access-denied-only-network-allowed");
require(msgSender() == colonyNetworkAddress, "colony-access-denied-only-network-allowed");
// Function only valid on the Meta Colony
require(address(this) == IColonyNetwork(colonyNetworkAddress).getMetaColony(), "colony-access-denied-only-meta-colony-allowed");
// Not callable on Xdai
Expand All @@ -320,7 +188,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
ERC20Extended(token).mint(_wad);
assert(ERC20Extended(token).transfer(colonyNetworkAddress, _wad));

emit TokensMinted(msg.sender, colonyNetworkAddress, _wad);
emit TokensMinted(msgSender(), colonyNetworkAddress, _wad);
}

function registerColonyLabel(string memory colonyName, string memory orbitdb) public stoppable auth {
Expand Down Expand Up @@ -429,7 +297,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
initialiseDomain(newLocalSkill);

if (keccak256(abi.encodePacked(_metadata)) != keccak256(abi.encodePacked(""))) {
emit DomainMetadata(msg.sender, domainCount, _metadata);
emit DomainMetadata(msgSender(), domainCount, _metadata);
}
}

Expand All @@ -438,7 +306,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
authDomain(_permissionDomainId, _childSkillIndex, _domainId)
{
if (keccak256(abi.encodePacked(_metadata)) != keccak256(abi.encodePacked(""))) {
emit DomainMetadata(msg.sender, _domainId, _metadata);
emit DomainMetadata(msgSender(), _domainId, _metadata);
}
}

Expand Down Expand Up @@ -467,7 +335,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
userAddress >>= 96;

// Require that the user is proving their own reputation in this colony.
if (address(colonyAddress) != address(this) || address(userAddress) != msg.sender) {
if (address(colonyAddress) != address(this) || address(userAddress) != msgSender()) {
return false;
}

Expand Down Expand Up @@ -495,7 +363,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
// we need to do once we know what's in it!
this.finishUpgrade();

emit ColonyUpgraded(msg.sender, currentVersion, _newVersion);
emit ColonyUpgraded(msgSender(), currentVersion, _newVersion);
}

// v7 to v8
Expand All @@ -513,25 +381,39 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
colonyAuthority.setRoleCapability(uint8(ColonyRole.Arbitration), address(this), sig, true);
}

function getMetatransactionNonce(address _user) override public view returns (uint256 nonce){
return metatransactionNonces[_user];
}

function incrementMetatransactionNonce(address _user) override internal {
// We need to protect the metatransaction nonce slots, otherwise those with recovery
// permissions could replay metatransactions, which would be a disaster.
// What slot are we setting?
// This mapping is in slot 34 (see ColonyStorage.sol);
uint256 slot = uint256(keccak256(abi.encode(uint256(_user), uint256(METATRANSACTION_NONCES_SLOT))));
protectSlot(slot);
metatransactionNonces[_user] = add(metatransactionNonces[_user], 1);
}

function checkNotAdditionalProtectedVariable(uint256 _slot) public view recovery {
require(_slot != COLONY_NETWORK_SLOT, "colony-protected-variable");
}

function approveStake(address _approvee, uint256 _domainId, uint256 _amount) public stoppable {
approvals[msg.sender][_approvee][_domainId] = add(approvals[msg.sender][_approvee][_domainId], _amount);
approvals[msgSender()][_approvee][_domainId] = add(approvals[msgSender()][_approvee][_domainId], _amount);

ITokenLocking(tokenLockingAddress).approveStake(msg.sender, _amount, token);
ITokenLocking(tokenLockingAddress).approveStake(msgSender(), _amount, token);
}

function obligateStake(address _user, uint256 _domainId, uint256 _amount) public stoppable {
approvals[_user][msg.sender][_domainId] = sub(approvals[_user][msg.sender][_domainId], _amount);
obligations[_user][msg.sender][_domainId] = add(obligations[_user][msg.sender][_domainId], _amount);
approvals[_user][msgSender()][_domainId] = sub(approvals[_user][msgSender()][_domainId], _amount);
obligations[_user][msgSender()][_domainId] = add(obligations[_user][msgSender()][_domainId], _amount);

ITokenLocking(tokenLockingAddress).obligateStake(_user, _amount, token);
}

function deobligateStake(address _user, uint256 _domainId, uint256 _amount) public stoppable {
obligations[_user][msg.sender][_domainId] = sub(obligations[_user][msg.sender][_domainId], _amount);
obligations[_user][msgSender()][_domainId] = sub(obligations[_user][msgSender()][_domainId], _amount);

ITokenLocking(tokenLockingAddress).deobligateStake(_user, _amount, token);
}
Expand Down Expand Up @@ -586,7 +468,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
fundingPotId: fundingPotCount
});

emit DomainAdded(msg.sender, domainCount);
emit DomainAdded(msgSender(), domainCount);
emit FundingPotAdded(fundingPotCount);
}

Expand Down
Loading