Skip to content

Instantly share code, notes, and snippets.

@fetsorn
Created March 3, 2021 10:58
Show Gist options
  • Save fetsorn/ae7ac21ef89d9d3a45beb0d51a5725ce to your computer and use it in GitHub Desktop.
Save fetsorn/ae7ac21ef89d9d3a45beb0d51a5725ce to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.7.6+commit.7338295f.js&optimize=false&runs=200&gist=
REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.
SCRIPTS
The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
Scripts have full access to the web3.js and ethers.js libraries.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
pragma solidity >=0.6.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/presets/ERC20PresetMinterPauser.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/access/Ownable.sol";
library QueueLib {
struct Queue {
bytes32 first;
bytes32 last;
mapping(bytes32 => bytes32) nextElement;
mapping(bytes32 => bytes32) prevElement;
}
function drop(Queue storage queue, bytes32 rqHash) public {
bytes32 prevElement = queue.prevElement[rqHash];
bytes32 nextElement = queue.nextElement[rqHash];
if (prevElement != bytes32(0)) {
queue.nextElement[prevElement] = nextElement;
} else {
queue.first = nextElement;
}
if (nextElement != bytes32(0)) {
queue.prevElement[nextElement] = prevElement;
} else {
queue.last = prevElement;
}
}
// function next(Queue storage queue, bytes32 startRqHash) public view returns(bytes32) {
// if (startRqHash == 0x000)
// return queue.first;
// else {
// return queue.nextElement[startRqHash];
// }
// }
function push(Queue storage queue, bytes32 elementHash) public {
if (queue.first == 0x000) {
queue.first = elementHash;
queue.last = elementHash;
} else {
queue.nextElement[queue.last] = elementHash;
queue.prevElement[elementHash] = queue.last;
queue.nextElement[elementHash] = bytes32(0);
queue.last = elementHash;
}
}
}
interface ISubscriberBytes {
function attachValue(bytes calldata value) external;
}
contract Token is ERC20PresetMinterPauser, Ownable {
constructor(string memory name, string memory symbol) ERC20PresetMinterPauser(name, symbol) public {
}
function addMinter(address minter) external {
require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to add minter");
_setupRole(MINTER_ROLE, minter);
}
}
contract IBPort is ISubscriberBytes, Ownable {
enum RequestStatus {
None,
New,
Rejected,
Success,
Returned
}
struct UnwrapRequest {
address homeAddress;
bytes32 foreignAddress;
uint amount;
}
event RequestCreated(uint, address, bytes32, uint);
address public nebula;
Token public tokenAddress;
mapping(uint => RequestStatus) public swapStatus;
mapping(uint => UnwrapRequest) public unwrapRequests;
QueueLib.Queue public requestsQueue;
constructor(address _nebula, address _tokenAddress) public {
nebula = _nebula;
tokenAddress = Token(_tokenAddress);
}
function deserializeUint(bytes memory b, uint startPos, uint len) internal pure returns (uint) {
uint v = 0;
for (uint p = startPos; p < startPos + len; p++) {
v = v * 256 + uint(uint8(b[p]));
}
return v;
}
function deserializeAddress(bytes memory b, uint startPos) internal pure returns (address) {
return address(uint160(deserializeUint(b, startPos, 20)));
}
function deserializeStatus(bytes memory b, uint pos) internal pure returns (RequestStatus) {
uint d = uint(uint8(b[pos]));
if (d == 0) return RequestStatus.None;
if (d == 1) return RequestStatus.New;
if (d == 2) return RequestStatus.Rejected;
if (d == 3) return RequestStatus.Success;
if (d == 4) return RequestStatus.Returned;
revert("invalid status");
}
function attachValue(bytes calldata value) override external {
require(msg.sender == nebula, "access denied");
for (uint pos = 0; pos < value.length; ) {
bytes1 action = value[pos]; pos++;
if (action == bytes1("m")) {
uint swapId = deserializeUint(value, pos, 32); pos += 32;
uint amount = deserializeUint(value, pos, 32); pos += 32;
address receiver = deserializeAddress(value, pos); pos += 20;
mint(swapId, amount, receiver);
continue;
}
if (action == bytes1("c")) {
uint swapId = deserializeUint(value, pos, 32); pos += 32;
RequestStatus newStatus = deserializeStatus(value, pos); pos += 1;
changeStatus(swapId, newStatus);
continue;
}
revert("invalid data");
}
}
function mint(uint swapId, uint amount, address receiver) internal {
require(swapStatus[swapId] == RequestStatus.None, "invalid request status");
tokenAddress.mint(receiver, amount);
swapStatus[swapId] = RequestStatus.Success;
}
function changeStatus(uint swapId, RequestStatus newStatus) internal {
require(swapStatus[swapId] == RequestStatus.New, "invalid request status");
swapStatus[swapId] = newStatus;
QueueLib.drop(requestsQueue, bytes32(swapId));
}
function createTransferUnwrapRequest(uint amount, bytes32 receiver) public {
uint id = uint(keccak256(abi.encodePacked(msg.sender, receiver, block.number, amount)));
unwrapRequests[id] = UnwrapRequest(msg.sender, receiver, amount);
swapStatus[id] = RequestStatus.New;
tokenAddress.burnFrom(msg.sender, amount);
QueueLib.push(requestsQueue, bytes32(id));
emit RequestCreated(id, msg.sender, receiver, amount);
}
function nextRq(uint rqId) public view returns (uint) {
return uint(requestsQueue.nextElement[bytes32(rqId)]);
}
function prevRq(uint rqId) public view returns (uint) {
return uint(requestsQueue.prevElement[bytes32(rqId)]);
}
function transferTokenOwnership(address newOwner) external virtual onlyOwner {
tokenAddress.transferOwnership(newOwner);
}
}
pragma solidity ^0.7.0;
library QueueLib {
struct Queue {
bytes32 first;
bytes32 last;
mapping(bytes32=>bytes32) nextElement;
mapping(bytes32=>bytes32) prevElement;
}
function drop(Queue storage queue, bytes32 rqHash) public {
if (queue.first == rqHash && queue.last == rqHash) {
queue.first = 0x000;
queue.last = 0x000;
} else if (queue.first == rqHash) {
queue.first = queue.nextElement[rqHash];
} else if (queue.last == rqHash) {
queue.last = queue.prevElement[rqHash];
}
}
function next(Queue storage queue, bytes32 startRqHash) public view returns(bytes32) {
if (startRqHash == 0x000)
return queue.first;
else {
return queue.nextElement[startRqHash];
}
}
function push(Queue storage queue, bytes32 elementHash) public {
if (queue.first == 0x000) {
queue.first = elementHash;
queue.last = elementHash;
} else {
queue.nextElement[queue.last] = elementHash;
queue.prevElement[elementHash] = queue.last;
queue.last = elementHash;
}
}
}
library NModels {
uint8 constant oracleCountInEpoch = 5;
enum DataType {
Int64,
String,
Bytes
}
struct Subscription {
address owner;
address payable contractAddress;
uint8 minConfirmations;
uint256 reward;
}
struct Pulse {
bytes32 dataHash;
uint256 height;
}
struct Oracle {
address owner;
bool isOnline;
bytes32 idInQueue;
}
}
interface ISubscriberBytes {
function attachValue(bytes calldata value) external;
}
interface ISubscriberInt {
function attachValue(int64 value) external;
}
interface ISubscriberString {
function attachValue(string calldata value) external;
}
contract Gravity {
mapping(uint256=>address[]) public rounds;
uint256 public bftValue;
uint256 public lastRound;
constructor(address[] memory consuls, uint256 newBftValue) public {
rounds[0] = consuls;
bftValue = newBftValue;
}
function getConsuls() external view returns(address[] memory) {
return rounds[lastRound];
}
function getConsulsByRoundId(uint256 roundId) external view returns(address[] memory) {
return rounds[roundId];
}
function updateConsuls(address[] memory newConsuls, uint8[] memory v, bytes32[] memory r, bytes32[] memory s, uint256 roundId) public {
uint256 count = 0;
require(roundId > lastRound, "round less last round");
bytes32 dataHash = hashNewConsuls(newConsuls, roundId);
address[] memory consuls = rounds[lastRound];
for(uint i = 0; i < consuls.length; i++) {
count += ecrecover(dataHash, v[i], r[i], s[i]) == consuls[i] ? 1 : 0;
}
require(count >= bftValue, "invalid bft count");
rounds[roundId] = newConsuls;
lastRound = roundId;
}
function hashNewConsuls(address[] memory newConsuls, uint256 roundId) public pure returns(bytes32) {
bytes memory data;
for(uint i = 0; i < newConsuls.length; i++) {
data = abi.encodePacked(data, newConsuls[i]);
}
return keccak256(abi.encodePacked(data, roundId));
}
}
contract Nebula {
event NewPulse(uint256 pulseId, uint256 height, bytes32 dataHash);
event NewSubscriber(bytes32 id);
mapping(uint256=>bool) public rounds;
QueueLib.Queue public oracleQueue;
QueueLib.Queue public subscriptionsQueue;
QueueLib.Queue public pulseQueue;
address[] public oracles;
uint256 public bftValue;
address public gravityContract;
NModels.DataType public dataType;
bytes32[] public subscriptionIds;
uint256 public lastPulseId;
mapping(bytes32 => NModels.Subscription) public subscriptions;
mapping(uint256 => NModels.Pulse) public pulses;
mapping(uint256 => mapping(bytes32 => bool)) public isPublseSubSent;
constructor(NModels.DataType newDataType, address newGravityContract, address[] memory newOracle, uint256 newBftValue) public {
dataType = newDataType;
oracles = newOracle;
bftValue = newBftValue;
gravityContract = newGravityContract;
}
receive() external payable { }
//----------------------------------public getters--------------------------------------------------------------
function getOracles() public view returns(address[] memory) {
return oracles;
}
function getSubscribersIds() public view returns(bytes32[] memory) {
return subscriptionIds;
}
function hashNewOracles(address[] memory newOracles) public pure returns(bytes32) {
bytes memory data;
for(uint i = 0; i < newOracles.length; i++) {
data = abi.encodePacked(data, newOracles[i]);
}
return keccak256(data);
}
//----------------------------------public setters--------------------------------------------------------------
function sendHashValue(bytes32 dataHash, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) public {
uint256 count = 0;
for(uint i = 0; i < oracles.length; i++) {
count += ecrecover(dataHash,
v[i], r[i], s[i]) == oracles[i] ? 1 : 0;
}
require(count >= bftValue, "invalid bft count");
pulses[lastPulseId] = NModels.Pulse(dataHash, block.number);
emit NewPulse(lastPulseId, block.number, dataHash);
lastPulseId = lastPulseId + 1;
}
function updateOracles(address[] memory newOracles, uint8[] memory v, bytes32[] memory r, bytes32[] memory s, uint256 newRound) public {
uint256 count = 0;
bytes32 dataHash = hashNewOracles(newOracles);
address[] memory consuls = Gravity(gravityContract).getConsuls();
for(uint i = 0; i < consuls.length; i++) {
count += ecrecover(dataHash, v[i], r[i], s[i]) == consuls[i] ? 1 : 0;
}
require(count >= bftValue, "invalid bft count");
oracles = newOracles;
rounds[newRound] = true;
}
function validateDataProvider() internal view returns(bool) {
for(uint i = 0; i < oracles.length; i++) {
if (oracles[i] == msg.sender) {
return true;
}
}
return false;
}
function sendValueToSubByte(bytes memory value, uint256 pulseId, bytes32 subId) public {
require(validateDataProvider(), "caller is not one of the oracles");
sendValueToSub(pulseId, subId);
ISubscriberBytes(subscriptions[subId].contractAddress).attachValue(value);
}
function sendValueToSubInt(int64 value, uint256 pulseId, bytes32 subId) public {
require(validateDataProvider(), "caller is not one of the oracles");
sendValueToSub(pulseId, subId);
ISubscriberInt(subscriptions[subId].contractAddress).attachValue(value);
}
function sendValueToSubString(string memory value, uint256 pulseId, bytes32 subId) public {
require(validateDataProvider(), "caller is not one of the oracles");
sendValueToSub(pulseId, subId);
ISubscriberString(subscriptions[subId].contractAddress).attachValue(value);
}
//----------------------------------internals---------------------------------------------------------------------
function sendValueToSub(uint256 pulseId, bytes32 subId) internal {
require(isPublseSubSent[pulseId][subId] == false, "sub sent");
isPublseSubSent[pulseId][subId] = true;
}
function subscribe(address payable contractAddress, uint8 minConfirmations, uint256 reward) public {
bytes32 id = keccak256(abi.encodePacked(abi.encodePacked(msg.sig, msg.sender, contractAddress, minConfirmations)));
require(subscriptions[id].owner == address(0x00), "rq is exist");
subscriptions[id] = NModels.Subscription(msg.sender, contractAddress, minConfirmations, reward);
QueueLib.push(subscriptionsQueue, id);
subscriptionIds.push(id);
emit NewSubscriber(id);
}
}
pragma solidity ^0.7.0;
contract Gravity {
mapping(uint256=>address[]) public rounds;
uint256 public bftValue;
uint256 public lastRound;
constructor(address[] memory consuls, uint256 newBftValue) public {
rounds[0] = consuls;
bftValue = newBftValue;
}
function getConsuls() external view returns(address[] memory) {
return rounds[lastRound];
}
function getConsulsByRoundId(uint256 roundId) external view returns(address[] memory) {
return rounds[roundId];
}
function updateConsuls(address[] memory newConsuls, uint8[] memory v, bytes32[] memory r, bytes32[] memory s, uint256 roundId) public {
uint256 count = 0;
require(roundId > lastRound, "round less last round");
bytes32 dataHash = hashNewConsuls(newConsuls, roundId);
address[] memory consuls = rounds[lastRound];
for(uint i = 0; i < consuls.length; i++) {
count += ecrecover(dataHash, v[i], r[i], s[i]) == consuls[i] ? 1 : 0;
}
require(count >= bftValue, "invalid bft count");
rounds[roundId] = newConsuls;
lastRound = roundId;
}
function hashNewConsuls(address[] memory newConsuls, uint256 roundId) public pure returns(bytes32) {
bytes memory data;
for(uint i = 0; i < newConsuls.length; i++) {
data = abi.encodePacked(data, newConsuls[i]);
}
return keccak256(abi.encodePacked(data, roundId));
}
}
pragma solidity >=0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/presets/ERC20PresetMinterPauser.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/access/Ownable.sol";
contract Token is ERC20PresetMinterPauser, Ownable {
constructor(string memory name, string memory symbol) ERC20PresetMinterPauser(name, symbol) public {
}
function addMinter(address minter) external {
require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to add minter");
_setupRole(MINTER_ROLE, minter);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment