Solidity Wrapped Assets
A wrapped asset is a token minted one for one against a locked asset. One wrapped token always stands for one locked unit, and burning it returns that unit.
Wrapping mints the same amount it locks
Wrapping 100 locks 100 and mints 100 wrapped tokens. The balance, the supply, and the locked total all read 100.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract WrappedAsset {
mapping(address => uint256) public balances;
uint256 public totalSupply;
uint256 public totalLocked;
function wrap(uint256 amount) public {
require(amount > 0, "amount must be above zero");
balances[msg.sender] += amount;
totalSupply += amount;
totalLocked += amount;
}
function unwrap(uint256 amount) public {
require(balances[msg.sender] >= amount, "balance too low");
balances[msg.sender] -= amount;
totalSupply -= amount;
totalLocked -= amount;
}
}
Unwrapping burns and lowers the backing
Unwrapping 40 burns 40 tokens and frees 40 locked units. All three totals fall from 100 to 60 together.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract WrappedAsset {
mapping(address => uint256) public balances;
uint256 public totalSupply;
uint256 public totalLocked;
function wrap(uint256 amount) public {
require(amount > 0, "amount must be above zero");
balances[msg.sender] += amount;
totalSupply += amount;
totalLocked += amount;
}
function unwrap(uint256 amount) public {
require(balances[msg.sender] >= amount, "balance too low");
balances[msg.sender] -= amount;
totalSupply -= amount;
totalLocked -= amount;
}
}
Supply always equals the locked backing
After a wrap, a partial unwrap, and another wrap, the supply still equals the locked total. That equality is the one-to-one rule.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract WrappedAsset {
mapping(address => uint256) public balances;
uint256 public totalSupply;
uint256 public totalLocked;
function wrap(uint256 amount) public {
require(amount > 0, "amount must be above zero");
balances[msg.sender] += amount;
totalSupply += amount;
totalLocked += amount;
}
function unwrap(uint256 amount) public {
require(balances[msg.sender] >= amount, "balance too low");
balances[msg.sender] -= amount;
totalSupply -= amount;
totalLocked -= amount;
}
}
Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract WrappedAsset {
mapping(address => uint256) public balances;
uint256 public totalSupply;
uint256 public totalLocked;
function wrap(uint256 amount) public {
require(amount > 0, "amount must be above zero");
balances[msg.sender] += amount;
totalSupply += amount;
totalLocked += amount;
}
function unwrap(uint256 amount) public {
require(balances[msg.sender] >= amount, "balance too low");
balances[msg.sender] -= amount;
totalSupply -= amount;
totalLocked -= amount;
}
}
Key points
- Wrapped asset: A wrapped asset is a token minted one for one against a locked asset.
- Backing: Backing is the locked value that stands behind every wrapped token in circulation.
- Wrap: Wrapping locks an amount and mints the same amount of wrapped tokens.
- Unwrap: Unwrapping burns wrapped tokens and frees the same amount of locked value.
- One to one: One to one means one wrapped token always stands for exactly one locked unit.
- Restatement: Wrapping mints the same amount it locks, and unwrapping burns the same amount it frees.
In this example, the first holder wraps 100 and a second holder wraps 50, then the second holder unwraps 20. The two balances add to 130, and the supply and the locked total both read 130.
Note
This lesson tracks the locked amount as a number in one contract, which keeps the one-to-one rule visible without a second chain.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Remix IDE
Exercise
A holder wraps 100, then unwraps 25. What are the supply and the locked total?