Smart Contracts
Comprehensive documentation for OAS smart contracts, including deployment addresses, ABIs, and integration guides.
Contract Addresses
Mainnet (Ethereum)
Testnet (Sepolia)
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
}
Security Audits
All OAS smart contracts have been audited by leading security firms: