Skip to main content

assets

Stellar Assets (classic) + trustlines + Stellar Asset Contract (SAC) bridge to smart contracts. Covers asset issuance, distribution, authorization flags, clawback, regulated assets, trustline management, and the SAC interop layer that exposes classic assets as SEP-41 contract tokens. Use when tokenizing real-world assets, issuing stablecoins, managing trustlines, or bridging classic assets to smart contracts.

소스 정보

저장소
stellar/stellar-dev-skill
최근 소스 활동
2026년 9월 4일 12:33
감지된 SKILL.md 언어
영어
스타
53
포크
64

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
assets
description
Stellar Assets (classic) + trustlines + Stellar Asset Contract (SAC) bridge to smart contracts. Covers asset issuance, distribution, authorization flags, clawback, regulated assets, trustline management, and the SAC interop layer that exposes classic assets as SEP-41 contract tokens. Use when tokenizing real-world assets, issuing stablecoins, managing trustlines, or bridging classic assets to smart contracts.
user-invocable
true
argument-hint
[asset task]
# Stellar Assets, Trustlines, and SAC Stellar's native token mechanism: classic asset issuance, trustlines, and the Stellar Asset Contract (SAC) bridge that makes classic assets usable from smart contracts. Default to classic assets over custom contract tokens unless you need custom logic. ## When to use this skill - Issuing a new asset (stablecoin, security token, utility token) - Setting up trustlines from a client or contract - Managing issuer flags (auth required, auth revocable, clawback) - Bridging a classic asset into a smart contract via SAC - Building regulated-asset flows (compliance, KYC, freeze) ## Related skills - Custom token contracts (when classic isn't enough) → `../smart-contracts/SKILL.md` - UI flows for trustline creation and asset display → `../dapp/SKILL.md` - Looking up balances and trustline state → `../data/SKILL.md` - Token-related SEPs (SEP-41, SEP-7, etc.) → `../standards/SKILL.md` --- ## Overview Stellar has two token mechanisms: 1. **Stellar Assets (Classic)**: Built-in, highly efficient, full ecosystem support 2. **Contract tokens (SEP-41)**: Custom contracts with flexible logic **Recommendation**: Prefer Stellar Assets unless you need custom token logic. ## Stellar Assets (Classic) ### Asset Types | Type | Description | |------|-------------| | Native (XLM) | Stellar's native currency, no trustline needed | | Credit | Issued by an account, requires trustline | | Liquidity Pool Shares | Represent LP positions | ### Asset Identifiers ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; // Native XLM const xlm = StellarSdk.Asset.native(); // Credit asset (code + issuer) const usdc = new StellarSdk.Asset( "USDC", "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN" ); // Asset code rules: // - 1-4 chars: alphanumeric (credit_alphanum4) // - 5-12 chars: alphanumeric (credit_alphanum12) ``` ## Issuing Assets ### Create Issuing Account ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; const server = new StellarSdk.Horizon.Server("https://horizon-testnet.stellar.org"); // 1. Create issuing account (should be separate from distribution) const issuerKeypair = StellarSdk.Keypair.random(); const distributorKeypair = StellarSdk.Keypair.random(); // 2. Fund accounts (testnet) await fetch(`https://friendbot.stellar.org?addr=${issuerKeypair.publicKey()}`); await fetch(`https://friendbot.stellar.org?addr=${distributorKeypair.publicKey()}`); ``` ### Issue Asset ```typescript const asset = new StellarSdk.Asset("MYTOKEN", issuerKeypair.publicKey()); // 1. Distributor creates trustline to issuer const distributorAccount = await server.loadAccount(distributorKeypair.publicKey()); const trustlineTx = new StellarSdk.TransactionBuilder(distributorAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.changeTrust({ asset: asset, limit: "1000000", // Max amount to hold }) ) .setTimeout(180) .build(); trustlineTx.sign(distributorKeypair); await server.submitTransaction(trustlineTx); // 2. Issuer sends tokens to distributor const issuerAccount = await server.loadAccount(issuerKeypair.publicKey()); const issueTx = new StellarSdk.TransactionBuilder(issuerAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.payment({ destination: distributorKeypair.publicKey(), asset: asset, amount: "1000000", }) ) .setTimeout(180) .build(); issueTx.sign(issuerKeypair); await server.submitTransaction(issueTx); ``` ### Lock Issuing Account For fixed-supply tokens, lock the issuer: ```typescript const lockTx = new StellarSdk.TransactionBuilder(issuerAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.setOptions({ masterWeight: 0, // Disable master key }) ) .setTimeout(180) .build(); lockTx.sign(issuerKeypair); await server.submitTransaction(lockTx); // Issuer can never issue more tokens ``` ## Asset Flags Configure issuer account flags for compliance: ```typescript const setFlagsTx = new StellarSdk.TransactionBuilder(issuerAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.setOptions({ setFlags: StellarSdk.AuthRequiredFlag | // Trustlines require approval StellarSdk.AuthRevocableFlag | // Can freeze trustlines StellarSdk.AuthClawbackEnabledFlag, // Can clawback tokens }) ) .setTimeout(180) .build(); ``` ### Flag Descriptions | Flag | Effect | |------|--------| | `AUTH_REQUIRED` | Users must get approval before receiving tokens | | `AUTH_REVOCABLE` | Issuer can freeze user balances | | `AUTH_IMMUTABLE` | Flags cannot be changed (permanent) | | `AUTH_CLAWBACK_ENABLED` | Issuer can clawback tokens from accounts | ### Authorize Trustline ```typescript // When AUTH_REQUIRED is set, approve trustlines: const authorizeTx = new StellarSdk.TransactionBuilder(issuerAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.setTrustLineFlags({ trustor: userPublicKey, asset: asset, flags: { authorized: true, // authorizedToMaintainLiabilities: true, // Partial auth }, }) ) .setTimeout(180) .build(); ``` ### Clawback Tokens ```typescript // Requires AUTH_CLAWBACK_ENABLED flag const clawbackTx = new StellarSdk.TransactionBuilder(issuerAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.clawback({ asset: asset, from: targetAccountId, amount: "100", }) ) .setTimeout(180) .build(); ``` ## Trustlines ### Create Trustline ```typescript const changeTrustTx = new StellarSdk.TransactionBuilder(userAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.changeTrust({ asset: asset, limit: "10000", // Max amount to hold (see "Remove Trustline" for limit: "0") }) ) .setTimeout(180) .build(); ``` ### Remove Trustline `ChangeTrust` with `limit: "0"` deletes a trustline, but only if the trustline is in a deletable state. Before submitting: 1. **Identify the asset by code AND issuer.** A wallet-display entry or a claimable balance is not a trustline — only an actual classic asset trustline (or liquidity pool share) on the account can be removed with `ChangeTrust`. Claimable balances are removed by claiming or clawing them back, not by `ChangeTrust`. 2. **Clear the balance.** The trustline balance must be exactly `0` — send remaining funds back to the issuer (burns them) or to another account. 3. **Clear offers / buying liabilities.** Open DEX offers that buy the asset create buying liabilities; cancel them before removal (`manageSellOffer` with `amount: "0"`, `manageBuyOffer` with `buyAmount: "0"`). 4. **Exit liquidity pool positions.** An asset trustline referenced by a liquidity pool (`liquidity_pool_use_count > 0`, per CAP-0038) cannot be deleted — withdraw from the pool and remove the pool-share trustline first. ```typescript // 1. Check the trustline is deletable const account = await server.loadAccount(userPublicKey); const trustline = account.balances.find( (b) => b.asset_type !== "native" && b.asset_type !== "liquidity_pool_shares" && b.asset_code === asset.getCode() && b.asset_issuer === asset.getIssuer() ); if (!trustline) throw new Error("No trustline (display/claimable state only?)"); if (parseFloat(trustline.balance) !== 0) throw new Error("Balance must be 0 — send funds away or back to issuer"); if (parseFloat(trustline.buying_liabilities) !== 0) throw new Error("Cancel open offers buying this asset first"); // Liquidity-pool usage (precondition 4) is not visible on this balance line — // if the asset is still in one of the account's pools, the submit below fails // with op_cannot_delete. Withdraw and remove the pool-share trustline first. // 2. Submit removal and surface the specific result code const removeTrustTx = new StellarSdk.TransactionBuilder(account, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation( StellarSdk.Operation.changeTrust({ asset: asset, limit: "0", // Delete the trustline }) ) .setTimeout(180) .build(); removeTrustTx.sign(userKeypair); try { await server.submitTransaction(removeTrustTx); } catch (e) { const codes = e.response?.data?.extras?.result_codes?.operations ?? []; if (codes.includes("op_invalid_limit")) { // CHANGE_TRUST_INVALID_LIMIT: balance or buying liabilities remain throw new Error("Clear balance and open offers before removing trustline"); } if (codes.includes("op_cannot_delete")) { // CHANGE_TRUST_CANNOT_DELETE: trustline is used by a liquidity pool throw new Error("Withdraw from liquidity pools referencing this asset first"); } throw e; } ``` ### Check Trustline Status ```typescript const account = await server.loadAccount(userPublicKey); const trustline = account.balances.find( (b) => b.asset_type !== "native" && b.asset_code === "USDC" && b.asset_issuer === usdcIssuer ); if (trustline) { console.log("Balance:", trustline.balance); console.log("Limit:", trustline.limit); console.log("Authorized:", trustline.is_authorized); } ``` ## Stellar Asset Contract (SAC) SAC provides a smart-contract interface for Stellar Assets, enabling smart contract interactions. ### Deploy SAC for Existing Asset ```bash # Get the SAC address for an asset stellar contract asset deploy \ --asset USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN \ --source-account alice \ --network testnet ``` ### SAC Address Derivation ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; const asset = new StellarSdk.Asset("USDC", issuerPublicKey); const contractId = asset.contractId(StellarSdk.Networks.TESTNET); // Returns the deterministic SAC contract address ``` ### Using SAC in Smart Contracts ```rust use soroban_sdk::{token::Client as TokenClient, Address, Env}; pub fn transfer_asset( env: Env, from: Address, to: Address, asset_contract: Address, amount: i128, ) { from.require_auth(); // Use standard token interface let token = TokenClient::new(&env, &asset_contract); token.transfer(&from, &to, &amount); } ``` ### SAC vs Custom Token Interface SAC implements the standard SEP-41 token interface: - `balance(id: Address) -> i128` - `transfer(from: Address, to: Address, amount: i128)` - `approve(from: Address, spender: Address, amount: i128, expiration_ledger: u32)` - `allowance(from: Address, spender: Address) -> i128` - `decimals() -> u32` - `name() -> String` - `symbol() -> Symbol` ## When to Use What ### Use Stellar Assets When: - Standard fungible token (currency, stablecoin) - Need full ecosystem support (wallets, exchanges) - Regulatory compliance features (freeze, clawback) - Performance critical (classic operations are cheaper) - DEX integration via order book ### Use Custom Contract Tokens When: - Complex transfer logic (royalties, fees, restrictions) - Custom authorization schemes - Non-standard token behaviors - Integration with custom DeFi contracts - NFTs or semi-fungible tokens > Reach for a fully custom token as a last resort. Many of the cases above > (royalties, fees, transfer restrictions) can be built with a regular > issued Stellar asset plus a thin "SAC admin" contract that drives the > SAC's `mint`, `burn`, and `clawback` functions. That keeps the ecosystem > compatibility of a normal asset (DEX, anchors, wallets, trustlines) while > still layering on custom logic. Prefer a custom contract token only when > you need token behavior the SAC genuinely cannot express. > > **This runs in production today.** USDT0 > (`USDT0:GATISXX6BZ6NC7IKQBY37CJD4SOZL3CYZJWXEDG6JVIY4WBS6KXJHN6Q`) is a > classic asset whose SAC admin is a contract: a role-gated manager that > forwards `mint`, `clawback`, `set_authorized`, and `set_admin` to the SAC, > with a cross-chain bridge holding only the minter role. See > [setting a custom SAC admin](https://developers.stellar.org/docs/build/guides/tokens/custom-sac-admin) > for the pattern and `../cross-chain/layerzero.md` for that deployment. > > One hard prerequisite: **lock the issuer** (master key weight `0`). > Payments from a classic issuer are minting, so an unlocked issuer can mint > outside the contract and bypass the role model entirely. > > **Do it in this order: `set_admin` first, then lock the issuer.** A SAC's > admin starts as the issuer account, and only the *current* admin can > authorize the first `set_admin`. Lock the issuer before that call and the > admin stays the locked issuer forever, because nobody can sign the handover > ([SAC admin guide](https://developers.stellar.org/docs/build/guides/tokens/custom-sac-admin)). > After the handover the SAC still mints under the new admin, so lock the > issuer then. ### Use SAC When: - Need a Stellar asset inside a smart contract - Building DeFi protocols with existing assets - Bridge between classic and smart contract operations ## Querying Assets ### Get Account Balances ```typescript
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기