mirror of
https://github.com/embarklabs/subspace.git
synced 2025-02-07 12:19:44 +00:00
383 lines
12 KiB
Solidity
383 lines
12 KiB
Solidity
pragma solidity >=0.5.0 <0.6.0;
|
|
|
|
import "./License.sol";
|
|
import "./ArbitrationLicense.sol";
|
|
import "../common/MessageSigned.sol";
|
|
import "../common/Ownable.sol";
|
|
|
|
/**
|
|
* @title MetadataStore
|
|
* @dev User and offers registry
|
|
*/
|
|
contract MetadataStore is MessageSigned {
|
|
|
|
struct User {
|
|
bytes32 pubkeyA;
|
|
bytes32 pubkeyB;
|
|
string location;
|
|
string username;
|
|
}
|
|
|
|
struct Offer {
|
|
int8 margin;
|
|
uint[] paymentMethods;
|
|
uint limitL;
|
|
uint limitU;
|
|
address asset;
|
|
string currency;
|
|
address payable owner;
|
|
address payable arbitrator;
|
|
bool deleted;
|
|
}
|
|
|
|
License public sellingLicenses;
|
|
ArbitrationLicense public arbitrationLicenses;
|
|
|
|
mapping(address => User) public users;
|
|
mapping(address => uint) public user_nonce;
|
|
|
|
Offer[] public offers;
|
|
mapping(address => uint256[]) public addressToOffers;
|
|
mapping(address => mapping (uint256 => bool)) public offerWhitelist;
|
|
|
|
bool internal _initialized;
|
|
|
|
event OfferAdded(
|
|
address owner,
|
|
uint256 offerId,
|
|
address asset,
|
|
string location,
|
|
string currency,
|
|
string username,
|
|
uint[] paymentMethods,
|
|
uint limitL,
|
|
uint limitU,
|
|
int8 margin
|
|
);
|
|
|
|
event OfferRemoved(address owner, uint256 offerId);
|
|
|
|
/**
|
|
* @param _sellingLicenses Sellers licenses contract address
|
|
* @param _arbitrationLicenses Arbitrators licenses contract address
|
|
*/
|
|
constructor(address _sellingLicenses, address _arbitrationLicenses) public {
|
|
init(_sellingLicenses, _arbitrationLicenses);
|
|
}
|
|
|
|
/**
|
|
* @dev Initialize contract (used with proxy). Can only be called once
|
|
* @param _sellingLicenses Sellers licenses contract address
|
|
* @param _arbitrationLicenses Arbitrators licenses contract address
|
|
*/
|
|
function init(
|
|
address _sellingLicenses,
|
|
address _arbitrationLicenses
|
|
) public {
|
|
assert(_initialized == false);
|
|
|
|
_initialized = true;
|
|
|
|
sellingLicenses = License(_sellingLicenses);
|
|
arbitrationLicenses = ArbitrationLicense(_arbitrationLicenses);
|
|
}
|
|
|
|
/**
|
|
* @dev Get datahash to be signed
|
|
* @param _username Username
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _nonce Nonce value (obtained from user_nonce)
|
|
* @return bytes32 to sign
|
|
*/
|
|
function _dataHash(string memory _username, bytes32 _pubkeyA, bytes32 _pubkeyB, uint _nonce) internal view returns (bytes32) {
|
|
return keccak256(abi.encodePacked(address(this), _username, _pubkeyA, _pubkeyB, _nonce));
|
|
}
|
|
|
|
/**
|
|
* @notice Get datahash to be signed
|
|
* @param _username Username
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @return bytes32 to sign
|
|
*/
|
|
function getDataHash(string calldata _username, bytes32 _pubkeyA, bytes32 _pubkeyB) external view returns (bytes32) {
|
|
return _dataHash(_username, _pubkeyA, _pubkeyB, user_nonce[msg.sender]);
|
|
}
|
|
|
|
/**
|
|
* @dev Get signer address from signature. This uses the signature parameters to validate the signature
|
|
* @param _username Status username
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _nonce User nonce
|
|
* @param _signature Signature obtained from the previous parameters
|
|
* @return Signing user address
|
|
*/
|
|
function _getSigner(
|
|
string memory _username,
|
|
bytes32 _pubkeyA,
|
|
bytes32 _pubkeyB,
|
|
uint _nonce,
|
|
bytes memory _signature
|
|
) internal view returns(address) {
|
|
bytes32 signHash = _getSignHash(_dataHash(_username, _pubkeyA, _pubkeyB, _nonce));
|
|
return _recoverAddress(signHash, _signature);
|
|
}
|
|
|
|
/**
|
|
* @notice Get signer address from signature
|
|
* @param _username Status username
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _nonce User nonce
|
|
* @param _signature Signature obtained from the previous parameters
|
|
* @return Signing user address
|
|
*/
|
|
function getMessageSigner(
|
|
string calldata _username,
|
|
bytes32 _pubkeyA,
|
|
bytes32 _pubkeyB,
|
|
uint _nonce,
|
|
bytes calldata _signature
|
|
) external view returns(address) {
|
|
return _getSigner(_username, _pubkeyA, _pubkeyB, _nonce, _signature);
|
|
}
|
|
|
|
/**
|
|
* @dev Adds or updates user information
|
|
* @param _user User address to update
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _location New location
|
|
* @param _username New status username
|
|
*/
|
|
function _addOrUpdateUser(
|
|
address _user,
|
|
bytes32 _pubkeyA,
|
|
bytes32 _pubkeyB,
|
|
string memory _location,
|
|
string memory _username
|
|
) internal {
|
|
User storage u = users[_user];
|
|
u.pubkeyA = _pubkeyA;
|
|
u.pubkeyB = _pubkeyB;
|
|
u.location = _location;
|
|
u.username = _username;
|
|
}
|
|
|
|
/**
|
|
* @notice Adds or updates user information via signature
|
|
* @param _signature Signature
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _location New location
|
|
* @param _username New status username
|
|
* @return Signing user address
|
|
*/
|
|
function addOrUpdateUser(
|
|
bytes calldata _signature,
|
|
bytes32 _pubkeyA,
|
|
bytes32 _pubkeyB,
|
|
string calldata _location,
|
|
string calldata _username,
|
|
uint _nonce
|
|
) external returns(address payable _user) {
|
|
_user = address(uint160(_getSigner(_username, _pubkeyA, _pubkeyB, _nonce, _signature)));
|
|
|
|
require(_nonce == user_nonce[_user], "Invalid nonce");
|
|
|
|
user_nonce[_user]++;
|
|
_addOrUpdateUser(_user, _pubkeyA, _pubkeyB, _location, _username);
|
|
|
|
return _user;
|
|
}
|
|
|
|
/**
|
|
* @notice Adds or updates user information
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _location New location
|
|
* @param _username New status username
|
|
* @return Signing user address
|
|
*/
|
|
function addOrUpdateUser(
|
|
bytes32 _pubkeyA,
|
|
bytes32 _pubkeyB,
|
|
string calldata _location,
|
|
string calldata _username
|
|
) external {
|
|
_addOrUpdateUser(msg.sender, _pubkeyA, _pubkeyB, _location, _username);
|
|
}
|
|
|
|
/**
|
|
* @dev Add a new offer with a new user if needed to the list
|
|
* @param _asset The address of the erc20 to exchange, pass 0x0 for Eth
|
|
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
|
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
|
* @param _location The location on earth
|
|
* @param _currency The currency the user want to receive (USD, EUR...)
|
|
* @param _username The username of the user
|
|
* @param _paymentMethods The list of the payment methods the user accept
|
|
* @param _limitL Lower limit accepted
|
|
* @param _limitU Upper limit accepted
|
|
* @param _margin The margin for the user from 0 to 100
|
|
* @param _arbitrator The arbitrator used by the offer
|
|
*/
|
|
function addOffer(
|
|
address _asset,
|
|
bytes32 _pubkeyA,
|
|
bytes32 _pubkeyB,
|
|
string memory _location,
|
|
string memory _currency,
|
|
string memory _username,
|
|
uint[] memory _paymentMethods,
|
|
uint _limitL,
|
|
uint _limitU,
|
|
int8 _margin,
|
|
address payable _arbitrator
|
|
) public {
|
|
require(sellingLicenses.isLicenseOwner(msg.sender), "Not a license owner");
|
|
require(arbitrationLicenses.isAllowed(msg.sender, _arbitrator), "Arbitrator does not allow this transaction");
|
|
|
|
require(_margin <= 100, "Margin too high");
|
|
require(_margin >= -100, "Margin too low");
|
|
require(_limitL <= _limitU, "Invalid limits");
|
|
require(msg.sender != _arbitrator, "Cannot arbitrate own offers");
|
|
|
|
_addOrUpdateUser(
|
|
msg.sender,
|
|
_pubkeyA,
|
|
_pubkeyB,
|
|
_location,
|
|
_username
|
|
);
|
|
|
|
Offer memory newOffer = Offer(
|
|
_margin,
|
|
_paymentMethods,
|
|
_limitL,
|
|
_limitU,
|
|
_asset,
|
|
_currency,
|
|
msg.sender,
|
|
_arbitrator,
|
|
false
|
|
);
|
|
|
|
uint256 offerId = offers.push(newOffer) - 1;
|
|
offerWhitelist[msg.sender][offerId] = true;
|
|
addressToOffers[msg.sender].push(offerId);
|
|
|
|
emit OfferAdded(
|
|
msg.sender,
|
|
offerId,
|
|
_asset,
|
|
_location,
|
|
_currency,
|
|
_username,
|
|
_paymentMethods,
|
|
_limitL,
|
|
_limitU,
|
|
_margin);
|
|
}
|
|
|
|
/**
|
|
* @notice Remove user offer
|
|
* @dev Removed offers are marked as deleted instead of being deleted
|
|
* @param _offerId Id of the offer to remove
|
|
*/
|
|
function removeOffer(uint256 _offerId) external {
|
|
require(offerWhitelist[msg.sender][_offerId], "Offer does not exist");
|
|
|
|
offers[_offerId].deleted = true;
|
|
offerWhitelist[msg.sender][_offerId] = false;
|
|
emit OfferRemoved(msg.sender, _offerId);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the offer by Id
|
|
* @dev normally we'd access the offers array, but it would not return the payment methods
|
|
* @param _id Offer id
|
|
* @return Offer data (see Offer struct)
|
|
*/
|
|
function offer(uint256 _id) external view returns (
|
|
address asset,
|
|
string memory currency,
|
|
int8 margin,
|
|
uint[] memory paymentMethods,
|
|
uint limitL,
|
|
uint limitH,
|
|
address payable owner,
|
|
address payable arbitrator,
|
|
bool deleted
|
|
) {
|
|
Offer memory theOffer = offers[_id];
|
|
|
|
// In case arbitrator rejects the seller
|
|
address payable offerArbitrator = theOffer.arbitrator;
|
|
if(!arbitrationLicenses.isAllowed(theOffer.owner, offerArbitrator)){
|
|
offerArbitrator = address(0);
|
|
}
|
|
|
|
return (
|
|
theOffer.asset,
|
|
theOffer.currency,
|
|
theOffer.margin,
|
|
theOffer.paymentMethods,
|
|
theOffer.limitL,
|
|
theOffer.limitU,
|
|
theOffer.owner,
|
|
offerArbitrator,
|
|
theOffer.deleted
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the offer's owner by Id
|
|
* @dev Helper function
|
|
* @param _id Offer id
|
|
* @return Seller address
|
|
*/
|
|
function getOfferOwner(uint256 _id) external view returns (address payable) {
|
|
return (offers[_id].owner);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the offer's asset by Id
|
|
* @dev Helper function
|
|
* @param _id Offer id
|
|
* @return Token address used in the offer
|
|
*/
|
|
function getAsset(uint256 _id) external view returns (address) {
|
|
return (offers[_id].asset);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the offer's arbitrator by Id
|
|
* @dev Helper function
|
|
* @param _id Offer id
|
|
* @return Arbitrator address
|
|
*/
|
|
function getArbitrator(uint256 _id) external view returns (address payable) {
|
|
return (offers[_id].arbitrator);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the size of the offers
|
|
* @return Number of offers stored in the contract
|
|
*/
|
|
function offersSize() external view returns (uint256) {
|
|
return offers.length;
|
|
}
|
|
|
|
/**
|
|
* @notice Get all the offer ids of the address in params
|
|
* @param _address Address of the offers
|
|
* @return Array of offer ids for a specific address
|
|
*/
|
|
function getOfferIds(address _address) external view returns (uint256[] memory) {
|
|
return addressToOffers[_address];
|
|
}
|
|
}
|