Skip to main content

Custom Contract Development

This guide is aimed at developers who need to implement custom business logic that goes beyond the standard templates available in ISBE.

When You Need a Custom Contract

Use a Template if:

  • Your use case fits one of the available templates.
  • You don't need complex logic beyond what the template offers.
  • You want to deploy quickly without a compliance process.
  • You prefer to simplify development time.

Available templates:

  • ERC20 - Fungible tokens (utility, points, credits)
  • ERC721 - Non-fungible tokens (certificates, unique assets)
  • ERC3643 - Regulated tokens (financial instruments)
  • AssetEventTracker - Event traceability
  • HashTimestamp - Document timestamping

See Template Catalog

Develop Custom if:

  • You need specific business logic that does not exist in templates.
  • You require a combination of non-standard features.
  • Your use case has particular business rules.

Examples of custom cases:

  • Voting system with specific rules.
  • Marketplace with custom auction logic.
  • Insurance contract with specific premium calculation.
  • Loan system with particular conditions.
  • Asset management with complex workflow.

Compliance Requirements

The compliance process ensures that smart contracts deployed on ISBE meet established technical, security, and architectural standards.

Note on responsibility: Responsibility for the functional behavior and business logic of the contract rests with the use case provider. ISBE verifies:

  • Minimum technical security controls.
  • Compliance with ISBE architectural standards.
  • Correct implementation of access controls and pausability.

Mandatory Technical Controls

Every contract to be homologated in ISBE must comply with the following technical controls. These controls are necessary both for basic security and for compatibility with the ISBE architecture.

1. Access Control (RBAC)

Your contract must implement clear roles based on the ISBE access control system.

ISBE provides the role system. You only need to use the onlyRole modifier in your functions:

contract MyContract {
function sensitiveOperation() external onlyRole(_OPERATOR_ROLE) {
// Your logic
}

function issue(address recipient, uint256 amount)
external
onlyRole(_ISSUER_ROLE)
{
// Your issuance logic
}
}

Requirements:

  • Application of the principle of least privilege.
  • Clear documentation of roles and associated permissions.
  • Mechanisms for role revocation and rotation.
info

Mandatory requirement for Modality 2 (Custom Contract)

Your contract must include the ISBE governance address in the PAUSER_ROLE as a condition for homologation. This role allows ISBE to pause the contract in network emergency situations. The subsequent revocation of this role by the owner is technically possible but constitutes a violation of the network's terms of use.

2. Pausability

Critical functions must be pausable via the ISBE centralized pause system.

ISBE provides the pause system. You only need to use the whenNotPaused modifier in your critical functions:

contract MyContract {
// Protected critical operations
function issue(address recipient, uint256 amount)
external
whenNotPaused
onlyRole(_ISSUER_ROLE)
{
// Your issuance logic
}

function transfer(address recipient, uint256 amount)
external
whenNotPaused
{
// Your transfer logic
}
}

Functions that must be protected:

  • Token issuance.
  • Transfers.
  • Critical parameter changes.
  • Asset operations.
  • All functions modifying critical state.

3. Storage Integrity

The storage layout is critical for the ISBE architecture. Your contract must use unstructured storage to avoid collisions.

Fundamental requirement: You must implement your storage using the unstructured storage pattern to ensure compatibility with ISBE.

Example of unstructured storage:

contract MyContract {
// Unique position calculated to avoid collisions
bytes32 constant STORAGE_POSITION = keccak256("my.use.case.storage");

struct MyStorage {
uint256 maxLimit;
uint256 minLimit;
bool active;
mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowances;
}

/**
* @dev Internal function to access the storage slot.
* @return storage_ Reference to the MyStorage struct in storage.
*/
function _myStorage() private pure returns (MyStorage storage storage_) {
bytes32 position = STORAGE_POSITION;
assembly {
storage_.slot := position
}
}

// Storage usage
function getMaxLimit() external view returns (uint256) {
return _myStorage().maxLimit;
}

function setMaxLimit(uint256 _limit) external {
_myStorage().maxLimit = _limit;
}
}

Requirements:

  • Mandatory unstructured storage implementation.
  • Use of unique storage slots calculated with keccak256.
  • No storage collisions.
  • No changes to storage position in updates.
  • Exhaustive documentation of the storage structure.

Important: The use of unstructured storage is mandatory for all custom contracts in ISBE.

4. Traceability Events

Emit events for all sensitive actions.

Example of events:

contract MyContract {
// Define clear events
event TokenIssued(address indexed recipient, uint256 amount, address indexed issuer);
event ConfigurationUpdated(uint256 oldValue, uint256 newValue, address indexed admin);
event RoleAssigned(bytes32 indexed role, address indexed account, address indexed admin);
event Paused(address indexed account);
event Unpaused(address indexed account);

function issue(address recipient, uint256 amount)
external
onlyRole(ISSUER_ROLE)
whenNotPaused
{
balances[recipient] += amount;
emit TokenIssued(recipient, amount, msg.sender);
}

function updateLimit(uint256 newLimit)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
uint256 old = maxLimit;
maxLimit = newLimit;
emit ConfigurationUpdated(old, newLimit, msg.sender);
}
}

Requirements:

  • Events on sensitive actions and state changes.
  • Record of role and permission changes.
  • Pause and reactivation events.
  • Sufficient information for subsequent auditing.

5. Testing and Analysis

You must provide evidence of code quality.

Example of unit tests:

describe("MyContract", function () {
describe("Access control", function () {
it("Only ISSUER_ROLE can issue tokens", async function () {
await expect(
contract.connect(user).issue(destination, 100)
).to.be.revertedWith("AccessControl");
});
});

describe("Pausability", function () {
it("Does not allow operations when paused", async function () {
await contract.pause();
await expect(
contract.issue(destination, 100)
).to.be.revertedWith("Pausable: paused");
});
});
});

Requirements:

  • Unit and integration tests.
  • Code coverage on critical paths.
  • Static analysis without open critical findings (Slither, Mythril, etc.).
  • Documentation of test cases.

6. Reproducible Build

Provide all information to deterministically recompile your contract.

Configuration example:

{
"compiler": {
"version": "0.8.20",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
}
}
}
}

Includes:

  • Exact Solidity version.
  • Compilation flags.
  • metadata.json
  • Dependencies (package.json, lock file).
  • Verifiable equivalence between bytecode and source code.
  • Separation of logic: If your contract is complex, separate logic into modules to facilitate adaptation.
  • GDPR Compliance: Do not store or expose unnecessary personal data.
  • Transparency: Facilitate public verification (BlockScout, Sourcify) and emission of relevant logs.

Compliance Levels

ISBE establishes three compliance levels based on the evidence provided. Homologation timelines depend on this level and the completeness of the delivered documentation:

LevelTypical ProfileEstimated Time
Level C (Minimum Compliance)Simple contract, basic business logic, full documentation2-4 weeks
Level B (Advanced Compliance)Complex contract, additional property testing4-8 weeks
Level A (Certified Compliance)Regulated financial instrument, external audit required4-12 weeks

Note: An incomplete dossier will restart the timeline from the receipt of the corrected documentation.

Level C - Minimum Compliance

  • Unit and integration tests passed.
  • Static analysis without critical findings.
  • Access controls (RBAC) verified.
  • Pausability implemented.
  • Reproducible build.

Level B - Advanced Compliance

  • Everything included in Level C.
  • Additional property and invariant testing.

Level A - Certified Compliance

  • Everything included in Level B.
  • External security audit.

Compliance Dimensions

The process evaluates four main dimensions that ensure both security and proper integration into the ISBE architecture:

1. Technical Compliance - Architecture

ISBE technical requirements:

  • Storage integrity: Unstructured storage implementation without collisions.
  • Unique selectors: Functions with non-conflicting selectors.
  • No hidden functions: Full transparency in capabilities and upgrades.
  • Upgrade management: Compatible with ISBE update processes.

2. ISBE Governance Compliance

Your contract will integrate with the ISBE governance system:

  • Access control (RBAC): Correct use of roles with onlyRole.
  • Managed pausability: The ISBE pause system will control your critical functions.
  • Upgrade control: Changes will follow the ISBE governance process.
  • No backdoors: Hidden administrative functions prohibited.

3. Minimum Security Compliance

  • Unit and integration tests.
  • Reproducible and verifiable compilation.
  • Static analysis without critical findings.
  • Code coverage on critical paths.

4. European Regulatory Compliance

  • GDPR: Do not expose or store unnecessary personal data.
  • Applicable regulations: Compliance with regulations according to the type and use of the digital asset.
  • eIDAS2: Digital identity and traceability.
  • NIS2: Operational resilience and cybersecurity.

Homologation Process Flow

1. Request Registration

The provider delivers:

  • Completed application form.
  • Repository with source code (public or private).
  • Specific commit/branch.
  • Compilation metadata (Solidity version, flags).
  • Unstructured storage documentation: Slots used and structure.
  • Deployment scripts: Complete deployment scripts.
  • Unit test results.
  • Role and permission documentation.

Delivery requirements:

  • Contract with full source code.
  • Storage using unstructured pattern.
  • Deployment scripts.
  • Versioning policy (latest or specific version).

Output: Case ID, initial checklist.

2. Technical Evaluation

Verification of minimum controls and compliance with ISBE architecture:

  • Execution of unit and integration tests.
  • Static code analysis.
  • RBAC and pausability verification.
  • Validation of unstructured storage implementation.
  • Function selector verification (no collisions).
  • Traceability event validation.
  • Deployment script verification.

Compliance analysis: ISBE evaluates if your contract:

  • Is correctly implemented.
  • Uses unstructured storage properly.
  • Has no selector conflicts.
  • Is correctly integrated with governance.

Output: Test report, findings report, signed checklist.

3. Build Verification

  • Deterministic recompilation.
  • Bytecode ↔ source code equivalence verification.
  • Deployment parameter validation.

Output: Equivalence proof, metadata.json.

4. Opinion and Labeling

  • Assignment of compliance level (A/B/C).
  • Technical committee resolution.
  • Issuance of homologation report.

Output: Approved/conditioned/rejected report, level label.

5. Publication

  • Registration in the ISBE catalog.
  • Contract deployment on ISBE.
  • Public on-chain verification (BlockScout).
  • Temporal timestamping of evidence (TSA).
  • Version configuration (latest or specific).
  • Documentation of the deployed contract.

Output: Catalog entry, verified contract, full documentation.

6. Change and Upgrade Management

For contract updates:

  • Documented change proposal.
  • Impact assessment on unstructured storage.
  • Compatibility validation.
  • Re-execution of minimum controls.
  • Change control.
  • New version with full traceability.

Important: Upgrades follow the ISBE governance process and must maintain storage compatibility.


Critical Rules

Restricted Pausability

Pauses are only activated for:

  • Duly justified force majeure.
  • Court order with official documentation.

Upgrade Transparency

  • Hidden update functions prohibited.
  • All changes follow controlled ISBE processes.
  • Upgrades are controlled by ISBE governance.
  • Clear separation between business logic and configuration.
  • Full traceability of changes.

No On-Chain Rollback

No state rollbacks are performed. In case of a major incident, it is evaluated case by case.

Provider Responsibility

The provider is responsible for:

  • The functional behavior and business logic of their contract.
  • Fixing bugs in their specific logic.
  • Compliance with regulations applicable to their use case.

ISBE is responsible for:

  • Verification of minimum technical security controls.
  • Compliance with ISBE architectural standards.
  • Validation of the correct implementation of access controls and pausability.
  • Contract deployment and management on the network.

Important: ISBE does not audit the full business logic of each use case.


What You Must Deliver to ISBE

If you develop a custom contract for homologation in ISBE:

Self-Validation Checklist

Use this list to verify your dossier before submitting it:

  • Source Code: Accessible repository with complete code and specified commit/branch.
  • Unstructured Storage: Verifiable correct implementation and slots documentation.
  • Roles and Permissions: Clear documentation of the RBAC schema used (PAUSER_ROLE included).
  • Events: Emission of events for all sensitive actions.
  • Tests: Suite of unit and integration tests with coverage report.
  • Security Analysis: Static analysis report (Slither, Mythril) with no open critical findings.
  • Metadata: metadata.json file and exact flags to ensure a reproducible build.
  • Deployment: Complete and documented deployment scripts.

1. Implemented Contract

  • Full source code: Your contract with all its logic.
  • Unstructured Storage: Correct implementation of the storage pattern.
  • Unique selectors: No conflicts with other ISBE contracts.
  • Governance integration: Compatible RBAC and pausability.

2. Deployment Scripts

  • Complete scripts: Contract deployment scripts.
  • Version policy: Point to latest or set specific version.

3. Technical Documentation

  • Storage documentation: Slots used, data structure.
  • Role documentation: Permissions and access control.
  • Function documentation: What each function does, parameters.
  • Compilation metadata: Solidity version, flags, dependencies.

4. Quality Evidence

  • Full tests: Unit and integration with coverage.
  • Static analysis: Without open critical findings.
  • Reproducible build: Bytecode ↔ source code equivalence.

What You DO NOT Need to Do

As a custom contract provider:

  • You don't need to manage infrastructure: ISBE manages deployment.

What You SHOULD Do

As a custom contract provider:

  • Implement your contract: With all the business logic.
  • Use unstructured storage: Mandatory to avoid collisions.
  • Implement clear RBAC: With well-defined and documented roles.
  • Implement pausability: In all critical functions with whenNotPaused.
  • Document comprehensively: Storage, roles, functions.
  • Provide full tests: Coverage of critical paths.
  • Emit traceability events: For all sensitive actions.
  • Reproducible build: With full compilation metadata.
  • Deployment scripts: Complete and documented.
  • No hidden functions: Full transparency in contract capabilities.

Complete Custom Contract Example

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

/// @title BatchTraceabilityContract
/// @notice Contract for product batch traceability
/// @dev Example custom contract with ISBE compliance controls
contract BatchTraceabilityContract {
// Unique storage position
bytes32 constant STORAGE_POSITION = keccak256("batch.traceability.storage");

struct Batch {
bytes32 id;
string product;
uint256 productionDate;
address producer;
bool active;
}

struct BatchTraceabilityStorage {
mapping(bytes32 => Batch) batches;
bytes32[] batchIds;
uint256 totalBatches;
}

// Events
event BatchCreated(bytes32 indexed id, string product, address producer);
event BatchUpdated(bytes32 indexed id, bool active);

/**
* @dev Access to unstructured storage
* @return storage_ Reference to BatchTraceabilityStorage in storage
*/
function _batchTraceabilityStorage()
private
pure
returns (BatchTraceabilityStorage storage storage_)
{
bytes32 position = STORAGE_POSITION;
assembly {
storage_.slot := position
}
}

/// @notice Creates a new batch
/// @param _id Unique batch identifier
/// @param _product Product name
function createBatch(bytes32 _id, string memory _product)
external
onlyRole(_OPERATOR_ROLE)
whenNotPaused
{
BatchTraceabilityStorage storage $ = _batchTraceabilityStorage();

require(_id != bytes32(0), "ID cannot be zero");
require(bytes(_product).length > 0, "Product cannot be empty");
require($.batches[_id].id == bytes32(0), "Batch already exists");

$.batches[_id] = Batch({
id: _id,
product: _product,
productionDate: block.timestamp,
producer: msg.sender,
active: true
});

$.batchIds.push(_id);
$.totalBatches++;

emit BatchCreated(_id, _product, msg.sender);
}

/// @notice Deactivates an existing batch
/// @param _id Batch identifier
function deactivateBatch(bytes32 _id)
external
onlyRole(_OPERATOR_ROLE)
whenNotPaused
{
BatchTraceabilityStorage storage $ = _batchTraceabilityStorage();

require($.batches[_id].id != bytes32(0), "Batch does not exist");
require($.batches[_id].active, "Batch already deactivated");

$.batches[_id].active = false;
emit BatchUpdated(_id, false);
}

/// @notice Queries a batch by its ID
/// @param _id Batch identifier
/// @return The queried batch
function getBatch(bytes32 _id)
external
view
returns (Batch memory)
{
return _batchTraceabilityStorage().batches[_id];
}

/// @notice Gets total registered batches
/// @return Total batches
function getTotalBatches() external view returns (uint256) {
return _batchTraceabilityStorage().totalBatches;
}
}

Next Steps

Once your contract is homologated:

  1. You will receive a compliance report with the assigned level (A/B/C).
  2. Your contract will be deployed and will be operational on ISBE.
  3. It will be available for public verification on BlockScout.
  4. You will be able to manage updates following the change process.