Business identifier smart contract¶
The Business Identifier contact allows a business to manage their service accounts on the network and includes crucial business information such as the registered name, authorised representatives, official smart contracts, and other relevant data required to establish a degree of transparency and trust between the business and any party that wishes to transact with it on the network.
Once a business is verified, the accredited issuer who facilities the process will provide the applicant with the address where the contract has been deployed on Redbelly, for example:
The contract is used to record and maintain:
- a list of smart contracts officially associated with the business.
- a list of public addresses that serve as the business's service accounts
- a list of public addresses that serve as the business's authorised representatives
- the public details about the business
The following is an example of the business identifier contract structure.
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.22;
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
interface IBusinessPermission {
function requestBusinessPermissionForUser(address _walletAddress) external;
function removeBusinessPermissionForUser(address _walletAddress) external;
}
enum Action {
ADD,
REMOVE
}
/**
* @title BusinessContract
* @dev A contract that represents a business entity.
*/
contract BusinessIdentifier is AccessControlUpgradeable, UUPSUpgradeable {
/**
* @dev Role identifier for authorized representatives. (Directors or Office holders)
*/
bytes32 public constant AUTHORISED_REPRESENTATIVE_ROLE = keccak256("AUTHORISED_REPRESENTATIVE");
/**
* @dev Role identifier for authorized delegates. (Contractors, Developers, etc.)
*/
bytes32 public constant AUTHORISED_DELEGATE_ROLE = keccak256("AUTHORISED_DELEGATE");
string public companyName;
string public incorporatedName;
string public identifierType;
string public identifier;
string public businessAddress;
bool public isBeneficialOwner;
address[] public smartContracts;
uint256 public batchLimit;
IBusinessPermission private _businessPermission;
error UnauthorisedAccess(string);
error InvalidAddress(string);
error BatchSizeExceeded(uint256 actualSize, uint256 maxSize);
error EmptyAddressArray();
error InvalidBatchLimit(uint256 limit);
error RoleUpdateFailed(string);
function initialize(
address _admin,
string memory _companyName,
string memory _incorporatedName,
string memory _identifierType,
string memory _identifier,
string memory _businessAddress,
bool _isBeneficialOwner,
address _businessPermissionAddress
) public initializer {
__AccessControl_init();
__UUPSUpgradeable_init();
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
companyName = _companyName;
incorporatedName = _incorporatedName;
identifierType = _identifierType;
identifier = _identifier;
businessAddress = _businessAddress;
isBeneficialOwner = _isBeneficialOwner;
_businessPermission = IBusinessPermission(_businessPermissionAddress);
batchLimit = 100;
}
event SmartContractAdded(address indexed _address, address indexed _by);
event SmartContractRemoved(address indexed _address, address indexed _by);
event RoleUpdated(
address indexed userAddress,
bytes32 indexed role,
Action action,
string message
);
event AddressDoesNotExist(address indexed _address, bytes32 indexed role);
event RoleAlreadyExists(address indexed _address, bytes32 indexed role);
modifier onlyAdmin() {
if (!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {
revert UnauthorisedAccess("Caller must be IDP");
}
_;
}
modifier onlyAuthorisedRepresentative() {
if (
!hasRole(AUTHORISED_REPRESENTATIVE_ROLE, _msgSender()) &&
!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())
) {
revert UnauthorisedAccess(
"Caller must have IDP or Authorised Representative"
);
}
_;
}
modifier onlyAuthorisedDelegate() {
if (
!hasRole(AUTHORISED_DELEGATE_ROLE, _msgSender()) &&
!hasRole(AUTHORISED_REPRESENTATIVE_ROLE, _msgSender()) &&
!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())
) {
revert UnauthorisedAccess(
"Caller must have IDP or Authorised Representative or Authorised Delegate"
);
}
_;
}
modifier validateBatchSize(address[] memory _addresses) {
if (_addresses.length == 0) {
revert EmptyAddressArray();
}
if (_addresses.length > batchLimit) {
revert BatchSizeExceeded(_addresses.length, batchLimit);
}
_;
}
event UpdatedCompanyName(string _companyName);
event UpdatedIncorporatedName(string _incorporatedName);
event UpdatedIdentifierType(string _identifierType);
event UpdatedIdentifier(string _identifier);
event UpdatedBusinessAddress(string _businessAddress);
event UpdatedIsBeneficialOwner(bool _isBeneficialOwner);
function updateCompanyName(string memory _companyName) external onlyAdmin {
companyName = _companyName;
emit UpdatedCompanyName(_companyName);
}
function updateIncorporatedName(
string memory _incorporatedName
) external onlyAdmin {
incorporatedName = _incorporatedName;
emit UpdatedIncorporatedName(_incorporatedName);
}
function updateIdentifierType(
string memory _identifierType
) external onlyAdmin {
identifierType = _identifierType;
emit UpdatedIdentifierType(_identifierType);
}
function updateIdentifier(string memory _identifier) external onlyAdmin {
identifier = _identifier;
emit UpdatedIdentifier(_identifier);
}
function updateBusinessAddress(
string memory _businessAddress
) external onlyAdmin {
businessAddress = _businessAddress;
emit UpdatedBusinessAddress(_businessAddress);
}
function updateIsBeneficialOwner(
bool _isBeneficialOwner
) external onlyAdmin {
isBeneficialOwner = _isBeneficialOwner;
emit UpdatedIsBeneficialOwner(_isBeneficialOwner);
}
function addAuthorisedRepresentative(
address[] memory _addresses
) external onlyAdmin {
_updateRole(
_addresses,
AUTHORISED_REPRESENTATIVE_ROLE,
Action.ADD,
_businessPermission.requestBusinessPermissionForUser
);
}
function removeAuthorisedRepresentative(
address[] memory _addresses
) external onlyAdmin {
_updateRole(
_addresses,
AUTHORISED_REPRESENTATIVE_ROLE,
Action.REMOVE,
_businessPermission.removeBusinessPermissionForUser
);
}
function addAuthorisedDelegate(
address[] memory _addresses
) external onlyAuthorisedRepresentative {
_updateRole(
_addresses,
AUTHORISED_DELEGATE_ROLE,
Action.ADD,
_businessPermission.requestBusinessPermissionForUser
);
}
function removeAuthorisedDelegate(
address[] memory _addresses
) external onlyAuthorisedRepresentative {
_updateRole(
_addresses,
AUTHORISED_DELEGATE_ROLE,
Action.REMOVE,
_businessPermission.removeBusinessPermissionForUser
);
}
function _updateRole(
address[] memory _addresses,
bytes32 role,
Action action,
function(address) external businessPermissionFn
) internal validateBatchSize(_addresses) {
for (uint256 i = 0; i < _addresses.length; ) {
address _address = _addresses[i];
bool hasRoleBefore = hasRole(role, _address);
bool shouldUpdate = (action == Action.ADD && !hasRoleBefore) ||
(action == Action.REMOVE && hasRoleBefore);
if (shouldUpdate) {
try businessPermissionFn(_address) {
if (action == Action.ADD) {
_grantRole(role, _address);
emit RoleUpdated(
_address,
role,
action,
"Successfully Added"
);
} else {
_revokeRole(role, _address);
emit RoleUpdated(
_address,
role,
action,
"Successfully Removed"
);
}
} catch (bytes memory reason) {
revert RoleUpdateFailed(
string(abi.encodePacked("Error:", string(reason)))
);
}
}
unchecked {
i++;
}
}
}
function updateBatchLimit(
uint256 _newLimit
) external onlyAuthorisedRepresentative {
if (_newLimit == 0) revert InvalidBatchLimit(_newLimit);
batchLimit = _newLimit;
}
function addSmartContract(
address _address
) external onlyAuthorisedDelegate {
if (_address == address(0)) {
revert InvalidAddress("Invalid address.");
}
smartContracts.push(_address);
emit SmartContractAdded(_address, _msgSender());
}
function removeSmartContract(
address _address
) external onlyAuthorisedDelegate {
for (uint256 i = 0; i < smartContracts.length; ) {
if (smartContracts[i] == _address) {
smartContracts[i] = smartContracts[smartContracts.length - 1];
smartContracts.pop();
break;
}
unchecked {
i++;
}
}
emit SmartContractRemoved(_address, _msgSender());
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
}
Interacting with a deployed business identifier contract¶
Important
Only authorised representatives will be able to interact with a deployed business identifier contract. To add additional authorised representatives, contact the accredited issuer that registered your business.
1. Go to the block explorer¶
Routescan is the recommended block explorer for interacting with your deployed business identifier smart contract
3. Interact with your contract¶
- Navigate to the "Contract" tab, and then the "Write as proxy" tab.
- Select the function you want to interact with, e.g. "addAuthorisedDelegate", "addAuthorisedRepresentative", etc.
- Add the list of addresses whose role you wish to update in the contract as an array, e.g. ["0x1234....", "0x5678...."].
- When updating the list of authorised delegates, the added addresses will automatically be given write access to the network as a "sub account" of the business.
- Click the "Write" button to execute the function and update the contract.
Note that to execute any of the functions you will need to connect to the block explorer with the same public address you used to register as an Authorised Representative.
Functions and roles¶
The following is a list of functions available to authorised representatives of the business in the BusinessIdentifier smart contract.
The following roles are associated with the smart contract management:
- Authorised delegate (a business service account. For example, this public address could be managed by an employee or infrastructure)
- Authorised representative (an individual director or officeholder of the business)
Click the "Write" button to execute the function and update the contract.
Managing service accounts¶
| Function | Description | Allowed role |
|---|---|---|
| addAuthorisedDelegate | Adds an array of public addresses to the authorisedDelegates array and grants those addresses network permission. | Authorised representative only |
| removeAuthorisedDelegate | Removes an array of public addresses from the authorisedDelegates array and revokes network permission from those addresses. | Authorised representative only |
Managing smart contracts¶
| Function | Description | Allowed role |
|---|---|---|
| addSmartContract | Adds a smart contract address to the smartContracts array. | Authorised representative or Authorised delegate |
| removeSmartContract | Removes a smart contract address from the smartContracts array. | Authorised representative or Authorised delegate |
Managing authorised representatives¶
To manage authorised representatives, you will need to contact the accredited issuer that first registered the business. This is a security measure.
Updating business profile¶
To protect the integrity of the official information on-chain, updating the fundamental details about a business (which were verified before the business smart contract was generated) requires re-verification of the business.
To do so, raise a request with the accredited issuer who created the business smart contract, detailing the required changes to the BusinessIdentifier smart contract.
This includes if the need arises to:
- remove a company director
- add a company director
- update the beneficial owner
- update the business address
- update identifier
- update identifier type
- update the incorporated name
- update the company name