|
| 1 | +# SDK Suggestions — Plugin Developer Wishlist |
| 2 | + |
| 3 | +Feedback from building 26+ plugins (183+ tools) against SDK v1.0.0. |
| 4 | +These suggestions target pain points shared across multiple plugins. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## Priority 1: High Impact |
| 9 | + |
| 10 | +### `sdk.ton.sendRaw(to, value, body, opts?)` |
| 11 | + |
| 12 | +**Problem**: Every plugin that interacts with a custom smart contract must duplicate ~30 lines of wallet boilerplate: read `wallet.json`, derive keypair, create `WalletContractV5R1`, get seqno, build internal message, sendTransfer with SendMode flags. This is repeated in gaspump, x1000, sbt, stormtrade, groypfi — any plugin with custom opcodes. |
| 13 | + |
| 14 | +**Proposal**: |
| 15 | +```js |
| 16 | +await sdk.ton.sendRaw(tokenAddress, "1.5", bodyCell, { |
| 17 | + bounce: true, // default true |
| 18 | + stateInit: null, // optional, for deploy |
| 19 | +}); |
| 20 | +// Returns: { seqno, walletAddress, hash? } |
| 21 | +``` |
| 22 | + |
| 23 | +- Signs automatically from the agent wallet |
| 24 | +- Manages seqno internally (prevents collisions on rapid calls) |
| 25 | +- Uses `SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS` by default |
| 26 | +- Accepts a Cell body (from `@ton/core`) |
| 27 | + |
| 28 | +**Plugins that benefit**: x1000, gaspump, sbt, stormtrade, groypfi, evaa — all 7 on-chain plugins. |
| 29 | + |
| 30 | +**Impact**: Eliminates `getAgentWallet()` pattern from every plugin. Wallet management stays centralized in the agent. Plugins only build the Cell body. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +### `sdk.ton.getAddressObject()` |
| 35 | + |
| 36 | +**Problem**: `sdk.ton.getAddress()` returns a string. But Cell builders need an `Address` object (from `@ton/core`) for `storeAddress()`. Plugins must `Address.parse(sdk.ton.getAddress())` or keep a separate wallet loading path just to get the native object. |
| 37 | + |
| 38 | +**Proposal**: |
| 39 | +```js |
| 40 | +const addr = sdk.ton.getAddressObject(); |
| 41 | +// Returns: Address instance from @ton/core |
| 42 | +// Can be used directly in beginCell().storeAddress(addr) |
| 43 | +``` |
| 44 | + |
| 45 | +**Plugins that benefit**: x1000, gaspump, sbt — any plugin that builds Cell bodies containing the agent's address. |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +### `sdk.ton.cell(fn)` / `sdk.ton.beginCell()` |
| 50 | + |
| 51 | +**Problem**: Every on-chain plugin needs `@ton/core`'s `beginCell()`. Since `@ton/core` is CJS, plugins must use the `createRequire(realpathSync(process.argv[1]))` workaround — 3 lines of boilerplate that are easy to get wrong and confusing for new plugin authors. |
| 52 | + |
| 53 | +**Proposal** (option A — expose builder): |
| 54 | +```js |
| 55 | +const body = sdk.ton.beginCell() |
| 56 | + .storeUint(0x94826557, 32) |
| 57 | + .storeUint(0, 64) |
| 58 | + .endCell(); |
| 59 | +``` |
| 60 | + |
| 61 | +**Proposal** (option B — callback helper): |
| 62 | +```js |
| 63 | +const body = sdk.ton.cell(b => b |
| 64 | + .storeUint(0x94826557, 32) |
| 65 | + .storeUint(0, 64) |
| 66 | +); |
| 67 | +``` |
| 68 | + |
| 69 | +Option A is simpler and more flexible (plugin authors already know the `beginCell()` API from TON docs). |
| 70 | + |
| 71 | +**Plugins that benefit**: All 7 on-chain plugins. Also makes the SDK self-contained — no `createRequire` needed for basic Cell building. |
| 72 | + |
| 73 | +--- |
| 74 | + |
| 75 | +## Priority 2: Medium Impact |
| 76 | + |
| 77 | +### `sdk.ton.sendRawBatch(messages[])` |
| 78 | + |
| 79 | +**Problem**: Some operations need multiple messages in one transaction (e.g., deploy + initial buy, or batch claims). Currently plugins build the messages array manually. |
| 80 | + |
| 81 | +**Proposal**: |
| 82 | +```js |
| 83 | +await sdk.ton.sendRawBatch([ |
| 84 | + { to: factoryAddr, value: "1.1", body: deployBody, bounce: false, stateInit }, |
| 85 | + { to: tokenAddr, value: "0.5", body: buyBody }, |
| 86 | +]); |
| 87 | +``` |
| 88 | + |
| 89 | +**Plugins that benefit**: gaspump (deploy + register), x1000 (potential future batch ops), stormtrade (multi-position). |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +### `sdk.ton.toNano(amount)` / `sdk.ton.fromNano(amount)` — already exists but... |
| 94 | + |
| 95 | +**Problem**: `sdk.ton.toNano()` exists but returns a string. On-chain Cell builders need `bigint` for `storeCoins()`. Plugins end up importing `toNano` from `@ton/ton` directly anyway. |
| 96 | + |
| 97 | +**Proposal**: Ensure `sdk.ton.toNano()` returns `bigint` (or add `sdk.ton.toNanoBigInt()`). |
| 98 | + |
| 99 | +--- |
| 100 | + |
| 101 | +### `sdk.ton.runGetMethod(address, method, args?)` |
| 102 | + |
| 103 | +**Problem**: Some plugins need to call GET methods on contracts (e.g., `get_jetton_data`, `get_wallet_address`, custom methods). Currently they must create their own `TonClient` instance. |
| 104 | + |
| 105 | +**Proposal**: |
| 106 | +```js |
| 107 | +const result = await sdk.ton.runGetMethod(contractAddr, "get_jetton_data"); |
| 108 | +// Returns: parsed stack (numbers, addresses, cells) |
| 109 | +``` |
| 110 | + |
| 111 | +**Plugins that benefit**: gaspump, x1000, giftindex (on-chain order book reads), any plugin querying contract state. |
| 112 | + |
| 113 | +--- |
| 114 | + |
| 115 | +## Priority 3: Nice to Have |
| 116 | + |
| 117 | +### `sdk.ton.waitForTransaction(address, seqno, opts?)` |
| 118 | + |
| 119 | +**Problem**: After sending a transaction, plugins tell users "check after ~15 seconds". There's no way to confirm the transaction landed. Every plugin repeats this pattern. |
| 120 | + |
| 121 | +**Proposal**: |
| 122 | +```js |
| 123 | +const confirmed = await sdk.ton.waitForTransaction(walletAddr, seqno, { |
| 124 | + timeout: 30000, // ms, default 60s |
| 125 | + interval: 3000, // polling interval |
| 126 | +}); |
| 127 | +// Returns: { success: boolean, hash?, lt? } |
| 128 | +``` |
| 129 | + |
| 130 | +**Plugins that benefit**: All on-chain plugins. Enables reliable "transaction confirmed" responses. |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +### `sdk.ton.estimateGas(to, value, body)` |
| 135 | + |
| 136 | +**Problem**: Plugins hardcode gas amounts (0.05, 0.1, 0.15 TON). These are estimates that might be too low or wasteful as the network evolves. |
| 137 | + |
| 138 | +**Proposal**: |
| 139 | +```js |
| 140 | +const gas = await sdk.ton.estimateGas(tokenAddr, "0", claimBody); |
| 141 | +// Returns: estimated TON needed (string) |
| 142 | +``` |
| 143 | + |
| 144 | +--- |
| 145 | + |
| 146 | +### `sdk.ton.constants` |
| 147 | + |
| 148 | +**Problem**: Plugins import `SendMode` from `@ton/core` just to use `SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS`. If `sendRaw` is implemented, this becomes less needed — but useful as a general reference. |
| 149 | + |
| 150 | +**Proposal**: |
| 151 | +```js |
| 152 | +sdk.ton.constants.SendMode.PAY_GAS_SEPARATELY // 1 |
| 153 | +sdk.ton.constants.SendMode.IGNORE_ERRORS // 2 |
| 154 | +sdk.ton.constants.JETTON_TRANSFER_OP // 0x0f8a7ea5 |
| 155 | +``` |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## Summary |
| 160 | + |
| 161 | +| Method | Priority | Plugins impacted | Boilerplate eliminated | |
| 162 | +|--------|----------|-----------------|----------------------| |
| 163 | +| `sendRaw()` | P1 | 7 (all on-chain) | ~30 lines per operation | |
| 164 | +| `getAddressObject()` | P1 | 3+ | `Address.parse()` calls | |
| 165 | +| `beginCell()` | P1 | 7 (all on-chain) | `createRequire` workaround | |
| 166 | +| `sendRawBatch()` | P2 | 3 | Manual message array building | |
| 167 | +| `toNano()` as bigint | P2 | 7 | Double import of toNano | |
| 168 | +| `runGetMethod()` | P2 | 4+ | Manual TonClient creation | |
| 169 | +| `waitForTransaction()` | P3 | 7 | "Check after 15s" pattern | |
| 170 | +| `estimateGas()` | P3 | 7 | Hardcoded gas amounts | |
| 171 | +| `constants` | P3 | 7 | SendMode/opcode imports | |
| 172 | + |
| 173 | +**The top 3 (`sendRaw` + `getAddressObject` + `beginCell`) together would eliminate the need for plugins to ever touch `wallet.json`, `createRequire`, or `@ton/core` imports directly.** Plugin deploy.js files would shrink from ~200 lines to ~50 lines. |
0 commit comments