ERC20TransferAuthorization.sol (6930B)
1 pragma solidity >0.8.0; 2 3 // SPDX-License-Identifier: GPL-3.0-or-later 4 5 contract ERC20TransferAuthorization { 6 struct Transaction { 7 uint256 value; 8 address sender; 9 address recipient; 10 address token; 11 uint32 serial; 12 uint32 yay; 13 uint32 nay; 14 //uint256 blockNumber; 15 // bit 1: started 16 // bit 2: approved 17 // bit 3: rejected 18 // bit 4: finalized 19 // bit 5: transfererror 20 uint8 result; 21 } 22 23 mapping ( uint32 => mapping ( address => int8 )) public vote; 24 //mapping ( uint256 => address[] ) public voters; 25 mapping( uint32 => Transaction ) public requests; 26 //mapping(address => uint256[]) public requestSenderIndex; 27 //mapping(address => uint256[]) public requestRecipientIndex; 28 address public owner; 29 uint32 hi; 30 uint32 lo; 31 uint32 public count; 32 uint32 public quorum; 33 uint32 public vetoThreshold; 34 uint32 public signerCount; 35 36 mapping(address => bool) public signers; 37 address[] public writers; 38 39 event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint32 _serial); 40 event Executed(uint32 _serial); 41 event TransferFail(uint32 _serial); 42 event QuorumSet(uint32 indexed _quorum, uint32 indexed _vetoThreshold, uint32 indexed _signerCount); 43 event WriterAdded(address _signer); 44 event WriterRemoved(address _signer); 45 event Vetoed(uint32 indexed _serial, uint32 indexed _yays, uint32 indexed _nays); 46 event Approved(uint32 indexed _serial, uint32 indexed _yays, uint32 indexed _nays); 47 event Rejected(uint32 indexed _serial, uint32 indexed _yays, uint32 indexed _nays); 48 49 constructor() { 50 owner = msg.sender; 51 hi = 1; 52 lo = 1; 53 addWriter(msg.sender); 54 setThresholds(1, 0); 55 } 56 57 function isWriter(address _signer) public view returns (bool) { 58 return signers[_signer]; 59 } 60 61 function addWriter(address _signer) public returns (uint32) { 62 require(msg.sender == owner, 'ERR_ACCESS'); 63 require(signers[_signer] == false, 'ERR_NOTFOUND'); 64 65 signers[_signer] = true; 66 signerCount++; 67 emit WriterAdded(_signer); 68 return signerCount; 69 } 70 71 function removeWriter(address _signer) public returns (uint32) { 72 //require(msg.sender == owner || msg.sender == _signer, 'ERR_ACCESS'); 73 require(msg.sender == owner, 'ERR_ACCESS'); 74 require(signers[_signer] == true, 'ERR_NOTFOUND'); 75 require(signerCount > quorum && signerCount > vetoThreshold, 'ERR_REDUCE_THRESHOLD_FIRST'); 76 77 signers[_signer] = false; 78 signerCount--; 79 emit WriterRemoved(_signer); 80 return signerCount; 81 } 82 83 function setThresholds(uint32 _quorum, uint32 _vetoThreshold) public returns (bool) { 84 require(_quorum <= signerCount); 85 require(_quorum > 0); 86 require(_vetoThreshold <= signerCount); 87 88 quorum = _quorum; 89 vetoThreshold = _vetoThreshold; 90 emit QuorumSet(quorum, vetoThreshold, signerCount); 91 return true; 92 } 93 94 // create new request 95 function createRequest(address _sender, address _recipient, address _token, uint256 _value) public returns (uint32) { 96 Transaction storage txx = requests[hi]; 97 98 txx.serial = hi; 99 txx.recipient = _recipient; 100 txx.sender = _sender; 101 txx.token = _token; 102 txx.value = _value; 103 txx.result = 1; 104 105 count++; 106 hi++; 107 108 emit NewRequest(txx.sender, txx.recipient, txx.token, txx.value, txx.serial); 109 110 return txx.serial; 111 } 112 113 // if request was oldest in index, move the pointer to oldest request to next oldest unfinished request. 114 // if no unfinished requests exits, it will point to newest request 115 function removeItem(uint32 _serialToRemove) private returns (uint32) { 116 count--; 117 118 if (count > 0) { 119 if (_serialToRemove == lo) { 120 uint32 i; 121 i = getSerialAt(0); 122 if (i == 0) { 123 lo = hi; 124 } else { 125 lo = i; 126 } 127 } 128 } else if (lo != hi) { 129 lo = hi; 130 } 131 132 return lo; 133 } 134 135 // index of newest vote 136 function lastSerial() public view returns ( uint32 ) { 137 return hi - 1; 138 } 139 140 // index of oldest unfinished vote 141 function nextSerial() public view returns ( uint32 ) { 142 if (hi - lo == 0) { 143 if (hi == 1) { 144 return 0; 145 } 146 Transaction storage txx = requests[lo]; 147 if (txx.result > 0) { 148 return lo; 149 } 150 return 0; 151 } 152 return lo; 153 } 154 155 // get the nth unfinished vote, where nth is _idx, starting at 0 156 function getSerialAt(uint32 _idx) public view returns ( uint32 ) { 157 uint32 i; 158 159 if (lo == hi) { 160 return 0; 161 } 162 163 for (uint32 j = lo; j < hi; j++) { 164 Transaction storage txx = requests[j]; 165 if (txx.result & 7 == 1) { 166 if (i == _idx) { 167 return txx.serial; 168 } 169 i++; 170 } 171 } 172 return 0; 173 } 174 175 // vote yay, one per signer 176 function yay(uint32 _serial) public returns (uint32) { 177 require(signers[msg.sender], 'ERR_ACCESS'); 178 require(vote[_serial][msg.sender] == 0, 'ERR_ALREADYVOTED'); 179 180 Transaction storage txx = requests[_serial]; 181 require(txx.result == 1); 182 183 vote[txx.serial][msg.sender] = 1; 184 //voters[txx.serial].push(msg.sender); 185 txx.yay++; 186 187 checkResult(txx.serial); 188 189 return txx.yay; 190 } 191 192 // vote nay, one per signer 193 function nay(uint32 _serial) public returns (uint32) { 194 require(signers[msg.sender], 'ERR_ACCESS'); 195 require(vote[_serial][msg.sender] == 0, 'ERR_ALREADYVOTED'); 196 197 Transaction storage txx = requests[_serial]; 198 require(txx.result == 1); 199 200 vote[txx.serial][msg.sender] = -1; 201 //voters[txx.serial].push(msg.sender); 202 txx.nay++; 203 204 checkResult(txx.serial); 205 206 return txx.nay; 207 } 208 209 // locks the state of the vote if quorum or veto is reached 210 // returns true if state changes 211 function checkResult(uint32 _serial) public returns (bool) { 212 bool result; 213 214 Transaction storage txx = requests[_serial]; 215 216 if (txx.result < 1 || txx.result & 6 > 0) { 217 return result; 218 } 219 220 if (txx.yay >= quorum) { 221 txx.result |= 2; 222 emit Approved(txx.serial, txx.yay, txx.nay); 223 result = true; 224 } else if (vetoThreshold > 0 && txx.nay >= vetoThreshold) { 225 txx.result |= 4; 226 removeItem(txx.serial); 227 emit Vetoed(txx.serial, txx.yay, txx.nay); 228 result = true; 229 } else if (signerCount - txx.nay < quorum) { 230 txx.result |= 4; 231 removeItem(txx.serial); 232 emit Rejected(txx.serial, txx.yay, txx.nay); 233 result = true; 234 } 235 236 return result; 237 } 238 239 // execute transfer. needs positive vote result 240 function executeRequest(uint32 _serial) public returns (bool) { 241 Transaction storage txx = requests[_serial]; 242 243 require(txx.serial > 0, 'ERR_INVALID_REQUEST'); 244 require(txx.result & 11 == 3, 'ERR_NOT_ENDORSED'); 245 246 removeItem(txx.serial); 247 txx.result |= 8; 248 249 (bool success, bytes memory _r) = txx.token.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", txx.sender, txx.recipient, txx.value)); 250 251 //txx.blockNumber = block.number; 252 //requestSenderIndex[txx.sender].push(txx.serial); 253 //requestRecipientIndex[txx.recipient].push(txx.serial); 254 if (!success) { 255 revert('ERR_TRANSFER_FAIL'); 256 } 257 if (_r[31] == 0x01) { 258 emit Executed(_serial); 259 } else { 260 txx.result |= 16; // this edit is for convenience only. since bit 4 is already set, it is not re-entrant. 261 emit TransferFail(_serial); 262 } 263 264 return success; 265 } 266 }