---
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.