Logo
blank Skip to main content

Why FinTech Turns to Tokenized Deposits — And How to Implement Them

Traditional bank deposits weren’t designed for today’s digital economy. Slow settlements, limited automation, and rigid legacy systems hold back modern businesses. 

That’s why leading FinTech companies and forward-thinking banks are adopting tokenized deposits — a faster, programmable, and more efficient alternative built for real-time business.

In this article, Apriorit experts provide a practical overview of how tokenized deposits work and what it takes to implement them. You’ll learn their business benefits, key technical challenges, architectural components, and steps required to build a tokenized deposit system.

This article will be useful for technical leaders in FinTech and banking who are looking for ways to provide blockchain-based services alongside traditional banking services.

What are tokenized deposits?

With traditional money deposits, customers entrust their funds to depository institutions for safekeeping. These depository institutions act as centralized custodians of customer funds, maintaining an internal database that tracks all accounts and transactions. Based on a bank’s terms and rates, deposit accounts can also earn interest.

The traditional deposit model, while well-established, faces several limitations: 

  • Limited cross-border functionality with high fees 
  • Lack of real-time processing and transparency 
  • Delayed international or interbank transfer settlements 
  • Restricted business hours and bank holidays
  • Dependency on centralized systems that are vulnerable to outages and attacks

These limitations make FinTech services inflexible and poorly protected, which is a deal-breaker for many customers. That’s why FinTech companies turn to blockchain and tokenized deposits.

How do tokenized deposits differ from traditional deposits?

Tokenized, or blockchain-based, deposits are claims against a depository institution for stated amounts of money recorded on a blockchain. They function as digital representations of conventional deposits, maintaining the same economic value but registered in tokenized form. 

Tokenized deposits are a part of real-world asset (RWA) tokenization — a rapidly growing market estimated to reach $2 trillion by 2030.

Here’s how tokenized deposits work: 

  1. A customer — an individual or a business — makes a deposit.
  2. The depository institution mints deposit tokens (corresponding to the deposit value) to the customer’s wallet. 
  3. The customer can access their funds on the blockchain, which enables peer-to-peer transfers within the institution and integration with smart contracts for automated financial operations.
  4. The customer can redeem their tokens for fiat currency whenever needed, as the deposited tokens are evidence of a deposit claim against a depository institution.
Tokenized deposit workflow

Tokenized bank deposits can be part of next-gen payment infrastructure according to McKinsey and others. Together with stablecoins and central bank digital currencies, tokenized deposits create a way to digitalize and improve traditional banking services. They can support any banking use case, from investing to international transfers, while speeding up banking services and securing customers’ funds on the blockchain.

Let’s take a closer look at all the benefits of tokenized deposits for financial organizations.

Plan on adding blockchain operations to your list of FinTech services?

Partner with Apriorit to design custom smart contracts with low transaction costs and fast operations.

How FinTech benefits from tokenized deposits

By using blockchain capabilities, tokenized deposits address several limitations of the traditional deposit model and improve it with: 

  • Real-time processing. Blockchain technology provides near-instantaneous processing and settlement. Transaction processing time depends on the blockchain block time. For example, the average Bitcoin block time is approximately 10 minutes, while Ethereum has an approximate block time of 12 seconds. 
  • Transaction transparency. Since every transaction is recorded on a distributed ledger, users have complete visibility over a transaction’s details, history, and status. 
  • Continuous operation. Blockchain networks operate continuously, allowing tokenized deposits to be accessed and transferred 24/7. Users can conduct transactions at any time, regardless of weekends, holidays, or time zones.
  • Resilience of a decentralized system. The decentralized nature of blockchain ensures that the network remains operational even if some nodes experience issues, significantly improving system resilience and reliability.
  • Reduced fees and no limits on cross-border functionality. When using blockchain, users only pay transaction fees for public chains or transfer fees if the blockchain provider applies custom logic. There are no geographical limitations for transactions.
  • Flexible programmability. FinTech companies can support existing deposit-taking activities, such as the conditional transfer of funds, modify common banking activities, and add custom logic and services on top. They can implement complex transaction logic without manual intervention, thus reducing the risk of human errors or delays. 

Sounds like a perfect substitute for slow and clunky traditional deposits, doesn’t it? Then why doesn’t every bank already provide tokenized deposits? Let’s examine the key limitations and challenges that you can encounter when implementing such deposits.

Challenges of using tokenized deposits in FinTech

Tokenized deposits promise faster settlement, improved interoperability, and new models of liquidity management. But for FinTech leaders, moving from traditional digital money to blockchain architectures introduces three sets of non-trivial challenges.

ChallengeSolution
Regulatory uncertainty around blockchain in finance– Adopt a regulatory-first design philosophy
– Engage regulators
– Document every development step
Complicated technical integration with legacy banking systems– Study the tokenized deposit model of your choice 
– Create an integration team with diverse IT experience
Blockchain adoption challenges for non-tech-savvy users– Educate users
– Mask blockchain operations with traditional banking interfaces

1. Regulatory uncertainty

Tokenized commercial bank deposits are treated as ordinary deposits on the bank’s balance sheet, so they generally fall under existing banking requirements. However, there are some dedicated laws and regulations like the GENIUS Act in the United States that impose specific rules for stablecoins and tokens. Banking agencies and governments are also preparing dedicated laws and regulatory requirements that will govern tokenized deposits as digital, blockchain-specific assets.

As a result, institutions often face unclear guidance on token classification, custody of digital assets, licensing requirements, and ongoing compliance obligations.

Solution: There’s no way to peek over the hill and predict exact future requirements for tokenized deposits. Nevertheless, FinTech companies can prepare for upcoming changes by adopting a regulatory-first design philosophy. Engage regulators early, document your blockchain architecture and controls transparently, and align system design with compliance expectations from day one to avoid costly rework later.

2. Complicated technical integration 

Banking and FinTech infrastructures often keep using legacy solutions that are challenging to integrate with new technologies like blockchain. Tokenized deposit models significantly impact data flows, transaction processing, and even settlement logic, often rendering them incompatible with traditional banking processes.

Solution: The nature of challenges you’ll encounter depends on the tokenized deposit model you choose. The first step is to research and choose a token representation model (we’ll discuss them later). You’ll also benefit from assembling a special integration team that combines blockchain experts with traditional banking IT professionals. 

3. User experience barriers

The complexity of wallets, seed phrases, key management, and on-chain terminology can significantly slow adoption, especially for retail users or non-technical business clients. Without appropriate support, introducing tokenized deposits may result in an increased number of customer support tickets or high customer churn.

Solution: The way to solve this issue depends on your product’s target audience. For more tech-savvy users who are familiar with the blockchain concept, provide tips and educational materials to help them adjust to the new technology. Alternatively, you can abstract blockchain complexity behind familiar banking interfaces and eliminate the need for users to manage private keys directly.

With this basic theoretical information in mind, let’s see an example of a tokenized deposit workflow in practice.

Read also

How to Build a Custom Know Your Customer (KYC) System for Your FinTech Product

See how building a tailored KYC solution strengthens fraud prevention and ensures accurate user verification. Explore essential features and development considerations for secure FinTech onboarding.

Learn more
Know your Customer custom development

Implementing tokenized deposits: Preparing for development

When it comes to technical implementation, the first question that arises is the choice of blockchain type. You can choose a public or private blockchain network, with both types having their own benefits and limitations.

Public vs private blockchain networks

Public blockchain networks like Bitcoin and Ethereum are open networks on which any user can launch a blockchain node. They are governed by a community of users rather than a single organization. The community also creates and maintains development tools, libraries, and documentation. 

Choosing a public blockchain for your tokenized deposits speeds up the implementation and time to market. It also ensures data security thanks to data decentralization. 

Private, or permissioned, blockchains restrict participation to authorized entities, typically a FinTech institution itself and (potentially) selected partners. Such networks offer financial institutions greater control over their tokenized deposit infrastructure. 

Private blockchains work best for organizations that prioritize control over their data and processes rather than quick development or time to market.

BenefitsLimitations
Public blockchain– Anyone can launch a node
– Lower implementation costs
– Rich DeFi integration options
– Mature development ecosystems
– Higher security through decentralization
– High usage may result in performance issues
– Limited configurability with set protocol rules
– No way to configure parameters like consensus or gas costs
Private blockchain– High configurability of any blockchain elements
– Enhanced transaction privacy
– Consistently high performance and scalability
– High infrastructure costs
– Complex implementation and maintenance 
– Limited DeFi ecosystem
– Centralized governance concerns around trust and security

When choosing among public blockchains, consider a high-volume chain with a big development community such as Ethereum or Solana. Going with a private blockchain provides your development team with complete technical freedom but requires more resources. To simplify the development process, consider using popular existing solutions like Hyperledger Fabric or Quorum.

Token implementation approaches

A tokenized deposit can be:

  • Account-based — a traditional deposit held at a depository institution that is represented as an account balance on a blockchain. The institution remains liable for the account balance, and users cannot interact with the blockchain directly. Instead, they request transfers through the bank, which is responsible for updating the ledger state.
  • Token-based — transferable tokens are issued by a depository institution on a blockchain that evidence deposit claims for stated amounts. The institution is liable for the fiat value represented by the tokens. Users can initiate transfers and other supported operations themselves, subject to any restrictions defined in the smart contract.

Then comes the role of the blockchain itself, which can be:

  • Native, meaning that the blockchain serves as the primary recordkeeping ledger and the prevailing source of truth over any other ledger. 
  • Non-native, meaning that the blockchain mirrors an off-chain record, and that record is the prevailing source of truth. 

In our practical implementation example, we’ll cover token-based deposits, as they take advantage of extensive programmability and blockchain functionality and cover many different use cases. You can use our example for native and non-native implementations.

Read also

Custom DeFi Aggregators Explained: Benefits, Challenges, and Development Tips

Explore practical strategies for building a DeFi aggregator tailored to your business needs, ensuring scalability, security, and a competitive advantage in the decentralized finance space.

Learn more
Custom DeFi Aggregators Explained

Writing smart contracts for tokenized deposits

Our sample project will consist of three main elements:

  • A Token contract that represents a tokenized deposit value and follows the ERC20 standard for better compatibility with limitations such as access restrictions for non-whitelisted users.
  • A Bank contract that centrally manages all restrictions and governance processes and also stores the whitelist of users. Admins have the ability to add users to the whitelist, as well as mint and burn. For simplicity, our example contract will have one admin. 
  • An Interest contract that is used for managing users’ interest rates. It locks tokens, transfers them to the bank, updates rewards, and can be used for applying penalties. 

Now, let’s examine the functionality of each smart contract.

1. Token contract

For our tokens, we’ll create a smart contract based on the OpenZeppelin implementation with some custom functionality. We’ll extend the standard ERC20 functionality with access control mechanisms that ensure only whitelisted users can participate in token operations. 

Let’s initialize our contract:

Solidity
// Reference to the bank contract instance 
    IBank _bankContract; 
 
    // Contract initialization 
    // Initializes ERC20 token with given `name` and `symbol`, connects Bank contract instance. 
    constructor( 
        string memory name, 
        string memory symbol, 
        address bankContract 
    ) ERC20(name, symbol) { 
    	_bankContract = IBank(bankContract); 
    } 

Then, we need to enforce access control using two key modifiers: bankOnly to restrict minting and burning operations exclusively to the Bank contract, and whitelistedOnly to verify user authorization for transfers and approvals. The contract maintains a direct connection to the Bank through the IBank interface, allowing real-time whitelist verification.

Solidity
// Modifier for functions that can only be called by the Bank 
    modifier bankOnly() { 
        require(_msgSender() == address(_bankContract), "Caller is not a Bank"); 
    	_; 
    } 
 
    // Modifier for checking account status through Bank contract 
    // i.e. whether or not account is whitelisted 
    modifier whitelistedOnly(address account) { 
        require( 
        	_bankContract.isAccountWhitelisted(account), 
            "Account is not whitelisted" 
        ); 
    	_; 
    }

In this code, we use methods from ERC20 but modify some of them with custom access restrictions:

  • Transfers — Allowed only if the recipient is whitelisted by the Bank contract.
  • Approve — The caller (owner) must be whitelisted before granting allowances.
  • Mint — Can only be called by the Bank contract and only to a whitelisted account.
  • Burn — Can only be called by the Bank contract.

Let’s first implement transfer function:

Solidity
/** 
 	*  Default ERC20 `transfer` with contract-specific restrictions 
 	* 
     *  Requirements: 
 	* 
     *  - `to` cannot be the zero address. 
 	*  - `to` must be a whitelisted account 
     *  - the caller must have a balance of at least `value` 
 	*/ 
    function transfer(address to, uint256 value) 
        public 
        override 
        whitelistedOnly(to) 
        returns (bool) 
    { 
    	_transfer(_msgSender(), to, value); 
        return true;

Now, we need to add transferFrom function:

Solidity
/* 
 	* Same requirements as for `transfer` 
 	* 
 	* No need to check the from address status as tokens can only be 
 	* transferred and minted to whitelisted users. 
 	* 
 	* Additionally, the sender can be checked for its status, depending on the logic you 
 	* choose for approve functionality below. 
 	*/ 
    function transferFrom( 
        address from, 
        address to, 
        uint256 value 
    ) public override whitelistedOnly(to) returns (bool) { 
        address spender = _msgSender(); 
    	_spendAllowance(from, spender, value); 
    	_transfer(from, to, value); 
        return true; 
    }

Note that the Bank contract must implement proper validation to define who has the  authority to mint and burn tokens, as well as the business conditions triggering these operations.

Solidity
/* 
 	*  Default ERC20 `approve` with contract-specific restrictions 
 	* 
     *  Requirements: 
 	* 
 	*  - `sender` is a whitelisted account 
     *  - (optional) The `spender` check allows you to control who can interact with users' funds 
 	*	With or without this check, transfers are restricted to whitelisted users, 
 	*    but some external operators can do transfers on behalf of some whitelisted users. 
 	*/ 
    function approve(address spender, uint256 value) 
        public 
        override 
        whitelistedOnly(_msgSender()) 
        returns (bool) 
    { 
        address owner = _msgSender(); 
    	_approve(owner, spender, value); 
        return true; 
    } 


 
/* 
 	* The `mint` function is used to mint new tokens 
 	* 
 	* Requirements: 
 	* 
 	* - can only be called by the Bank contract 
 	* - `to` has to be a whitelisted account 
 	*/ 
    function mint(address to, uint256 value) 
        public 
        bankOnly 
        whitelistedOnly(to) 
    { 
    	_mint(to, value); 
    } 
 
    /* 
 	* The `burn` function is used to burn tokens 
 	* 
 	* Requirements: 
 	* 
 	* - can only be called by the Bank contract 
 	*/ 
    function burn(address from, uint256 value) public bankOnly { 
    	_burn(from, value); 
    }

2. Bank contract

The Bank contract mostly consists of straightforward functionality for actions such as adding or removing whitelisted users. We won’t cover it in this article, as these functions are typical for such a contract. 

Let’s define the core contract’s storage variables: who the admin is, which token and interest manager it works with, and a whitelist of approved users.

Solidity
// Address of the admin 
    address admin; 
    // Reference to the Deposit Token instance 
    IDepositToken depositToken; 
    // Reference to the Interest Contract instance 
    IInterestManager interestManager; 
    // Mapping of whitelisted users with their account status 
    mapping(address => bool) whitelistedUsers;

Then, let’s add the following events and modifiers:

Solidity
// Emitted when Deposit is processed 
    event DepositProcessed(address indexed user, uint256 value); 
    // Emitted when Withdrawal is requested 
    event Withdrawal(address indexed user, uint256 value); 
    // Emitted when Interest is set up for user 
    event UserInterestSetup( 
        address indexed user, 
        uint16 interestRate, 
        uint256 lockPeriod, 
        uint256 expirationDate 
    ); 
    // Emitted when tokens are locked for interest 
    event TokensLockedForInterest(address indexed user, uint256 amountLocked); 
    // Emitted when the user unlocks tokens and claims interest 
    event InterestClaimed( 
        address indexed user, 
        uint256 principal, 
        uint256 interestEarned 
    ); 
    // Emitted when new user is added to whitelist 
    event WhitelistedUserAdded(address indexed user); 
    // Emitted when user is deleted from whitelist 
    event WhitelistedUserRemoved(address indexed user); 
 
    // Modifier for functions that require Admin rights 
    modifier onlyAdmin() { 
        require(msg.sender == admin, "Not authorized"); 
    	_; 
    } 
    // Modifier for checking user status, i.e., whether or not the user is whitelisted 
    modifier whitelistedOnly(address user) { 
        require(whitelistedUsers[user], "Account is not whitelisted"); 
    	_; 
    }

Next, we can add methods that define the contract’s deposit, withdrawal, interest, and whitelist management flows:

Solidity
/* 
 	* Function for minting new tokens to the user, i.e. deposit process 
 	* 
 	* Requirements: 
 	* 
 	* - can only be called by the admin 
 	* - `user` must be whitelisted 
 	*/ 
    function processDeposit(address user, uint256 depositValue) 
        public 
        onlyAdmin 
        whitelistedOnly(user) 
    { 
        depositToken.mint(user, depositValue); 
        emit DepositProcessed(user, depositValue); 
    } 
 
    /* 
 	* Function for withdrawing from the bank, i.e., claiming tokens with interest 
 	* 
 	* Requirements: 
 	* - `user` must be whitelisted 
 	*/ 
    function requestWithdrawal(uint256 amount) public { 
        address user = msg.sender; 
        // The only thing we need to check is the `balance` of the `sender` 
        // As only whitelisted users can have a non-zero balance 
        require(amount != 0, "Cannot withdraw zero amount"); 
        require(depositToken.balanceOf(user) >= amount, "Insufficient balance"); 
 
        // Burn the tokens from User's balance 
        depositToken.burn(user, amount); 
 
        // Emit event for off-chain processing 
        emit Withdrawal(user, amount); 
    } 
 
    // View for checking `account` status, i.e., whether `account` is whitelisted 
    function isAccountWhitelisted(address account) public view returns (bool) { 
        return whitelistedUsers[account] || account == address(this); 
    } 
 
    /* 
 	* Function for the Bank to set up user's Interest Rates 
 	* 
 	* @param `user` - Address of the user to set up interest for 
 	* @param `interestRate` - Interest rate for the period in basis points (1 bp = 0.01%) 
 	* @param `lockPeriod` - End timestamp after which the user can redeem tokens with full interest 
 	* @param `minLockPeriod` - The minimum time duration (in seconds) tokens must remain locked 
 	* @param `minLocked` - Minimum number of tokens that the user can lock 
 	* @param `maxLocked` - Maximum number of tokens that the user can lock 
 	* @param `penalty` - Penalty in basis points applied if redeeming before lockPeriod ends (applied to gains only) 
 	* @param `expirationDate` - Timestamp when this interest rate offer expires (in seconds) 
 	* 
 	* Requirements: 
 	* - can only be called by the admin 
 	* - `user` must be whitelisted 
 	*/ 
    function setupUserInterest( 
        address user, 
        uint16 interestRate, 
        uint16 penalty, 
        uint256 lockPeriod, 
        uint256 minLockPeriod, 
        uint256 minLocked, 
        uint256 maxLocked, 
        uint256 expirationDate 
    ) public onlyAdmin whitelistedOnly(user) { 
        // Call the interest contract function to set up Interest conditions 
        interestManager.setupUserInterestConditions( 
        	user, 
            interestRate, 
        	penalty, 
            lockPeriod, 
            minLockPeriod, 
            minLocked, 
            maxLocked, 
            expirationDate 
        ); 
 
        emit UserInterestSetup(user, interestRate, lockPeriod, expirationDate); 
    } 
 
    /* 
 	* Function for the user to lock tokens and start gaining interest on the specified conditions 
 	* Can also be used to lock additional tokens 
 	* 
 	* Requirements: 
 	* 
 	* - sender balance has to be >= amount to lock 
 	* - `amount` cannot be zero, this also checks the user status as only whitelisted accounts can have a balance 
 	*/ 
    function startGainingInterest(uint256 amount) public { 
        address user = msg.sender; 
        // Check for the `sender` balance, same logic as in `withdrawal` 
        require(amount != 0, "Cannot lock zero token amount"); 
        require(depositToken.balanceOf(user) >= amount, "Insufficient balance"); 
 
        // Call the Interest contract to initiate tokens lock and interest gain start 
        interestManager.lockTokensForInterest(user, amount); 
 
        emit TokensLockedForInterest(user, amount); 
    } 
 
    /* 
 	* Function for the user to claim interest after the end of a full lock period 
 	* 
 	* Requirements: 
 	* 
 	* - sender must be a whitelisted user 
 	* - Bank contract must have enough Deposit Tokens to return the principal 
 	*   (initially locked tokens that are transferred to the Bank contract) 
 	*/ 
    function claimInterest() public whitelistedOnly(msg.sender) { 
        address user = msg.sender; 
        // Get locked amount and gains 
        (uint256 principal, uint256 interestEarned) = interestManager 
            .unlockAndRedeemInterest(user); 
 
        // Return locked amount 
        depositToken.transfer(user, principal); 
        // Pay out interest gains by minting new tokens 
        depositToken.mint(user, interestEarned); 
 
        emit InterestClaimed(user, principal, interestEarned); 
    } 
 
    /* 
 	* Function for the user for a partial claim before the end date, but after min lock period 
 	* 
 	* Requirements: 
 	* 
 	* - sender must be a whitelisted user 
 	* - `amount` must be non-zero 
 	*/ 
    function claimPartial(uint256 amount) public whitelistedOnly(msg.sender) { 
        require(amount != 0, "Cannot partial claim zero tokens"); 
        address user = msg.sender; 
        // Get the locked amount and gains - penalty for early retrieval 
        (uint256 principal, uint256 interestEarned) = interestManager 
            .partialUnlockAndRedeemInterest(user, amount); 
 
        // Return locked amount 
        depositToken.transfer(user, principal); 
        // Pay out interest gains by minting new tokens 
        depositToken.mint(user, interestEarned); 
 
        emit InterestClaimed(user, principal, interestEarned); 
    } 
 
    // Function for the Admin to add new users to the whitelist 
    function addToWhitelist(address user) public onlyAdmin { 
        whitelistedUsers[user] = true; 
        emit WhitelistedUserAdded(user); 
    } 
 
    // Function for the Admin to remove users from the whitelist 
    function removeFromWhitelist(address user) public onlyAdmin { 
        // Remove user from whitelist 
        delete whitelistedUsers[user]; 
        // Clean up user proposition 
        // Clean up of proposition is optional, as only whitelisted users can Unlock their tokens 
        interestManager.nullifyProposition(user); 
        // Burn user tokens 
        depositToken.burn(user, depositToken.balanceOf(user)); 
        emit WhitelistedUserRemoved(user); 
    } 
 
    function setToken(address _depositToken) public onlyAdmin { 
        depositToken = IDepositToken(_depositToken); 
    } 
 
    function setInterestManager(address _interestManager) public onlyAdmin { 
        interestManager = IInterestManager(_interestManager); 
    }

Now, our core logic for bank and token interactions is complete. However, any deposit should accrue interest, so let’s add that functionality.

Read also

How to Protect Your Assets: Crypto Wallet Security Best Practices

Get expert guidance on building and maintaining secure crypto wallets that ensure asset safety, compliance, and resilience against cyber threats.

Learn more
Crypto wallet security best practices

3. Interest contract

Our Interest contract manages the complex logic of time-locked deposits with interest accrual and penalty mechanisms. We’ll implement a comprehensive InterestInfo struct that tracks all aspects of a user’s interest-bearing position, including rates, lock periods, penalties, and accumulated rewards. 

The contract provides three main operational flows: 

  • initial setup through setupUserInterestConditions() where the Bank defines the terms
  • token locking via lockTokensForInterest() that transfers user tokens to the Bank and activates interest accrual
  • redemption through either unlockAndRedeemInterest() for full withdrawal after the lock period or partialUnlockAndRedeemInterest() for early withdrawal with a penalty applied 

The core interest calculation happens in updateInterestGains() using a time-weighted formula that calculates earnings based on locked amount, interest rate, and elapsed time, ensuring accurate reward distribution throughout the lock period. 

First, let’s think about things we need to store:

Solidity
// Mapping of users to information about interest gains 
    mapping(address => InterestInfo) public interests; 
    // Bank and Token references 
    IBank bank; 
    IDepositToken token;

For this contract, we only need the bankOnly() modifier: 

Solidity
// Modifier for Functions that can only be called by the Bank 
    modifier bankOnly() { 
        require(msg.sender == address(bank), "Not Authorized"); 
    	_; 
    }

Now, we need to define a custom data type that will hold all interest-related parameters and the state for each user. The InterestInfo struct groups fields into one structure for easy management and calculation of interest conditions.

Solidity
// Structure for storing interest information for each user 
    struct InterestInfo { 
        // Indicates whether interest gains are activated 
        bool active; 
        // Rate that is applied in basis points (1 bp = 0.01%) 
        uint16 interestRate; 
        // Penalty in basis points for early withdrawal 
        uint16 penalty; 
        // Timestamp of the last interest update 
        uint256 lastInterestUpdate; 
        // Time period for locking, e.g. 30 days (in seconds) 
        uint256 lockPeriod; 
        // Date after which redeeming is possible, e.g. 10 days (in seconds) 
        uint256 minLockPeriod; 
        // Total amount locked by the user 
        uint256 totalLocked; 
        // Minimum amount that can be locked 
        uint256 minLocked; 
        // Maximum amount that can be locked 
        uint256 maxLocked; 
        // Total interest gained so far 
        uint256 interestGained; 
        // End time of the lock period (timestamp) 
        uint256 endLock; 
        // Date until the proposition is active (timestamp) 
        uint256 expirationDate; 
    }

Now, let’s add the interest lifecycle logic: configure a user’s interest terms, lock tokens, and redeem. Let’s also include maintenance utilities to nullify offers and an internal accrual routine that updates interest over time using basis points and elapsed duration.

Solidity
// Function for setting up user interest conditions, called by the bank //contract 
function setupUserInterestConditions( 
        address user, 
        uint16 interestRate, 
        uint16 penalty, 
        uint256 lockPeriod, 
        uint256 minLockPeriod, 
        uint256 minLocked, 
        uint256 maxLocked, 
        uint256 expirationDate 
    ) public bankOnly { 
        // Make sure there are no active interest gains with other conditions 
        //In our example, we will make it possible for only 1 interest gain to be active at a 
        //time 
        require(!interests[user].active); 
        // Parameter validation 
        require (lockPeriod > minLockPeriod, "Lock period cannot be less than min lock period"); 
        require (minLocked <= maxLocked, "Min lock value cannot be greater than max lock value"); 
        require(expirationDate > block.timestamp, "Expiration date must be greater than present time"); 
 
        // Set up initial user state 
    	interests[user] = InterestInfo({ 
        	active: false, 
            interestRate: interestRate, 
        	penalty: penalty, 
            lastInterestUpdate: 0, 
            lockPeriod: lockPeriod, 
            minLockPeriod: minLockPeriod, 
            totalLocked: 0, 
            minLocked: minLocked, 
            maxLocked: maxLocked, 
            interestGained: 0, 
            endLock: 0, 
            expirationDate: expirationDate 
        }); 
    } 
           
      	// Called by the bank contract when user wants to lock tokens and start gaining //interest 
    function lockTokensForInterest(address user, uint256 amount) 
        public 
        bankOnly 
    { 
        uint256 currentTime = block.timestamp; 
        // First thing to check is whether now < expirationDate to make sure that 
        // the offer is not expired
        require(currentTime < interests[user].expirationDate, "Offer Expired"); 
 
        // Next, we need to check whether it's the first time locking the tokens 
        // or locking additional 
        if (!interests[user].active) { 
            // First time locking tokens 
 
            // Check that locked amount is within bounds 
            require( 
            	amount >= interests[user].minLocked && 
                	amount <= interests[user].maxLocked, 
                "Invalid Amount" 
            ); 
 
            // Transfer locked tokens from `user` to Bank 
            // Tokens should be previously approved to this contract for >= amount 
            token.transferFrom(user, address(bank), amount); 
 
            // After token transfer, the user starts earning interest 
        	interests[user].active = true; 
        	interests[user].totalLocked = amount; 
        	interests[user].lastInterestUpdate = currentTime; 
        	interests[user].endLock = currentTime + interests[user].lockPeriod; 
        } else { 
            // Additional tokens being locked 
 
            // Check whether locking new tokens will exceed the limit 
            uint256 newTotal = interests[user].totalLocked + amount; 
            require( 
                newTotal <= interests[user].maxLocked, 
                "Cannot lock more than max" 
            ); 
            // Check that the period for locking tokens has not ended 
            require( 
                currentTime <= interests[user].endLock, 
                "Out of LockPeriod" 
            ); 
 
            // Update the reward with the old `totalLocked` value 
            updateInterestGains(user); 
            // Update `totalLocked` value 
        	interests[user].totalLocked += amount; 
        } 
    } 
 
      	//  Function called by the bank when the user initiates redemption after the //lock period ends 
    function unlockAndRedeemInterest(address user) 
        public 
        bankOnly 
        returns (uint256, uint256) 
    { 
        InterestInfo storage userInterest = interests[user]; 
        require(userInterest.active, "Proposition is not active"); 
        require( 
            block.timestamp > userInterest.endLock, 
            "Lock period has not ended" 
        ); 
 
        updateInterestGains(user); 
 
        uint256 principal = userInterest.totalLocked; 
        uint256 interestEarned = userInterest.interestGained; 
        delete interests[user]; 
        return (principal, interestEarned); 
    } 
        
      	// Function for partial redeeming before the lock period ends
// Penalty is applied 
    function partialUnlockAndRedeemInterest(address user, uint256 amount) 
        public 
        bankOnly 
        returns (uint256, uint256) 
    { 
        InterestInfo storage userInterest = interests[user]; 
        require(userInterest.active, "Proposition is not active"); 
 
        // Check if partial unlock is possible 
        uint256 minLockTimestamp = userInterest.endLock - 
            userInterest.lockPeriod + 
            userInterest.minLockPeriod; 
        require( 
            block.timestamp >= minLockTimestamp, 
            "Minimum lock period has not ended" 
        ); 
 
        // Check whether the remainder is greater than the minimum value that the user must hold 
        require( 
            (userInterest.totalLocked - amount) >= userInterest.minLocked, 
            "Amount to unlock exceeds the limit of minimum amount user required to hold" 
        ); 
 
        // Update rewards 
        updateInterestGains(user); 
 
        // Apply penalty to interest gains 
        uint256 interestEarned = (userInterest.interestGained * 
            (10000 - userInterest.penalty)) / 10000; 
 
        userInterest.totalLocked -= amount; 
        userInterest.interestGained = 0; 
 
        return (amount, interestEarned); 
    } 
 
    // Utility function used for cleanup of the proposition when the user is deleted from the whitelist 
    // Similar functions can be created if the Bank wants to have control over propositions 
    function nullifyProposition(address user) public bankOnly { 
        require(!bank.isAccountWhitelisted(user), "User is whitelisted"); 
        delete interests[user]; 
    } 
 
    function updateInterestGains(address user) internal { 
        // Get current interest info for user 
        InterestInfo storage userInterest = interests[user]; 
 
        // Calculate elapsed time since the last update 
        // Use the minimum of the current time and end lock time 
        uint256 timeSinceLastUpdate = min( 
            block.timestamp, 
            userInterest.endLock 
        ) - userInterest.lastInterestUpdate; 
 
        // Calculate earnings for this period 
        // Using formula: 
        // earnings = lockedValue * interestRate * elapsedPeriod / (10000 * fullPeriod) 
        uint256 interestEarned = (userInterest.totalLocked * 
            userInterest.interestRate * 
            timeSinceLastUpdate) / (10000 * userInterest.lockPeriod); 
 
        // Update the state 
        userInterest.interestGained += interestEarned; 
        userInterest.lastInterestUpdate = min( 
            block.timestamp, 
            userInterest.endLock 
        ); 
    } 
 
    // Helper function to get the minimum of two values 
    function min(uint256 a, uint256 b) internal pure returns (uint256) { 
        return a < b ? a : b; 
    }

With that, our tokenized deposit implementation is complete. Now, let’s see how it works step by step.

Transaction flow for the tokenized deposit model

Our simplified tokenized deposit model accepts fiat currencies from users, sets an interest rate, and allows users to claim their deposits with complete or partial interest payouts.

How do our tokenized deposits work

1. Token deposit:  

  • User deposits fiat currency into the traditional banking system 
  • Admin calls processDeposit to mint tokens to the user’s wallet 
  • User now has tokens in their wallet that can be used or locked for interest 

2. Interest setup (this step can be done even before the deposit if the user is registered):  

  • Admin uses setupUserInterest in the Bank contract to configure unique interest parameters for a user 
  • This calls setupUserInterestConditions in the Interest contract 
  • Parameters include interest rate, lock period, minimum lock time, and penalty rates 

3. Locking tokens for interest:  

  • User decides to earn interest by calling startGainingInterest with the desired amount 
  • Bank contract verifies the user’s balance and whitelist status 
  • Interest contract’s lockTokensForInterest is called to verify that the token amount is within allowed min/max limits, calculate the end lock time, and update the user’s interest-earning state to active 

4. Interest accrual:  

  • While tokens are locked, interest accumulates based on the configured rate 
  • The updateInterestGains function calculates earned interest using the formula earnings = lockedValue * interestRate * elapsedPeriod / (10000 * fullPeriod) 
  • This calculation happens whenever the user interacts with the contract
     

5. Complete redemption:  

  • After the full lock period ends, the user calls claimInterest 
  • The Bank contract calls unlockAndRedeemInterest in the Interest contract 
  • The Interest contract calculates the final interest amount and returns both principal and interest 
  • The Bank contract transfers the principal back to the user and mints new tokens for the interest gained 
  • The user receives their original tokens plus additional tokens as interest 

6. Partial early redemption:  

  • If the user needs some tokens before the full lock period, they call claimPartial 
  • This triggers partialUnlockAndRedeemInterest in the Interest contract 
  • A penalty is applied to earned interest (calculated as interestGained * (10000 – penalty) / 10000) 
  • User receives requested tokens plus reduced interest amount 
  • Remaining tokens stay locked, and all accumulated interest is reset to zero 

7. Withdrawal:

  • User initiates the withdrawal by calling requestWithdrawal 
  • The bank burns the tokens from the user’s balance 
  • The emitted event is processed by the banking system 
  • The user can either cash out or keep the traditional deposit

This workflow is similar to the traditional banking process for a deposit, where a customer can store their money, benefit from accruing interest, and withdraw their money with partial or full benefits. But unlike the banking system, our tokenized model ensures fast, secure, and fair processing of a customer’s requests free from clerical errors and banking holidays.

Implement custom tokenized transactions with Apriorit

Apriorit’s experience working with FinTech companies covers both traditional and blockchain-based banking services. Working with us, you can expect:

  • Niche technical expertise accessible to you without investing in hiring and onboarding. Apriorit specialists will augment your internal team with practical blockchain development skills.
  • Compliance with FinTech requirements built into your product from day one. We make sure to bake compliance with all applicable regulations, standards, and laws into your product’s software requirements. This ensures that every line of code we write is compliant.
  • Balanced teams of expert developers, QA specialists, and project managers with diverse skill sets and levels of seniority. We’ll help you build exactly the team your project needs — and avoid paying for skills you won’t use.

When sharing their feedback, clients who chose us for their FinTech development projects emphasize our streamlined communication, proactive approach, and commitment to agreed deadlines. Here are some of our recent blockchain-based FinTech projects:

  • Building a DeFi aggregator for smarter asset management. A client asked us to help them aggregate several DeFi platforms. We built a custom system that combines multiple data sources, offering accurate APY calculations, liquidity insights, and credit health metrics within a single dashboard.
  • Evaluating smart contract security for a DeFi company. Our client was looking for cybersecurity experts to conduct an audit of their decentralized yield farm for creating liquidity and trading. During a smart contract security audit, the Apriorit team discovered and helped fix several high- and medium-risk vulnerabilities that could result in unexpected system behavior and security breaches.
  • Developing a blockchain application for international remittance payments. Upon our client’s request, our team designed a smart economy, created an Ontology-based network, and built several apps. This infrastructure allowed the client to launch an international remittance payments platform with over 11,000 active users and a transaction cost of less than $0.0001.

Whether you need to implement tokenized deposits, stablecoin integration, or a custom payment platform, our team will deliver a stable and reliable solution that fits your business needs.

Conclusion

Tokenized deposits represent a significant evolution in banking infrastructure, bridging traditional financial services with the programmability and efficiency of blockchain. This technology enables real-time settlement, programmable money features, and improved transparency – all of which were previously impossible with legacy systems.

While tokenized deposits will never completely substitute traditional bank deposits, we can expect them to augment a typical lineup of FinTech services. Financial institutions worldwide are increasingly recognizing blockchain’s transformative potential, with dozens of global banks either exploring or actively implementing blockchain solutions.

Implementing tokenized deposits in a secure, cost-efficient, and performant way is a task for an experienced FinTech development team. At Apriorit, we provide you with the tech skills and expertise needed to develop such a solution.

Ready to provide cutting-edge FinTech services?

Leverage Apriorit’s skills to jump-start your blockchain development journey!

FAQ

What is a tokenized deposit?

<p>Tokenized deposits are blockchain-based representations of traditional bank deposits that remove liabilities on the part of the issuing bank. They enable 24/7 transfers, programmable payments, and more efficient liquidity and treasury operations.</p>
<p>Unlike stablecoins or CBDCs, tokenized deposits fit fully within existing regulations, making them faster and simpler for banks to deploy for intrafirm settlements and corporate payment workflows.</p>

How does payment tokenization work?

<p>Payment tokenization replaces sensitive card data with a unique, non-exploitable token during a transaction.</p>
<p>When a customer pays online or in-app, their card number is sent to a tokenization service, which generates a token and stores the real card data securely in a vault. Merchants process these tokens instead of card numbers, reducing the potential for fraud. If compromised, a token is useless. This improves security, simplifies PCI compliance, and enables safer recurring and mobile payments.</p>

What is tokenization in banking?

<p>Tokenization replaces sensitive information such as credit card numbers or account identifiers with a unique, non-sensitive token that cannot be reverse-engineered or used to retrieve the original data. This ensures that real account details remain protected during transactions, storage, and data exchange.</p>
<p>Banks typically use two types of tokenization:</p>
<ul class=apriorit-list-markers-green>
<li>Payment tokenization, which secures card and account data during digital payments</li>
<li>Asset tokenization, which converts financial assets into digital tokens for easier transfer, trading, and programmability</li>
</ul>

Are tokenized deposits safe?

<p>Tokenized deposits offer a safety profile similar to traditional bank deposits because they are backed 1:1 by funds held at regulated banks and may fall under existing deposit insurance frameworks. Their value and legal status remain tied to the issuing bank, not to market volatility.</p>
<p>However, they also introduce new technology-driven risks, including potential smart contract flaws, operational errors in the tokenization platform, and broader cybersecurity vulnerabilities. Managing these risks requires rigorous audits, strong access controls, and resilient blockchain infrastructure.</p>

Have a question?

Ask our expert!

Maksym-Itskovych
Maksym Itskovych

Director of Global Markets Development

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.

* By sending us your request you confirm that you read and accepted our Terms & Conditions and Privacy Policy.