@mysten/sui 2.16.3 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/dist/bcs/bcs.d.mts +6 -6
- package/dist/bcs/index.d.mts +20 -20
- package/dist/graphql/generated/queries.d.mts.map +1 -1
- package/dist/graphql/generated/queries.mjs.map +1 -1
- package/dist/graphql/generated/tada-env.d.mts +82 -1
- package/dist/grpc/client.d.mts +2 -0
- package/dist/grpc/client.d.mts.map +1 -1
- package/dist/grpc/client.mjs +2 -0
- package/dist/grpc/client.mjs.map +1 -1
- package/dist/grpc/proto/google/protobuf/struct.mjs +1 -1
- package/dist/grpc/proto/google/protobuf/struct.mjs.map +1 -1
- package/dist/grpc/proto/google/rpc/status.mjs +1 -1
- package/dist/grpc/proto/google/rpc/status.mjs.map +1 -1
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.d.mts +66 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.d.mts.map +1 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.mjs +48 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.mjs.map +1 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.d.mts +135 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.d.mts.map +1 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.mjs +144 -0
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.mjs.map +1 -0
- package/dist/grpc/proto/sui/rpc/v2/checkpoint.mjs +1 -1
- package/dist/grpc/proto/sui/rpc/v2/checkpoint.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/checkpoint_contents.mjs +3 -3
- package/dist/grpc/proto/sui/rpc/v2/checkpoint_contents.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/checkpoint_summary.mjs +3 -3
- package/dist/grpc/proto/sui/rpc/v2/checkpoint_summary.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/effects.d.mts +71 -3
- package/dist/grpc/proto/sui/rpc/v2/effects.d.mts.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/effects.mjs +71 -5
- package/dist/grpc/proto/sui/rpc/v2/effects.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/event.mjs +1 -1
- package/dist/grpc/proto/sui/rpc/v2/event.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/executed_transaction.mjs +2 -2
- package/dist/grpc/proto/sui/rpc/v2/executed_transaction.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/ledger_service.mjs +3 -3
- package/dist/grpc/proto/sui/rpc/v2/ledger_service.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/move_package.d.mts.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/move_package.mjs +13 -13
- package/dist/grpc/proto/sui/rpc/v2/move_package.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/move_package_service.mjs +1 -1
- package/dist/grpc/proto/sui/rpc/v2/move_package_service.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/object.d.mts.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/object.mjs +1 -1
- package/dist/grpc/proto/sui/rpc/v2/object.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/signature.mjs +3 -3
- package/dist/grpc/proto/sui/rpc/v2/signature.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.d.mts.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.mjs +1 -1
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/state_service.d.mts.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/state_service.mjs +3 -3
- package/dist/grpc/proto/sui/rpc/v2/state_service.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/system_state.mjs +2 -2
- package/dist/grpc/proto/sui/rpc/v2/system_state.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/transaction.d.mts.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/transaction.mjs +16 -16
- package/dist/grpc/proto/sui/rpc/v2/transaction.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.mjs +4 -4
- package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.mjs.map +1 -1
- package/dist/grpc/proto/types.d.mts +3 -2
- package/dist/grpc/proto/types.mjs +11 -1
- package/dist/grpc/proto/types.mjs.map +1 -1
- package/dist/jsonRpc/types/params.d.mts +5 -5
- package/dist/transactions/Transaction.d.mts +25 -11
- package/dist/transactions/Transaction.d.mts.map +1 -1
- package/dist/transactions/Transaction.mjs +8 -3
- package/dist/transactions/Transaction.mjs.map +1 -1
- package/dist/transactions/index.d.mts +2 -2
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/dist/zklogin/jwt-utils.d.mts.map +1 -1
- package/dist/zklogin/jwt-utils.mjs.map +1 -1
- package/dist/zklogin/utils.d.mts.map +1 -1
- package/dist/zklogin/utils.mjs +9 -0
- package/dist/zklogin/utils.mjs.map +1 -1
- package/docs/bcs.md +11 -6
- package/docs/clients/core.md +10 -9
- package/docs/clients/grpc.md +1 -1
- package/docs/index.md +176 -22
- package/docs/llms-index.md +6 -9
- package/docs/migrations/0.38.md +2 -2
- package/docs/migrations/sui-1.0.md +2 -2
- package/docs/migrations/sui-2.0/json-rpc-migration.md +76 -33
- package/docs/plugins.md +29 -5
- package/docs/transactions/basics.md +279 -0
- package/docs/transactions/coins-and-balances.md +293 -0
- package/docs/transactions/offline.md +192 -0
- package/docs/transactions/reference.md +380 -0
- package/docs/transactions/signing-and-execution.md +401 -0
- package/package.json +27 -27
- package/src/graphql/generated/queries.ts +46 -1
- package/src/graphql/generated/schema.graphql +49 -3
- package/src/graphql/generated/tada-env.ts +97 -1
- package/src/grpc/client.ts +3 -0
- package/src/grpc/proto/google/protobuf/struct.ts +1 -1
- package/src/grpc/proto/google/rpc/error_details.ts +4 -4
- package/src/grpc/proto/google/rpc/status.ts +1 -1
- package/src/grpc/proto/sui/forking/v1alpha/forking_service.client.ts +125 -0
- package/src/grpc/proto/sui/forking/v1alpha/forking_service.ts +230 -0
- package/src/grpc/proto/sui/rpc/v2/checkpoint.ts +1 -1
- package/src/grpc/proto/sui/rpc/v2/checkpoint_contents.ts +3 -3
- package/src/grpc/proto/sui/rpc/v2/checkpoint_summary.ts +3 -3
- package/src/grpc/proto/sui/rpc/v2/effects.ts +112 -6
- package/src/grpc/proto/sui/rpc/v2/event.ts +1 -1
- package/src/grpc/proto/sui/rpc/v2/executed_transaction.ts +2 -2
- package/src/grpc/proto/sui/rpc/v2/ledger_service.ts +3 -3
- package/src/grpc/proto/sui/rpc/v2/move_package.ts +19 -13
- package/src/grpc/proto/sui/rpc/v2/move_package_service.ts +1 -1
- package/src/grpc/proto/sui/rpc/v2/object.ts +7 -1
- package/src/grpc/proto/sui/rpc/v2/signature.ts +3 -3
- package/src/grpc/proto/sui/rpc/v2/signature_verification_service.ts +7 -1
- package/src/grpc/proto/sui/rpc/v2/state_service.ts +9 -3
- package/src/grpc/proto/sui/rpc/v2/system_state.ts +2 -2
- package/src/grpc/proto/sui/rpc/v2/transaction.ts +22 -16
- package/src/grpc/proto/sui/rpc/v2/transaction_execution_service.ts +4 -4
- package/src/grpc/proto/types.ts +1 -0
- package/src/jsonRpc/types/params.ts +5 -5
- package/src/transactions/Transaction.ts +36 -5
- package/src/transactions/index.ts +1 -0
- package/src/version.ts +1 -1
- package/src/zklogin/jwt-utils.ts +3 -0
- package/src/zklogin/utils.ts +17 -0
- package/docs/faucet.md +0 -26
- package/docs/hello-sui.md +0 -115
- package/docs/install.md +0 -61
- package/docs/transaction-building/basics.md +0 -299
- package/docs/transaction-building/gas.md +0 -61
- package/docs/transaction-building/intents.md +0 -62
- package/docs/transaction-building/offline.md +0 -73
- package/docs/transaction-building/sponsored-transactions.md +0 -22
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Coins and Balances
|
|
2
|
+
|
|
3
|
+
> Work with coin objects and address balances in transactions
|
|
4
|
+
|
|
5
|
+
Sui has two systems for holding fungible token balances: coin objects and address balances.
|
|
6
|
+
|
|
7
|
+
**Coin objects** are individual onchain objects, each with its own ID, version, and balance. You own
|
|
8
|
+
specific coin objects and need to split, merge, and track them.
|
|
9
|
+
|
|
10
|
+
**Address balances** are an accumulator per address per coin type. There are no objects to manage
|
|
11
|
+
because deposits automatically merge into a single balance, and you withdraw from it as needed.
|
|
12
|
+
|
|
13
|
+
Both systems coexist. An address's total balance for a given coin type is the sum of its coin object
|
|
14
|
+
balances and its address balance.
|
|
15
|
+
|
|
16
|
+
Address balances have no object versions. When a transaction has no versioned object inputs (for
|
|
17
|
+
example, a balance transfer built entirely from address balance withdrawals), it won't be
|
|
18
|
+
invalidated by other transactions from the same address executing concurrently.
|
|
19
|
+
|
|
20
|
+
## `tx.coin` and `tx.balance`
|
|
21
|
+
|
|
22
|
+
`tx.coin()` and `tx.balance()` are the recommended ways to get tokens in a transaction. They
|
|
23
|
+
automatically draw from both coin objects and address balances.
|
|
24
|
+
|
|
25
|
+
### Getting a `Coin`
|
|
26
|
+
|
|
27
|
+
`tx.coin()` produces a `Coin<T>`. Use it for transfers and most operations:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
|
|
31
|
+
const tx = new Transaction();
|
|
32
|
+
|
|
33
|
+
// SUI (balance is in MIST — 1 SUI = 1,000,000,000 MIST)
|
|
34
|
+
tx.transferObjects([tx.coin({ balance: 1_000_000_000n })], '0xRecipientAddress');
|
|
35
|
+
|
|
36
|
+
// Another coin type
|
|
37
|
+
tx.transferObjects(
|
|
38
|
+
[tx.coin({ balance: 1_000_000n, type: '0xPackageId::module::CoinType' })],
|
|
39
|
+
'0xRecipientAddress',
|
|
40
|
+
);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Getting a `Balance`
|
|
44
|
+
|
|
45
|
+
`tx.balance()` produces a `Balance<T>`. Use it for Move functions that expect a balance directly, or
|
|
46
|
+
for gasless transactions:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
const tx = new Transaction();
|
|
50
|
+
|
|
51
|
+
tx.moveCall({
|
|
52
|
+
target: '0xPackage::module::deposit',
|
|
53
|
+
arguments: [tx.object('0xPoolId'), tx.balance({ balance: 1_000_000_000n })],
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Sending to address balances
|
|
58
|
+
|
|
59
|
+
To deposit tokens into a recipient's address balance (instead of creating a coin object), use
|
|
60
|
+
`tx.balance()` with `balance::send_funds`:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const tx = new Transaction();
|
|
64
|
+
|
|
65
|
+
tx.moveCall({
|
|
66
|
+
target: '0x2::balance::send_funds',
|
|
67
|
+
typeArguments: ['0x2::sui::SUI'],
|
|
68
|
+
arguments: [tx.balance({ balance: 1_000_000_000n }), tx.pure.address('0xRecipientAddress')],
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> **Note:** Transactions built entirely from `tx.balance()` and gasless-eligible Move calls like `send_funds`
|
|
73
|
+
> and `redeem_funds` might qualify as [gasless transactions](#gasless-transactions).
|
|
74
|
+
|
|
75
|
+
### Options
|
|
76
|
+
|
|
77
|
+
| Option | Type | Default | Description |
|
|
78
|
+
| ------------ | ------------------ | ---------- | ------------------------------------------------------------------------ |
|
|
79
|
+
| `balance` | `bigint \| number` | _required_ | Amount in base units (MIST for SUI) |
|
|
80
|
+
| `type` | `string` | SUI | Coin type. Defaults to `0x2::sui::SUI` |
|
|
81
|
+
| `useGasCoin` | `boolean` | `true` | For SUI, split from the gas coin. Set `false` for sponsored transactions |
|
|
82
|
+
|
|
83
|
+
For SUI, `tx.coin()` splits from the gas coin by default. For sponsored transactions where the gas
|
|
84
|
+
coin belongs to the sponsor, set `useGasCoin: false`:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
tx.transferObjects([tx.coin({ balance: 100n, useGasCoin: false })], recipient);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `coinWithBalance`
|
|
91
|
+
|
|
92
|
+
`coinWithBalance()` is a standalone alias for `tx.coin()`:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
|
|
96
|
+
const tx = new Transaction();
|
|
97
|
+
tx.transferObjects([coinWithBalance({ balance: 1_000_000_000 })], recipient);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## How resolution works
|
|
101
|
+
|
|
102
|
+
When you call `tx.coin()` or `tx.balance()`, the SDK adds a placeholder intent to the transaction.
|
|
103
|
+
At build time, the resolver replaces it with concrete commands based on the sender's funds:
|
|
104
|
+
|
|
105
|
+
- **`tx.balance()` with sufficient address balance:** Uses a direct `FundsWithdrawal` through
|
|
106
|
+
`balance::redeem_funds`. No coin objects are used, so the transaction has no versioned object
|
|
107
|
+
inputs from this intent, keeping it eligible for parallel execution.
|
|
108
|
+
|
|
109
|
+
- **Otherwise, coins are needed:** Fetches the sender's coin objects and address balance in
|
|
110
|
+
parallel. Merges available coins (topping up from address balance if needed), then splits the
|
|
111
|
+
exact amounts. For `tx.balance()` intents, the split results are converted to `Balance<T>` through
|
|
112
|
+
`coin::into_balance`, and any remainder is returned to the sender's address balance through
|
|
113
|
+
`coin::send_funds`.
|
|
114
|
+
|
|
115
|
+
The resolver prefers address balances when possible to avoid introducing versioned object
|
|
116
|
+
dependencies.
|
|
117
|
+
|
|
118
|
+
Zero-balance requests resolve to `balance::zero` or `coin::zero` with no network lookups.
|
|
119
|
+
|
|
120
|
+
## Checking balances
|
|
121
|
+
|
|
122
|
+
Use `getBalance` to see both coin objects and address balance:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
const { balance } = await grpcClient.getBalance({
|
|
126
|
+
owner: '0xMyAddress',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
console.log(balance.balance); // total balance as string (coin objects + address balance)
|
|
130
|
+
console.log(balance.coinBalance); // balance from coin objects only
|
|
131
|
+
console.log(balance.addressBalance); // balance from address balance only
|
|
132
|
+
console.log(balance.coinType); // e.g. "0x2::sui::SUI"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
All balance values are returned as strings. Use `BigInt(balance.balance)` for arithmetic.
|
|
136
|
+
|
|
137
|
+
## Manual coin operations
|
|
138
|
+
|
|
139
|
+
For fine-grained control, you can split and merge coins manually.
|
|
140
|
+
|
|
141
|
+
### Splitting coins
|
|
142
|
+
|
|
143
|
+
`splitCoins` creates new coins from an existing coin:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const tx = new Transaction();
|
|
147
|
+
|
|
148
|
+
// Split specific amounts from a coin you own
|
|
149
|
+
const [coin1, coin2] = tx.splitCoins('0xMyCoinId', [1_000_000, 2_000_000]);
|
|
150
|
+
|
|
151
|
+
tx.transferObjects([coin1], '0xAlice');
|
|
152
|
+
tx.transferObjects([coin2], '0xBob');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Split the gas coin for SUI:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
const [coin] = tx.splitCoins(tx.gas, [1_000_000_000]);
|
|
159
|
+
tx.transferObjects([coin], '0xRecipientAddress');
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Merging coins
|
|
163
|
+
|
|
164
|
+
`mergeCoins` combines multiple coins into one:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
const tx = new Transaction();
|
|
168
|
+
|
|
169
|
+
tx.mergeCoins('0xCoin1', ['0xCoin2', '0xCoin3']);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Working with address balances directly
|
|
173
|
+
|
|
174
|
+
### Withdrawing from address balance
|
|
175
|
+
|
|
176
|
+
Create a withdrawal input and redeem it to get a `Coin<T>`:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
const tx = new Transaction();
|
|
180
|
+
|
|
181
|
+
const [coin] = tx.moveCall({
|
|
182
|
+
target: '0x2::coin::redeem_funds',
|
|
183
|
+
typeArguments: ['0x2::sui::SUI'],
|
|
184
|
+
arguments: [tx.withdrawal({ amount: 1_000_000_000 })],
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
tx.transferObjects([coin], '0xRecipientAddress');
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Or get a `Balance<T>` directly:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
const [balance] = tx.moveCall({
|
|
194
|
+
target: '0x2::balance::redeem_funds',
|
|
195
|
+
typeArguments: ['0x2::sui::SUI'],
|
|
196
|
+
arguments: [tx.withdrawal({ amount: 1_000_000_000 })],
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
For non-SUI coin types, pass the `type` parameter:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
const [coin] = tx.moveCall({
|
|
204
|
+
target: '0x2::coin::redeem_funds',
|
|
205
|
+
typeArguments: ['0xPackageId::module::USDC'],
|
|
206
|
+
arguments: [tx.withdrawal({ amount: 1_000_000, type: '0xPackageId::module::USDC' })],
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Depositing into address balances
|
|
211
|
+
|
|
212
|
+
Use `coin::send_funds` to deposit a coin into a recipient's address balance:
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
const tx = new Transaction();
|
|
216
|
+
|
|
217
|
+
tx.moveCall({
|
|
218
|
+
target: '0x2::coin::send_funds',
|
|
219
|
+
typeArguments: ['0x2::sui::SUI'],
|
|
220
|
+
arguments: [tx.object('0xMyCoinObjectId'), tx.pure.address('0xRecipientAddress')],
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Listing coin objects
|
|
225
|
+
|
|
226
|
+
Use `listCoins` to see specific coin objects:
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
const { objects, hasNextPage, cursor } = await grpcClient.listCoins({
|
|
230
|
+
owner: '0xMyAddress',
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
for (const coin of objects) {
|
|
234
|
+
console.log(coin.objectId, coin.balance);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Gasless transactions
|
|
239
|
+
|
|
240
|
+
Gasless transactions enable peer-to-peer payments of qualified stablecoins to execute without paying
|
|
241
|
+
gas fees in SUI. The sender does not need to hold SUI in their wallet. Gasless transactions are an
|
|
242
|
+
extension of
|
|
243
|
+
[address balances](https://docs.sui.io/onchain-finance/asset-custody/address-balances/using-address-balances),
|
|
244
|
+
built around `0x2::balance::send_funds` with `gasPrice = 0` and `gasBudget = 0` on the transaction.
|
|
245
|
+
See the
|
|
246
|
+
[Sui gasless stablecoin transfers guide](https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers)
|
|
247
|
+
for full details, including the current
|
|
248
|
+
[allowlist of eligible stablecoins](https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers#eligible-stablecoins).
|
|
249
|
+
|
|
250
|
+
Gasless transactions are limited to transactions that send funds as balances for specific
|
|
251
|
+
allowlisted stablecoins. Using `0x2::balance::send_funds` with `tx.balance()` is the recommended way
|
|
252
|
+
to build gasless transactions with the TypeScript SDK.
|
|
253
|
+
|
|
254
|
+
### SDK support (gRPC and GraphQL)
|
|
255
|
+
|
|
256
|
+
When using the gRPC or GraphQL transports, transactions that qualify are automatically detected, and
|
|
257
|
+
the gas price is set when the transaction is built.
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
|
|
261
|
+
const client = new SuiGrpcClient({ url: 'https://grpc.mainnet.sui.io:443' });
|
|
262
|
+
|
|
263
|
+
// Mainnet USDC. For the full set of allowlisted gasless stablecoins, see:
|
|
264
|
+
// https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers#eligible-stablecoins
|
|
265
|
+
const USDC = '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';
|
|
266
|
+
|
|
267
|
+
const tx = new Transaction();
|
|
268
|
+
tx.setSender(keypair.toSuiAddress());
|
|
269
|
+
|
|
270
|
+
tx.moveCall({
|
|
271
|
+
target: '0x2::balance::send_funds',
|
|
272
|
+
typeArguments: [USDC],
|
|
273
|
+
arguments: [tx.balance({ type: USDC, balance: 1_000_000 }), tx.pure.address(recipient)],
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const result = await client.signAndExecuteTransaction({
|
|
277
|
+
transaction: tx,
|
|
278
|
+
signer: keypair,
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The JSON-RPC transport does not automatically detect gasless eligibility. You can opt in by setting
|
|
283
|
+
the gas price to zero with `tx.setGasPrice(0)`, but only after confirming the coin type is on the
|
|
284
|
+
[allowlist](https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers#eligible-stablecoins)
|
|
285
|
+
and the PTB shape is eligible. Otherwise the transaction fails at validation.
|
|
286
|
+
|
|
287
|
+
> **Warning:** If your wallet builds transactions with gRPC but executes through a different transport (for
|
|
288
|
+
> example, dapp-kit wallets that still execute over JSON-RPC), pass the gRPC client to `build` so
|
|
289
|
+
> the gas price still gets set correctly:
|
|
290
|
+
>
|
|
291
|
+
> ```tsx
|
|
292
|
+
> const bytes = await tx.build({ client: grpcClient });
|
|
293
|
+
> ```
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Building Offline
|
|
2
|
+
|
|
3
|
+
> Build transactions without a network connection
|
|
4
|
+
|
|
5
|
+
Normally the SDK resolves object versions, estimates gas, and fills in other details by querying the
|
|
6
|
+
network. For offline building of backend services, air-gapped signing, or pre-built transactions,
|
|
7
|
+
you must provide this information yourself. See also the Sui documentation on
|
|
8
|
+
[offline signing](https://docs.sui.io/guides/developer/transactions/transaction-auth/offline-signing)
|
|
9
|
+
for the protocol-level details.
|
|
10
|
+
|
|
11
|
+
## Transactions without owned object inputs
|
|
12
|
+
|
|
13
|
+
When your transaction only uses shared objects, party objects, and/or address balance withdrawals,
|
|
14
|
+
there are no owned object versions to look up. Use `tx.withdrawal()` directly instead of `tx.coin()`
|
|
15
|
+
or `coinWithBalance` because they require a client to resolve coin objects at build time.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
|
|
19
|
+
const tx = new Transaction();
|
|
20
|
+
|
|
21
|
+
// Use tx.withdrawal() + coin::redeem_funds to withdraw from address balance.
|
|
22
|
+
// No coin object lookups needed — fully offline.
|
|
23
|
+
const [coin] = tx.moveCall({
|
|
24
|
+
target: '0x2::coin::redeem_funds',
|
|
25
|
+
typeArguments: ['0x2::sui::SUI'],
|
|
26
|
+
arguments: [tx.withdrawal({ amount: 1_000_000_000 })],
|
|
27
|
+
});
|
|
28
|
+
tx.transferObjects([coin], '0xRecipientAddress');
|
|
29
|
+
|
|
30
|
+
// Shared/party objects only need objectId + initialSharedVersion (both stable)
|
|
31
|
+
tx.moveCall({
|
|
32
|
+
target: '0xPackage::module::function',
|
|
33
|
+
arguments: [
|
|
34
|
+
tx.sharedObjectRef({
|
|
35
|
+
objectId: '0xSharedObjectId',
|
|
36
|
+
initialSharedVersion: '1',
|
|
37
|
+
mutable: true,
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Required configuration for all offline builds
|
|
43
|
+
tx.setSender('0xSenderAddress');
|
|
44
|
+
tx.setGasPrice(1000); // query getReferenceGasPrice() beforehand, or use a known value
|
|
45
|
+
tx.setGasBudget(50_000_000);
|
|
46
|
+
tx.setGasPayment([]); // empty array = pay gas from address balance
|
|
47
|
+
|
|
48
|
+
// Expiration is required when there are no owned objects for gas or inputs
|
|
49
|
+
tx.setExpiration({
|
|
50
|
+
ValidDuring: {
|
|
51
|
+
minEpoch: 100, // current epoch
|
|
52
|
+
maxEpoch: 101, // current epoch + 1
|
|
53
|
+
minTimestamp: null,
|
|
54
|
+
maxTimestamp: null,
|
|
55
|
+
chain: 'mainnet', // or 'testnet', 'devnet'
|
|
56
|
+
nonce: 0,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Build without a client
|
|
61
|
+
const bytes = await tx.build();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This enables fully stateless construction. You only need the sender address, reference gas price,
|
|
65
|
+
epoch, and chain identifier.
|
|
66
|
+
|
|
67
|
+
## Party objects
|
|
68
|
+
|
|
69
|
+
Party objects are address-owned but consensus-versioned, with per-address permissions. They are
|
|
70
|
+
referenced the same way as shared objects:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
tx.sharedObjectRef({
|
|
74
|
+
objectId: '0xPartyObjectId',
|
|
75
|
+
initialSharedVersion: '1',
|
|
76
|
+
mutable: true,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Key properties for offline building:
|
|
81
|
+
|
|
82
|
+
- **No version lookup needed**: `initialSharedVersion` is stable and set once when the object
|
|
83
|
+
becomes a party object
|
|
84
|
+
- **Enable pipelining**: Submit multiple transactions on the same party object without waiting for
|
|
85
|
+
each one to finalize
|
|
86
|
+
- **Cannot be used for gas**: Use address balance for gas payment (`setGasPayment([])`)
|
|
87
|
+
|
|
88
|
+
## Transactions with owned object inputs
|
|
89
|
+
|
|
90
|
+
When your transaction uses owned or immutable objects, you must provide the exact version and digest
|
|
91
|
+
for each one:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
|
|
95
|
+
const tx = new Transaction();
|
|
96
|
+
|
|
97
|
+
// Owned objects need exact version and digest
|
|
98
|
+
tx.transferObjects(
|
|
99
|
+
[
|
|
100
|
+
tx.objectRef({
|
|
101
|
+
objectId: '0xOwnedObjectId',
|
|
102
|
+
version: '42',
|
|
103
|
+
digest: 'abc123...',
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
'0xRecipientAddress',
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Receiving objects also need exact version and digest
|
|
110
|
+
tx.moveCall({
|
|
111
|
+
target: '0xPackage::module::receive',
|
|
112
|
+
arguments: [
|
|
113
|
+
tx.objectRef({
|
|
114
|
+
objectId: '0xParentId',
|
|
115
|
+
version: '10',
|
|
116
|
+
digest: 'def456...',
|
|
117
|
+
}),
|
|
118
|
+
tx.receivingRef({
|
|
119
|
+
objectId: '0xReceivingId',
|
|
120
|
+
version: '5',
|
|
121
|
+
digest: 'ghi789...',
|
|
122
|
+
}),
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Gas payment with specific coin objects
|
|
127
|
+
tx.setGasPayment([{ objectId: '0xGasCoinId', version: '3', digest: 'jkl012...' }]);
|
|
128
|
+
|
|
129
|
+
tx.setSender('0xSenderAddress');
|
|
130
|
+
tx.setGasPrice(1000);
|
|
131
|
+
tx.setGasBudget(50_000_000);
|
|
132
|
+
|
|
133
|
+
const bytes = await tx.build();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Required configuration for all offline builds
|
|
137
|
+
|
|
138
|
+
Every offline-built transaction must have the following:
|
|
139
|
+
|
|
140
|
+
| Method | Description |
|
|
141
|
+
| ----------------- | --------------------------------------------------------------- |
|
|
142
|
+
| `setSender()` | The address executing the transaction |
|
|
143
|
+
| `setGasPrice()` | Reference gas price (query `getReferenceGasPrice()` beforehand) |
|
|
144
|
+
| `setGasBudget()` | Maximum gas to spend (in MIST) |
|
|
145
|
+
| `setGasPayment()` | Coin object references, or `[]` for address balance |
|
|
146
|
+
|
|
147
|
+
### Expiration
|
|
148
|
+
|
|
149
|
+
Set an expiration when your transaction uses no owned objects for gas or inputs. This applies when
|
|
150
|
+
you use address balances for gas (`setGasPayment([])`) with only shared and party object inputs:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
tx.setExpiration({
|
|
154
|
+
ValidDuring: {
|
|
155
|
+
minEpoch: 100, // current epoch
|
|
156
|
+
maxEpoch: 101, // typically current epoch + 1
|
|
157
|
+
minTimestamp: null,
|
|
158
|
+
maxTimestamp: null,
|
|
159
|
+
chain: 'mainnet',
|
|
160
|
+
nonce: 0, // increment for multiple transactions in the same epoch
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
You can also use epoch-based expiration:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
tx.setExpiration({ Epoch: 100 });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
> **Note:** When building with a client, the SDK sets expiration automatically. You only need the manual
|
|
172
|
+
> configuration above for fully offline builds.
|
|
173
|
+
|
|
174
|
+
## Serialization
|
|
175
|
+
|
|
176
|
+
### Building to bytes
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Build to BCS bytes (Uint8Array) — fully offline, all data must be provided
|
|
180
|
+
const bytes = await tx.build();
|
|
181
|
+
|
|
182
|
+
// Build with a client — only makes network requests when there is unresolved data to look up
|
|
183
|
+
const bytes = await tx.build({ client: grpcClient });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Converting bytes back to a Transaction
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const tx = Transaction.from(bytes);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
This works with BCS byte arrays, base64-encoded strings, and JSON strings (from `toJSON()`).
|