--- title: write sidebar_position: 5 description: "Typed contract.write namespace that signs and broadcasts a contract's state-changing functions, resolving to the transaction id string." keywords: ["write", "contract", "state-changing function", "send transaction", "typed abi", "tronweb"] --- # write > Added in **6.4.0**. `contract.write` is a typed, ergonomic namespace that exposes a contract's **state-changing** functions. A write is signed (with the instance's default key, or with a private key supplied via the `account` option), broadcast, and resolves to the **transaction id (`string`)**. Read-only (`view` / `pure`) functions live in the [`read`](./read.md) namespace instead. When the ABI is declared `as const`, function names and argument types are checked at **compile time**. This is the recommended alternative to the flat [`.send()`](./method.send.md) surface. :::note Every namespace method is **async**. All validation (argument count, missing signer, account, call value, …) surfaces as a **promise rejection**, never a synchronous throw — always `await` the call. ::: ## Usage ```ts // Signature (each method on the namespace) contract.write.(args: unknown[], options?: WriteOptions): Promise // returns the txID contract.write.(options?: WriteOptions): Promise // for a no-argument method type WriteOptions = { account?: string; value?: number | bigint; feeLimit?: number; tokenId?: string; tokenValue?: number; permissionId?: number; }; ``` ```ts const abi = [...] as const; const contract = tronWeb.contract(abi, 'contractAddress'); // resolves to the transaction id const txID = await contract.write.methodName([arg0, arg1, ...], options); ``` The first positional argument is the **array of method arguments**. For a method that takes no arguments you may pass nothing, or pass the options object directly. ## Parameters | Parameter | Description | Type | | --------- | ----------- | ---- | | args | Array of the method's arguments, in ABI order. Addresses may be base58 or hex. Omit for a no-arg method. | `unknown[]` | | options | Optional. Call options (see below). | `object` | **`write` options** | Option | Description | Type | | ------ | ----------- | ---- | | account | A **private key** whose derived address owns and signs the transaction — lets you issue the write from a non-default signer (even when the instance has no default key). Must be a 64-hex private key (optional `0x`); any other value rejects. Treat it as secret. When omitted, the instance's default key signs. | string | | value | TRX (in **sun**) attached to the call (`callValue`). | number \| bigint | | feeLimit | Maximum energy fee, in sun. Defaults to `tronWeb.feeLimit`. | number | | tokenId | TRC10 token id to send with the call. | string | | tokenValue | TRC10 token amount to send. | number | | permissionId | Permission id for multi-signature. | number | :::caution The `account` option is a **private key**, not an address — its derived address becomes the transaction owner/signer, so one instance can issue writes from different signers. Keep the value secret and never expose it client-side. When omitted, the instance's default key signs. ::: ## Returns `Promise` — the broadcast transaction id. ## Selector form (overloads) Each function is additionally exposed under its **full selector**, e.g. `contract.write['transfer(address,uint256)']`. The selector form pins that exact overload, so same-arity overloads can be addressed unambiguously: ```ts const txID = await contract.write['transfer(address,uint256)'](['TYYY...', 1], { feeLimit: 90_000_000 }); ``` When two overloads share the same arity, the bare name cannot pick one and the call rejects with `Ambiguous overloaded function "" ...` — use the selector key to disambiguate. :::tip The selector key is a function's **canonical signature** — its name followed by the parenthesized input types in canonical form (bare `uint` / `int` widen to `uint256` / `int256`, tuples flatten to `(type,…)`; `trcToken` is kept as-is per the TVM convention). Rather than hand-writing it, derive it from an ABI fragment with `tronWeb.utils.abi.buildFunctionSelector` — it returns this exact string, the same value the namespace registers internally: ```ts const fragment = abi.find((f) => f.name === 'transfer'); const key = tronWeb.utils.abi.buildFunctionSelector(fragment); // 'transfer(address,uint256)' await contract.write[key](['TYYY...', 1], { feeLimit: 90_000_000 }); ``` ::: :::note Namespaces enumerate each method under **both** its bare name **and** its selector, so `Object.keys(contract.write)` returns two entries per function: ```ts Object.keys(contract.write); // ['transfer', 'transfer(address,uint256)'] ``` ::: ## Functions named `write` If the ABI literally defines a function called `write`, it stays callable through the legacy flat surface, and the namespace is served from the **same** object — so both work: ```ts await contract.write(5).send(); // legacy flat call → txID await contract.write.write([7], { feeLimit: 90_000_000 }); // namespace entry → txID ``` ## Validation & errors All of the following reject the returned promise: | Condition | Error message | | --------- | ------------- | | Wrong number of arguments | `Contract function "" expects argument(s) but received .` | | No signer configured | `Method "" modifies state and requires a signer. Set a private key or default address on the TronWeb instance.` | | `account` is set but is not a valid private key | `The "account" option must be a private key.` | | Ambiguous same-arity overload (bare name) | `Ambiguous overloaded function "" for the given arguments; pass the full signature ...` | | Calling `write` on a `view` / `pure` function | `Function "" is read-only.` | | Invalid `value` | `call value cannot be negative` / `call value must be an integer` / `call value exceeds safe integer range` / `call value must be a number or bigint` | ## Example ```ts const abi = [ { type: 'function', name: 'transfer', stateMutability: 'nonpayable', inputs: [ { name: 'recipient', type: 'address' }, { name: 'amount', type: 'uint256' }, ], outputs: [], }, ] as const; async function demo() { const contract = tronWeb.contract(abi, 'TContractAddress...'); // signed with the instance's default key; resolves to the txID const txID = await contract.write.transfer(['TRecipient...', 100], { feeLimit: 100_000_000 }); console.log(txID); // output-start '5e3b3...e0c9' // output-end // …or sign with a specific private key — its derived address owns the transaction const txID2 = await contract.write.transfer(['TRecipient...', 100], { account: signerPrivateKey, feeLimit: 100_000_000 }); } demo(); ``` See also the [`read`](./read.md) namespace and the flat [`send()`](./method.send.md) surface.