// TS wrapper for the price-aware-vault example contract. // // Demonstrates the canonical wrapper shape: deploy + send/get for each // receiver + a typed reply for each getter. Use as a starting point for // your own Phoebe consumer wrapper. import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode, } from '@ton/core'; import { type PriceLeaf, priceLeafToCell, } from '@titon-network/phoebe-sdk'; const OP_DEPOSIT = 0x10; const OP_REQUEST_WITHDRAW = 0x11; export type PriceAwareVaultInitConfig = { owner: Address; /** The Phoebe address this vault trusts for FulfillPrice callbacks. Pinned at deploy. */ phoebe: Address; }; export function priceAwareVaultConfigToCell(cfg: PriceAwareVaultInitConfig): Cell { return beginCell() .storeAddress(cfg.owner) .storeAddress(cfg.phoebe) .storeUint(0, 64) // nextQueryId .storeBit(false) // pending: empty map .endCell(); } export type SendDepositOpts = { value: bigint }; export type SendRequestWithdrawOpts = { value: bigint; amount: bigint; feedId: number; proof: Cell; leaf: PriceLeaf; /** 0 = no staleness check; non-zero = reject if cached snapshot older than this many seconds. */ maxStaleness?: number; }; export type PendingReply = { /** Depositor address; `null` when no pending withdrawal exists for the queryId. */ depositor: Address | null; amount: bigint; }; export class PriceAwareVault implements Contract { constructor( readonly address: Address, readonly init?: { code: Cell; data: Cell }, ) {} static createFromAddress(address: Address): PriceAwareVault { return new PriceAwareVault(address); } static createFromConfig( cfg: PriceAwareVaultInitConfig, code: Cell, workchain = 0, ): PriceAwareVault { const data = priceAwareVaultConfigToCell(cfg); const init = { code, data }; return new PriceAwareVault(contractAddress(workchain, init), init); } async sendDeploy(provider: ContractProvider, via: Sender, value: bigint): Promise { await provider.internal(via, { value, bounce: false, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().endCell(), }); } async sendDeposit(provider: ContractProvider, via: Sender, opts: SendDepositOpts): Promise { await provider.internal(via, { value: opts.value, bounce: true, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().storeUint(OP_DEPOSIT, 32).endCell(), }); } async sendRequestWithdraw( provider: ContractProvider, via: Sender, opts: SendRequestWithdrawOpts, ): Promise { // Body shape: opcode | amount | feedId | proofRef | leaf-inline | maxStaleness // Mirrors `struct (0x11) RequestWithdraw` in price-aware-vault.tolk. const leafCell = priceLeafToCell(opts.leaf); const body = beginCell() .storeUint(OP_REQUEST_WITHDRAW, 32) .storeCoins(opts.amount) .storeUint(opts.feedId, 16) .storeRef(opts.proof) .storeSlice(leafCell.beginParse()) .storeUint(opts.maxStaleness ?? 0, 32) .endCell(); await provider.internal(via, { value: opts.value, bounce: true, sendMode: SendMode.PAY_GAS_SEPARATELY, body, }); } async getOwner(provider: ContractProvider): Promise
{ const res = await provider.get('owner', []); return res.stack.readAddress(); } async getPhoebe(provider: ContractProvider): Promise
{ const res = await provider.get('phoebe', []); return res.stack.readAddress(); } async getNextQueryId(provider: ContractProvider): Promise { const res = await provider.get('nextQueryId', []); return res.stack.readBigNumber(); } /** Returns the depositor + amount for a pending withdrawal, or `(null, 0n)` if none. */ async getPending(provider: ContractProvider, queryId: bigint): Promise { const res = await provider.get('pending', [{ type: 'int', value: queryId }]); const depositor = res.stack.readAddressOpt(); const amount = res.stack.readBigNumber(); return { depositor, amount }; } }