Replies: 6 comments 3 replies
-
On second thought, I belive we should have balances per address. |
Beta Was this translation helpful? Give feedback.
-
// SPDX-License-Identifier: AGPL-3.0-only
// written by @tfius
pragma solidity ^0.8.0;
contract FaveACL {
struct Entry {
uint8 permissions; // Permissions are stored as a bit mask
uint256 timestamp;
uint256 access_cost; // Cost in tokens to access this resource
uint256 payed_amount; // Amount of tokens payed to access this resource
}
// Mapping from owner to resource identifier to Ethereum address to entry
mapping(address => mapping(bytes32 => mapping(address => Entry))) public aclEntries;
// Permission flags
uint8 constant READ_PERMISSION = 0x01;
uint8 constant WRITE_PERMISSION = 0x02;
uint8 constant EXECUTE_PERMISSION = 0x04;
// Add more permission flags as needed
uint256 constant TOKENS_PER_ETH = 10000;
// Event to log ACL entry modifications
event ACLModified(
address indexed ethereumAddress,
bytes32 indexed resourceIdentifier,
uint8 permissions,
uint256 timestamp,
uint256 accessCost
);
// Event to log ACL entry modifications
event ACLTransfer(
address indexed fromAddress,
address indexed toAddress,
bytes32 indexed resourceIdentifier,
uint256 accessCost
);
// Function to add or update an ACL entry
function addOrUpdateEntry(
bytes32 _resourceIdentifier,
address _ethereumAddress,
uint8 _permissions,
uint256 _accessCost
) public {
aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress] = Entry({
permissions: _permissions,
timestamp: 0, // block.timestamp, // nothing payed for yet
access_cost: _accessCost,
payed_amount: 0
});
emit ACLModified(_ethereumAddress, _resourceIdentifier, _permissions, block.timestamp, _accessCost);
}
// Function to remove an ACL entry
function removeEntry(bytes32 _resourceIdentifier, address _ethereumAddress) public {
Entry storage entry = aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress];
uint256 accessCost = entry.access_cost;
uint256 payments = entry.payed_amount;
if(payments>=0)
{
staked[_ethereumAddress] += payments; // return payed tokens to the sender
}
delete aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress];
emit ACLModified(_ethereumAddress, _resourceIdentifier, 0, block.timestamp, accessCost);
}
// Function to get entry (to get permissions for a specific resource)
function getEntry(address owner, bytes32 _resourceIdentifier, address _ethereumAddress) public view returns (Entry memory) {
return aclEntries[owner][_resourceIdentifier][_ethereumAddress];
}
mapping(address => uint256) public balances; // Token balances
mapping(address => uint256) public staked; // Staked tokens
event DepositedAndStaked(address indexed user, uint256 ethAmount, uint256 tokenAmount);
event Withdrawn(address indexed user, uint256 ethAmount, uint256 tokenAmount);
// Function to deposit ETH, receive tokens, and stake them immediately
function depositAndStake() external payable {
require(msg.value > 0, "Must send ETH to deposit");
uint256 tokens = msg.value * TOKENS_PER_ETH;
// Directly adding the tokens to the staked amount
staked[msg.sender] += tokens;
emit DepositedAndStaked(msg.sender, msg.value, tokens);
}
// Function to unstake tokens and withdraw ETH
function unstakeAndWithdraw(uint256 tokenAmount) external {
require(staked[msg.sender] >= tokenAmount, "Insufficient staked tokens");
uint256 ethAmount = tokenAmount / TOKENS_PER_ETH;
staked[msg.sender] -= tokenAmount;
payable(msg.sender).transfer(ethAmount);
emit Withdrawn(msg.sender, ethAmount, tokenAmount);
}
// Function to check the staked balance of a user
function getStakedBalance(address _user) public view returns (uint256) {
return staked[_user];
}
// Function to transfer tokens from the receiver's stake to the entry from provider
function transferForAccess(
bytes32 _resourceIdentifier,
address _provider,
uint256 _amount
) public {
Entry storage entry = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(staked[msg.sender] >= entry.access_cost, "Insufficient staked tokens");
staked[msg.sender] -= _amount;
// Transfer tokens from the receiver's stake to the sender
entry.payed_amount += _amount;
entry.timestamp = block.timestamp; // timestamp for dispute resolution starts
emit ACLTransfer(msg.sender, _provider, _resourceIdentifier, _amount);
}
// Function to transfer tokens from the consumer's access stake to the provider
function chargeForServing(
bytes32 _resourceIdentifier,
address _consumer) public
{
Entry storage entry = aclEntries[msg.sender][_resourceIdentifier][_consumer];
require(entry.payed_amount >= entry.access_cost, "Insufficient payed tokens");
entry.payed_amount -= entry.access_cost;
staked[msg.sender] += entry.access_cost;
emit ACLTransfer(msg.sender, _consumer, _resourceIdentifier, entry.access_cost);
}
// Function if nothing happened in 24 hours, transfer tokens from the provider Entry to sender
function dispute(
bytes32 _resourceIdentifier,
address _provider) public
{
Entry storage entry = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(entry.payed_amount > 0, "Nothing to dispute");
if(entry.timestamp + 1 days > block.timestamp)
{
staked[msg.sender] += entry.payed_amount; // return funds back to the sender (consumer)
entry.payed_amount = 0;
entry.timestamp = 0;
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Expanded contract to be ERC20 compliant. // SPDX-License-Identifier: AGPL-3.0-only
// written by @tfius
pragma solidity ^0.8.0;
contract FaveACL {
struct Entry {
uint8 permissions; // Permissions are stored as a bit mask
uint256 timestamp; // used for dispute resolution
uint256 access_cost; // Cost in tokens to access this resource
uint256 payed_amount; // Amount of tokens payed to access this resource
}
// Mapping from owner to resource identifier to Ethereum address to entry
mapping(address => mapping(bytes32 => mapping(address => Entry))) public aclEntries;
// Permission flags
uint8 constant READ_PERMISSION = 0x01;
uint8 constant WRITE_PERMISSION = 0x02;
uint8 constant EXECUTE_PERMISSION = 0x04;
// Add more permission flags as needed
uint256 constant TOKENS_PER_ETH = 10000;
// ERC20 events
event Transfer(address indexed from,address indexed to,uint256 value);
event Approval(address indexed owner,address indexed spender,uint256 value);
// Event to log ACL entry modifications
event ACLModified(
address indexed ethereumAddress,
bytes32 indexed resourceIdentifier,
uint8 permissions,
uint256 timestamp,
uint256 accessCost
);
// Event to log ACL entry modifications
event ACLTransfer(
address indexed fromAddress,
address indexed toAddress,
bytes32 indexed resourceIdentifier,
uint256 accessCost
);
// Function to add or update an ACL entry
function addOrUpdateEntry(
bytes32 _resourceIdentifier,
address _ethereumAddress,
uint8 _permissions,
uint256 _accessCost
) public {
aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress] = Entry({
permissions: _permissions,
timestamp: 0, // block.timestamp, // nothing payed for yet
access_cost: _accessCost,
payed_amount: 0
});
emit ACLModified(_ethereumAddress, _resourceIdentifier, _permissions, block.timestamp, _accessCost);
}
// Function to remove an ACL entry
function removeEntry(bytes32 _resourceIdentifier, address _ethereumAddress) public {
Entry storage entry = aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress];
uint256 accessCost = entry.access_cost;
uint256 payments = entry.payed_amount;
if(payments>=0)
{
_balances[_ethereumAddress] += payments; // return payed tokens to the sender
}
delete aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress];
emit ACLModified(_ethereumAddress, _resourceIdentifier, 0, block.timestamp, accessCost);
}
// Function to get entry (to get permissions for a specific resource)
function getEntry(address owner, bytes32 _resourceIdentifier, address _ethereumAddress) public view returns (Entry memory) {
return aclEntries[owner][_resourceIdentifier][_ethereumAddress];
}
string public _name = "FaVe";
uint256 public currentSupply = 0;
mapping(address => uint256) private _balances; // Token balances
mapping (address => mapping (address => uint256)) private _allowed;
//mapping(address => uint256) public staked; // Staked tokens
event DepositedAndStaked(address indexed user, uint256 ethAmount, uint256 tokenAmount);
event Withdrawn(address indexed user, uint256 ethAmount, uint256 tokenAmount);
// Function to deposit ETH, receive tokens, and stake them immediately
function depositAndStake() external payable {
require(msg.value > 0, "Must send ETH to deposit");
uint256 tokens = msg.value * TOKENS_PER_ETH;
// Directly adding the tokens to the staked amount
_balances[msg.sender] += tokens;
currentSupply += tokens;
emit DepositedAndStaked(msg.sender, msg.value, tokens);
}
// Function to unstake tokens and withdraw ETH
function unstakeAndWithdraw(uint256 tokenAmount) external {
require(_balances[msg.sender] >= tokenAmount, "Insufficient balance");
uint256 ethAmount = tokenAmount / TOKENS_PER_ETH;
_balances[msg.sender] -= tokenAmount;
currentSupply -= tokenAmount;
payable(msg.sender).transfer(ethAmount);
emit Withdrawn(msg.sender, ethAmount, tokenAmount);
}
// Function to transfer tokens from the receiver's stake to the entry from provider
function transferForAccess(
bytes32 _resourceIdentifier,
address _provider,
uint256 _amount // can transfer more than access cost
) public {
Entry storage entry = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(_balances[msg.sender] >= _amount, "Insufficient balance");
_balances[msg.sender] -= _amount;
// Transfer tokens from the receiver's stake to the sender
entry.payed_amount += _amount;
entry.timestamp = block.timestamp; // timestamp for dispute resolution starts
emit ACLTransfer(msg.sender, _provider, _resourceIdentifier, _amount);
}
// Function to transfer tokens from the consumer's access stake to the provider
function chargeForServing(
bytes32 _resourceIdentifier,
address _consumer) public
{
Entry storage entry = aclEntries[msg.sender][_resourceIdentifier][_consumer];
require(entry.payed_amount >= entry.access_cost, "Insufficient payed tokens");
entry.payed_amount -= entry.access_cost;
_balances[msg.sender] += entry.access_cost;
entry.timestamp = block.timestamp; // timestamp for dispute resolution starts
emit ACLTransfer(msg.sender, _consumer, _resourceIdentifier, entry.access_cost);
}
// Function if nothing happened in 24 hours, transfer tokens from the provider Entry to sender
function dispute(
bytes32 _resourceIdentifier,
address _provider) public
{
Entry storage entry = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(entry.payed_amount > 0, "Nothing to dispute");
if(entry.timestamp + 1 days > block.timestamp)
{
_balances[msg.sender] += entry.payed_amount; // return funds back to the sender (consumer)
entry.payed_amount = 0;
entry.timestamp = 0;
}
}
// ERC20 token functions
function name() public view returns (string memory) { return _name;}
function symbol() public view returns (string memory) { return _name;}
function decimals() public pure returns (uint8) { return 18; }
function totalSupply() public view returns (uint256) { return currentSupply; }
function balanceOf(address _owner) public view returns (uint256 balance) { return _balances[_owner]; }
function transfer(address _to, uint256 _value) public returns (bool success)
{
require(_value <= _balances[msg.sender]);
require(_to != address(0));
_balances[msg.sender] -= _value;
_balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
{
require(_value <= _balances[_from]);
require(_value <= _allowed[_from][msg.sender]);
require(_to != address(0));
_balances[_from] -= _value;
_balances[_to] += _value;
_allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool success)
{
require(_spender != address(0));
_allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
{
return _allowed[_owner][_spender];
}
} |
Beta Was this translation helpful? Give feedback.
-
just talking to myself here, what if we only keep pure balance, no staking, no tokens. Then contract is much simpler: // SPDX-License-Identifier: AGPL-3.0-or-later
// written by @tfius
pragma solidity ^0.8.0;
contract FaveACL {
struct Entry {
uint8 permissions; // Permissions are stored as a bit mask
uint256 timestamp; // used for dispute resolution
uint256 access_cost; // Cost in ETH to access this resource
uint256 payed_amount; // Amount of ETH paid to access this resource
}
// Mapping from owner to resource identifier to Ethereum address to entry
mapping(address => mapping(bytes32 => mapping(address => Entry))) public aclEntries;
// Contract balance
uint256 public contractBalance;
// Permission flags
uint8 constant READ_PERMISSION = 0x01;
uint8 constant WRITE_PERMISSION = 0x02;
uint8 constant EXECUTE_PERMISSION = 0x04;
// Add more permission flags as needed
// Event to log ACL entry modifications
event ACLModified(
address indexed ethereumAddress,
bytes32 indexed resourceIdentifier,
uint8 permissions,
uint256 timestamp,
uint256 accessCost
);
// Event to log ACL entry modifications
event ACLTransfer(
address indexed fromAddress,
address indexed toAddress,
bytes32 indexed resourceIdentifier,
uint256 accessCost
);
// Function to add or update an ACL entry
function addOrUpdateEntry(
bytes32 _resourceIdentifier,
address _ethereumAddress,
uint8 _permissions,
uint256 _accessCost
) public {
aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress] = Entry({
permissions: _permissions,
timestamp: 0, // block.timestamp, // nothing paid for yet
access_cost: _accessCost,
payed_amount: 0
});
emit ACLModified(_ethereumAddress, _resourceIdentifier, _permissions, block.number, _accessCost);
}
// Function to remove an ACL entry
function removeEntry(bytes32 _resourceIdentifier, address _ethereumAddress) public {
Entry storage entry = aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress];
uint256 accessCost = entry.access_cost;
uint256 payments = entry.payed_amount;
if(payments > 0)
{
entry.payed_amount = 0;
contractBalance -= payments; // Update contract balance
payable(msg.sender).transfer(payments); // return paid ETH to the sender
}
delete aclEntries[msg.sender][_resourceIdentifier][_ethereumAddress];
emit ACLModified(_ethereumAddress, _resourceIdentifier, 0, block.number, accessCost);
}
// Function to get entry (to get permissions for a specific resource)
function getEntry(address owner, bytes32 _resourceIdentifier, address _ethereumAddress) public view returns (Entry memory) {
return aclEntries[owner][_resourceIdentifier][_ethereumAddress];
}
// Function to transfer ETH for access
function transferForAccess(
bytes32 _resourceIdentifier,
address _provider
) public payable {
Entry storage entry = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(msg.value >= entry.access_cost, "Insufficient ETH sent");
// Update entry information
entry.payed_amount += msg.value;
entry.timestamp = block.number; // timestamp for dispute resolution starts
// Update contract balance
contractBalance += msg.value;
emit ACLTransfer(msg.sender, _provider, _resourceIdentifier, msg.value);
}
// Function to charge ETH for serving a resource
function chargeForServing(
bytes32 _resourceIdentifier,
address _consumer
) public {
Entry storage entry = aclEntries[msg.sender][_resourceIdentifier][_consumer];
require(entry.payed_amount >= entry.access_cost, "Insufficient payed ETH");
// Transfer ETH to the provider
uint256 amountToTransfer = entry.access_cost;
entry.payed_amount -= entry.access_cost;
contractBalance -= amountToTransfer; // Update contract balance
payable(msg.sender).transfer(amountToTransfer);
entry.timestamp = block.number; // timestamp for dispute resolution starts
emit ACLTransfer(msg.sender, _consumer, _resourceIdentifier, amountToTransfer);
}
// Function if nothing happened in 24 hours, transfer ETH from the provider Entry to sender
function dispute(
bytes32 _resourceIdentifier,
address _provider
) public {
Entry storage entry = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(entry.payed_amount > 0, "Nothing to dispute");
if (block.number >= entry.timestamp + (24 hours / 15 seconds))
{
uint256 amountToRefund = entry.payed_amount;
entry.payed_amount = 0;
contractBalance -= amountToRefund; // Update contract balance
payable(msg.sender).transfer(amountToRefund); // return ETH back to the sender (consumer)
entry.timestamp = 0;
}
}
}
|
Beta Was this translation helpful? Give feedback.
-
For fairOS we only need ACL, not the transfer/charge/dispute etc. The Group Key Management I described will fail to revoke access. So If access is granted once then it cannot be removed. |
Beta Was this translation helpful? Give feedback.
-
Here is update FaveACL // SPDX-License-Identifier: AGPL-3.0-or-later
// written by @tfius
pragma solidity ^0.8.0;
contract FaveACL {
struct Entry {
address owner;
bytes32 resourceIdentifier;
uint8 permissions; // Permissions are stored as a bit mask
uint256 timestamp; // used for dispute resolution
uint256 access_cost; // Cost in ETH to access this resource
uint256 payed_amount; // Amount of ETH paid to access this resource
}
Entry[] public entries; // all entries
mapping(address => uint256[]) public userEntries;
// Mapping from owner to resource identifier to Ethereum address to entry
mapping(address => mapping(bytes32 => mapping(address => uint256))) public aclEntries;
mapping(address => uint256) public stakeBalance;
mapping(address => uint256) public amountInDispute;
// Contract balance
uint256 public contractBalance;
// Permission flags
uint8 constant READ_PERMISSION = 0x01;
uint8 constant WRITE_PERMISSION = 0x02;
uint8 constant EXECUTE_PERMISSION = 0x04;
// Add more permission flags as needed
// Event to log ACL entry modifications
event ACLModified(
address indexed ethereumAddress,
bytes32 indexed resourceIdentifier,
uint8 permissions,
uint256 timestamp,
uint256 accessCost
);
// Event to log ACL entry modifications
event ACLTransfer(
address indexed fromAddress,
address indexed toAddress,
bytes32 indexed resourceIdentifier,
uint256 accessCost
);
// Event to log ACL dispute resolution
event ACLNotServed(
address indexed fromAddress,
bytes32 indexed resourceIdentifier,
address indexed toAddress,
uint256 amount
);
// Function to add or update an ACL entry
function addOrUpdateEntry(
bytes32 _resourceIdentifier,
address _userAddress,
uint8 _permissions,
uint256 _accessCost
) public {
uint256 entryId = aclEntries[msg.sender][_resourceIdentifier][_userAddress];
if (entryId == 0) {
// Add new entry
entries.push(Entry({
owner: msg.sender,
resourceIdentifier: _resourceIdentifier,
permissions: _permissions,
timestamp: 0, // block.timestamp, // nothing payed for yet
access_cost: _accessCost,
payed_amount: 0
}));
aclEntries[msg.sender][_resourceIdentifier][_userAddress] = entries.length;
userEntries[_userAddress].push(entryId);
} else {
// Update existing entry
Entry storage entry = entries[aclEntries[msg.sender][_resourceIdentifier][_userAddress]-1];
// check to see if there is no outstanding payed_amount
require(entry.payed_amount == 0, "Cannot update entry with outstanding payed_amount");
entry.permissions = _permissions;
entry.access_cost = _accessCost;
}
emit ACLModified(_userAddress, _resourceIdentifier, _permissions, block.number, _accessCost);
}
// Function to get entry (to get permissions for a specific resource)
function getEntry(address owner, bytes32 _resourceIdentifier, address _userAddress) public view returns (Entry memory) {
uint256 entryId = aclEntries[owner][_resourceIdentifier][_userAddress];
require(entryId != 0, "Entry does not exist");
return entries[aclEntries[owner][_resourceIdentifier][_userAddress]-1];
}
// Function get all entries for ethereum address
function getEntries(address _userAddress) public view returns (Entry[] memory) {
Entry[] memory _entries = new Entry[](userEntries[_userAddress].length);
for (uint256 i = 0; i < userEntries[_userAddress].length; i++) {
_entries[i] = entries[userEntries[_userAddress][i]-1];
}
return _entries;
}
// Function to transfer ETH for access
function payForAccess(
bytes32 _resourceIdentifier,
address _provider
) public payable returns (Entry memory) {
uint256 entryId = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(entryId != 0, "Entry does not exist");
Entry storage entry = entries[entryId-1];
require(msg.value >= entry.access_cost, "Insufficient ETH sent");
// requrire that no previous payForAccess was made
require(entry.payed_amount == 0, "Already payed for access");
// Update entry information
entry.payed_amount += entry.access_cost;
entry.timestamp = block.number; // timestamp for dispute resolution starts
// check to see if msg.value is more than access_cost
if(msg.value > entry.access_cost)
{
uint256 amountToRefund = msg.value - entry.access_cost;
payable(msg.sender).transfer(amountToRefund); // return ETH back to the sender (consumer)
}
// Update contract balance
contractBalance += entry.access_cost;
emit ACLTransfer(msg.sender, _provider, _resourceIdentifier, msg.value);
return entry;
}
// Function to charge ETH for serving a resource
function getPayedForServing(
bytes32 _resourceIdentifier,
address _consumer
) public {
uint256 entryId = aclEntries[msg.sender][_resourceIdentifier][_consumer];
require(entryId != 0, "Entry does not exist");
Entry storage entry = entries[entryId-1];
require(entry.payed_amount != 0, "Insufficient payed ETH");
// Transfer ETH to the provider
uint256 amountToTransfer = entry.payed_amount;
contractBalance -= amountToTransfer; // Update contract balance
payable(msg.sender).transfer(amountToTransfer);
entry.timestamp = 0; // served, no more time disputes
emit ACLTransfer(msg.sender, _consumer, _resourceIdentifier, amountToTransfer);
}
// Function if nothing happened in 24 hours, transfer ETH from the provider Entry to sender
function dispute(
bytes32 _resourceIdentifier,
address _provider
) public {
uint256 entryId = aclEntries[_provider][_resourceIdentifier][msg.sender];
require(entryId != 0, "Entry does not exist");
Entry storage entry = entries[entryId-1];
require(entry.payed_amount > 0, "Nothing to dispute");
require(entry.timestamp != 0, "Nothing to dispute");
if (block.number >= entry.timestamp + (24 hours / 15 seconds))
{
uint256 amountToRefund = entry.payed_amount;
entry.payed_amount = 0;
stakeBalance[_provider] -= amountToRefund;
contractBalance -= amountToRefund; // Update contract balance
payable(msg.sender).transfer(amountToRefund); // return ETH back to the sender (consumer)
entry.timestamp = 0;
emit ACLNotServed(_provider, _resourceIdentifier, msg.sender, amountToRefund);
}
}
// each provider must stake ETH if they wanna charge for serving
function stake() public payable {
require(msg.value > 0, "Insufficient ETH sent");
contractBalance += msg.value;
stakeBalance[msg.sender] += msg.value;
}
function withdrawStake(uint256 _amount) public {
require(stakeBalance[msg.sender] >= _amount, "Insufficient balance");
stakeBalance[msg.sender] -= _amount;
contractBalance -= _amount;
payable(msg.sender).transfer(_amount);
}
// how much provider has staked
function getStake(address _provider) public view returns (uint256) {
return stakeBalance[_provider];
}
}
|
Beta Was this translation helpful? Give feedback.
-
Owner is owner address of FaVe data.
ResourceID = hash(owner, collection_name).
Owner grants permision to resourceId for Ethereum Address.
ACL model
User or group, the resource, and the permissions granted (e.g., read, write, delete).
API and Middleware:
Implement APIs and middleware that check the ACL before granting or denying access to resources.
When a user requests access to a resource, check the ACL to verify if they have the necessary permissions. If not, deny access.
Interface
Create an admin/user interface for managing ACL entries. Admins should be able to add, modify, or remove ACL entries as needed.
Logging and Auditing:
Essential for monitoring and security analysis - Implement logging and auditing mechanisms to track access to resources. This could go against FDS principles. But needs to be supported for users that might need it.
Security Considerations
Is it secure against common security threats, such as injection attacks and privilege escalation ?
On chain multi tenant ACL:
We could also implement it using on-chain contract, which would enable FairOS to use ACL lists to, would be global and immutable. A contract would something like this:
Multitenant ACL contract could be either extended with payments, or another contract can be introduced for payments. wdyt @asabya ?
Beta Was this translation helpful? Give feedback.
All reactions