Smart Contracts

Comprehensive documentation for OAS smart contracts, including deployment addresses, ABIs, and integration guides.

Contract Addresses

Mainnet (Ethereum)

OASToken: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8
AssetFactory: 0x5FbDB2315678afecb367f032d93F642f64180aa3
KYCRegistry: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
DividendManager: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9

Testnet (Sepolia)

OASToken: 0x123d35Cc6634C0532925a3b844Bc9e7595f0bEb8
AssetFactory: 0x456DB2315678afecb367f032d93F642f64180aa3
KYCRegistry: 0x789E46736679d2D9a65F0992F2272dE9f3c7fa6e0
DividendManager: 0xABCEd3AccA5a467e9e704C703E8D87F634fB0Fc9

OASToken Contract

The main governance and utility token of the OAS platform.

Contract Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract OASToken is ERC20, ERC20Burnable, Pausable, AccessControl {
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    
    uint256 public constant MAX_SUPPLY = 1_000_000_000 * 10**18; // 1 billion
    
    constructor() ERC20("Omni Asset Token", "OAS") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(PAUSER_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
    }
    
    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        require(totalSupply() + amount <= MAX_SUPPLY, "Max supply exceeded");
        _mint(to, amount);
    }
    
    function pause() public onlyRole(PAUSER_ROLE) {
        _pause();
    }
    
    function unpause() public onlyRole(PAUSER_ROLE) {
        _unpause();
    }
}

Integration Example

import { ethers } from 'ethers';
import OASTokenABI from '@omniasset/contracts/OASToken.json';

// Connect to contract
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const oasToken = new ethers.Contract(
  OAS_TOKEN_ADDRESS,
  OASTokenABI,
  provider
);

// Read token balance
const balance = await oasToken.balanceOf(userAddress);
console.log('OAS Balance:', ethers.utils.formatEther(balance));

// Transfer tokens
const signer = provider.getSigner();
const tokenWithSigner = oasToken.connect(signer);
const tx = await tokenWithSigner.transfer(
  recipientAddress,
  ethers.utils.parseEther('100')
);
await tx.wait();

AssetFactory Contract

Factory contract for deploying new tokenized assets with standardized configurations.

Create Asset Function

contract AssetFactory {
    struct AssetConfig {
        string name;
        string symbol;
        uint256 totalSupply;
        uint8 decimals;
        address[] initialHolders;
        uint256[] initialBalances;
        bool kycRequired;
        bool transferRestricted;
    }
    
    event AssetCreated(
        address indexed tokenAddress,
        string name,
        string symbol,
        address indexed creator
    );
    
    function createAsset(AssetConfig memory config) 
        external 
        returns (address tokenAddress) 
    {
        // Validate configuration
        require(bytes(config.name).length > 0, "Name required");
        require(bytes(config.symbol).length > 0, "Symbol required");
        require(config.totalSupply > 0, "Invalid supply");
        
        // Deploy new security token
        SecurityToken newToken = new SecurityToken(
            config.name,
            config.symbol,
            config.totalSupply,
            config.decimals
        );
        
        // Configure compliance rules
        if (config.kycRequired) {
            newToken.enableKYC(kycRegistry);
        }
        
        if (config.transferRestricted) {
            newToken.enableTransferRestrictions();
        }
        
        // Distribute initial tokens
        for (uint i = 0; i < config.initialHolders.length; i++) {
            newToken.transfer(
                config.initialHolders[i],
                config.initialBalances[i]
            );
        }
        
        emit AssetCreated(
            address(newToken),
            config.name,
            config.symbol,
            msg.sender
        );
        
        return address(newToken);
    }
}

SecurityToken Contract

ERC-1400 compliant security token with partition management and compliance features.

Core Functions

contract SecurityToken is ERC1400 {
    mapping(address => bool) public frozen;
    mapping(bytes32 => Partition) public partitions;
    
    struct Partition {
        uint256 amount;
        bytes32 partition;
        bool locked;
        uint256 lockupUntil;
    }
    
    // Transfer with compliance check
    function transferWithData(
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (bool) {
        require(!frozen[msg.sender], "Account frozen");
        require(!frozen[to], "Recipient frozen");
        require(_checkCompliance(msg.sender, to, value), "Compliance failed");
        
        _transfer(msg.sender, to, value);
        emit TransferWithData(msg.sender, to, value, data);
        return true;
    }
    
    // Issue new tokens to specific partition
    function issue(
        address tokenHolder,
        uint256 value,
        bytes32 partition
    ) external onlyIssuer {
        _mint(tokenHolder, value);
        partitions[partition].amount += value;
        emit Issued(tokenHolder, value, partition);
    }
    
    // Redeem tokens from partition
    function redeem(
        uint256 value,
        bytes32 partition,
        bytes calldata data
    ) external {
        require(partitions[partition].amount >= value, "Insufficient partition balance");
        require(!partitions[partition].locked, "Partition locked");
        
        partitions[partition].amount -= value;
        _burn(msg.sender, value);
        emit Redeemed(msg.sender, value, partition, data);
    }
    
    // Force transfer for legal compliance
    function forceTransfer(
        address from,
        address to,
        uint256 value,
        bytes calldata data
    ) external onlyController {
        _transfer(from, to, value);
        emit ForceTransfer(from, to, value, data);
    }
}

DividendManager Contract

Automated dividend distribution system for security token holders.

Dividend Distribution

contract DividendManager {
    struct Dividend {
        uint256 totalAmount;
        uint256 claimedAmount;
        uint256 snapshotId;
        uint256 paymentDate;
        address paymentToken;
        mapping(address => bool) claimed;
    }
    
    mapping(uint256 => Dividend) public dividends;
    uint256 public nextDividendId;
    
    function declareDividend(
        address token,
        uint256 amount,
        address paymentToken,
        uint256 paymentDate
    ) external onlyOwner returns (uint256 dividendId) {
        // Take snapshot of current holders
        uint256 snapshotId = ISecurityToken(token).snapshot();
        
        dividendId = nextDividendId++;
        Dividend storage dividend = dividends[dividendId];
        dividend.totalAmount = amount;
        dividend.snapshotId = snapshotId;
        dividend.paymentDate = paymentDate;
        dividend.paymentToken = paymentToken;
        
        // Transfer payment tokens to contract
        IERC20(paymentToken).transferFrom(
            msg.sender,
            address(this),
            amount
        );
        
        emit DividendDeclared(dividendId, token, amount, paymentDate);
        return dividendId;
    }
    
    function claimDividend(uint256 dividendId, address token) external {
        Dividend storage dividend = dividends[dividendId];
        require(block.timestamp >= dividend.paymentDate, "Not payable yet");
        require(!dividend.claimed[msg.sender], "Already claimed");
        
        // Calculate dividend amount based on snapshot
        uint256 balance = ISecurityToken(token).balanceOfAt(
            msg.sender,
            dividend.snapshotId
        );
        uint256 totalSupply = ISecurityToken(token).totalSupplyAt(
            dividend.snapshotId
        );
        
        uint256 dividendAmount = (balance * dividend.totalAmount) / totalSupply;
        
        dividend.claimed[msg.sender] = true;
        dividend.claimedAmount += dividendAmount;
        
        // Transfer dividend
        IERC20(dividend.paymentToken).transfer(
            msg.sender,
            dividendAmount
        );
        
        emit DividendClaimed(msg.sender, dividendId, dividendAmount);
    }
}

KYCRegistry Contract

Manages KYC verification status for platform participants.

KYC Management

contract KYCRegistry {
    struct KYCData {
        bool verified;
        uint256 verificationDate;
        uint256 expiryDate;
        string jurisdiction;
        uint8 investorType; // 0: Retail, 1: Accredited, 2: Institutional
    }
    
    mapping(address => KYCData) public kycData;
    mapping(address => bool) public verifiers;
    
    modifier onlyVerifier() {
        require(verifiers[msg.sender], "Not a verifier");
        _;
    }
    
    function verifyAddress(
        address user,
        uint256 expiryDate,
        string memory jurisdiction,
        uint8 investorType
    ) external onlyVerifier {
        KYCData storage data = kycData[user];
        data.verified = true;
        data.verificationDate = block.timestamp;
        data.expiryDate = expiryDate;
        data.jurisdiction = jurisdiction;
        data.investorType = investorType;
        
        emit KYCVerified(user, msg.sender, expiryDate);
    }
    
    function revokeVerification(address user) external onlyVerifier {
        kycData[user].verified = false;
        emit KYCRevoked(user, msg.sender);
    }
    
    function isVerified(address user) external view returns (bool) {
        KYCData memory data = kycData[user];
        return data.verified && block.timestamp < data.expiryDate;
    }
}

Transfer Rules Engine

Compliance Rules

contract TransferRules {
    struct Rule {
        bool enabled;
        uint256 minHoldingPeriod;
        uint256 maxTransferAmount;
        uint256 maxHolders;
        bool requireKYC;
        bool requireAccredited;
    }
    
    mapping(address => Rule) public tokenRules;
    mapping(address => mapping(address => uint256)) public lastTransfer;
    
    function canTransfer(
        address token,
        address from,
        address to,
        uint256 amount
    ) external view returns (bool, string memory) {
        Rule memory rule = tokenRules[token];
        
        if (!rule.enabled) {
            return (true, "No restrictions");
        }
        
        // Check KYC
        if (rule.requireKYC) {
            if (!IKYCRegistry(kycRegistry).isVerified(to)) {
                return (false, "KYC required");
            }
        }
        
        // Check accreditation
        if (rule.requireAccredited) {
            if (!isAccredited(to)) {
                return (false, "Accredited investor only");
            }
        }
        
        // Check holding period
        if (lastTransfer[token][from] > 0) {
            uint256 holdingTime = block.timestamp - lastTransfer[token][from];
            if (holdingTime < rule.minHoldingPeriod) {
                return (false, "Holding period not met");
            }
        }
        
        // Check transfer amount
        if (amount > rule.maxTransferAmount) {
            return (false, "Amount exceeds limit");
        }
        
        // Check max holders
        if (IERC20(token).balanceOf(to) == 0) {
            if (getHolderCount(token) >= rule.maxHolders) {
                return (false, "Max holders reached");
            }
        }
        
        return (true, "Transfer allowed");
    }
}

Deployment & Networks

Supported Networks

Ethereum Mainnet

Chain ID: 1

RPC: https://eth.omniasset.io

Polygon

Chain ID: 137

RPC: https://polygon.omniasset.io

BSC

Chain ID: 56

RPC: https://bsc.omniasset.io

Arbitrum

Chain ID: 42161

RPC: https://arb.omniasset.io

Contract Verification

# Verify on Etherscan
npx hardhat verify --network mainnet \
  --contract contracts/OASToken.sol:OASToken \
  0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8

# Verify with constructor arguments
npx hardhat verify --network mainnet \
  --constructor-args arguments.js \
  0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8

Upgradeable Contracts

// Deploy upgradeable contract
const AssetFactory = await ethers.getContractFactory("AssetFactory");
const proxy = await upgrades.deployProxy(
  AssetFactory,
  [adminAddress],
  { initializer: 'initialize' }
);
await proxy.deployed();

// Upgrade contract
const AssetFactoryV2 = await ethers.getContractFactory("AssetFactoryV2");
const upgraded = await upgrades.upgradeProxy(
  proxy.address,
  AssetFactoryV2
);

console.log("Upgraded to:", upgraded.address);

Gas Optimization

Best Practices

Batch Operations

// Batch transfer to save gas
function batchTransfer(
    address[] calldata recipients,
    uint256[] calldata amounts
) external {
    require(recipients.length == amounts.length, "Length mismatch");
    
    for (uint i = 0; i < recipients.length; i++) {
        _transfer(msg.sender, recipients[i], amounts[i]);
    }
}

Storage Optimization

// Pack struct variables
struct Asset {
    uint128 amount;     // Slot 1
    uint128 price;      // Slot 1
    address owner;      // Slot 2
    uint32 timestamp;   // Slot 2
    bool active;        // Slot 2
}