Experten-Skill für Terra/Cosmos Blockchain SDK Entwicklung. Nutze diesen Skill wenn: - Smart Contract Wrapper implementiert werden - CosmJS Client-Code geschrieben wird - Wallet-Integration entwickelt wird - Blockchain-Transaktionen debuggt werden - Gas-Optimierung benötigt wird Trigger-Keywords: "contract", "CosmJS", "wallet", "transaction", "gas", "Terra", "LUNC", "blockchain"
Dieser Skill enthält Best Practices für die Entwicklung des Lumos Luna SDK.
@cosmjs/cosmwasm-stargate, @cosmjs/stargate, @cosmjs/proto-signing@terra-money/feather.js, @terra-money/terra.protoWICHTIG: Context7 für aktuelle CosmJS Patterns nutzen:
Context7: resolve-library-id → "cosmjs"
Context7: query-docs → "[specific topic]"
// Lazy singleton pattern für CosmJS Clients
private queryClient: CosmWasmClient | null = null;
private signingClient: SigningCosmWasmClient | null = null;
async getQueryClient(): Promise<CosmWasmClient> {
if (!this.queryClient) {
this.queryClient = await CosmWasmClient.connect(this.rpcEndpoint);
}
return this.queryClient;
}
async getSigningClient(): Promise<SigningCosmWasmClient> {
if (!this.signingClient) {
if (!this.signer) throw new WalletNotConnectedError();
this.signingClient = await SigningCosmWasmClient.connectWithSigner(
this.rpcEndpoint,
this.signer,
{ gasPrice: GasPrice.fromString('0.015uluna') }
);
}
return this.signingClient;
}
disconnect(): void {
this.queryClient?.disconnect();
this.signingClient?.disconnect();
this.queryClient = null;
this.signingClient = null;
}
/**
* Query contract state - kostenlos, keine Signatur nötig
*/
async getStats(): Promise<ContractStats> {
const client = await this.getQueryClient();
const result = await client.queryContractSmart(
this.contractAddress,
{ get_stats: {} } // Rust: QueryMsg::GetStats {}
);
// Type-safe transformation
return {
totalBurned: result.total_burned,
proofCount: result.proof_count,
lastUpdate: new Date(result.last_update * 1000),
};
}
/**
* Execute contract method - kostet Gas
*/
async burn(amount: string, memo?: string): Promise<BurnResult> {
const client = await this.getSigningClient();
// Simuliere zuerst für Gas-Schätzung
const msg = { burn: {} };
const funds = [{ denom: 'uluna', amount }];
const gasEstimate = await client.simulate(
this.senderAddress,
[{
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: MsgExecuteContract.fromPartial({
sender: this.senderAddress,
contract: this.contractAddress,
msg: Buffer.from(JSON.stringify(msg)),
funds,
}),
}],
memo
);
// Execute mit 30% Buffer
const result = await client.execute(
this.senderAddress,
this.contractAddress,
msg,
{
amount: [{ denom: 'uluna', amount: String(Math.ceil(gasEstimate * 0.015 * 1.3)) }],
gas: String(Math.ceil(gasEstimate * 1.3)),
},
memo,
funds
);
return {
txHash: result.transactionHash,
gasUsed: result.gasUsed,
events: this.parseEvents(result.events),
};
}
// Custom Error Hierarchy
export class LumosError extends Error {
constructor(message: string, public code: string, public details?: unknown) {
super(message);
this.name = 'LumosError';
}
}
export class WalletNotConnectedError extends LumosError {
constructor() {
super('Wallet not connected. Call connect() first.', 'WALLET_NOT_CONNECTED');
}
}
export class InsufficientFundsError extends LumosError {
constructor(required: string, available: string) {
super(
`Insufficient funds: need ${required} uluna, have ${available} uluna`,
'INSUFFICIENT_FUNDS',
{ required, available }
);
}
}
export class ContractError extends LumosError {
constructor(message: string, txHash?: string) {
super(message, 'CONTRACT_ERROR', { txHash });
}
}
// Usage
try {
const result = await client.execute(...);
} catch (error) {
if (error.message.includes('insufficient funds')) {
throw new InsufficientFundsError(amount, balance);
}
if (error.message.includes('unauthorized')) {
throw new ContractError('Not authorized to perform this action');
}
throw new LumosError('Transaction failed', 'TX_FAILED', error);
}
// Types sollten Rust Contract Types spiegeln
export interface BurnProof {
id: number;
burner: string;
amount: string;
timestamp: number;
txHash: string;
}
export interface ContractStats {
totalBurned: string;
proofCount: number;
lastUpdate: Date;
}
// Config Types
export interface SDKConfig {
lcd?: string;
rpc?: string;
chainId?: string;
contracts?: {
proofOfBurn?: string;
negativeStaking?: string;
stableVault?: string;
};
}
| Network | Chain ID | RPC | LCD |
|---|---|---|---|
| Mainnet | columbus-5 | terra-classic-rpc.publicnode.com | terra-classic-lcd.publicnode.com |
| Testnet | rebel-2 | terra-classic-testnet-rpc.publicnode.com | terra-classic-testnet-lcd.publicnode.com |
| Contract | Mainnet Address |
|---|---|
| Proof-of-Burn | terra188fdkkvf54er3t6e9zgz6v24d7scykv3lqflpqr8xn063ff387uq0ch3d0 |
| Negative-Staking | terra13kz7rzdpad9hxdkzn4dln2kfcxy8c7pe0lcwfu8ue9n2dlqqlscqnegaws |
| Stable-Vault | terra1g6pyt9gz3efumwpdvtspg9phge0kuqk4vhugdpv3906yvfkapv6q4s7rvj |
/add-primitive — Neuen Contract Wrapper erstellen/test-contract — Contract gegen Network testen/debug-tx — Transaktionen debuggen