Contracts and addresses

Every address, the signing domain, and the callable surface.

Everything below is public and can be inspected on Tronscan. The transparency dashboard publishes the same list with live balances.

Addresses (TRON mainnet)

ContractAddress
unitUSD (UNIT token)TRfLrnvVey1iMmYxynissaRjzK6KfeRoKE
MinterTYQuJSyBrExVtzaXBz3WvoTJ2N154DKAyt
stakedUnit (vault)TMsYapPhrro2DPdMs4GVppBaQikcggkBRU
CumulativeMerkleDrop (rewards)TBF1oRyhUjoN2xGkSNyiu2X1NVT3sdfCTQ
USDT (collateral)TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
USDD (reserve route)TXDk8mbtRbXeYuMNS83CfKPaYYT8XWv9Hz
jUSDD (JustLend market)TKFRELGGoRgiayhwJTNNLqCNjFoLBh3Mnf
USDT PSM (reserve route)TBXW4hS5KYjjbJXDpnrPf4zhkLwrpUjbyz
Signer walletTLtt2dSKqeUDnrB13d3KPdPGnCD7LyJM5o
Admin walletTXogJukTBnhs29x49JsBgP5TdbKwu9qJQT

Previous deployments, no longer used by the app — do not send funds to them:

ContractAddress
unitUSDTUEvq5gn63mVexaduankGdHWY7wwW9EudN
MinterTYpL3Fiye4i44SAWUx4NCok2D6bPzSdFKH
stakedUnitTPoKey4iWyCeMXMRGipzCtcMKepcxHTuLo
CumulativeMerkleDropTACrRi7fE3UKCaNfL3yinC52gbA6pyF15o

Precision

USDT, UNIT and vault shares all use 6 decimals. One UNIT is 1000000 in base units. Do not read decimals() per transaction — a failed read that returns zero scales an amount by a factor of one million.

Signing domain

EIP-712 domain
  name              Unit Minter
  version           3
  chainId           728126428
  verifyingContract 0xf62ece8fe87928b8b183242bf92b8fc6474e3f6d

The verifying contract is the Minter's own address in hexadecimal. Call eip712Domain() on the Minter to confirm it. A signature produced for a different verifying contract is rejected — this is what binds an authorisation to one deployment.

Signed structs:

Mint(
  address account,
  uint256 assets,
  bool    stake,
  uint256 nonce,
  uint256 deadline
)

Redeem(
  address account,
  uint256 assets,
  bool    unstake,
  uint256 nonce,
  uint256 deadline
)

The nonce comes from nonces(account) on the Minter.

Minter

function mint(
    uint256 assets,
    bool stake,
    uint256 minUnitOut,
    uint256 deadline,
    bytes signature
);

function redeem(
    uint256 assets,
    bool unstake,
    uint256 minUsdtOut,
    uint256 deadline,
    bytes signature
);

function nonces(address account) view returns (uint256);
function eip712Domain() view returns (...);

function paused() view returns (bool);
function pause();                                    // restricted role
function unpause();                                  // restricted role
function withdraw(address token, address to, uint256 amount); // admin
function executeCall(                               // admin
    address target,
    uint256 value,
    bytes data
);

Roles: SIGNER_ROLE, KEEPER_ROLE, DISTRIBUTOR_ROLE, DEFAULT_ADMIN_ROLE. Immutable references: UNIT, USDT, USDD, PSM, jUSDD, stakedUnit.

Events: Minted, Redeemed, RewardsDistributed, Paused, Unpaused.

Errors: InsufficientOutput, PermitExpired, InvalidAccountNonce, InvalidIntegration, OperationFailed, EnforcedPause, TransferFeeActive, ZeroAddress.

While the Minter is paused, the calls it guards revert with EnforcedPause. Pausing acts on the Minter only — holding and transferring UNIT are not affected.

unitUSD

function mint(address to, uint256 assets);           // MINTER_ROLE
function burn(uint256 assets);
function burnFrom(address from, uint256 assets);

Standard TRC-20 surface otherwise, plus role management. Only the Minter holds MINTER_ROLE. Events: Transfer, Approval.

The token has no freeze, blacklist or confiscation functions. No address can be frozen and no balance can be moved without its holder's signature.

stakedUnit

function deposit(uint256 amount, address receiver);
function withdraw(uint256 amount, address receiver, address owner);
function depositFor(address account, uint256 value);
function withdrawTo(address account, uint256 value);

function asset() view returns (address);             // unitUSD
function underlying() view returns (address);        // unitUSD

Standard TRC-20 surface otherwise. The vault has no freeze or confiscation functions either.

Checking the system yourself

  1. totalSupply() on unitUSD against the reserve position at jUSDD.
  2. hasRole(MINTER_ROLE, …) on unitUSD — only the Minter should hold it.
  3. USDT(), PSM(), USDD(), jUSDD() on the Minter — immutable, and they must match the table above.
  4. eip712Domain() on the Minter.
UNIT