Skip to content

Repository files navigation

TUSDT Contracts Workspace

Ink! contracts for a collateralized TUSDT system with vault borrowing, a lending/borrowing pool, liquidation auctions, and on-chain governance plus a treasury that books protocol fees.

The contracts split into three layers:

  • Protocoltusdt-erc20 (token, multi-minter), tusdt-vault-alpha (CDP borrowing/liquidation backed by subnet alpha), tusdt-lending-pool (TAO/TUSDT lending with alpha collateral, lToken receipt tokens, utilization-based interest rates), tusdt-auction (ascending-bid liquidation auctions for the vault), tusdt-oracle (collateral pricing). The vault owns the token/auction/oracle instances it creates; the lending pool spawns its own lToken children.
  • Governance & treasurytusdt-governance (token-holder proposals plus a maintainer/council authority that steers the protocol contracts) and tusdt-treasury (per-fund accounting for fees, released only by governance).

Prerequisites

  • Rust 1.89 (pinned via rust-toolchain.toml — rustup picks it up automatically)
  • wasm32-unknown-unknown target
  • cargo-contract 5.x
  • Node.js + Yarn (for the isolated contract tooling under tools/)
  • A Contracts-enabled Substrate node (local or remote)
rustup target add wasm32-unknown-unknown
cargo install --locked cargo-contract

Justfile

One-command gates mirror CI (requires just):

Command What it does
just fmt cargo fmt --all
just check cargo check --workspace
just test cargo test --workspace
just clippy cargo clippy --workspace --all-targets -- -D warnings
just lint fmtclippy-fixclippy
just build <name> Build one contract's artifacts (e.g. just build tusdt-lending-pool)
just build-all Build all 8 contracts' artifacts

Build

Build all crates:

cargo check

Build contract artifacts (.contract, .wasm, metadata):

cargo contract build --manifest-path contracts/tusdt-erc20/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-auction/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-oracle/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-vault-alpha/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-lending-pool/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-treasury/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-governance/Cargo.toml --release
cargo contract build --manifest-path contracts/tusdt-election/Cargo.toml --release

Artifacts are produced in target/ink/.

Lint, Format & Test

# Format all Rust source files
cargo fmt -- --check

# Lint with clippy (deny warnings)
cargo clippy --all-targets --all-features -- -D warnings

# Auto-fix formatting
cargo fmt

# Run all tests across the workspace
cargo test --workspace

# Run a single contract's tests
cargo test -p tusdt-vault-alpha

# Run a single named test with output
cargo test -p tusdt-vault-alpha <test_name> -- --nocapture

Panic-free policy (compiler-enforced)

Contracts are panic-free by construction — the clippy lints that are denied workspace-wide in the root Cargo.toml:

arithmetic-side-effects = "deny"
unwrap-used = "deny"
expect-used = "deny"
indexing-slicing = "deny"

Contract code must use .ok_or(...)/checked_*/saturating_*; a panic can never revert a message or brick a query. Rare provably-unreachable exceptions carry a narrowly-scoped #[allow] with a justification comment. Test modules opt out via the #![allow(...)] header at the top of each tests.rs (test ergonomics only — production code stays strict).

CI gate: cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace

⚠️ cargo contract build isolates each contract into a temp dir, so contracts define their [lints.clippy] deny block inline in their own Cargo.tomllints.workspace = true inheritance is used only by the non-contract crates (env, primitives, voting, test-support).

Test infrastructure (test-support/)

The off-chain chain-extension mock (MockExtension — function ids 0/15/36/1/2/5|6/25 plus register_mock*/set_caller helpers) used to be copy-pasted into every contract's tests.rs. It now lives once in the test-support workspace crate and is wired as a dev-dependency of all 8 contracts. When the chain extension gains a function id, update the mock once.

Error catalog (docs/errors/)

Every Error variant of all 8 contracts is documented in docs/errors/ — one page per contract with the enum's doc comment, the messages that return each variant, and client guidance (input error? authorization? retry?). docs/errors/index.md links them all. Add/rename a variant → update its page in the same commit.

Contract Tooling (tools/)

Shared deployment scripts and on-chain tests live in an isolated TypeScript subproject under tools/. The current iteration exposes upload support for erc20, auction, oracle, and vault-alpha, plus a single vault-alpha deployment entrypoint. The treasury, governance, and election upload scripts are also available.

Setup:

cd tools
yarn install
cp .env.example .env

Default .env values target a local dev node:

  • WS_URL=ws://127.0.0.1:9944

Scripts and tests use the standard local dev accounts (//Alice, //Bob, //Charlie, //Dave, //Eve, //Ferdie) from the shared dev-account helper. When needed, you can override the selected account via SURI environment variables such as CONTRACT_UPLOADER=//Alice, CONTRACT_DEPLOYER=//Alice.

Useful commands:

cd tools
yarn build:erc20-artifacts
yarn build:auction-artifacts
yarn build:oracle-artifacts
yarn build:vault-artifacts
yarn erc20:upload
yarn auction:upload
yarn oracle:upload
yarn vault:upload
yarn vault:deploy --token-code-hash <TOKEN_CODE_HASH> --auction-code-hash <AUCTION_CODE_HASH> --oracle-code-hash <ORACLE_CODE_HASH> --treasury-address <SS58> --oracle-netuid <NETUID> --hotkey <SS58>
yarn test:oracle

Deployment (Recommended Order)

tusdt-vault-alpha::new takes a treasury address, code hashes for token/auction/oracle, oracle_netuid (subnet for oracle reporters), and hotkey (staking hotkey for alpha collateral). The vault instantiates the token, auction, and oracle internally. Because the vault creates the TUSDT token, the token address is not known until the vault exists — deploy the treasury after the vault and wire it in via update_treasury.

  1. Upload ERC20 code (tusdt-erc20) and capture code hash.

  2. Upload Auction code (tusdt-auction) and capture code hash.

  3. Upload Oracle code (tusdt-oracle) and capture code hash.

  4. Instantiate Alpha Vault (tusdt-vault-alpha::new) with:

    • treasury — a placeholder address for now (e.g. the deployer); reassigned in step 7.
    • token_code_hash
    • auction_code_hash
    • oracle_code_hash
    • oracle_netuid — subnet whose registered neurons may submit oracle prices
    • hotkey — staking hotkey for alpha collateral deposits

    The deployer becomes the initial governance of the vault, auction, and oracle.

  5. Read the token / auction / oracle addresses from the vault (get_token_address, get_auction_address, get_oracle_address).

  6. Instantiate Treasury (tusdt-treasury::new) with the vault's TUSDT token address.

  7. Wire the vault's fee recipient: tusdt-vault-alpha::update_treasury(treasury).

  8. Add vault as minter on ERC20: tusdt-erc20::add_minter(vault_address).

  9. Upload Election code (tusdt-election) and capture code hash.

  10. Instantiate Governance (tusdt-governance::new) with the treasury, vault, auction, oracle addresses, initial maintainer, and election_code_hash.

  11. Hand control to the governance contract:

    • tusdt-treasury::set_governance(governance)
    • tusdt-vault-alpha::update_governance(governance) — propagates to auction and oracle too.
  12. Seat the council: tusdt-governance::set_council([c1..c5]).

  13. Approve target subnets: tusdt-governance::vault_set_approved_netuid(N, true) for each subnet.

  14. Configure per-netuid params: tusdt-governance::vault_set_contract_params(N, params) for each subnet (24h timelock, then anyone calls tusdt-vault-alpha::execute_contract_params_update(N)).

  15. (Optional) Adjust global params: tusdt-governance::vault_set_global_params(config) (24h timelock, then anyone calls tusdt-vault-alpha::execute_global_params_update()).

After step 11 the protocol contracts are steered exclusively by the governance contract, and within governance the maintainer/council split (see Governance & Treasury) applies.

Example CLI for the protocol layer (adjust URL/account):

cargo contract upload \
  --manifest-path contracts/tusdt-erc20/Cargo.toml \
  --suri //Alice --url ws://127.0.0.1:9944

cargo contract upload \
  --manifest-path contracts/tusdt-auction/Cargo.toml \
  --suri //Alice --url ws://127.0.0.1:9944

cargo contract upload \
  --manifest-path contracts/tusdt-oracle/Cargo.toml \
  --suri //Alice --url ws://127.0.0.1:9944

cargo contract instantiate \
  --manifest-path contracts/tusdt-vault-alpha/Cargo.toml \
  --constructor new \
  --args <TREASURY_OR_PLACEHOLDER> <ERC20_CODE_HASH> <AUCTION_CODE_HASH> <ORACLE_CODE_HASH> <ORACLE_NETUID> <HOTKEY> \
  --suri //Alice --url ws://127.0.0.1:9944

Then deploy the governance layer and wire the roles:

cargo contract instantiate \
  --manifest-path contracts/tusdt-treasury/Cargo.toml \
  --constructor new \
  --args <TUSDT_TOKEN_ADDRESS> \
  --suri //Alice --url ws://127.0.0.1:9944

cargo contract upload \
  --manifest-path contracts/tusdt-election/Cargo.toml \
  --suri //Alice --url ws://127.0.0.1:9944

cargo contract instantiate \
  --manifest-path contracts/tusdt-governance/Cargo.toml \
  --constructor new \
  --args <TREASURY_ADDRESS> <VAULT_ADDRESS> <AUCTION_ADDRESS> <ORACLE_ADDRESS> <MAINTAINER> \
         <ELECTION_CODE_HASH> \
  --suri //Alice --url ws://127.0.0.1:9944

Prefer the tools/ workflow above instead of using cargo contract for upload/deploy operations where a TS script already exists. The e2e suite covers the oracle (yarn test:oracle) and lending pool liquidation (yarn test:liquidation); upload scripts exist for erc20, auction, oracle, vault, treasury, governance, and election (yarn <name>:upload, paired with build:<name>-artifacts), while vault:deploy is the only full deployment entrypoint — deploy and wire the rest with cargo contract as shown.

Lending Pool

The lending pool (tusdt-lending-pool) is a standalone protocol contract that enables:

  • Supply TAO/TUSDT to earn variable yield (receive lTAO/lTUSDT receipt tokens)
  • Supply Alpha collateral (one market per approved subnet) to gain borrowing power
  • Borrow TAO/TUSDT against Alpha collateral with health factor checks
  • Direct full-seizure liquidation when health factor drops below 1.0 — repays the full debt on both markets, seizes all alpha collateral (platform liquidation fee, default 5%), and writes off any residual bad debt as a market deficit

Source layout: the contract module is split into lib.rs (storage, events, messages, queries) plus params.rs (interest/alpha/global param structs + validation), rates.rs (interest accrual, borrow-rate curve, cash accounting), and risk.rs (oracle pricing, health factor, borrow capacity, liquidation math).

Deployment (Lending Pool)

  1. Upload the lending pool code and capture the code hash:
    cargo contract upload \
      --manifest-path contracts/tusdt-lending-pool/Cargo.toml \
      --suri //Alice --url ws://127.0.0.1:9944
  2. Instantiate the pool with the existing treasury, TUSDT token, oracle, lToken code hash (reuses tusdt-erc20 code), and a staking hotkey:
    cargo contract instantiate \
      --manifest-path contracts/tusdt-lending-pool/Cargo.toml \
      --constructor new \
      --args <TREASURY_ADDRESS> <TUSDT_TOKEN_ADDRESS> <ORACLE_ADDRESS> \
             <LTOKEN_CODE_HASH> <POOL_HOTKEY> \
      --suri //Alice --url ws://127.0.0.1:9944
    The pool spawns two child lToken instances (lTAO for market 0, lTUSDT for market 1).
  3. Read the spawned lToken addresses:
    • pool.get_ltoken_address(0) → lTAO
    • pool.get_ltoken_address(1) → lTUSDT
  4. Approve alpha markets: pool.set_approved_netuid(netuid, true) (maintainer) for each target subnet; per-subnet collateral params are configured separately via the timelocked set_alpha_params.
  5. (Optional) Wire governance: pool.update_governance(governance_address), then call governance.update_pool_address(pool_address) to record the pool in the governance contract.
  6. (Optional) Adjust interest rate params, alpha params, or global params via timelocked updates.

Lending pool lifecycle

  1. Supply: Users deposit TAO (payable) or TUSDT (transfer_from) → receive lTAO/lTUSDT at the current exchange rate. Exchange rate starts at 1.0 and grows as interest accrues.
  2. Supply Alpha collateral: Users call deposit_alpha(netuid, amount) → atomically pulls the caller's alpha stake into the pool's coldkey via chain extension func 25 (same mechanism as the vault). One market per approved subnet.
  3. Borrow: Users with alpha collateral can borrow TAO or TUSDT. Borrowing power is: collateral_value × collateral_factor − existing_debt. Health factor must stay ≥ 1.0. One hour of interest is charged up front — the position's debt exceeds the amount from the very first block (hour-beginning charging), and the next increase arrives with the market's next hourly accrual. Repaying inside that hour does not refund it.
  4. Repay: Repay TAO (payable) or TUSDT (transfer_from). Repaid assets stay as pool liquidity — no burn. Interest accrues hourly while borrowed.
  5. Withdraw collateral: Only allowed when the account remains healthy after withdrawal. Uses chain extension func 6 (transfer_stake) to return stake to the user's coldkey.
  6. Liquidate: Permissionless. When health_factor < 1.0, anyone can call liquidate(borrower): the liquidator repays the borrower's full debt on both markets (interest accrued first) and receives the borrower's entire alpha collateral, minus the platform's liquidation fee (default 5%, capped at the surplus share of the seized collateral). If the collateral cannot cover the debt, the residual is written off as a frozen market deficit (DeficitReported) that the maintainer can fund from the reserve via cover_deficit.

Interest rate model

  • Utilization-based 2-zone curve: U = total_debt / (total_debt + cash).
    • Zone 1 (U ≤ optimal): base_rate + slope1 × U / optimal
    • Zone 2 (U > optimal): base_rate + slope1 + slope2 × (U − optimal) / (1 − optimal)
  • Hourly compounding via checked_pow with the first hour prepaid at borrow time (charge_prepaid_hour) — interest starts accruing from the moment of the borrow instead of after the first whole hour; the prepaid premium is split reserve/suppliers exactly like index-driven interest.
  • Reserve factor (default 20%): share of borrower interest sent to the protocol treasury. supplier_rate = borrow_rate × U × (1 − reserve_factor).
  • lToken exchange rate: underlying = ltoken_balance × exchange_rate. Exchange rate starts at 1.0 and grows monotonically as supplier interest accrues. Non-rebasing (Compound-style).

Alpha yield sweep

Alpha collateral continues earning native staking yield while supplied. A permissionless claim_alpha_excess(netuid) function unstakes the full excess (available stake per the chain extension − booked collateral) and transfers the proceeds to the treasury as native TAO. There is no yield index and no 25/75 split — the entire excess goes to the treasury.

Default parameters

Asset base_rate slope1 slope2 optimal_util reserve_factor
TAO 0% 4% 96% 80% 20%
TUSDT 0% 3% 97% 80% 20%
Alpha param Default
Collateral factor 50%
Liquidation threshold 60%
Liquidation fee (platform cut) 5%
Alpha supply cap 0 (unlimited)
Global param Default
Max oracle age 30 min
Supply caps (TAO/TUSDT) 0 (unlimited)
Borrow caps (TAO/TUSDT) 0 (unlimited)

Useful pool read methods

  • get_market_state(market_id)MarketState (total_supplied, total_scaled_debt, total_debt, borrow_index, exchange_rate, reserve_accrued, last_update)
  • get_position(market_id, user)Position (ltoken_balance, scaled_debt, alpha_principal)
  • get_exchange_rate(market_id), get_borrow_index(market_id), get_utilization(market_id)
  • get_borrow_rate(market_id), get_supply_rate(market_id) — current annualized rates
  • get_underlying_balance(market_id, user) → underlying value of lToken position
  • get_user_debt(market_id, user) → current debt in underlying units
  • get_user_debt_details(market_id, user)(debt, principal) — debt includes accrued interest; interest = debt − principal. Principal is tracked in a dedicated mapping updated on borrow/repay/liquidate; positions created before principal tracking fall back to an estimate.
  • get_alpha_markets()Vec<(netuid, AlphaMarketParams)> — list all approved alpha markets
  • get_user_alpha_position(user, netuid) → alpha principal for a specific subnet
  • get_netuid_total_collateral(netuid), get_market_deficit(market_id)
  • paused(), governance(), treasury(), platform(), get_pool_hotkey(), get_oracle_address()
  • Paginated: get_positions(user, page), get_all_positions(page) (10 per page)

Health factor, borrow capacity, and collateral/debt value are computed by internal helpers (get_health_factor, get_available_borrow_tusdt, get_collateral_value_tusdt, get_debt_value_tusdt) — they are not #[ink(message)] queries, so they cannot be called on-chain; clients derive them from the queryable market/position data.

Governance forwarders

After wiring (pool.update_governance(governance)), the governance contract can steer the pool:

Forwarder Gated by Purpose
pool_set_approved_netuid(netuid, approved) maintainer Add/remove alpha collateral markets
pool_set_market_params(market, config) / cancel maintainer Schedule timelocked interest rate changes
pool_set_alpha_params(netuid, config) / cancel maintainer Schedule timelocked alpha param changes
pool_set_global_params(config) / cancel maintainer Schedule timelocked global param changes
pool_unpause maintainer Resume operations
pool_update_pool_hotkey(new_hotkey, netuids) maintainer Migrate alpha stake to new hotkey

All param changes follow the 24h timelock: schedule (governance) → execute (permissionless, time-gated) → cancel (governance). Params can be read at any time via get_pending_*_params_update.

Working Flow

1) Vault lifecycle (atomic pull deposit)

  1. User stakes alpha under the vault's hotkey (if not already staked there) — the pull keeps the hotkey.
  2. User creates vault: create_alpha_vault(amount, netuid) — the contract atomically pulls amount of the caller's alpha into its own coldkey via the caller-forwarded caller_transfer_stake chain extension (function 25) and opens the CDP in the same message. Deposits are always attributed to the caller; no separate intent or transfer_stake extrinsic is needed. Requires the subnet's TransferToggle to be on and the amount to exceed the chain's minimum stake (0.002 TAO equivalent); failures revert cleanly with StakeTransferFailed.
  3. User borrows token: borrow_token(vault_id, amount).
  4. User repays token: repay_token(vault_id, amount).
  5. User adds more alpha collateral: add_alpha_collateral(vault_id, amount) — pulls exactly amount from the caller, same mechanism as vault creation.
  6. User releases alpha collateral: release_alpha_collateral(vault_id, amount, dest_coldkey) — returns stake via chain extension.

Deposit messages are EOA-facing: a contract calling the vault would pull its own stake, since the chain extension forwards the immediate caller's origin.

2) Interest model

The vault charges no interest: the borrowed amount equals the debt at all times and repayments are 1:1. Hourly-compounding interest exists only in the lending pool — the vault's borrow_token mints exactly amount and repay_token burns exactly amount.

3) Liquidation flow

  1. Anyone can call trigger_liquidation_auction(owner, vault_id) when vault exceeds liquidation threshold.
  2. Auction contract creates an auction tied to that vault.
  3. Bidders approve token allowance to auction contract, then call place_bid.
  4. After end time, call finalize_auction on auction contract.
  5. Vault settlement: settle_liquidation_auction(owner, vault_id).

4) Admin flow

Privileged protocol actions are gated by each contract's governance role. Before hand-off this is the deployer; after wiring (deployment steps 7 & 9) it is the governance contract, and you drive them through governance's forwarders rather than calling the protocol contracts directly. See Governance & Treasury for who may invoke what.

Risk params split into two scopes, both applied behind a 24h timelock:

  • Per-netuid — governance schedules set_contract_params(netuid, params) per subnet (after hand-off via the vault_set_contract_params forwarder). Params: collateral_ratio, liquidation_ratio, liquidation_fee (all basis points). Falls back to defaults for unconfigured netuids.
  • Global (all netuids) — governance schedules set_global_params(config) (after hand-off via the vault_set_global_params forwarder). Params: transaction_fee (basis points), auction_duration_ms, max_oracle_age_ms, vault_creation_fee (native TAO in rao).

Governance also controls which subnets are accepted via set_approved_netuid(netuid, approved) (exposed after hand-off through the vault_set_approved_netuid forwarder).

Oracle reporting is permissionless: any registered neuron of the oracle's subnet (stake above the minimum) may submit_price for the open round. The validator and the max price deviation are governance-set. The active round is committed by the validator via commit_round; governance can also commit an emergency override price (see below).

Default per-netuid params:

  • Collateral ratio: 150%
  • Liquidation ratio: 120%
  • Liquidation fee: 11%

Default global params:

  • Transaction fee: 0.3% (30 bps)
  • Auction duration: 3_600_000 milliseconds (1 hour)
  • Max oracle age: 1_800_000 milliseconds (30 minutes)
  • Vault creation fee: 5_000_000 rao (0.005 TAO)

Governance & Treasury

Authorities

tusdt-governance carries two roles in addition to token-holder voting:

  • Maintainer — the top authority (the elected subnet owner). Set initially at construction and thereafter replaced only by the election contract via elect_maintainer. The maintainer seats the council (set_council), updates governance parameters (update_params), and drives the protocol config forwarders below. The election contract — instantiated by governance's constructor — runs the election and installs the winner as maintainer.
  • Council — a fixed committee of exactly 5 members set by the maintainer. The council performs operational duties: committing voting snapshots (submit_snapshot) and the emergency vault halt (vault_pause). Any single council member can act on these.

Token-holder proposals

Funding and signal proposals are decided by token-weighted voting against a committed Merkle snapshot (voting power is sqrt(snapshot balance) × time-staked multiplier, so flash-staking can't inflate it). Flow: submit_proposalvotefinalizeexecute. Submission is gated on the proposer's subnet alpha stake and a monthly submission window; passing requires both quorum (a fraction of circulating supply) and an approval threshold. A passed Funding proposal calls treasury.release(...) on execution; NonFunding proposals are signal-only.

Steering the protocol (forwarders)

After the role hand-off, the governance contract holds the governance role on the vault, auction, and oracle. It exposes thin forwarders that perform the cross-contract call; the protocol contracts see the governance contract as their governance caller. Authorization is decided inside governance:

Forwarder Gated by Target
vault_set_contract_params(netuid, params), vault_cancel_contract_params_update(netuid) maintainer vault-alpha (per-netuid timelocked params)
vault_set_global_params(config), vault_cancel_global_params_update() maintainer vault-alpha (global timelocked params: fee, auction duration, oracle age)
vault_set_approved_netuid(netuid, approved) maintainer vault-alpha (accepted collateral subnets)
vault_update_treasury, vault_update_platform, vault_unpause maintainer vault-alpha
vault_pause council (fast emergency halt) vault-alpha
oracle_set_validator, oracle_set_max_price_deviation maintainer oracle
oracle_commit_round maintainer (emergency price — drives liquidations) oracle
auction_set_admin maintainer auction

update_governance on the protocol contracts is intentionally not forwarded — the vault's role is fixed after wiring (and it propagates role changes to the auction/oracle itself).

Treasury

tusdt-treasury books protocol fees into named funds — Emergency, Operation, Insurance, Dividend, Buyback, Voting — in both Tusdt and Native denominations. distribute() splits incoming balance across the funds; release(fund, token_kind, amount, recipient) pays out and is callable only by governance (i.e. via an executed Funding proposal). The deployer is the initial governance until set_governance hands control to the governance contract.

Useful Read Methods

  • Vault: get_vault, get_total_debt, get_contract_params(netuid), get_global_params(), is_approved_netuid(netuid), get_oracle_address, get_vaults, get_all_vaults
  • Oracle: get_latest_price, get_current_round_summary, get_round_price, get_price_history, get_price_history_count, get_round_submissions
  • Chain extension: get_alpha_price(netuid) — on-chain subnet alpha/TAO price (RAO-scaled by 1e9)
  • Auction: get_auction, get_active_vault_auction, get_bid, get_all_auctions, get_active_auctions
  • Token: balance_of, allowance, total_supply
  • Governance: maintainer, election, netuid, council, is_council, params, current_epoch, get_snapshot, quorum, proposal_count, get_proposal, has_voted
  • Treasury: governance, token, fund_balance_tusdt, fund_balance_native

Notes

  • tusdt-vault-alpha owns the token and auction instances it creates; tusdt-lending-pool spawns its own lToken children (lTAO, lTUSDT) reusing the tusdt-erc20 code hash.
  • Alpha collateral is verified via chain extension (get_stake_info). Both the vault and lending pool act as coldkeys for staked alpha, using the caller-forwarded caller_transfer_stake (func 25) for atomic pull deposits.
  • Pricing: TUSDT_per_alpha = oracle_TUSDT_per_TAO * (get_alpha_price(netuid) / 1_000_000_000).
  • Borrowing from the vault mints TUSDT to borrower; the lending pool transfers existing TUSDT (no mint on borrow — pool liquidity comes from suppliers).
  • Vault repayment and settlement burn TUSDT; lending pool repayment keeps TUSDT as pool cash.
  • Protocol fees accrue to tusdt-treasury; only tusdt-governance can release them.
  • After wiring, the vault/auction/oracle/pool are governed by tusdt-governance; the maintainer and council act through its forwarders rather than calling those contracts directly.
  • The lending pool uses direct full-seizure liquidation with a platform fee (not auctions). The existing tusdt-auction contract serves the vault's liquidation path only.

About

Ink! contracts for a collateralized TUSDT system with vault borrowing, interest accrual, and liquidation auctions with treasury,governance and election.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages