CustomFragment gift withdrawal
CustomFragment replaces the old local-only completion page behind
payments.getStarGiftWithdrawalUrl with a self-hosted TON mainnet mint flow.
The RPC's existing SRP/2FA check remains the gate: its returned withdrawal URL
is a short-lived bearer capability. Username minting is not implemented.
Contracts
The upstream TON reference implementation requested for new collections is
vendored at contracts/nft-v1.1. It is the
ton-blockchain/acton-contracts nft-v1.1 package (including its wrappers,
scripts, and tests), with only a local Acton manifest pinned to 1.1.0 so it
can be built reproducibly alongside gramsrv.
cd contracts/nft-v1.1
acton build NftCollection
acton test tests
That reference collection deliberately accepts deployment messages only from its admin address. The already deployed InvGram Gifts collection remains the signed-mint compatibility collection for existing withdrawal URLs; it is not replaced in place (TON contract addresses and storage layouts are immutable). A new upstream collection can be deployed for a future relayer flow after its admin wallet and collection address are configured.
The contracts in contracts/customfragment are an idiomatic typed Tolk rewrite
of the minting subset of Telegram's GPL-3.0 TeleMint contracts. They use a new
storage layout and deterministic item address scheme, so they are not address-
compatible with the original FunC contracts.
cd contracts/customfragment
acton build
acton test --coverage --coverage-format text
acton wrapper CustomFragmentCollection
acton wrapper CustomFragmentItem
Deploy CustomFragmentItem, then deploy CustomFragmentCollection with:
subwalletId: the configured uint32 (default1196573005, ASCIIGRAM);publicKey: the Ed25519 public key printed by telesrv on startup;itemCode: the compiled CustomFragment item code;- collection metadata and royalty cells for the deployment.
Deployment is intentionally an operator action: telesrv will never deploy a
mainnet contract or generate/replace the mint authority key automatically.
The included deploy-mainnet Acton script deploys the collection from the
global customfragment-deployer wallet and takes the authority seed only from
CUSTOM_FRAGMENT_SIGNING_SEED.
Server configuration
Set the TELESRV_CUSTOM_FRAGMENT_* variables documented in .env.example and
enable the public Web listener. The collection address must be a TON basechain
mainnet address. Keep the signing key outside the repository with restrictive
filesystem permissions.
Set TELESRV_CUSTOM_FRAGMENT_PUBLIC_BASE_URL to a dedicated HTTPS host served
by the same Web listener. It must differ from the native client's internal link
host; otherwise Android may parse /gift-withdrawal/{token} as a username link
instead of opening the browser.
The browser sends a wallet-bound, five-minute authorization to TON Connect. The
server does not accept the wallet callback as proof. It derives the expected NFT
address with get_nft_address_by_index, reads get_nft_data through proof-
checking mainnet lite servers, and atomically marks the gift exported only when
the index, collection and owner all match.
The claim path uses the same fail-closed compare-and-swap rule: the database owner observed when a TON Proof challenge is issued must still be the owner when the single-use challenge is committed. A concurrent ownership sync or second claim therefore returns a conflict instead of overwriting a newer projection.
Public routes:
/custom-fragment— service and collection information;/gift-withdrawal/{token}— private mint page returned by the TL method;/custom-fragment/tonconnect-manifest.json— TON Connect manifest;/custom-fragment/collection.json— TEP-64 metadata for InvGram Gifts;/custom-fragment/metadata/gift/{slug}.json— TEP-64 metadata;/custom-fragment/media/gift/{slug}.png— rendered gift poster with its backdrop, tiled pattern, and model rest frame;/custom-fragment/media/gift/{slug}.lottie.json— the selected model's raw Lottie JSON, cached in memory and suitable for client-side animation.
The public Web listener also exposes /botfather and /stickers as local
Telegram-style mini apps. They use only relative /api/miniapps/* requests;
creation is delegated to the existing bot/files services and requires signed
Telegram initData. A newly generated bot token is returned once over the
server response and is not retained by the page.
The media routes and all metadata URLs use
TELESRV_CUSTOM_FRAGMENT_PUBLIC_BASE_URL, so CustomFragment can live on a
dedicated HTTPS host while the main server keeps its normal public URL.